【问题标题】:Android: restore RecyclerView item positions after app re-opensAndroid:应用重新打开后恢复 RecyclerView 项目位置
【发布时间】:2018-09-05 03:54:41
【问题描述】:

我有一个 CardView 项目的 RecyclerView 列表。我将 CardView 数据保存到 SQLite 数据库。用户可以在列表中上下拖动 CardView 来改变项目的顺序。当用户退出应用程序时,我想保存 RecyclerView 项目的当前顺序。然后当用户重新打开应用程序时,我想恢复 RecyclerView 项目的确切顺序。

我根据其他 SO 帖子尝试了多种方法,但没有运气:

--How to save RecyclerView's scroll position using RecyclerView.State?

--RecyclerView store / restore state between activities

--How to save scroll position of RecyclerView in Android?

我每次重新打开应用程序时得到的是基于 CardView 原始时间戳的默认顺序。它在列表顶部显示最新的 CardView 项,递减到最旧的 CardView 的最后一项。

这是我的代码:

public class MainActivity extends AppCompatActivity {

    private ArrayList<ListItem> allList;
    private RecyclerView mRecyclerView;
    private SQLiteDB sqLiteDB;
    private MyRecylerAdapter adapter;
    private static final String KEY_RECYCLER_STATE = "recycler_state";
    private Parcelable recyclerViewState;

    protected void onCreate(Bundle savedInstanceState) {

        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        allList = new ArrayList<>();
        allList.clear();
        allList = sqLiteDB.getAllDBItems();
        adapter = new MyRecylerAdapter(this, allList);
        mRecyclerView.setAdapter(adapter);
    }

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

        outState.putParcelable(KEY_RECYCLER_STATE, mRecyclerView.getLayoutManager().onSaveInstanceState());     
    }

    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        recyclerViewState = savedInstanceState.getParcelable(KEY_RECYCLER_STATE);
    }    

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

        if (mRecyclerView !=null) {
            mRecyclerView.getLayoutManager().onRestoreInstanceState(recyclerViewState);
        }
    }
}    

public class SQLiteDB extends SQLiteOpenHelper {

    ...
    public ArrayList<ListItem> getAllDBItems() {
    ArrayList<ListItem> modelList = new ArrayList<>();

    SQLiteDatabase db = getReadableDatabase();        

    String[] columns = {
            ItemContract.ItemEntry.A,
            ItemContract.ItemEntry.B,
            ItemContract.ItemEntry.C,
            ItemContract.ItemEntry.D,
            ItemContract.ItemEntry.E,
            ItemContract.ItemEntry.F,
            ItemContract.ItemEntry.G,
            ItemContract.ItemEntry.H,
            ItemContract.ItemEntry.I,
            ItemContract.ItemEntry.J
    };

    Cursor getCursor = db.query(
            TABLE_NAME, 
            columns, 
            null,    
            null,   
            null,   
            null,   
            null    
    );

    try {
        if (getCursor.getCount() > 0) {
            getCursor.moveToFirst(); 

            while (!getCursor.isAfterLast()) {

                do { 
                    ListItem listItem = new ListItem();
                    listItem.setId(Integer.parseInt(getCursor.getString(getCursor.getColumnIndex(A))));
                    listItem.setType(getCursor.getString(getCursor.getColumnIndex(B)));
                    listItem.setTypeColor(Integer.parseInt(getCursor.getString(getCursor.getColumnIndex(C))));
                    listItem.setTodo(getCursor.getString(getCursor.getColumnIndex(D)));
                    listItem.setNote1(getCursor.getString(getCursor.getColumnIndex(E)));
                    listItem.setNote2(getCursor.getString(getCursor.getColumnIndex(F)));
                    listItem.setDuedate(getCursor.getString(getCursor.getColumnIndex(G)));
                    listItem.setDuetime(getCursor.getString(getCursor.getColumnIndex(H))); listItem.setTimestamp(Long.parseLong(getCursor.getString(getCursor.getColumnIndex(I))));
                    listItem.setRandint(Integer.parseInt(getCursor.getString(getCursor.getColumnIndex(J))));
                  modelList.add(0,listItem);
                } while (getCursor.moveToNext());
            }
        }
    } finally {
        if (getCursor != null && !getCursor.isClosed()) {
            getCursor.close();
        }
    } if(db.isOpen()) {
        db.close();
    }
    return modelList;
} 

public class ListItem {

    private int _id;
    private int _sortorder;

    public void setSortorder(int sortorder) {
    _sortorder = sortorder;

    } 
}

【问题讨论】:

  • 顺序是你的状态,所以让适配器(它知道数据集的位置)在退出应用程序时将其持久化,然后让它在启动时检索它...使用共享首选项来存储订单,例如按商品 ID
  • 所以使用共享首选项来存储项目的适配器位置。那么我如何在启动时检索而不运行将加载新的 RecyclerView 列表的所有 onCreate()?
  • 在您的活动 onResume() 中,您将调用 myAdapter.refreshDataset(),然后您将执行类似 mDataset = retrieveDataset(); notifyDataSetChanged(); ...请注意,您的 retrieveDataset() 可以应用不同的策略,例如仅在 order 不同时执行某些操作 ... order 状态,我将使用 id 数组表示它,例如 ' [11, 22, 33, 44]' 或'[11, 44, 22, 33]'。注意:一旦您将新排序的数据集作为 Item[] 检索到适配器,然后通知,适配器将完成 UI 的所有其余繁重工作。

标签: android android-recyclerview linearlayoutmanager


【解决方案1】:

在您的数据库表中添加一个名为“SortOrder”的字段,并在每次添加新行时增加此列中的值。 (您可以使用MAX 函数来确保该值始终是当前最高值的下一个)

检索项目时按此列排序。

当您将卡片拖放到新位置后,请使用新的排序顺序/位置更新该字段。

【讨论】:

  • 我喜欢。我是否在从适配器提交的“排序顺序”中获取位置保存?使用 getAdapterPosition?
  • 取决于您如何实现拖放。你必须知道你要下降的位置?不要忘记更新其他项目,因为它们的位置也可能会根据您拖放到/从的位置而改变。像这个库这样的库为您提供了一个可以使用的“OnItemDragFinished”回调。 github.com/h6ah4i/android-advancedrecyclerview
  • 我将 onMove() 与 ItemTouchHelper.SimpleCallback 对象一起使用,以便更新 RecyclerView 的适配器位置。如果我正确地考虑了这一点,那么我还将更新数据库中的 SortOrder 字段,然后在重新打开应用程序时检索项目时可以使用该字段。听起来我在正确的轨道上?
  • 听起来不错。
  • 这取决于你如何做。就个人而言,我会在拖动操作完成后立即更新该字段,但您的方法似乎与我所做的非常不同。我在这里整理了一个快速示例项目,大致展示了我自己将如何处理它:github.com/kuffs/Recyclerview-Drag-Demo
猜你喜欢
  • 2018-02-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多