先看看效果圖:
創(chuàng)新互聯(lián)專注于工農(nóng)網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠(chéng)為您提供工農(nóng)營(yíng)銷型網(wǎng)站建設(shè),工農(nóng)網(wǎng)站制作、工農(nóng)網(wǎng)頁(yè)設(shè)計(jì)、工農(nóng)網(wǎng)站官網(wǎng)定制、微信小程序服務(wù),打造工農(nóng)網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供工農(nóng)網(wǎng)站排名全網(wǎng)營(yíng)銷落地服務(wù)。
停在中間自動(dòng)翻頁(yè)
序言:最近接到一個(gè)任務(wù),做一個(gè)類似上面自動(dòng)翻頁(yè)的功能。可以看到,這一屏中有三張圖片顯示出來(lái)了,有兩張沒(méi)有顯示完全,看到設(shè)計(jì)圖的時(shí)候第一反應(yīng)是可以用viewpager來(lái)實(shí)現(xiàn),但是任務(wù)卻跟你開(kāi)了一個(gè)天大的玩笑,要求是以最左邊的圖片為基準(zhǔn),也就是說(shuō),左邊的圖片也得顯示完全,就像下圖所示,后來(lái)仔細(xì)想想viewpager好像沒(méi)有這樣的功能,也有可能是我不知道,我也沒(méi)有找到這樣的文章或者信息,希望知道的簡(jiǎn)友私戳交流一下,感激不盡,好了,言歸正傳
停在左邊
在開(kāi)始之前呢,首先介紹一個(gè)Google最新(其實(shí)在24.2.0版本的時(shí)候就已經(jīng)發(fā)布了)發(fā)布的一個(gè)東西SnapHelper,這玩意兒是對(duì)RecyclerView功能的一個(gè)拓展,有興趣的同學(xué)可以去看看它的源碼,SnapHelper的實(shí)現(xiàn)原理是監(jiān)聽(tīng)RecyclerView.OnFlingListener中的onFling接口,可以使RecyclerView實(shí)現(xiàn)類似ViewPager的功能,無(wú)論怎么滑動(dòng)最終停留在某頁(yè)正中間,那它和ViewPager的區(qū)別是什么呢?就是ViewPager不能一次連續(xù)滑動(dòng)多張圖片,而且不能定制(停在左邊,還是停在右邊)。下面我們一起來(lái)看看吧!
首先導(dǎo)入所需要的包,最低版本是v7-24.2.0,低了就沒(méi)有這個(gè)類了:
compile 'com.android.support:appcompat-v7:24.2.0'
compile 'com.android.support:recyclerview-v7:24.2.0'
這里系統(tǒng)自帶有一個(gè)類LinearSnapHelper,LinearSnapHelper繼承自SnapHelper,這個(gè)默認(rèn)是讓視圖停在中間的,你只需要將RecyclerView和LinearSnapHelper綁定在一起就行了:
LinearSnapHelper mLinearSnapHelper = new LinearSnapHelper();
mLinearSnapHelper.attachToRecyclerView(mRecyclerview);
效果如下:
當(dāng)然了,SnapHelper的功能絕不僅僅在此,你還可以定制化,讓他停在左邊,或者右邊,而你不需要重新繼承SnapHelper,直接繼承LinearSnapHelper就可以了,這里面有很多寫好的方法,然后你再重寫里面的兩個(gè)方法:
* (1)、calculateDistanceToFinalSnap:當(dāng)拖拽或滑動(dòng)結(jié)束時(shí)會(huì)回調(diào)該方法,返回一個(gè)out = int[2],out[0]x軸,out[1] y軸 ,這個(gè)值就是需要修正的你需要的位置的偏移量。 *
* (2)、findSnapView:這個(gè)方法用來(lái)獲取特定的視圖,當(dāng)返回null時(shí),表示沒(méi)有獲取到任何視圖 。*
完整的代碼:
public class LeftSnapHelper extends LinearSnapHelper { private OrientationHelper mHorizontalHelper; /** * 當(dāng)拖拽或滑動(dòng)結(jié)束時(shí)會(huì)回調(diào)該方法,該方法返回的是一個(gè)長(zhǎng)度為2的數(shù)組,out[0]表示橫軸,x[1]表示縱軸,這兩個(gè)值就是你需要修正的位置的偏移量 * * @param layoutManager * @param targetView * @return */ @Override public int[] calculateDistanceToFinalSnap(RecyclerView.LayoutManager layoutManager, View targetView) { //注:由于是橫向滾動(dòng),在這里我們只考慮橫軸的值 int[] out = new int[2]; if (layoutManager.canScrollHorizontally()) { out[0] = distanceToStart(targetView, getHorizontalHelper(layoutManager)); } else { out[0] = 0; } return out; } /** * 這個(gè)方法是計(jì)算偏移量 * * @param targetView * @param helper * @return */ private int distanceToStart(View targetView, OrientationHelper helper) { return helper.getDecoratedStart(targetView) - helper.getStartAfterPadding(); } @Override public View findSnapView(RecyclerView.LayoutManager layoutManager) { return findStartView(layoutManager, getHorizontalHelper(layoutManager)); } /** * 找到第一個(gè)顯示的view * @param layoutManager * @param helper * @return */ private View findStartView(RecyclerView.LayoutManager layoutManager, OrientationHelper helper) { if (layoutManager instanceof LinearLayoutManager) { int firstChild = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition(); int lastChild = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition(); if (firstChild == RecyclerView.NO_POSITION) { return null; } //這是為了解決當(dāng)翻到最后一頁(yè)的時(shí)候,最后一個(gè)Item不能完整顯示的問(wèn)題 if (lastChild == layoutManager.getItemCount() - 1) { return layoutManager.findViewByPosition(lastChild); } View child = layoutManager.findViewByPosition(firstChild); //得到此時(shí)需要左對(duì)齊顯示的條目 if (helper.getDecoratedEnd(child) >= helper.getDecoratedMeasurement(child) / 2 && helper.getDecoratedEnd(child) > 0) { return child; } else { return layoutManager.findViewByPosition(firstChild + 1); } } return super.findSnapView(layoutManager); } /** * 獲取視圖的方向 * * @param layoutManager * @return */ private OrientationHelper getHorizontalHelper(@NonNull RecyclerView.LayoutManager layoutManager) { if (mHorizontalHelper == null) { mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager); } return mHorizontalHelper; } }
當(dāng)然了,你也可以讓它停在右邊:只需要在上面的基礎(chǔ)上修改findSnapView方法即可:
public class RightSnapHelper extends LinearSnapHelper { private OrientationHelper mHorizontalHelper; /** * 當(dāng)拖拽或滑動(dòng)結(jié)束時(shí)會(huì)回調(diào)該方法,該方法返回的是一個(gè)長(zhǎng)度為2的數(shù)組,out[0]表示橫軸,x[1]表示縱軸,這兩個(gè)值就是你需要修正的位置的偏移量 * * @param layoutManager * @param targetView * @return */ @Override public int[] calculateDistanceToFinalSnap(RecyclerView.LayoutManager layoutManager, View targetView) { //注:由于是橫向滾動(dòng),在這里我們只考慮橫軸的值 int[] out = new int[2]; if (layoutManager.canScrollHorizontally()) { out[0] = distanceToEnd(targetView, getHorizontalHelper(layoutManager)); } else { out[0] = 0; } return out; } /** * 這個(gè)方法是計(jì)算偏移量 * * @param targetView * @param helper * @return */ private int distanceToEnd(View targetView, OrientationHelper helper) { return helper.getDecoratedEnd(targetView) - helper.getEndAfterPadding(); } @Override public View findSnapView(RecyclerView.LayoutManager layoutManager) { return findEndView(layoutManager, getHorizontalHelper(layoutManager)); } /** * 找到第一個(gè)顯示的view * * @param layoutManager * @param helper * @return */ private View findEndView(RecyclerView.LayoutManager layoutManager, OrientationHelper helper) { if (layoutManager instanceof LinearLayoutManager) { int lastChild = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition(); if (lastChild == RecyclerView.NO_POSITION) { return null; } View child = layoutManager.findViewByPosition(lastChild); //得到此時(shí)需要右對(duì)齊顯示的條目 if (helper.getDecoratedStart(child) >= helper.getDecoratedMeasurement(child) / 2 && helper.getDecoratedStart(child) > 0) { return child; } else { return layoutManager.findViewByPosition(lastChild - 1); } } return super.findSnapView(layoutManager); } /** * 獲取視圖的方向 * * @param layoutManager * @return */ private OrientationHelper getHorizontalHelper(@NonNull RecyclerView.LayoutManager layoutManager) { if (mHorizontalHelper == null) { mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager); } return mHorizontalHelper; } }
效果:
停在右邊
那如何讓它能無(wú)限的滑動(dòng)呢?
這個(gè)當(dāng)然是要在Adapter里面“做手腳”了,讓獲取Item總數(shù)的方法返回Integer.MAX_VALUE就可以了:
@Override public int getItemCount() { return Integer.MAX_VALUE; }
然后在onBindViewHolder中獲取list中的值時(shí)相應(yīng)的取余就好了:
@Override public void onBindViewHolder(RecyclerViewHolder holder, int position) { Glide.with(mContext).load(mList.get(position % mList.size()) .getImageUrl()).placeholder(R.mipmap.ic_launcher) .into(holder.ivImage); holder.tvName.setText(mList.get(position % mList.size()).getName()); }
好了,做到這里就完成了80%了,接下來(lái)我們要讓它能夠自動(dòng)滾動(dòng),如何能自動(dòng)滾動(dòng)呢?這里可以參考一下ViewPager中自動(dòng)滾動(dòng)的效果,這里L(fēng)Z使用的是Timer來(lái)實(shí)現(xiàn),Timer有一個(gè)每隔多長(zhǎng)時(shí)間執(zhí)行一次的功能,在這里正好:
private int cnt = 2; //表示當(dāng)前最右邊顯示的item的position private boolean isSlidingByHand = false; //表示是否是手在滑動(dòng) private boolean isSlidingAuto = true; //表示是否自動(dòng)滑動(dòng) timer.schedule(new TimerTask() { @Override public void run() { if (isSlidingAuto) { myHandler.sendEmptyMessage(CHANGE_ITEM); } } }, 1000, 3000);
考慮到這里有兩種滑動(dòng),一種是用戶手動(dòng)的滑動(dòng),另一種是我們的Timer來(lái)出發(fā)滑動(dòng),我們還得對(duì)RecyclerView設(shè)置監(jiān)聽(tīng),來(lái)簡(jiǎn)單判斷一下是用戶觸發(fā)的還是Timer觸發(fā)的:
alRecyclerview.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { LinearLayoutManager manager = (LinearLayoutManager) recyclerView.getLayoutManager(); int firstVisibleItemPosition = manager.findFirstVisibleItemPosition(); switch (newState) { case SCROLL_STATE_IDLE: //(靜止沒(méi)有滾動(dòng)) if (isSlidingByHand) { Message msg = myHandler.obtainMessage(); msg.arg1 = firstVisibleItemPosition; msg.what = CHANGE_ITEM; myHandler.sendMessage(msg); } break; case SCROLL_STATE_DRAGGING: //(正在被外部拖拽,一般為用戶正在用手指滾動(dòng)) isSlidingByHand = true; isSlidingAuto = false; break; case SCROLL_STATE_SETTLING: //(自動(dòng)滾動(dòng)) if (isSlidingByHand) { isSlidingAuto = false; } else { isSlidingAuto = true; } break; } } });
最后的處理結(jié)果當(dāng)然是交給Handler來(lái)了:
private static class MyHandler extends Handler { //采用弱引用的方式,防止內(nèi)存泄漏 WeakReferenceweakReference; public MyHandler(CenterActivity mActivity) { this.weakReference = new WeakReference<>(mActivity); } @Override public void handleMessage(Message msg) { final CenterActivity mActivity = weakReference.get(); Log.d(TAG, "handleMessage: " + "handler is running"); if (mActivity.isSlidingByHand) { mActivity.cnt = msg.arg1; mActivity.isSlidingByHand = false; mActivity.isSlidingAuto = true; mActivity.cnt+=2; //讓RecyclerView平滑的滾動(dòng) mActivity.alRecyclerview.smoothScrollToPosition(++mActivity.cnt); } else { mActivity.alRecyclerview.smoothScrollToPosition(++mActivity.cnt); } } }
這樣差不多就好了,但是還有一個(gè)問(wèn)題不知道各位有沒(méi)有關(guān)注到,滾動(dòng)的時(shí)候并沒(méi)有那么平滑,找了好久不知道是什么原因,希望知道的朋友底下評(píng)論告知一聲。
參考文章:
OrientationHelper API
Android24.2.0支持庫(kù)中的SnapHelper學(xué)習(xí)和使用
感謝以上資料的幫助。
源碼我已上傳至Github,想了解的朋友可以自行下載,當(dāng)然,如果有更好的想法,也可以一起交流。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持創(chuàng)新互聯(lián)。