在使用Firebase和ChildEventListener时,如何在Orientation更改后保留RecyclerView的位置?

Dre*_*rko 23 android firebase firebase-realtime-database

我正在开发一个简单的APOD应用程序,它可以实现:

  • RecyclerView
  • CardView
  • Firebase
  • Picasso

该应用程序从中抓取图像和文本,Firebase并将其Firebase Storage显示在a中CardView,并OnClickListener为每个设置View.当用户点击图片时,我打开一个新ActivityIntent.第二个Activity显示原始单击的图像,以及有关它的更多信息.

GridLayoutManager如果用户的手机是VERTICAL,我已经使用1列实现了这一切,如果用户的手机是HORIZONTAL则使用3列.

我遇到的问题是我似乎无法挽救RecyclerView方向改变的立场.我已经尝试了我能找到的每一个选项,但似乎都没有.唯一的结论,我可以想出,是在旋转,我毁坏FirebaseChildEventListener,以避免内存泄漏,一旦定位完成后,Firebase再查询,因为新实例的数据库ChildEventListener.

有什么方法可以挽救我RecyclerView对方向改变的立场吗?我不想要,android:configChanges因为它不会让我改变我的布局,我已经尝试过保存为一个Parcelable,这是不成功的.我确信这很容易让我失踪,但是,嘿,我是新手.我非常感谢您对我的代码的任何帮助或建议.谢谢!

下面是我的课程,我只简化了必要的代码.

主要活动

public class MainActivity extends AppCompatActivity {

private RecyclerAdapter mRecyclerAdapter;
private DatabaseReference mDatabaseReference;
private RecyclerView mRecyclerView;
private Query query;


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
    setContentView(R.layout.activity_main);
    getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    final int columns = getResources().getInteger(R.integer.gallery_columns);

    mDatabaseReference = FirebaseDatabase.getInstance().getReference();
    query = mDatabaseReference.child("apod").orderByChild("date");

    mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
    mRecyclerView.setHasFixedSize(true);
    mRecyclerView.setLayoutManager(new GridLayoutManager(this, columns));


    mRecyclerAdapter = new RecyclerAdapter(this, query);
    mRecyclerView.setAdapter(mRecyclerAdapter);


  }

@Override
public void onDestroy() {
    mRecyclerAdapter.cleanupListener();
  }

}
Run Code Online (Sandbox Code Playgroud)

RecyclerAdapter

public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ApodViewHolder> {

private final Context mContext;
private final ChildEventListener mChildEventListener;
private final Query mDatabaseReference;
private final List<String> apodListIds = new ArrayList<>();
private final List<Apod> apodList = new ArrayList<>();

public RecyclerAdapter(final Context context, Query ref) {
    mContext = context;
    mDatabaseReference = ref;


    ChildEventListener childEventListener = new ChildEventListener() {


        @Override
        public void onChildAdded(DataSnapshot dataSnapshot, String s) {
            int oldListSize = getItemCount();
            Apod apod = dataSnapshot.getValue(Apod.class);

            //Add data and IDs to the list
            apodListIds.add(dataSnapshot.getKey());
            apodList.add(apod);

            //Update the RecyclerView
            notifyItemInserted(oldListSize - getItemCount() - 1);
        }

        @Override
        public void onChildChanged(DataSnapshot dataSnapshot, String s) {

        }

        @Override
        public void onChildRemoved(DataSnapshot dataSnapshot) {
            String apodKey = dataSnapshot.getKey();
            int apodIndex = apodListIds.indexOf(apodKey);

            if (apodIndex > -1) {

                // Remove data and IDs from the list
                apodListIds.remove(apodIndex);
                apodList.remove(apodIndex);

                // Update the RecyclerView
                notifyDataSetChanged();
            }
        }

        @Override
        public void onChildMoved(DataSnapshot dataSnapshot, String s) {

        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }

    };
    ref.addChildEventListener(childEventListener);
    mChildEventListener = childEventListener;

 }

   @Override
    public int getItemCount() {
    return apodList.size();
}
    public void cleanupListener() {
    if (mChildEventListener != null) {
        mDatabaseReference.removeEventListener(mChildEventListener);
    }
  }

}
Run Code Online (Sandbox Code Playgroud)

jit*_*555 7

旋转活动被破坏并再次重新创建,这意味着所有对象都被销毁并重新创建,并且布局也会重新绘制.

你想停止重新创建你可以使用android:configChanges的活动,但这是不好的做法,也不是正确的方法.

现在唯一剩下的就是在轮换之前保存recyclerview的位置,并在重新创建活动之后获取它.

//保存recyclerview的位置

 @Override
    public void onSaveInstanceState(Bundle savedInstanceState) {
      // Save UI state changes to the savedInstanceState.
      // This bundle will be passed to onCreate if the process is
      // killed and restarted.
      savedInstanceState.putInt("position", mRecyclerView.getAdapterPosition()); // get current recycle view position here.
      super.onSaveInstanceState(savedInstanceState);
    }
Run Code Online (Sandbox Code Playgroud)

//恢复价值

    @Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
    setContentView(R.layout.activity_main);
    getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    final int columns = getResources().getInteger(R.integer.gallery_columns);

    mDatabaseReference = FirebaseDatabase.getInstance().getReference();
    query = mDatabaseReference.child("apod").orderByChild("date");

    mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
    mRecyclerView.setHasFixedSize(true);
    mRecyclerView.setLayoutManager(new GridLayoutManager(this, columns));


    mRecyclerAdapter = new RecyclerAdapter(this, query);
    mRecyclerView.setAdapter(mRecyclerAdapter);
   if(savedInstanceState != null){
     // scroll to existing position which exist before rotation.
     mRecyclerView.scrollToPosition(savedInstanceState.getInt("position"));
   }  

  }
Run Code Online (Sandbox Code Playgroud)

希望这些能帮到你.

  • 没有方法`RecyclerView.getAdapterPosition()` (4认同)

Dre*_*rko 6

编辑:

我终于能够使用多种因素来完成这项工作。如果其中任何一项被遗漏,它就根本行不通。

GridLayoutManager在我的 onCreate 中新建

gridLayoutManager = new GridLayoutManager(getApplicationContext(), columns);
Run Code Online (Sandbox Code Playgroud)

在 onPause 中保存 RecyclerView 状态

private final String KEY_RECYCLER_STATE = "recycler_state"; 
private static Bundle mBundleRecyclerViewState;
private Parcelable mListState = null;`


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

    mBundleRecyclerViewState = new Bundle();
    mListState = mRecyclerView.getLayoutManager().onSaveInstanceState();
    mBundleRecyclerViewState.putParcelable(KEY_RECYCLER_STATE, mListState);


}
Run Code Online (Sandbox Code Playgroud)

包括configChangesAndroidManifest.xml

保存和恢复RecyclerView状态是不够的。我也不得不违背AndroidManifest.xml常规并将我的更改为 'android:configChanges="orientation|screenSize"'

<activity
        android:name=".MainActivity"
        android:configChanges="orientation|screenSize"
        >
Run Code Online (Sandbox Code Playgroud)

恢复RecyclerView状态onConfigurationChanged使用Handler

处理程序是最重要的部分之一,如果不包含它,它将无法工作,并且RecyclerView将重置为 0的位置。我想这是几年前的一个缺陷,并且从未修复过。然后我根据方向更改了GridLayoutManager's setSpanCount

    @Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    columns = getResources().getInteger(R.integer.gallery_columns);

    if (mBundleRecyclerViewState != null) {
        new Handler().postDelayed(new Runnable() {

            @Override
            public void run() {
                mListState = mBundleRecyclerViewState.getParcelable(KEY_RECYCLER_STATE);
                mRecyclerView.getLayoutManager().onRestoreInstanceState(mListState);

            }
        }, 50);
    }

    // Checks the orientation of the screen
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {

        gridLayoutManager.setSpanCount(columns);

    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {

        gridLayoutManager.setSpanCount(columns);

    }
    mRecyclerView.setLayoutManager(gridLayoutManager);
}
Run Code Online (Sandbox Code Playgroud)

就像我说的,所有这些变化都需要包括在内,至少对我来说是这样。我不知道这是否是最有效的方法,但在花了很多小时后,它对我有用。