日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

打造炫酷通用的ViewPager指示器 Adapter模式适配所有 1

發布時間:2025/3/21 编程问答 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 打造炫酷通用的ViewPager指示器 Adapter模式适配所有 1 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

###1.概述


上一期我們已經寫了一篇 打造炫酷通用的ViewPager指示器 - 玩轉字體變色 可是這種效果雖然絢爛可以裝裝A和C之間,但是在實際的大多數效果中并不常見,只是在內涵段子中有這個效果而已,那么這一期我們就用Adapter適配器模式適配所有的效果,堪稱終結者。附視頻地址:http://pan.baidu.com/s/1dENNO33      

###2.效果實現


2.1 整合上一個實例:

我還是還是拿上一個實例來做演示吧。這里我貼幾種常見的效果,首先聲明Android自帶的有這個控件叫TabLayout,大家可以自己用用試試看好用不?我也用過但是不做任何評價,自己造的輪子還是想怎么用就怎么用。

        還有一些奇葩的效果如每個頭部Item布局不一樣,還有上面是圖片下面是文字選中的效果各不相同等等,我們都要去適配。    2.2 實現思路:

我在老早的時候用過ViewPageIndicator,還沒畢業出來工作的時候,好不好用我也不做評價,就是那個時候搞了一晚上沒搞出來第二天一看原來是activity的Theme主題沒有配置,大家手上肯定也有類似的效果也都可以用,只是以個人的理解來自己造一個輪子。   2.2.1 控件肯定是繼承ScrollView因為可以左右滑動,如果再去自定義ViewGroup肯定不劃算。   2.2.2 怎樣才能適合所有的效果,難道我們把所有可能出現的效果都寫一遍嗎?這的確不太可能,所以肯定采用Adapter適配器模式。   2.2.3 我們先動起來從簡單的入手,先做到動態的添加不同的布局條目再說吧。

2.3 自定義TrackIndicatorView動態添加布局:

這里為了適配所有效果,所以決定采用適配器Adapter設計模式,上面也提到過。至于什么是適配器模式大家需要看一下這個 Android設計模式源碼解析之適配器(Adapter)模式 這是理論篇,但是仔細看過我博客的哥們應該知道我其實 Adapter設計模式理論與實踐相結合寫過很多效果和框架了。這里不做過多的講解,寫著寫著看著看著就會了就理解了。

2.3.1 我們再也不能直接傳字符串數組或是傳對象數組過去讓自定義View去處理了,所以我們先確定一個自定義的Adapter類,getCount() 和 getView(int position,ViewGroup parent) 先用這兩個方法吧后面想到了再說。

/*** Created by Darren on 2016/12/7.* Email: 240336124@qq.com* Description: 指示器的適配器*/ public abstract class IndicatorBaseAdapter{// 獲取總的條數public abstract int getCount();// 根據當前的位置獲取Viewpublic abstract View getView(int position,ViewGroup parent); } 復制代碼

2.3.2 然后我們來實現指示器的自定義View,TrackIndicatorView 繼承自 HorizontalScrollView 。然后我們利用傳遞過來的Adapter再去動態的添加,我這里就直接上代碼吧

/*** Created by Darren on 2016/12/13.* Email: 240336124@qq.com* Description: ViewPager指示器*/public class TrackIndicatorView extends HorizontalScrollView {// 自定義適配器private IndicatorBaseAdapter mAdapter;// Item的容器因為ScrollView只允許加入一個孩子private LinearLayout mIndicatorContainer;public TestIndicator(Context context) {this(context, null);}public TestIndicator(Context context, AttributeSet attrs) {this(context, attrs, 0);}public TestIndicator(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);// 初始化Indicator容器用來存放itemmIndicatorContainer = new LinearLayout(context);addView(mIndicatorContainer);}public void setAdapter(IndicatorBaseAdapter adapter) {if (adapter == null) {throw new NullPointerException("Adapter cannot be null!");}this.mAdapter = adapter;// 獲取Item個數int count = mAdapter.getCount();// 動態添加到布局容器for (int i = 0; i < count; i++) {View indicatorView = mAdapter.getView(i, mIndicatorContainer);mIndicatorContainer.addView(indicatorView);}} } 復制代碼

效果可想而知,可以寫一個Activity測試一下,目前可以動態的添加多個不同樣式的布局,如果超出一個屏幕可以左右滑動,我這里就不做演示,待會一起吧。      2.3.3 動態的制定指示器Item的寬度:      目前我們雖然能夠動態的去添加各種布局,但是Item的寬度是任意的,我們需要在布局文件中指定一屏顯示多少個,如果沒有指定那么就獲取Item中最寬的一個,如果不夠一屏顯示就默認顯示一屏。我們需要使用自定義屬性,這里就不做過多的講,實在不行大家就自己去看看有關自定義屬性的博客或是直接google搜索一下。

// 獲取一屏顯示多少個Item,默認是0private int mTabVisibleNums = 0;// 每個Item的寬度private int mItemWidth = 0;public TrackIndicatorView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);// 之前代碼省略...// 獲取自定義屬性值 一屏顯示多少個TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.TrackIndicatorView);mTabVisibleNums = array.getInt(R.styleable.TrackIndicatorView_tabVisibleNums,mTabVisibleNums);array.recycle();}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {super.onLayout(changed, l, t, r, b);if (changed) {// 指定Item的寬度mItemWidth = getItemWidth();int itemCounts = mAdapter.getCount();for (int i = 0; i < itemCounts; i++) {// 指定每個Item的寬度mIndicatorContainer.getChildAt(i).getLayoutParams().width = mItemWidth;}Log.e(TAG, "mItemWidth -> " + mItemWidth);}}/*** 獲取每一個條目的寬度*/public int getItemWidth() {int itemWidth = 0;// 獲取當前控件的寬度int width = getWidth();if (mTabVisibleNums != 0) {// 在布局文件中指定一屏幕顯示多少個itemWidth = width / mTabVisibleNums;return itemWidth;}// 如果沒有指定獲取最寬的一個作為ItemWidthint maxItemWidth = 0;int mItemCounts = mAdapter.getCount();// 總的寬度int allWidth = 0;for (int i = 0; i < mItemCounts; i++) {View itemView = mIndicatorContainer.getChildAt(i);int childWidth = itemView.getMeasuredWidth();maxItemWidth = Math.max(maxItemWidth, childWidth);allWidth += childWidth;}itemWidth = maxItemWidth;// 如果不足一個屏那么寬度就為 width/mItemCountsif (allWidth < width) {itemWidth = width / mItemCounts;}return itemWidth;} 復制代碼

目前我們各種情況都測試了一下,一種是直接在布局文件中指定一屏可見顯示4個,一種是不指定就默認以最大的Item的寬度為準,最后一種就是不指定又不足一個屏幕默認就顯示一屏。看一下效果吧

2.4結合ViewPager      接下來我們就需要結合ViewPager了,也就需要實現一系列重要的效果:   2.4.1. 當ViewPager滾動的時候頭部需要自動將當前Item滾動到最中心;   2.4.2. 點擊Item之后ViewPager能夠切換到對應的頁面;   2.4.3. 需要頁面切換之后需要回調,讓用戶切換當前選中的狀態,需要在Adapter中增加方法;   2.4.4. 有些效果需要加入指示器,但并不是每種效果都需要

2.4.1. 當ViewPager滾動的時候頭部自動將當前Item滾動到最中心   我們目前不光需要Adapter,還需要一個參數就是ViewPager,需要監聽ViewPager的滾動事件

/*** 重載一個setAdapter的方法* @param adapter 適配器* @param viewPager 聯動的ViewPager*/public void setAdapter(IndicatorBaseAdapter adapter, ViewPager viewPager) {// 直接調用重載方法setAdapter(adapter);// 為ViewPager添加滾動監聽事件this.mViewPager = viewPager;mViewPager.addOnPageChangeListener(this);}@Overridepublic void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {// 在ViewPager滾動的時候會不斷的調用該方法Log.e(TAG,"position --> "+position+" positionOffset --> "+positionOffset);// 在不斷滾動的時候讓頭部的當前Item一直保持在最中心indicatorScrollTo(position,positionOffset);}/*** 不斷的滾動頭部*/private void indicatorScrollTo(int position, float positionOffset) {// 當前的偏移量int currentOffset = (int) ((position + positionOffset) * mItemWidth);// 原始的左邊的偏移量int originLeftOffset = (getWidth()-mItemWidth)/2;// 當前應該滾動的位置int scrollToOffset = currentOffset - originLeftOffset;// 調用ScrollView的scrollTo方法scrollTo(scrollToOffset,0);} 復制代碼

目前我們滾動ViewPager的時候,當前指示器條目會一直保持在最中心,activity的代碼我就沒貼出來了,這個待會可以下載我的源碼看看。我們看看效果   

2.4.2. 點擊Item之后ViewPager能夠切換到對應的頁面

public void setAdapter(IndicatorBaseAdapter adapter) {if (adapter == null) {throw new NullPointerException("Adapter cannot be null!");}this.mAdapter = adapter;// 獲取Item個數int count = mAdapter.getCount();// 動態添加到布局容器for (int i = 0; i < count; i++) {View indicatorView = mAdapter.getView(i, mIndicatorContainer);mIndicatorContainer.addView(indicatorView);switchIndicatorClick(indicatorView,i);}}/*** Indicator條目點擊對應切換ViewPager*/private void switchIndicatorClick(View indicatorView, final int position) {indicatorView.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {if(mViewPager != null){// 對應切換ViewPagermViewPager.setCurrentItem(position);}// IndicatorItem對應滾動到最中心indicatorSmoothScrollTo(position);}});}/*** 滾動到當前的位置帶動畫*/private void indicatorSmoothScrollTo(int position) {// 當前的偏移量int currentOffset = ((position) * mItemWidth);// 原始的左邊的偏移量int originLeftOffset = (getWidth()-mItemWidth)/2;// 當前應該滾動的位置int scrollToOffset = currentOffset - originLeftOffset;// smoothScrollTosmoothScrollTo(scrollToOffset,0);} 復制代碼

我們運行起來之后會發現一個問題,我們點擊會切換對應的ViewPager但是這個時候還是會調用onPageScrolled()方法,這個就比較dan疼,所以我們必須解決,如果是點擊我就不讓其執行onPageScrolled()里面的代碼。      2.4.3. 需要頁面切換之后需要回調,讓用戶切換當前選中的狀態,需要在Adapter中增加方法 在Adapter中增加兩個回調方法,一個是高亮當前選中方法highLightIndicator(View view) ,恢復默認方法restoreIndicator(View view),這兩個方法可以不用寫成抽象的,為了方便我們干脆使用泛型

/*** Created by Darren on 2016/12/7.* Email: 240336124@qq.com* Description: 指示器的適配器*/ public abstract class IndicatorBaseAdapter<Q extends View>{// 獲取總的條數public abstract int getCount();// 根據當前的位置獲取Viewpublic abstract Q getView(int position, ViewGroup parent);// 高亮當前位置public void highLightIndicator(Q indicatorView){}// 重置當前位置public void restoreIndicator(Q indicatorView){} } 復制代碼

TrackIndicatorView

@Overridepublic void onPageSelected(int position) {// 重置上一個位置的狀態View lastView = mIndicatorContainer.getChildAt(mCurrentPosition);mAdapter.restoreIndicator(lastView);// 高亮當前位置的狀態mCurrentPosition = position;highLightIndicator(mCurrentPosition);}/*** 高亮當前位置*/private void highLightIndicator(int position) {View currentView = mIndicatorContainer.getChildAt(position);mAdapter.highLightIndicator(currentView);} 復制代碼

  一步兩步一步兩步總算是快到頭了,接下來我們只需要加入指示器就可以了,當前這里面涉及到屬性動畫,如果不是很了解那就去看一下我的視頻或者去google官網看一下吧。      2.4.4. 有些效果需要加入指示器,但并不是每種效果都需要

/*** Created by Darren on 2016/12/7.* Email: 240336124@qq.com* Description: 指示器的容器包括下標*/public class IndicatorContainer extends RelativeLayout {private LinearLayout mIndicatorContainer;private Context mContext;// 底部跟蹤的Viewprivate View mBottomTrackView;private String TAG = "IndicatorContainer";// 距離左邊的初始距離private int mInitLeftMargin = 0;private RelativeLayout.LayoutParams mBottomTrackParams;private int mTabWidth;public IndicatorContainer(Context context) {this(context, null);}public IndicatorContainer(Context context, AttributeSet attrs) {this(context, attrs, 0);}public IndicatorContainer(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);this.mContext = context;}@Overridepublic void addView(View child) {if (mIndicatorContainer == null) {// 初始化容器mIndicatorContainer = new LinearLayout(mContext);RelativeLayout.LayoutParams params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);super.addView(mIndicatorContainer, params);}mIndicatorContainer.addView(child);}public int getIndicatorCount() {return mIndicatorContainer.getChildCount();}public View getIndicatorAt(int index) {return mIndicatorContainer.getChildAt(index);}/*** 添加底部跟蹤指示器* @param bottomTrackView*/public void addBottomTrackView(View bottomTrackView) {if (bottomTrackView == null) return;mBottomTrackView = bottomTrackView;super.addView(mBottomTrackView);// 指定一個規則添加到底部mBottomTrackParams = (LayoutParams) mBottomTrackView.getLayoutParams();mBottomTrackParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);// 計算和指定指示器的寬度int width = mBottomTrackParams.width;mTabWidth = mIndicatorContainer.getChildAt(0).getLayoutParams().width;if (width == ViewGroup.LayoutParams.MATCH_PARENT) {width = mTabWidth;}// 計算跟蹤的View初始左邊距離if (width < mTabWidth) {mInitLeftMargin = (mTabWidth - width) / 2;}mBottomTrackParams.leftMargin = mInitLeftMargin;}/*** 底部指示器移動到當前位置*/public void bottomTrackScrollTo(int position, float offset) {if (mBottomTrackView == null) return;// Log.e(TAG,"position --> "+position+" offset --> "+offset);mBottomTrackParams.leftMargin = (int) (mInitLeftMargin + (position + offset) * mTabWidth);mBottomTrackView.setLayoutParams(mBottomTrackParams);}/*** 開啟一個動畫移動到當前位置*/public void smoothScrollToPosition(int position) {if (mBottomTrackView == null) return;// 獲取當前指示器距左邊的距離final int mCurrentLeftMargin = mBottomTrackParams.leftMargin;// 計算出最終的距離final int finalLeftMargin = mTabWidth * position + mInitLeftMargin;// 用于動畫執行的事件final int distance = finalLeftMargin - mCurrentLeftMargin;// 利用屬性動畫不斷的更新距離ObjectAnimator animator = ObjectAnimator.ofFloat(mBottomTrackView, "leftMargin",mCurrentLeftMargin, finalLeftMargin).setDuration(Math.abs(distance));animator.setInterpolator(new DecelerateInterpolator());animator.start();// 添加動畫監聽不斷的更新 leftMarginanimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {float currentLeftMargin = (float) animation.getAnimatedValue();// Log.e(TAG, "current --> " + currentLeftMargin);setBottomTrackLeftMargin((int) currentLeftMargin);}});}/*** 設置底部跟蹤指示器的左邊距離*/public void setBottomTrackLeftMargin(int bottomTrackLeftMargin) {mBottomTrackParams.leftMargin = bottomTrackLeftMargin;mBottomTrackView.setLayoutParams(mBottomTrackParams);} }復制代碼

最后我們看看一些奇葩的一些需求,這是錄制的效果,最后附視頻地址:http://pan.baidu.com/s/1dENNO33  

總結

以上是生活随笔為你收集整理的打造炫酷通用的ViewPager指示器 Adapter模式适配所有 1的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 性按摩玩人妻hd中文字幕 | 青娱乐av| 亚洲狠狠婷婷综合久久久久图片 | 中文字幕在线欧美 | 中出 在线 | 一级高清毛片 | 六月婷婷久久 | 男生操女生免费网站 | 天堂在线观看中文字幕 | 亚洲不卡在线观看 | 伊伊综合网 | 欧美被狂躁喷白浆精品 | 日韩中文字幕免费观看 | www.久久99 | 亚州av| 亚洲小说在线 | 欧美日韩国产综合在线 | 天天操天天干天天操 | 毛片com| 久久久久99精品国产片 | 欧美 日韩 国产 高清 | 日日日操操操 | 亚洲成年网 | 国产精品色悠悠 | 日韩一区二区三区精品视频 | 国产精品第三页 | 欧美在线一 | 女人喂男人奶水做爰视频 | www.亚洲一区 | 免费视频一二三区 | 天天干天天玩 | 欧美成人高清视频 | 免费看黄的网址 | 日本一区二区三区免费电影 | 亚洲精品乱码久久久久久蜜桃图片 | 新版红楼梦在线高清免费观看 | 国产91精品露脸国语对白 | 强开乳罩摸双乳吃奶羞羞www | 免费黄色在线观看 | 免费毛片软件 | 婷婷爱五月天 | 欧美一区二区三区激情啪啪 | 欧美国产在线视频 | 亚洲啪啪av | 91快色| 中文字字幕码一二三区 | 日韩成人片 | 四虎影院www | 浮妇高潮喷白浆视频 | 九九影视理伦片 | 亚洲我射 | 男人天堂tv| 精品人妻一区二区三区香蕉 | 久久精品大全 | 中文字幕一区二区三区免费看 | www.夜夜骑| 91视频 - 8mav| 俺去俺来也在线www色官网 | 亚洲福利视频一区二区三区 | 久久男人精品 | 日韩成人中文字幕 | 美女又黄又免费的视频 | 久草资源福利 | 欧美人在线 | 中文人妻一区二区三区 | 亚洲二区中文字幕 | 亚洲我射av | 国产sm调教视频 | 日韩精品久 | 久久久久久九九九九 | 天天干天天干天天干 | 色婷婷久久久亚洲一区二区三区 | 成人免费视频国产 | 亚洲性夜 | 日一日射一射 | 日韩激情电影在线 | 亚洲最大的成人网 | 国产嫩草在线观看 | 亚欧在线免费观看 | 少妇不卡视频 | 日本一区二区三区免费看 | 久久久精品人妻无码专区 | 91国内| 亚洲麻豆视频 | 影音先锋中文字幕在线视频 | 日产亚洲一区二区三区 | 波多野结衣欲乱上班族 | 美女高潮视频在线观看 | 红色假期黑色婚礼2 | 久久另类ts人妖一区二区 | 黄色一级免费看 | 公侵犯一区二区三区四区中文字幕 | 成人中文字幕在线观看 | 国产精品视频一区二区三区, | 亚洲色图一区二区 | 99re5| 国产情侣在线播放 | 精品人妻一区二区三区四区久久 | 一区二区欧美日韩 |