FragmentPagerAdapter和FragmentStatePagerAdapter源碼中的三寶

簡書 編程之樂
轉(zhuǎn)載請注明原創(chuàng)出處捕捂,謝謝交掏!

前言

FragmentPagerAdapter和FragmentStatePagerAdapter是我們開發(fā)中經(jīng)常遇到的兩個類狱从,尤其是和ViewPager的配合铐殃。幾乎我們每個Android開發(fā)者都被Fragment和ViewPager剂府,PopupWindow渊迁,適配等等一堆神坑折磨著慰照,尤其是Fragment神坑無數(shù),這些都是天天在用的組件琉朽,Google為什么留給我們這么多坑毒租。也正因如此,為了不掉進(jìn)坑里箱叁,就需要我們不斷去填坑墅垮。

下面是通過閱讀FragmentPagerAdapter和FragmentStatePagerAdapter能夠?qū)W到的知識點(diǎn):

  1. ViewPager刷新問題
  2. 適配器模式
  3. 觀察者模式

區(qū)別一: 狀態(tài)保存

我們在使用ViewPager的時候,經(jīng)常使用下面幾種方式:

ViewPager viewPager = findViewById(R.id.viewPager);
// 方式一
viewPager.setAdapter(new PagerAdapter() {
        private String mTitles[] ;
        private List<View> mViewList;

        @Override
        public int getCount() {
            return mViewList.size();
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            View view = mViewList.get(position);
            container.addView(view);
            return view;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return mTitles[position];
        }
});

// 方式二
viewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
    @Override
    public Fragment getItem(int position) {
        return fragments.get(position);
    }

    @Override
    public int getCount() {
        return fragments.size();
    }
});

// 方式三
viewPager.setAdapter(new FragmentStatePagerAdapter(getSupportFragmentManager()) {
    @Override
    public Fragment getItem(int position) {
        return fragments.get(position);
    }

    @Override
    public int getCount() {
        return fragments.size();
    }
});

用法大家都比較熟悉了耕漱,其中FragmentPagerAdapter 和 FragmentStatePagerAdapter有什么區(qū)別呢算色?

根據(jù)兩個類的名稱就可以知道FragmentStatePagerAdapter似乎是保存狀態(tài)的,我們分別去這兩個類找下它們的區(qū)別螟够,發(fā)現(xiàn)它們都重寫了父類PageAdapter的方法:

public abstract class PagerAdapter {
    // 省略 
    public static final int POSITION_UNCHANGED = -1;
    public static final int POSITION_NONE = -2;

    public Parcelable saveState() {
        return null;
    }

    public void restoreState(Parcelable state, ClassLoader loader) {
        
    }
}

分別查看它們的實現(xiàn):

FragmentPagerAdapter的實現(xiàn)

@Override
    public Parcelable saveState() {
        return null;
    }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {
    }

FragmentStatePagerAdapter的實現(xiàn)

 public Parcelable saveState() {
        Bundle state = null;
        if (mSavedState.size() > 0) {
            state = new Bundle();
            Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
            mSavedState.toArray(fss);
            state.putParcelableArray("states", fss);
        }
        for (int i=0; i<mFragments.size(); i++) {
            Fragment f = mFragments.get(i);
            if (f != null && f.isAdded()) {
                if (state == null) {
                    state = new Bundle();
                }
                String key = "f" + i;
                mFragmentManager.putFragment(state, key, f);
            }
        }
        return state;
    }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {
        if (state != null) {
            Bundle bundle = (Bundle)state;
            bundle.setClassLoader(loader);
            Parcelable[] fss = bundle.getParcelableArray("states");
            mSavedState.clear();
            mFragments.clear();
            if (fss != null) {
                for (int i=0; i<fss.length; i++) {
                    mSavedState.add((Fragment.SavedState)fss[i]);
                }
            }
            Iterable<String> keys = bundle.keySet();
            for (String key: keys) {
                if (key.startsWith("f")) {
                    int index = Integer.parseInt(key.substring(1));
                    Fragment f = mFragmentManager.getFragment(bundle, key);
                    if (f != null) {
                        while (mFragments.size() <= index) {
                            mFragments.add(null);
                        }
                        f.setMenuVisibility(false);
                        mFragments.set(index, f);
                    } else {
                        Log.w(TAG, "Bad fragment at key " + key);
                    }
                }
            }
        }
    }

可以很容易看出只有FragmentStatePagerAdapter對Fragment的狀態(tài)進(jìn)行了保存灾梦,而FragmentPagerAdapter則是空實現(xiàn)。

雖然兩個Adapter均有保存狀態(tài)的代碼妓笙,但是它們具體是在哪里被調(diào)用的呢若河?根據(jù)我們學(xué)過的Activity和Fragment的保存狀態(tài)的方式,我們知道狀態(tài)的恢復(fù)和保存一般在這些組件或者View里给郊,的確牡肉,它們是在ViewPager中。

public class ViewPager extends ViewGroup {
  @Override
    public Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState ss = new SavedState(superState);
        ss.position = mCurItem;
        if (mAdapter != null) {
            ss.adapterState = mAdapter.saveState();
        }
        return ss;
    }

    @Override
    public void onRestoreInstanceState(Parcelable state) {
        if (!(state instanceof SavedState)) {
            super.onRestoreInstanceState(state);
            return;
        }

        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());

        if (mAdapter != null) {
            mAdapter.restoreState(ss.adapterState, ss.loader);
            setCurrentItemInternal(ss.position, false, true);
        } else {
            mRestoredCurItem = ss.position;
            mRestoredAdapterState = ss.adapterState;
            mRestoredClassLoader = ss.loader;
        }
    }
}

因為ViewPager持有Adapter實例淆九,所以ViewPager的onSaveInstanceState和onRestoreInstanceState方法都是間接調(diào)用Adapter來執(zhí)行狀態(tài)的恢復(fù)和保存的统锤,我們看到ViewPager中間接調(diào)用了mAdapter.saveState()mAdapter.restoreState毛俏。


區(qū)別二: 實例銷毀 vs 視圖銷毀

除了上面的區(qū)別外,F(xiàn)ragmentStatePagerAdapter和FragmentPagerAdapter唯一的區(qū)別就是對Fragment對象的處理了饲窿。

我們平常使用ViewPager + PageAdater時候需要重寫很多方法煌寇,如開頭的那幾個案例,而ViewPager + FragmentPagerAdapter(FragmentStatePagerAdapter) 僅僅實現(xiàn)getItem和getCount兩個方法就夠了逾雄,核心方法instantiateItem和destroyItem內(nèi)部已經(jīng)做好了實現(xiàn)阀溶。

先看FragmentStatePagerAdapter類

 private ArrayList<Fragment> mFragments = new ArrayList<Fragment>();

 public Object instantiateItem(ViewGroup container, int position) {
        if (mFragments.size() > position) {
            Fragment f = mFragments.get(position);
            if (f != null) {
                return f;
            }
        }

        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }

        // 實例化fragment(交給我們實現(xiàn)的getItem方法)
        Fragment fragment = getItem(position);

        if (mSavedState.size() > position) {
            Fragment.SavedState fss = mSavedState.get(position);
            if (fss != null) {
                fragment.setInitialSavedState(fss);
            }
        }
        // 如果緩存 <= ViewPager傳入的position,說明當(dāng)前位置還未存入緩存.
        while (mFragments.size() <= position) {
            // 先占個坑
            mFragments.add(null);
        }
        fragment.setUserVisibleHint(false);
        // 填坑
        mFragments.set(position, fragment);
        // 填充視圖
        mCurTransaction.add(container.getId(), fragment);
        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        Fragment fragment = (Fragment) object;

        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        // 從緩存中移除
        mFragments.set(position, null);
        // 從FragmentManager中移除
        mCurTransaction.remove(fragment);
    }

再來看下FragmentPagerAdapter的兩個實現(xiàn)方法:

@Override
public Object instantiateItem(ViewGroup container, int position) {
    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }

    final long itemId = getItemId(position);

    // Do we already have this fragment?
    String name = makeFragmentName(container.getId(), itemId);
    Fragment fragment = mFragmentManager.findFragmentByTag(name);
    if (fragment != null) {
        mCurTransaction.attach(fragment);
    } else {
        fragment = getItem(position);
        mCurTransaction.add(container.getId(), fragment,
                makeFragmentName(container.getId(), itemId));
    }
    if (fragment != mCurrentPrimaryItem) {
        fragment.setMenuVisibility(false);
        fragment.setUserVisibleHint(false);
    }

    return fragment;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    if (mCurTransaction == null) {
        mCurTransaction = mFragmentManager.beginTransaction();
    }
    mCurTransaction.detach((Fragment)object);
}

FragmentStatePagerAdapter 內(nèi)部還做了個小緩存,這個不是重點(diǎn)鸦泳,我們主要關(guān)注

FragmentStatePagerAdapter

mCurTransaction.add(container.getId(), fragment);
mCurTransaction.remove(fragment);

和FragmentPagerAdapter

mCurTransaction.attach(fragment);
mCurTransaction.add(container.getId(), fragment,makeFragmentName(container.getId(), itemId));
mCurTransaction.detach((Fragment)object);

很明顯银锻,F(xiàn)ragmentStatePagerAdapter 對fragment進(jìn)行完全的添加和刪除操作,而FragmentPagerAdapter 則是對視圖進(jìn)行attach和detach做鹰。

總結(jié):
FragmentStatePagerAdapter 適合大量頁面击纬,不斷重建和銷毀
FragmentPagerAdapter 適合少量頁面,常駐內(nèi)存钾麸。

適配器模式

因為Android這個知識點(diǎn)有兩個設(shè)計模式的案例實在太經(jīng)典了更振,所以想順便拿來講一下,理解了這些饭尝,開發(fā)過程中常見兩個坑的問題:

  1. 懶加載
  2. 數(shù)據(jù)不更新
    經(jīng)過查看源碼就非常容易解決了肯腕!
public class ViewPager {
    
    private PagerAdapter mAdapter;
    public void setAdapter(PagerAdapter adapter) {
        adapter.xx();
        adapter.xxx();
        this.mAdapte = adapter;
        
        // ....
        requestLayout();
    }

    public void dataSetChanged() {
         final int adapterCount = mAdapter.getCount();
         // ....
         mAdapter.destroyItem(this, ii.position, ii.object);
         // ....
         // ....
    }
}

可以看到ViewPager持有的是PagerAdapter,ViewPager中間接調(diào)用了很多PagerAdapter的方法钥平,使用組合方式來代替繼承方式解耦实撒。
怎么看著那么像模板方法模式呢,設(shè)計模式中很多模式確實太像了帖池,比如代理模式裝飾器模式奈惑。

組合優(yōu)于繼承,總之睡汹,能用組合實現(xiàn)的不要用繼承。
前不久我看一個開源項目的代碼寂殉,大量的繼承和模板方法模式囚巴,看的我真的快懷疑自己智商了。

偽觀察者模式

再來看下ViewPager的代碼:

public class ViewPager {
    // 觀察者
    private PagerObserver mObserver;
    private PagerAdapter mAdapter;
    public void setAdapter(PagerAdapter adapter) {
        adapter.xx();
        adapter.xxx();
        this.mAdapte = adapter;
        if (mAdapter != null) {
            if (mObserver == null) {
                // 實例化觀察者對象
                mObserver = new PagerObserver();
            }
            // 傳遞一個觀察者mObserver對象供adapter調(diào)用
            mAdapter.setViewPagerObserver(mObserver);
        }
        // ....
        requestLayout();
    }

    public void dataSetChanged() {
         final int adapterCount = mAdapter.getCount();
         // ....
         mAdapter.destroyItem(this, ii.position, ii.object);
         // ....
         // ....
    }

    /**
     * 觀察者對象 
     */
    private class PagerObserver extends DataSetObserver {
        PagerObserver() {
        }

        @Override
        public void onChanged() {
            dataSetChanged();
        }
        @Override
        public void onInvalidated() {
            dataSetChanged();
        }
    }
}

在setAdapter中mAdapter.setViewPagerObserver(mObserver);
這里傳遞了一個內(nèi)部類對象名稱叫 PagerObserver友扰,大家要注意了彤叉,這個地方雖然起名叫做觀察者,我認(rèn)為是不合理的村怪,確實Android提供給我們一個注冊觀察者的接口來監(jiān)聽(后面詳細(xì)講)秽浇,不過我們常常用notifyDataChange() 來通知ViewPager數(shù)據(jù)更新這里的默認(rèn)實現(xiàn) 并沒有真正用 觀察者模式,可能是Google偷懶了吧甚负。
mAdapter.setViewPagerObserver(mObserver)傳遞的這個對象 更像回調(diào)柬焕∩蟛校回調(diào)接口的本質(zhì)不就是傳遞一個對象嗎?斑举? C語言的實現(xiàn)則是傳遞指針搅轿。JavaScript傳遞function。

看下我們經(jīng)常調(diào)用的notifyDataSetChanged方法:

public abstract class PagerAdapter {
    // 被觀察者,暫時不用管
    private final DataSetObservable mObservable = new DataSetObservable();
    // 冒充者,雖然也叫觀察者對象,但實際算是個回調(diào)對象
    private DataSetObserver mViewPagerObserver;

    void setViewPagerObserver(DataSetObserver observer) {
        synchronized (this) {
            mViewPagerObserver = observer;
        }
    }

    public void notifyDataSetChanged() {
        synchronized (this) {
            if (mViewPagerObserver != null) {
                mViewPagerObserver.onChanged();
            }
        }
        mObservable.notifyChanged();
    }
}

注釋寫的很明白富玷,PagerAdapter里面怎么可能同時充當(dāng)觀察者和被觀察者嘛璧坟,notifyDataSetChanged沒有用觀察者模式實現(xiàn)。

但是我們注意到了赎懦,notifyDataSetChanged方法的最后調(diào)用了
mObservable.notifyChanged();
這里才是真正的觀察者模式雀鹃,被觀察者準(zhǔn)備調(diào)用自己的方法通知所有的觀察者 數(shù)據(jù)改變了±剑可惜的是當(dāng)前 目前還木有人注冊褐澎,孤芳自賞!

這里暫時做個標(biāo)記伐蒋,我們最后在看Android的觀察者模式設(shè)計工三。

繼續(xù)跟蹤代碼,notifyDataSetChanged調(diào)用了mViewPagerObserver這個偽娘的onChanged方法(ViewPager中),
onChanged()調(diào)用了ViewPager的dataSetChanged方法:

  void dataSetChanged() {
        // This method only gets called if our observer is attached, so mAdapter is non-null.

        final int adapterCount = mAdapter.getCount();
        mExpectedAdapterCount = adapterCount;
        boolean needPopulate = mItems.size() < mOffscreenPageLimit * 2 + 1
                && mItems.size() < adapterCount;

        // newCurrItem 用于跟蹤標(biāo)記當(dāng)前ViewPager的所在頁
        int newCurrItem = mCurItem;

        boolean isUpdating = false;
        // 遍歷ViewPager中所有的items(每個ItemInfo中包含著fragment實例,position等信息)
        for (int i = 0; i < mItems.size(); i++) {
            final ItemInfo ii = mItems.get(i);
            // getItemPosition方法是我們根據(jù)需要重寫的方法,有三種值: POSITION_UNCHANGED和POSITION_NONE和pos(int類型)
            final int newPos = mAdapter.getItemPosition(ii.object);

            // (1). 如果getItemPosition()返回值是POSITION_UNCHANGED(默認(rèn)實現(xiàn)),不做處理
            if (newPos == PagerAdapter.POSITION_UNCHANGED) {
                continue;
            }

            // (2). 如果getItemPosition()返回值是POSITION_NONE,移除ViewPager的mItems中當(dāng)前正在遍歷著的ItemInfo
            if (newPos == PagerAdapter.POSITION_NONE) {
                mItems.remove(i);
                i--;

                if (!isUpdating) {
                    // 方法內(nèi)沒什么實際意義
                    mAdapter.startUpdate(this);
                    isUpdating = true;
                }
                // 同時調(diào)用adapter的銷毀方法銷毀當(dāng)前遍歷著的ItemInfo
                mAdapter.destroyItem(this, ii.position, ii.object);
                needPopulate = true;

                continue;
            }
            // (3). 如果getItemPosition()返回值是其他的值(如newPos = 3),則相當(dāng)于把[首次初始化的ViewPager中ItemInfo的position]重新賦值為指定的值.換個位置,這個特性一般我們很少用到.
            if (ii.position != newPos) {
                if (ii.position == mCurItem) {
                    // 如果當(dāng)前for循環(huán)中遍歷的ItemInfo.position正好等于ViewPager中的當(dāng)前頁下標(biāo),跟蹤標(biāo)記
                    newCurrItem = newPos;
                }

                ii.position = newPos;
                needPopulate = true;
            }
        }

        if (isUpdating) {
            mAdapter.finishUpdate(this);
        }

        Collections.sort(mItems, COMPARATOR);

        // 根據(jù)前面的分析, (2) 和 (3)都會導(dǎo)致重新請求布局
        if (needPopulate) {
            // Reset our known page widths; populate will recompute them.
            final int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                final View child = getChildAt(i);
                final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                if (!lp.isDecor) {
                    lp.widthFactor = 0.f;
                }
            }
            // 設(shè)置當(dāng)前頁,并重新布局或者是滾動到此頁
            setCurrentItemInternal(newCurrItem, false, true);
            requestLayout();
        }
    }

這個方法邏輯稍有點(diǎn)多先鱼,分析都寫在注釋里了俭正。

根據(jù)下面這段代碼

boolean needPopulate = mItems.size() < mOffscreenPageLimit * 2 + 1
                && mItems.size() < adapterCount;

我們知道給ViewPager新添加View或Fragment沒有任何問題.它會自動處理,但是更新 就有問題了焙畔,我們可能希望把某個頁面替換掉掸读,比如A->B.

但是根據(jù)這段代碼的邏輯,不重寫getItemPosition方法(默認(rèn)POSITION_UNCHANGED)的話是不會有任何變化的宏多。

通過重寫getItemPosition()方法

final int newPos = mAdapter.getItemPosition(ii.object);
// ... 
mAdapter.destroyItem(this, ii.position, ii.object);

我們可以看到ViewPager中只要有返回POSITION_NONE的項儿惫,那么就會銷毀該項并刷新。

但是不建議大家直接在adapter中這么干(雖然我是這么干的伸但,懶人):
反例如下:

@Override
public int getCount() {
    return this.mFragmentList.size();
}

public int getItemPosition(Object object) {
    return POSITION_NONE;
}

@Override
public Fragment getItem(int position) {
    return mFragmentList.get(position);
}

這樣會導(dǎo)致調(diào)用notifyDataChange時候ViewPager中每個Fragment都會被 mAdapter.destroyItem肾请。我們只是想更新某個Item就夠了,這一下子全部都
destroyItem一遍更胖,性能肯定造成浪費(fèi)铛铁。

大家可以根據(jù)自己的邏輯修改進(jìn)行實現(xiàn),其中object就是Fragment對象或view對象却妨,比如設(shè)置tag之類的饵逐,只令某一項返回POSITION_NONE。

public int getItemPosition(Object object) {
    return POSITION_NONE;
}

觀察者模式

首先聲明:雖然在ViewPager(充當(dāng)觀察者)和PagerAdapter(充當(dāng)被觀察者)中出現(xiàn)了觀察者模式的代碼彪标,但是ViewPager中并未注冊觀察者倍权。不過這里的案例非常經(jīng)典,不由得分析下作為記錄捞烟。同樣的薄声,ListView(充當(dāng)觀察者)和BaseAdapter(充當(dāng)被觀察者)則使用了這個模式并在ListView中注冊了觀察者当船,有興趣的可以查看相關(guān)源碼。

前面講了一個偽觀察者模式奸柬,繼續(xù)....

仍然是上次的代碼生年,notifyDataSetChanged最后一行調(diào)用了
mObservable.notifyChanged() 這才是正宗的觀察者模式。

public abstract class PagerAdapter {
    // 被觀察者
    private final DataSetObservable mObservable = new DataSetObservable();

    public void notifyDataSetChanged() {
        synchronized (this) {
            // 冒充者
            if (mViewPagerObserver != null) {
                mViewPagerObserver.onChanged();
            }
        }
        // 正宗
        mObservable.notifyChanged();
    }

    public void registerDataSetObserver(DataSetObserver observer) {
        mObservable.registerObserver(observer);
    }

    public void unregisterDataSetObserver(DataSetObserver observer) {
        mObservable.unregisterObserver(observer);
    }
}

如何在PagerAdapter 中給 被觀察者DataSetObservable 注冊一個觀察者?

注意:這段代碼僅做參考廓奕,Android并未真正注冊抱婉。

PagerAdapter pagerAdapter = new .. ;
pagerAdapter.registerDataSetObserver(new DataSetObserver() {
    @Override
    public void onChanged() {
        super.onChanged();
        // .... 實現(xiàn),這里copy偽娘那里的onChange()方法.
    }

    @Override
    public void onInvalidated() {
        super.onInvalidated();
        // .... 實現(xiàn)
    }
});

下面我們就來一起看看Android的被觀察者和觀察者是怎么寫的桌粉,可以借鑒參考下:

被觀察者
DataSetObservable

public class DataSetObservable extends Observable<DataSetObserver> {
    public void notifyChanged() {
        synchronized(mObservers) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }

    public void notifyInvalidated() {
        synchronized (mObservers) {
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onInvalidated();
            }
        }
    }
}

Observable

package android.database;

import java.util.ArrayList;

public abstract class Observable<T> {

    protected final ArrayList<T> mObservers = new ArrayList<T>();

    public void registerObserver(T observer) {
        if (observer == null) {
            throw new IllegalArgumentException("The observer is null.");
        }
        synchronized(mObservers) {
            if (mObservers.contains(observer)) {
                throw new IllegalStateException("Observer " + observer + " is already registered.");
            }
            mObservers.add(observer);
        }
    }

    public void unregisterObserver(T observer) {
        if (observer == null) {
            throw new IllegalArgumentException("The observer is null.");
        }
        synchronized(mObservers) {
            int index = mObservers.indexOf(observer);
            if (index == -1) {
                throw new IllegalStateException("Observer " + observer + " was not registered.");
            }
            mObservers.remove(index);
        }
    }

    public void unregisterAll() {
        synchronized(mObservers) {
            mObservers.clear();
        }
    }
}

觀察者

package android.database;

public abstract class DataSetObserver {

    public void onChanged() {
        // Do nothing
    }

    public void onInvalidated() {
        // Do nothing
    }
}

這就是Android經(jīng)典的觀察者模式.

源碼擴(kuò)展

ListView和ViewPager在適配器模式和觀察者模式存在諸多相似蒸绩,舉一反三讓我們的理解更加透徹。

ListView在setAdapter() 中注冊的觀察者.

public void setAdapter(ListAdapter adapter) {
    if (mAdapter != null && mDataSetObserver != null) {
        mAdapter.unregisterDataSetObserver(mDataSetObserver);
    }

    resetList();
    mRecycler.clear();

    if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
        mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter);
    } else {
        mAdapter = adapter;
    }

    mOldSelectedPosition = INVALID_POSITION;
    mOldSelectedRowId = INVALID_ROW_ID;

    super.setAdapter(adapter);

    if (mAdapter != null) {
        // 注冊觀察者
        mDataSetObserver = new AdapterDataSetObserver();
        mAdapter.registerDataSetObserver(mDataSetObserver);
    }

    requestLayout();
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末铃肯,一起剝皮案震驚了整個濱河市患亿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌押逼,老刑警劉巖步藕,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異挑格,居然都是意外死亡咙冗,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進(jìn)店門漂彤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來雾消,“玉大人,你說我怎么就攤上這事挫望×⑷螅” “怎么了?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵媳板,是天一觀的道長桑腮。 經(jīng)常有香客問我,道長拷肌,這世上最難降的妖魔是什么到旦? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮巨缘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘采呐。我一直安慰自己若锁,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布斧吐。 她就那樣靜靜地躺著又固,像睡著了一般仲器。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上仰冠,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天乏冀,我揣著相機(jī)與錄音,去河邊找鬼洋只。 笑死辆沦,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的识虚。 我是一名探鬼主播肢扯,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼担锤!你這毒婦竟也來了蔚晨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤肛循,失蹤者是張志新(化名)和其女友劉穎铭腕,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體多糠,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡累舷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了熬丧。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片笋粟。...
    茶點(diǎn)故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖析蝴,靈堂內(nèi)的尸體忽然破棺而出害捕,到底是詐尸還是另有隱情,我是刑警寧澤闷畸,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布尝盼,位于F島的核電站,受9級特大地震影響佑菩,放射性物質(zhì)發(fā)生泄漏盾沫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一殿漠、第九天 我趴在偏房一處隱蔽的房頂上張望赴精。 院中可真熱鬧,春花似錦绞幌、人聲如沸蕾哟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谭确。三九已至帘营,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間逐哈,已是汗流浹背芬迄。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留昂秃,地道東北人禀梳。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像械蹋,于是被迫代替她去往敵國和親出皇。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內(nèi)容