使用RecyclerView展示Firebase資料庫與雲端儲存的相片

使用RecyclerView展示Firebase資料庫與雲端儲存的相片

當我們在APP中設計了新增雲端相片功能,並將使用者想分享的資料儲存在雲端資料庫,再以雲端方式上傳手機中的相片至Firebase Storage的雲端儲存後,現在是時候將雲端資料以Android 5.0開始提供的RecyclerView清單元件展示了。

Firebase上的資料

Database

A7423

Storage

A7424

設計RecyclerView

在MainActivity中加入一個RecyclerView,設計一客製化layout展示每一個雲端相片,最後使用FirebaseRecyclerAdapter設定並展示。

加入RecyclerView

content_main.xml中加入CustomView

A7426

選擇RecyclerView

A7425

content_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="com.hank.firephoto.MainActivity"
    tools:showIn="@layout/activity_main">
    <android.support.v7.widget.RecyclerView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/recycler"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />
</RelativeLayout>

取得RecyclerView屬性

MainActivity.java

public class MainActivity extends AppCompatActivity implements FirebaseAuth.AuthStateListener {
    private RecyclerView recyclerView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        recyclerView = (RecyclerView) findViewById(R.id.recycler);
        recyclerView.setHasFixedSize(true);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
    }
}

請將onAuthStateChanged中的user物件也提昇成為屬性,並在取得user屬性後執行新設計的setupRecyclerView()方法:

@Override
public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
    Log.d(TAG, "onAuthStateChanged");
    user = firebaseAuth.getCurrentUser();
    if (user != null) {
        Log.d(TAG, "Uid:" + user.getUid());
        Log.d(TAG, "Email:" + user.getEmail());
        Log.d(TAG, "DisplayName:" + user.getDisplayName());
        getSharedPreferences(getString(R.string.pref_name), MODE_PRIVATE)
                .edit()
                .putString(getString(R.string.pref_account), user.getUid())
                .apply();
        DatabaseReference ref = FirebaseDatabase.getInstance()
                .getReference("users").child(user.getUid());
        ref.child("email").setValue(user.getEmail());
        ref.child("displayname").setValue(user.getDisplayName());
        setupRecyclerView();
    } else {

    }
}
private void setupRecyclerView() {
    DatabaseReference picRef = FirebaseDatabase.getInstance()
            .getReference("users").child(user.getUid()).child("pics");
}

在setupRecyclerView方法中,先取得資料庫中的users/UserID/pics參照。

Photo類別設計

設計與pics參照內資料屬性相同的JavaBean類別「Photo」:

A7427

public class Photo {
    String title;
    String imageUrl;
    String content;
    //setter與getter方法
}

在setupRecyclerView方法中接著呼叫limitToLast方法取得最後10筆資料,並設定ValueEventListener:

private void setupRecyclerView() {
    DatabaseReference picRef = FirebaseDatabase.getInstance()
            .getReference("users").child(user.getUid()).child("pics");
    picRef.limitToLast(10).addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            for (DataSnapshot msgSnapshot : dataSnapshot.getChildren()) {
                Photo photo = msgSnapshot.getValue(Photo.class);
                Log.i("Photo's Title:", photo.getTitle());
            }
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {
            Log.e("Photo", "failed: " + databaseError.getMessage());
        }
    });
}

使用這個方式雖然可以取得每個相片物件的資料,但也得要自行設計合適的Adapter類別,並自行撰寫相對應的方法,以產出正確的View物件。

使用FirebaseRecyclerAdapter

在此,我將使用另一種方式,只需要設計一個RecyclerView.ViewHolder類別,再使用FirebaseRecyclerAdapter,即可快速完成原本繁雜的設計工作。

設計單列資料版面 res/layout/row_photo.xml

使用RelativeLayout:

A7429

配置

A7430

原始碼:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent">
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/row_photo_image"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:src="@drawable/no_picture" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="Title"
        android:id="@+id/row_photo_title"
        android:layout_alignParentTop="true"
        android:layout_toRightOf="@+id/row_photo_image"
        android:layout_toEndOf="@+id/row_photo_image"
        android:layout_marginTop="34dp" />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:text="content"
        android:id="@+id/row_photo_content"
        android:layout_below="@+id/row_photo_title"
        android:layout_toRightOf="@+id/row_photo_image"
        android:layout_toEndOf="@+id/row_photo_image"
        android:layout_marginTop="36dp" />
</RelativeLayout>

或是使用LinearLayout的版本:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="wrap_content"
    android:orientation="horizontal">
    <ImageView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:id="@+id/row_photo_image"
        android:src="@drawable/no_picture"
        android:layout_weight="3" />
    <LinearLayout
        android:orientation="vertical"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:layout_marginLeft="10dp">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceLarge"
            android:text="Title"
            android:id="@+id/row_photo_title"
            android:layout_marginTop="10dp" />
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textAppearance="?android:attr/textAppearanceMedium"
            android:text="content"
            android:id="@+id/row_photo_content"
            android:layout_marginTop="10dp" />
    </LinearLayout>
</LinearLayout>

預覽圖:

A7432

導入Glide類別庫

build.gradle (Module:app)

 

dependencies {
    ...
    compile 'com.github.bumptech.glide:glide:3.7.0'
}
[Sync Project]

設計ViewHoler內部類別

在MainActivity內設計一PhotoViewHolder類別,代表一列資料的元件格式定義:

static class PhotoViewHolder extends RecyclerView.ViewHolder {
    ImageView image;
    TextView title;
    TextView content;

    public PhotoViewHolder(View itemView) {
        super(itemView);
        image = (ImageView) itemView.findViewById(R.id.row_photo_image);
        title = (TextView) itemView.findViewById(R.id.row_photo_title);
        content = (TextView) itemView.findViewById(R.id.row_photo_content);
    }

    public void setPhoto(Photo photo) {
        title.setText(photo.getTitle());
        content.setText(photo.getContent());
        Glide.with(image.getContext())
                .load(photo.getImageUrl())
                .into(image);
    }
}

在PhotoViewHolder中,我多設計了一個setPhoto方法,好讓未來能夠設定它內部資料。

最後萬事具備,只欠東風了,產生FirebaseRecyclerAdapter:

FirebaseRecyclerAdapter<Photo, PhotoViewHolder> adapter =
   new FirebaseRecyclerAdapter<Photo, PhotoViewHolder>(Photo.class, R.layout.row_photo, PhotoViewHolder.class, picRef) {
      @Override
      protected void populateViewHolder(PhotoViewHolder viewHolder, Photo model, int position) {
         viewHolder.setPhoto(model);
      }
};
recyclerView.setAdapter(adapter);

執行結果:

A7431

[版權聲明]

本文章版權為湯秉翰所有,授權範圍僅限綠豆湯網站使用,除Facebook之類社群等未更改本文章出處之分享行為不在此限,其他個人或公司未經作者同意,不得任意將本文章內容轉載至其他網站,或以任何形式重製,為以免觸犯著作權法,請尊重作者之智慧財產權。

相關文章:

Hank Tom

專長為程式語言、雲端服務開發,Linux系統管理, 任職:利拓科技 技術長,海林行動科技 技術總監 輔仁大學 兼任助理教授 , 為 Android高效入門>深度學習、CentOS 7建置、管理與伺服器架設實戰、Java網路程式設計、雲端網頁程式設計-Google App Engine應用實作 等書作者

This Post Has 4 Comments

  1. Avatar

    Hank
    I try to use the above program for practicing purpose in Android studio.
    Both “FirebaseRecyclerAdapter” will become to red color in studio.
    Is there any thing I missed?
    Steve
    “FirebaseRecyclerAdapter adapter =
    new FirebaseRecyclerAdapter(Photo.class, R.layout.row_photo, PhotoViewHolder.class, picRef)”

  2. Avatar

    Thank you so much
    I have been spending lots of time searching the way to merge firebase with recyclerview
    this gives me inspiration on that
    thank you again from hk

    1. Avatar

      It’s good to know that it helped, thanks for your feedback

發佈留言

×
×

Cart