我正在研究一个实现的简单apod应用程序:
-
RecyclerView
-
CardView
-
Firebase
-
Picasso
该应用程序捕获Firebase
和Firebase Storage
中的图像和文本,在CardView
中显示它们,并将OnClickListener
设置为每个View
。当用户单击图像时,我通过Intent
打开新的Activity
。第二个Activity
显示原始单击图像以及有关它的更多信息。
我已经使用GridLayoutManager
、1列实现了这一切,如果用户的手机是垂直的,如果用户的手机是水平的3列。
我遇到的问题是,我似乎无法保存RecyclerView
在方向更改上的位置。我已经尝试了我可以找到的每个选项,但似乎没有任何选择。我唯一可以得出的结论是,在旋转时,我正在销毁Firebase
的ChildEventListener
以避免内存泄漏,并且一旦取向完成,Firebase
由于ChildEventListener
的新实例而重新征服数据库。
有什么办法可以保存RecyclerView
在方向更改上的位置?我不想要android:configChanges
,因为它不会让我更改布局,并且我已经尝试保存为Parcelable
,这是不成功的。我敢肯定,这很容易我缺少,但是,我是新手开发的东西。对我的代码的任何帮助或建议都非常感谢。谢谢!
以下是我的课程,我只缩短了必要的代码。
MainActivity
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();
}
}
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);
}
}
}
编辑:
我终于能够使用多个因素进行这项工作。如果将其中的任何一个遗漏了,它根本无法使用。
在我的OnCreate
中创建新的GridLayoutManager
gridLayoutManager = new GridLayoutManager(getApplicationContext(), columns);
在Onpause
中保存回收库状态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);
}
在AndroidManifest.xml
configChanges
保存和恢复RecyclerView
状态还不够。我还必须违反谷物,然后将我的AndroidManifest.xml
更改为'Android:configchanges =&quot'entrientation | screensize&quort''
<activity
android:name=".MainActivity"
android:configChanges="orientation|screenSize">
使用Handler
onConfigurationChanged
中还原RecyclerView
状态处理程序是最重要的部分之一,如果不包括在内,它根本不起作用,RecyclerView
的位置将重置为0。我猜这是一个缺陷,从来没有固定的。然后,我根据方向更改了GridLayoutManager
的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);
}
就像我说的那样,至少对我来说,所有这些更改都需要包括在内。我不知道这是否是最有效的方法,但是花了很多小时,它对我有用。
,而旋转活动被破坏并重新销毁 - 再次创建,这意味着您的所有对象都被破坏和重新创建,并再次进行布局。
您想停止重新创建活动,您可以使用 android:configchanges ,但这是不良练习,也不是正确的方法。
现在唯一剩下的就是在旋转前保存回收库的位置,并在重新创建活动后将其保存。
。//保存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"));
}
}
希望这些对您有帮助。
实际上,当活动重新创建活动时,有更好的方法可以恢复回收器视图位置,这是通过使用回收器查看布局管理器选项以存储和还原最后一个位置(最后一个回收器视图布局状态)。看看这个教程。
用几个单词,您需要在相应的活动方法中调用布局管理器方法OnSaveInstanceState()和OnrestoreInstancestate(包裹状态)。最后,在使用数据填充适配器后,您必须立即恢复布局管理器状态。
例如,如果您在LinearlayOutManager的实现中查看,则您将看到此类如何管理状态(Store/Restore)
i最后做了。没有黑客或更改subtest.xml。经过长时间的阅读,我找到了关键:
因此,如果您在每个屏幕旋转上不同步加载数据然后将其张贴到主线程中,通过循环队列,您始终会因失去以前的滚动位置而最终。实际上,任何
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-posity-restoring-done-done-right-cff1e2104ac7
每次有方向更改时都会重新创建活动。因此,您必须将位置保存在捆绑包中,然后在将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()链接,以获取有关如何还原回收器视图状态的更多选项。