1. 程式人生 > >Android開發筆記 ViewPager 巢狀 RecyclerView instantiateItem資料初始化錯位問題

Android開發筆記 ViewPager 巢狀 RecyclerView instantiateItem資料初始化錯位問題

       在應用開發過程中,使用ViewPager巢狀RecyclerView實現整屏的橫滑是很常見的需求。在為ViewPager設定adapter的時候,需要重寫初始化方法,
   public Object instantiateItem(ViewGroup container, int position) {}來告訴ViewPager對應每個position展示什麼View,在這個方法中將
position對應的view  add進container,返回該view即可。
            @Override
            public Object instantiateItem(ViewGroup container, int position) {
                View view = LayoutInflater.from(getApplicationContext()).inflate(R.layout.detail, null);
                recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
                
                ViewGroup parent = (ViewGroup) view.getParent();
                if (parent != null) {
                    parent.removeView(view);
                }
                container.addView(view);

                if (mPresenter == null) {
                    initPresenter();
                }
                mPresenter.updateRecyclerView(pagerDataList.get(position), recyclerView);
                return view;
            }
在MVP模式的專案中,告訴Presenter該position頁需要請求的資料,將資料放入recyclerView即可。但是,在ViewPager初始化的時候,
instantiateItem()方法會連續執行position = 0 和 position = 1 兩次,當presenter需要去網路請求position = 0資料完成後,presenter持有的
recyclerView引用已經指向了position = 1的view內的recyclerView物件,資料也會被貼到position = 1的頁面裡。在viewPager的滑動過程中,每次滑動
預載入下一頁時instantiateItem只會執行一次,網路正常的情況下,不會出現錯亂問題。
    解決辦法:維護一個任務佇列,在每次presenter請求資料時,將資料的標記(id)與recyclerView存入對列,資料請求回來後,判斷是否有等待的任務,有等待的
任務,則任務出隊,請求資料,給recyclerView貼資料。
public static class Task {
        private int id;
        private RecyclerView recyclerView;

        public int getId() {
            return id;
        }

        public RecyclerView getRecyclerView() {
            return recyclerView;
        }

        public Task (int id, RecyclerView recyclerView) {
            this.id = id;
            this.recyclerView = recyclerView;
        }
    }

presenter中定義一個佇列變數,
private Queue<Task> caCheTaskQueue = new LinkedList<Task>();
每次需要請求一個也頁面的資料的時候,判斷佇列裡有沒有快取任務,並將本次任務入隊
    if (caCheTaskStack.isEmpty()) {
            initRecyclerView();//請求資料並交給recyclerView初始化
        }
    caCheTaskStack.offer(new Task(currentPageId, recyclerView));//入隊
在initRecyclerView()網路請求回撥裡,任務出隊,再判斷是否有任務等待。
caCheTaskStack.poll();//本次任務出隊
if (!caCheTaskStack.isEmpty()) {
    Task task = caCheTaskStack.peek();//快取任務讀取
    currentPageId = task.getId();
    recyclerView = task.getRecyclerView();
    initRecyclerView();//請求資料並交給recyclerView初始化
}