姚麗冰 學(xué)號(hào):16050120089
轉(zhuǎn)載:
https://juejin.im/entry/5a154b21f265da431d3c53d6
【嵌牛導(dǎo)讀】:前幾天寫了篇《用RecyclerView打造一個(gè)輪播圖》(以下簡(jiǎn)稱基礎(chǔ)版),看到有讀者評(píng)論說相比Viewpager,用RecyclerView看起來沒什么特別的優(yōu)勢(shì)。究其原因,目前只用到了RecyclerView最基礎(chǔ)的一部分功能。其實(shí)相比Viewpager實(shí)現(xiàn)的輪播圖,RecyclerView版的最大優(yōu)勢(shì)就在于它的靈活多變性,可定制性高。本篇文章將通過利用LayoutManger、SnapHelper等RecyclerView的輔助類來實(shí)現(xiàn)一系列更為炫酷的輪播圖。
【嵌牛鼻子】:RecyclerView,Viewpager,youtManger,SnapHelper,輪播圖
【嵌牛提問】:如何利用LayoutManger、SnapHelper等RecyclerView的輔助類來實(shí)現(xiàn)一系列更為炫酷的輪播圖。
【嵌牛正文】:
初試:豎直輪播圖
基礎(chǔ)版中,RecyclerView設(shè)置了默認(rèn)橫向的LinearLayoutManager:
LinearLayoutManager indicatorLayoutManager = new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false);
那么我們?cè)賮砑觽€(gè)屬性:
<attr name="orientation" format="enum">
? ? ? ? ? ? <enum name="horizontal" value="0"/>
? ? ? ? ? ? <enum name="vertical" value="1" />
</attr>
同時(shí)在布局文件中設(shè)置app:orientation="vertical",讓LinearLayoutManager的布局方向變?yōu)樨Q直(為了保持統(tǒng)一,標(biāo)示點(diǎn)也設(shè)置為同一方向),就是這么簡(jiǎn)單,一款豎直滑動(dòng)的無限輪播圖就打造完成了!
實(shí)戰(zhàn):仿魅族輪播圖
之前有篇文章ViewPager系列之 仿魅族應(yīng)用的廣告BannerView,不過這是用ViewPager實(shí)現(xiàn)的,那我們就來個(gè)RecyclerView版的,而實(shí)現(xiàn)重點(diǎn)的就在于自定義LayoutManger(如果不太了解這部分的知識(shí),請(qǐng)先移步學(xué)習(xí)下(╯︵╰))。
這次先上成果圖,再慢慢分析:
以上的效果僅僅是換了一個(gè)LayoutManger和一個(gè)itemview(為了顯示效果,imageview外面套了cardview)。
首先我們做準(zhǔn)備工作,定義幾個(gè)常量:
private? float SCALE_RATE ;當(dāng)前圖片放大比例
private? int mOrientation;布局方向(HORIZONTAL or VERTICAL)
private? int itemSpace;圖片之間的間距
自定layoutmanager第一步當(dāng)然是實(shí)現(xiàn)唯一必須要實(shí)現(xiàn)的方法:
? @Override
? ? public RecyclerView.LayoutParams generateDefaultLayoutParams() {
? ? ? ? return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
? ? ? ? ? ? ? ? ViewGroup.LayoutParams.WRAP_CONTENT);
? ? }
然而并沒有什么用,99%的自定義layoutmanager都是這么寫的,因?yàn)槲覀儧]有把view添加到 RecyclerView中。所以接下來就是重寫onLayoutChildren()來進(jìn)行布局,這個(gè)方法的作用相當(dāng)于普通Viewgroup中的onLayout()方法,在RecyclerView滾動(dòng),數(shù)據(jù)改變等情況都會(huì)調(diào)用此方法來重新布局。
@Override
? ? public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
? ? ? ? if (state.getItemCount() == 0) {
? ? ? ? ? ? removeAndRecycleAllViews(recycler);
? ? ? ? ? ? mOffset = 0;
? ? ? ? ? ? return;
? ? ? ? }//沒有Itemitemview,不做處理
? ? ? ? ensureLayoutState();
? ? ? ? if (getChildCount() == 0) {//沒有可見的itemview,初始化需要用到的一些參數(shù)
? ? ? ? ? ? View scrap = recycler.getViewForPosition(0);
? ? ? ? ? ? measureChildWithMargins(scrap, 0, 0);
? ? ? ? ? ? mDecoratedMeasurement = mOrientationHelper.getDecoratedMeasurement(scrap);
? ? ? ? ? ? mDecoratedMeasurementInOther = mOrientationHelper.getDecoratedMeasurementInOther(scrap);
? ? ? ? ? ? mSpaceMain = (mOrientationHelper.getTotalSpace() - mDecoratedMeasurement) / 2;
? ? ? ? ? ? mSpaceInOther = (mOrientationHelper.getTotalSpaceInOther() - mDecoratedMeasurementInOther) / 2;
? ? ? ? ? ? mInterval = setInterval();
? ? ? ? ? ? setUp();
? ? ? ? ? ? mLeftItems = (int) Math.abs(minRemoveOffset() / mInterval) + 1;
? ? ? ? ? ? mRightItems = (int) Math.abs(maxRemoveOffset() / mInterval) + 1;
? ? ? ? }
? ? ? ? if (mPendingScrollPosition != NO_POSITION) {
? ? ? ? ? ? mOffset = mReverseLayout ?
? ? ? ? ? ? ? ? ? ? mPendingScrollPosition * -mInterval : mPendingScrollPosition * mInterval;
? ? ? ? }
? ? ? ? //開始布局
? ? ? ? detachAndScrapAttachedViews(recycler);
? ? ? ? layoutItems(recycler);
? ? }
上面只是做一些初始化工作,接下來是layoutItems方法,就貼一些重要代碼:
取當(dāng)前可見的view進(jìn)行放置,遍歷計(jì)算位置
final int currentPos = mReverseLayout ?
? ? ? ? ? ? ? ? -getCurrentPositionOffset() : getCurrentPositionOffset();
? ? ? ? int start = currentPos - mLeftItems;
? ? ? ? int end = currentPos + mRightItems;
? for (int i = start; i < end; i++) {
? ? if (findViewByPosition(adapterPosition) == null) {
? ? ? ? ? ? ? ? ? ? final View scrap = recycler.getViewForPosition(adapterPosition);
? ? ? ? ? ? ? ? ? ? measureChildWithMargins(scrap, 0, 0);
? ? ? ? ? ? ? ? ? ? resetViewProperty(scrap);
? ? ? ? ? ? ? ? ? ? final float targetOffset = getProperty(i) - mOffset;
? ? ? ? ? ? ? ? ? ? layoutScrap(scrap, targetOffset);
具體的布局方法,主要就是,回收不可見的itemview,遍歷可見的itemview進(jìn)行位置計(jì)算并放置:
private void layoutScrap(View scrap, float targetOffset) {
? ? ? ? final int left = calItemLeft(scrap, targetOffset);
? ? ? ? final int top = calItemTop(scrap, targetOffset);
? ? ? ? if (mOrientation == VERTICAL) {
? ? ? ? ? ? layoutDecorated(scrap, mSpaceInOther + left, mSpaceMain + top,
? ? ? ? ? ? ? ? ? ? mSpaceInOther + left + mDecoratedMeasurementInOther, mSpaceMain + top + mDecoratedMeasurement);
? ? ? ? } else {
? ? ? ? ? ? layoutDecorated(scrap, mSpaceMain + left, mSpaceInOther + top,
? ? ? ? ? ? ? ? ? ? mSpaceMain + left + mDecoratedMeasurement, mSpaceInOther + top + mDecoratedMeasurementInOther);
? ? ? ? }
? ? ? ? setItemViewProperty(scrap, targetOffset);
? ? }
? ? //在滾動(dòng)時(shí)根據(jù)距離動(dòng)態(tài)縮放itemView(在這里你可以自定義滑動(dòng)動(dòng)畫,改變 itemView的屬性,透明度,大小,角度等等)
? ? private void setItemViewProperty(View itemView, float targetOffset) {
? ? ? ? float scale = calculateScale(targetOffset + mSpaceMain);
? ? ? ? itemView.setScaleX(scale);
? ? ? ? itemView.setScaleY(scale);
? ? }
接下來是處理滾動(dòng),讓recyclerview可以滾動(dòng)起來:
? @Override
? ? public boolean canScrollHorizontally() {
? ? ? ? return mOrientation == HORIZONTAL;
? ? }
? ? @Override
? ? public boolean canScrollVertically() {
? ? ? ? return mOrientation == VERTICAL;
? ? }
@Override
? ? public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
? ? ? ? //位移0、沒有子View 當(dāng)然不移動(dòng)
? ? ? if (getChildCount() == 0 || dy == 0) {
? ? ? ? ? ? return 0;
? ? ? ? }
? ? ? ? ? int willScroll = dy;
? ? ? ? ? float realDx = dy / getDistanceRatio();//真正滑動(dòng)的距離
? ? ? // 從新布局可見的view
? ? ? ? for (int i = 0; i < getChildCount(); i++) {
? ? ? ? ? ? final View scrap = getChildAt(i);
? ? ? ? ? ? final float delta = propertyChangeWhenScroll(scrap) - realDx;
? ? ? ? ? ? layoutScrap(scrap, delta);
? ? ? ? }
? ? }
因?yàn)樵谖覀冊(cè)诓季趾蜐L動(dòng)時(shí)考慮了橫向和豎向的情況,所以設(shè)置豎直的無限輪播圖也很簡(jiǎn)單:
new BannerLayoutManager(BannerLayoutManager.VERTICAL, Util.dp2px(10));(方向豎直,圖片間距10dp)
總結(jié)
看到這,我想大家都能看到用RecyclerView實(shí)現(xiàn)無限輪播圖的強(qiáng)大的指出了吧:
adapter可以處理無限輪播;layoutmanager可以處理布局和滑動(dòng)動(dòng)畫;SnapHelper可以讓iremview滑動(dòng)起來像viewpager一樣(一般用PagerSnapHelper就行了)。
而上面的所有動(dòng)畫效果僅僅都是通過改動(dòng)layoutmanager,然后再通過設(shè)置不同itemview,就可以做出各種效果。最后感謝以下文章,上面的實(shí)現(xiàn)思路大部分來自于他們。
github地址
參考資料
http://blog.csdn.net/zxt0601/article/details/52956504
http://www.lxweimin.com/p/7bb7556bbe10