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

By | 2016-08-29

當我們在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之類社群等未更改本文章出處之分享行為不在此限,其他個人或公司未經作者同意,不得任意將本文章內容轉載至其他網站,或以任何形式重製,為以免觸犯著作權法,請尊重作者之智慧財產權。

Category: Android Firebase 標籤: , , ,
Avatar

About Hank Tom

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

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

  1. AvatarSteve

    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)”

    Reply
  2. AvatarBoo

    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

    Reply

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *