如何在方向更改后保留RecyclerView的位置,同时使用Firebase& ChildEventListener?

时间:2017-02-28 16:29:30

标签: android firebase firebase-realtime-database

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

  • RecyclerView
  • CardView
  • Firebase
  • Picasso

该应用从FirebaseFirebase Storage抓取图片和文字,在CardView中显示,并为OnClickListener设置View。当用户点击图片时,我会通过Activity打开一个新的Intent。第二个Activity显示原始点击图片,以及有关它的更多信息。

如果用户的手机是垂直的话,我使用GridLayoutManager,1列实现了这一切,如果用户的手机是HORIZONTAL,则使用3列。

我遇到的问题是我似乎无法保存RecyclerView在方向更改方面的立场。我尝试过每一个我能找到的选项,但似乎都没有。我能想出的唯一结论是,在轮换时,我会摧毁Firebase ChildEventListener以避免内存泄漏,并且一旦方向完成,{{1由于Firebase的新实例,重新查询数据库。

有什么方法可以保存我ChildEventListener在定位方面的立场?我不想RecyclerView,因为它不会让我改变我的布局,而且我已经尝试保存为android:configChanges,这是不成功的。我确信这件事很容易让我失踪,但是嘿,我是开发新手。我非常感谢您对我的代码的任何帮助或建议。谢谢!

以下是我的课程,我只修改了必要的代码。

MainActivity

Parcelable

RecyclerAdapter

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();
  }

}

5 个答案:

答案 0 :(得分: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);
    }

//恢复值

    @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"));
   }  

  }

希望这些可以帮助你。

答案 1 :(得分:2)

修改

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

在我的onCreate中创建新的gridLayoutManager = new GridLayoutManager(getApplicationContext(), columns);

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


}

将RecyclerView状态保存在onPause

configChanges

AndroidManifest.xml

中加入RecyclerView

保存和恢复AndroidManifest.xml状态还不够。我也不得不反对这个问题并将我的<activity android:name=".MainActivity" android:configChanges="orientation|screenSize" > 改为&#39; android:configChanges =&#34; orientation | screenSize&#34;&#39;

RecyclerView

使用onConfigurationChanged

恢复Handler中的RecyclerView

处理程序是最重要的部分之一,如果它不包括在内,它根本不起作用,GridLayoutManager的位置将重置为0.我想这是一个缺陷回来几年,从未修复过。然后,我根据方向更改了setSpanCount&#39; @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); }

{{1}}

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

答案 2 :(得分:0)

每次有方向更改时都会重新创建活动。因此,您必须保存包中的位置,然后在第二次调用onCreate()时重新使用它。

@Override
public void onCreate(Bundle savedInstanceState) {

   super.onCreate(savedInstanceState);

   int recyclerViewPosition;
   if(savedInstanceState != null){
     recyclerViewPosition = savedInstanceState.getInt(RECYCLER_VIEW_POSITION);
   }

   mRecyclerView.scrollToPosition(recyclerViewPosition);

}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    //save the current recyclerview position 
    outState.putInt(RECYCLER_VIEW_POSITION, mRecyclerView.getAdapterPosition());
}

您可能需要查看RecyclerView.State()link,了解有关如何恢复回收站视图状态的更多选项。

答案 3 :(得分:0)

实际上,在重新创建活动时,还有更好的方法可以恢复回收站视图位置,这是通过使用回收站视图布局管理器选项来存储和恢复最后一个位置(最后一个回收站视图布局状态)。看看这个tutorial

用几句话,您需要在相应的活动方法中调用布局管理器方法onSaveInstanceState()和onRestoreInstanceState(Parcelable状态)。最后,您必须在使用数据填充适配器后立即恢复布局管理器状态。

例如,如果您查看LinearLayoutManager的实现,您将看到此类如何管理状态(存储/恢复)

答案 4 :(得分:0)

我最终做到了。没有hacks或manifest.xml中的更改。 经过长时间的阅读,我发现了重点:

因此,如果您在每次屏幕旋转时异步加载数据 然后通过Looper队列将其发布到主线程, 最终总是会失去先前的滚动位置。事实上, 任何

handler.post(this :: setListItems);

也会破坏它,因为此操作将发布到 排队并在列表开始布局后投放。

因此,如果要恢复回收者视图状态,则必须使用处理程序或缓存数据。这是代码示例(androidx片段)。

private static String STATE = "STATE";
    
    // the recycler view manager
    private GridLayoutManager layoutManager;
    
    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        // save recycler view layout
        if(layoutManager != null) {
            outState.putParcelable(STATE, layoutManager.onSaveInstanceState());
        }
    }
    
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
                             
        ...
        

        loadDataAsync(savedInstanceState);
        
    
        return view;
    }
    
    private void loadDataAsync(Bundle savedInstanceState){
    
        repository.getAll(new IMyListener()){
                @Override
                public void getAll(List<String> listResult) {
                        reyclerViewFilterableAdapter.setItems(listResult);
                        reyclerViewFilterableAdapter.notifyDataSetChanged();
                        new Handler().postDelayed(new Runnable() {

                            @Override
                            public void run() {
                                if (layoutManager != null) {
                                    layoutManager.onRestoreInstanceState(savedInstanceState.getParcelable(STATE));
                                }
                            }
                        }, 100);
                }
            });
    
    }

来源:https://medium.com/@dimezis/android-scroll-position-restoring-done-right-cff1e2104ac7