如何保持FirebaseRecyclerAdapter的滚动位置

时间:2017-07-31 20:02:44

标签: android firebase android-recyclerview recycler-adapter firebaseui

当我从另一个活动返回相同的活动时,我正在努力保持FirebaseRecyclerAdapter的滚动位置。

这是我设置FirebaseRecyclerAdapter

的函数
public void setTheScreen(){
   FirebaseRecyclerAdapter<Post,PostViewHolder> firebaseRecyclerAdapter = new FirebaseRecyclerAdapter<Post,PostViewHolder>(
           Post.class,
           R.layout.post_display_blueprint,
           PostViewHolder.class,
           postsNode
   ) {
       @Override
       public void onDataChanged() {
           if(dialog != null && dialog.isShowing()){
               dialog.dismiss();
           }
       }

       @Override
       protected void populateViewHolder(final PostViewHolder viewHolder, Post model, int position) {
           final String key = getRef(position).getKey();
           mRefPosts = FirebaseDatabase.getInstance().getReference().child("posts").child(key);
           mLoveNode = mRef.child("loves");
           mRefLoves = mRef.child("loves").child(key);
           viewHolder.setTitle(model.getTitle());
           viewHolder.setLoves(mRefLoves);
           viewHolder.setLoveButton(Uid,key);
           viewHolder.setImage(getApplicationContext(),model.getImageUrl());


           mRefPosts.addValueEventListener(new ValueEventListener() {
               @Override
               public void onDataChange(DataSnapshot dataSnapshot) {

                   String isAnonymous = dataSnapshot.child("anonymous").getValue(String.class);
                   if (isAnonymous != null && isAnonymous.equals("true")) {
                       viewHolder.anonymousButton.setVisibility(View.VISIBLE);
                   }

               }
               @Override
               public void onCancelled(DatabaseError databaseError) {

               }
           });


            viewHolder.mView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    Intent intent = new Intent(MainActivity.this,PostDisplayPageActivity.class);
                    intent.putExtra("Key",key);
                    startActivity(intent);

                }
            });
       }
   };
   mRecyclerView.setAdapter(firebaseRecyclerAdapter);
    firebaseRecyclerAdapter.notifyDataSetChanged();

}

通过点击适配器创建的任何视图,我将从我的 MainActivity 开始一个名为 PostDisplayPageActivity 的新活动,然后按下我正在完成 PostDisplayPageActivity 并返回 MainActivity

当我回到MainActivity时,列表从0位置开始

我在onPause和onResume方法中使用了以下代码,但它无效。

 @Override
protected void onPause() {
    super.onPause();

    currentVisiblePosition = ((LinearLayoutManager)mRecyclerView.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
    Toast.makeText(MainActivity.this,String.valueOf(currentVisiblePosition),Toast.LENGTH_SHORT).show();

}

@Override
protected void onResume() {
    super.onResume();

    setTheScreen();
    mRecyclerView.getLayoutManager().scrollToPosition(currentVisiblePosition);
    Toast.makeText(MainActivity.this,String.valueOf(currentVisiblePosition),Toast.LENGTH_SHORT).show();
    currentVisiblePosition = 0;


}

从那些Toasts我得到正确的位置,但视图总是从位置0开始。

我的RecyclerView我使用LinearLayoutManager作为 mRecyclerView.setLayoutManager(mLinearLayoutManager);

有人请告诉我我做错了什么或建议我采取不同的方式来做这件事。

2 个答案:

答案 0 :(得分:1)

我用一个更简单的解决方案解决了我的问题。我只是叫adapter.startListening();在onViewCreated()而不是onStart中,并称为adapter.stopListening();。在onDestroyView()中而不是onStop()中 这阻止了整个列表在下一次活动返回时重新生成,因此将滚动位置保留在以前的位置。

答案 1 :(得分:0)

遇到同样的问题,我发现了许多对我不起作用的建议。因此,我想出了一个解决方法来解决恢复 Firebase RecyclerView 状态/滚动位置的问题。

在实现 instanceStateFirebaseRecycler 的 Activity/Fragment 中保留 RecyclerView Firebase RecyclerView 的实例状态字段:

private Parcelable instanceStateFirebaseRecycler;

首先,保存 Firebase RecyclerView 的实例状态 instanceStateFirebaseRecycler,它保存滚动位置信息,onPause()

@Override
public void onPause() {
    saveScrollPosition();
    super.onPause();
}

其中 saveScrollPosition() 方法很简单:

private void saveScrollPosition(){
    instanceStateFirebaseRecycler = firebaseRecyclerView.getLayoutManager().onSaveInstanceState();
}

接下来是棘手的部分,您向 Firebase Adapter 添加一个侦听器,它会在适配器准备就绪时通知您,即当 getItemCount() 不为零时。在侦听器的回调中,您恢复回收器的状态/滚动位置:

firebaseAdapter = new FirebaseAdapter(options, new FirebaseAdapter.OnRestoreRecyclerStateListener() {
        @Override
        public void onRestoreRecyclerState() {
            restoreScrollPosition();
        }
    });
    firebaseRecyclerView.setAdapter(firebaseAdapter);

自定义 interface 的位置:

public interface OnRestoreRecyclerStateListener {
    void onRestoreRecyclerState();
}

并且在 callback Firebase Adapter 中触发 onBindViewHolder(我没有找到更好的地方)以确保适配器项目准备就绪:

@Override
protected void onBindViewHolder(@NonNull ViewHolder holder, int position, @NonNull MyModel model) {
    ...
    onRestoreRecyclerStateListener.onRestoreRecyclerState();
}

,以及 restoreScrollPosition() 方法所在的位置:

private void restoreScrollPosition(){
    if (instanceStateFirebaseRecycler == null) return;
    firebaseRecyclerView.getRootView().setVisibility(View.GONE);
    final Handler handler = new Handler(Looper.getMainLooper());
    handler.postDelayed(new Runnable() {
        @Override
        public void run() {
            if (instanceStateFirebaseRecycler == null) return;
            firebaseRecyclerView.getLayoutManager().onRestoreInstanceState(instanceStateFirebaseRecycler);
            instanceStateFirebaseRecycler = null;
            firebaseRecyclerView.getRootView().setVisibility(View.VISIBLE);
        }
    }, 100);
}

restoreScrollPosition() 方法中,我检查 instanceStateFirebaseRecycler == null 以避免不必要的运行。此外,我通过 Handler 延迟实例状态恢复,因为 items 尚未绘制,很快就会绘制。如果没有延迟地调用 onRestoreInstanceState,状态将不会按预期恢复。此外,我通过 RecyclerView 切换 setVisibility() 的可见性以隐藏丑陋的项目重新加载和快速滚动,用户可以将其解释为故障。

顺便说一下,@Sanju 建议的解决方案在流量和电池效率方面看起来有点危险,因为用户可以将正在运行的应用程序长时间置于后台,而监听器一直处于活动状态。