Android ViewPager無限滑動實現(xiàn)方案之一

ViewPager無限滑動虎忌,定時切換在項目是比較常見的功能先较,這篇文章的目的就是實現(xiàn)這樣一個功能敞贡。

實現(xiàn)思路

1.在數(shù)據(jù)源上<b>增加兩個一頭一尾的數(shù)據(jù)</b> 例如:[1 2 3 ]----->[<b>3</b> 1 2 3 <b>1</b>]稍走,此時數(shù)據(jù)源索引0的內容會等于索引length-2的內容皂林,數(shù)據(jù)源索引length-1的內容共的會等于索引1位置的內容

2.有了上面的思路再接下來就是利用ViewPager的滑動監(jiān)聽,在用戶滑到第一個或最后一個頁面戴质,調用ViewPager.setCurrentItem()方法進行位置的切換度宦。

ViewPager的使用不是文章的重點,文章只貼出重要的邏輯代碼告匠。

代碼實現(xiàn)

1.布局代碼
<pre>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="watson.code.infiniteviewpager.MainActivity">

<android.support.v4.view.ViewPager
    android:id="@+id/viewPager"
    android:layout_width="match_parent"
    android:layout_height="200dp"></android.support.v4.view.ViewPager>

<TextView
    android:id="@+id/currentPicturePostionTV"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:textColor="@android:color/black"
    android:textSize="20sp"/>

</LinearLayout>
</pre>

2.準備數(shù)據(jù):
<pre>
public class MainActivity extends AppCompatActivity {
private ViewPager mViewPager;
private TextView currentPicturePostionTV;<br />
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

    currentPicturePostionTV = (TextView) findViewById(R.id.currentPicturePostionTV);<br />

    //創(chuàng)建數(shù)據(jù)源
    List<Integer> pictures = new ArrayList<>();
    pictures.add(R.drawable.picture1);
    pictures.add(R.drawable.picture2);
    pictures.add(R.drawable.picture3);
    //額外增加兩個數(shù)據(jù)
    <b>pictures.add(pictures.get(0));
    pictures.add(0,pictures.get(pictures.size()-2));</b><br />

    mViewPager = (ViewPager) findViewById(R.id.viewPager);
    //安裝ViewPager頁面滑動監(jiān)聽
    setupPageChangeListener();
    mViewPager.setAdapter(new ViewPagerPictureAdapter(this,pictures));<br />
    //由于在數(shù)據(jù)源頭部增加了一個戈抄,所以初始化定位到1
    <b>mViewPager.setCurrentItem(1);</b>
}

}
</pre>

3.創(chuàng)建Adapter:
<pre>
public class ViewPagerPictureAdapter extends PagerAdapter {
private List<Integer> mData;
private Context mContext;
public ViewPagerPictureAdapter(Context context, List<Integer> data){
mData = data;
mContext = context;
}

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

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

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(),mData.get(position));
    ImageView imageView = new ImageView(mContext);
    imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
    imageView.setImageBitmap(bitmap);
    container.addView(imageView,new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
    return imageView;
}

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

}
</pre>

4.處理OnPageChangeListener的回調方法。you know后专,當一個頁面發(fā)生切換划鸽,其中會調用onPagerScrolled()方法以及onPageSelected()方法,在一個滑動過程中onPageScrolled()會回調多次,在其中可以監(jiān)聽到滑動進度裸诽;而onPageSelected()方法被調用是在手指釋放時并且達到了切換到其他頁面的條件(滑動的偏移量/速度)立即調用而且只調用一次嫂用,也就意味著松開手之后不是等到滑動進度完成百分之百才調用這方法。所以可以得出如果在onPageSelected()做切換肯定不能達到滿意的效果丈冬。所以要做的切換只能考慮放到onPageScrolled()方法嘱函。<b>后期補充</b>:<u>如果把切換放在onPageScrollStateChanged()方法內,那么判斷條件就是state == SCROLL_STATE_IDLE的條件埂蕊,但經過一番測試之后發(fā)現(xiàn)在ViewPager滑到頭尾兩端時往弓,回到SCROLL_STATE_IDLE狀態(tài)稍慢,就是說不會在onPageScrolled的postionOffset為0時就能回到這個狀態(tài)回調這個方法蓄氧,所以導致如果快速滑動滑到兩端時會出現(xiàn)滑動不流暢的現(xiàn)象函似。所以最終還是在onPageScrolled()做處理。</u>
<pre>
private void setupPageChangeListener() {
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//頁面過度滑動動畫完成判斷
<b>if (positionOffset == 0) {
if (position == 0) {
mViewPager.setCurrentItem(mViewPager.getAdapter().getCount() - 2, false);
} else if (position == mViewPager.getAdapter().getCount() - 1) {
mViewPager.setCurrentItem(1, false);
}
}</b>
}

    @Override
    public void onPageSelected(int position) {
        if(position == 0){
            position = mViewPager.getAdapter().getCount()-2;
        }else if(position == mViewPager.getAdapter().getCount() - 1){
            position = 1;
        }
        currentPicturePostionTV.setText(String.valueOf(position));
    }

    @Override
    public void onPageScrollStateChanged(int state) {}
});

}
</pre>

運行喉童,看下實現(xiàn)的效果:

未解決閃現(xiàn)問題.gif

從效果上發(fā)現(xiàn)撇寞,切換位置時會閃現(xiàn);作為一名有追求的程序員肯定是不能容忍的堂氯,于是開始各種分析蔑担,最初以為是Adapter里的instantiateItem()方法的創(chuàng)建View并顯示的過程所消耗的時間會比destroyItem()方法執(zhí)行的慢,結果大失所望祖灰,instantiateItem()方法無論何種情況都會比destoryItem()方法先調用钟沛;最終經各種嘗試之后畔规,無厘頭的還是在onPageScrolled()方法內切換頁面時采用延時策略局扶。再進一步發(fā)現(xiàn),更狗血的是延時策略的延時間可以為0叁扫,即只需要將切換的代碼放到Handler里面執(zhí)行就不會出現(xiàn)閃現(xiàn)的現(xiàn)象三妈。此時,一口老血以迅雷不及掩耳盜鈴之勢直線噴出...what's the fuck!!

<pre>
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 1:
mViewPager.setCurrentItem(mViewPager.getAdapter().getCount() - 2, false);
break;
case 2:
mViewPager.setCurrentItem(1, false);
break;
}
}
};
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//頁面過度滑動動畫完成
if (positionOffset == 0) {
if (position == 0) {
handler.sendEmptyMessageDelayed(1,0);
} else if (position == mViewPager.getAdapter().getCount() - 1) {
handler.sendEmptyMessageDelayed(2,0);
}
}
}

        @Override
        public void onPageSelected(int position) {
            if(position == 0){
                position = mViewPager.getAdapter().getCount()-2;
            }else if(position == mViewPager.getAdapter().getCount() - 1){
                position = 1;
            }
            currentPicturePostionTV.setText(String.valueOf(position));
        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    });

}
</pre>

不過最終效果還是很不錯的莫绣。


解決閃現(xiàn)問題.gif
只有一張圖片.gif

最后貼上一個封裝類:
<pre>
package watson.code.infiniteviewpager;

/**

  • Created by watson on 2017/5/31.
    */

public class InfiniteViewPager extends ViewPager {
private static final String TAG = InfiniteViewPager.class.getName();
private OnPageChangeListener mOnPageChangeListener;

public InfiniteViewPager(Context context) {
    this(context, null);
}

public InfiniteViewPager(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}

private Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case 0:
                setCurrentItem(1, false);
                break;
            case 1:
                setCurrentItem(getAdapter().getCount() - 2, false);
                break;
            case 2:
                setCurrentItem(getCurrentItem() + 1);
                loop();
                break;
        }
    }
};


public void init() {
    addOnPageChangeListener(mOnPageChangeListener = new OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            if (positionOffset == 0) {
                if (position == getAdapter().getCount() - 1) {
                    handler.sendEmptyMessage(0);
                } else if (position == 0) {
                    handler.sendEmptyMessage(1);
                }
            }
            if (mOnPageChangeListener != null && mOnPageChangeListener != this) {
                mOnPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels);
            }
        }

        @Override
        public void onPageSelected(int position) {
            if (mOnPageChangeListener != null && mOnPageChangeListener != this) {
                mOnPageChangeListener.onPageSelected(position);
            }
        }

        @Override
        public void onPageScrollStateChanged(int state) {
            //Log.i(TAG, "onPageScrollStateChanged: " + state);
            if (mOnPageChangeListener != null && mOnPageChangeListener != this) {
                mOnPageChangeListener.onPageScrollStateChanged(state);
            }
        }
    });
}

@Override
public void addOnPageChangeListener(OnPageChangeListener listener) {
    if (listener == mOnPageChangeListener) {
        super.addOnPageChangeListener(listener);
    } else {
        mOnPageChangeListener = listener;
    }
}

public int getDuration() {
    return duration;
}

public void setDuration(int duration) {
    this.duration = duration;
}

private int duration = 5000;

private boolean isAutoSwitchStatus;

// 開啟自動切換,條件必須是要先設置Adapter畴蒲,并且數(shù)據(jù)源不小于3
public void startLoop() {
    isAutoSwitchStatus = true;
    loop();
}

// 取消定時切換
public void cancelLoop() {
    cancel();
    isAutoSwitchStatus = false;
}

// 用于數(shù)據(jù)加載完之后loop
public void loop() {
    if (isAutoSwitchStatus && getVisibility() == VISIBLE && getAdapter() != null && getAdapter().getCount() >= 3) {
        //保證只有一個handler消息被發(fā)送
        handler.removeMessages(2);
        handler.sendEmptyMessageDelayed(2, duration);
    }
}

private void cancel() {
    if (isAutoSwitchStatus) {
        handler.removeMessages(2);
    }
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
    switch (ev.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN:
            cancel();
            break;
        case MotionEvent.ACTION_UP:
            loop();
            break;
    }
    return super.dispatchTouchEvent(ev);
}


@Override
public void setAdapter(PagerAdapter adapter) {
    super.setAdapter(adapter);
    setCurrentItem(1);
}

}

</pre>

在上面的封裝里沒有增加額外的接口,只是復寫了addOnPageChangeListener方法做了一些處理对室。所以使用時依然可以調用addOnPageChangeListener方法來設置監(jiān)聽器模燥。
<b>注意</b>:該封裝類中暴露的兩個方法,應在組件對應的生命周期方法內開啟和結束掩宜,以防止內存泄漏蔫骂。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市牺汤,隨后出現(xiàn)的幾起案子辽旋,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,383評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件补胚,死亡現(xiàn)場離奇詭異码耐,居然都是意外死亡,警方通過查閱死者的電腦和手機溶其,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評論 3 385
  • 文/潘曉璐 我一進店門骚腥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人瓶逃,你說我怎么就攤上這事桦沉。” “怎么了金闽?”我有些...
    開封第一講書人閱讀 157,852評論 0 348
  • 文/不壞的土叔 我叫張陵纯露,是天一觀的道長。 經常有香客問我代芜,道長埠褪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,621評論 1 284
  • 正文 為了忘掉前任挤庇,我火速辦了婚禮钞速,結果婚禮上,老公的妹妹穿的比我還像新娘嫡秕。我一直安慰自己渴语,他們只是感情好,可當我...
    茶點故事閱讀 65,741評論 6 386
  • 文/花漫 我一把揭開白布昆咽。 她就那樣靜靜地躺著驾凶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪掷酗。 梳的紋絲不亂的頭發(fā)上调违,一...
    開封第一講書人閱讀 49,929評論 1 290
  • 那天,我揣著相機與錄音泻轰,去河邊找鬼技肩。 笑死,一個胖子當著我的面吹牛浮声,可吹牛的內容都是我干的虚婿。 我是一名探鬼主播,決...
    沈念sama閱讀 39,076評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼泳挥,長吁一口氣:“原來是場噩夢啊……” “哼然痊!你這毒婦竟也來了?” 一聲冷哼從身側響起羡洁,我...
    開封第一講書人閱讀 37,803評論 0 268
  • 序言:老撾萬榮一對情侶失蹤玷过,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體辛蚊,經...
    沈念sama閱讀 44,265評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡粤蝎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,582評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了袋马。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片初澎。...
    茶點故事閱讀 38,716評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖虑凛,靈堂內的尸體忽然破棺而出碑宴,到底是詐尸還是另有隱情,我是刑警寧澤桑谍,帶...
    沈念sama閱讀 34,395評論 4 333
  • 正文 年R本政府宣布延柠,位于F島的核電站,受9級特大地震影響锣披,放射性物質發(fā)生泄漏贞间。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,039評論 3 316
  • 文/蒙蒙 一雹仿、第九天 我趴在偏房一處隱蔽的房頂上張望增热。 院中可真熱鬧,春花似錦胧辽、人聲如沸峻仇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,798評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽摄咆。三九已至,卻和暖如春奠骄,著一層夾襖步出監(jiān)牢的瞬間豆同,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,027評論 1 266
  • 我被黑心中介騙來泰國打工含鳞, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人芹务。 一個月前我還...
    沈念sama閱讀 46,488評論 2 361
  • 正文 我出身青樓蝉绷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親枣抱。 傳聞我的和親對象是個殘疾皇子熔吗,可洞房花燭夜當晚...
    茶點故事閱讀 43,612評論 2 350

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,837評論 25 707
  • 不會VLOOKUP公式,你就不算一個合格的職場人佳晶,VLOOKUP公式在日常辦公中非常實用和重要桅狠,下面給大家普及下這...
    蘭瑟鳶尾閱讀 1,714評論 0 1
  • 簡名千鳥錯, 永正安吉坐。 波及江北闊中跌, 帥以幾闊綽咨堤。
    月獨醉閱讀 97評論 0 0