前言
目前市面上已經很多Banner的第三方可以使用了
甚至是已經套好的ViewPager第三方 和Banner第三方。
這邊提供一個我自己常用的Indicator第三方
https://github.com/romandanylyk/PageIndicatorView
可以下載我的ViewPager demo 自己套套看這個第三方
情況是這樣的:
前幾天我使用Fragment,發現如果同時創建兩個相同的Fragment但不同內容的頁面,這個套件的Indicator會指向同一個id
簡而言之就是兩個Banner會共用到同一個Indicator
只好自己寫一個Indicator來用
ViewPager Banner 內的指示器 |
本日實作
先下載ViewPagerDemo我直接在這篇加內容
首先呢 先下載制作Indicator的圖片
分別是
圖片一 |
圖片二 |
有看到東西嗎?
沒看到沒關係,因為我也沒看到XD 本來就是白色的
在這講一下,一個是空心圓 一個是實心圓
下載的地方幫你擺在這 點我下載
改動的部分 |
一、先把你剛剛下載的圖片放到drawable內
此時你會注意看到圖片內有一個xml檔案叫indicator_selector.xml這其實可以自己創
每個item後面就是這張圖片被按到後的狀態
state_selected是被選取的狀態,true 是被選到,false則相反
看上圖,可以看出當true(被選取時),是實心圓,false(沒被選取)則是空心圓
這種寫法同樣可以套用在按鈕上,使用TextView設計一個按鈕,按下時的和非按下時會顯示不一樣的圖。詳情見:http://nikeru8.blogspot.tw/2017/03/textview.html
二、創建attrs.xml
命名為 attrs
attrs.xml<?xml version="1.0" encoding="utf-8"?> <resources> <attr name="indicatorIcon" format="reference" /> <attr name="indicatorMargin" format="dimension" /> <attr name="indicatorSmooth" format="boolean" /> <attr name="indicatorSize" format="dimension" /> <declare-styleable name="IndicatorView"> <!--指示器樣式--> <attr name="indicatorIcon" /> <!--指示器的間距--> <attr name="indicatorMargin" /> <!--指示器是否顯示滑動效果--> <attr name="indicatorSmooth" /> <!--指示器的大小--> <attr name="indicatorSize" /> </declare-styleable> </resources>
這裡是幹嘛的呢?
讓我們把畫面轉到activity_main.xml稍微更改了一下xml
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:custom="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"> <RelativeLayout android:layout_width="wrap_content" android:layout_height="250dp"> <android.support.v4.view.ViewPager android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="wrap_content" /> <com.hello.kaiser.viewpager.IndicatorView android:id="@+id/id_view_indicator" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_gravity="center_horizontal|bottom" android:layout_marginBottom="32dp" app:indicatorSmooth="false" custom:indicatorIcon="@drawable/indicator_selector" custom:indicatorMargin="5dp" custom:indicatorSize="9dp" /> </RelativeLayout> </RelativeLayout>
此時你會發現 在最外層的RelativeLayout 內
多了一個 xmlns:custom="http://schemas.android.com/apk/res-auto"
然後再看看新多的IndicatorView內
是不是有剛剛在attrs內新增的參數了,indicatorIcon、indicatorMargin ...這類的
那要如何使用它呢?
創一個IndicatorView.java吧
public class IndicatorView extends View implements ViewPager.OnPageChangeListener{ private Drawable mIndicator; //指示器的显示样式 private int mIndicatorSize; //指示器的大小 private boolean mSmooth; //是否显示滑动效果 private int mMargin; //指示器之间的间距 private int mDefaultMargin; //默认的间距 private int mSelectedItem; //选中的指示器 private int mWidth; //测量后得到的控件的宽度 private int mCount; //viewpager的页数,即指示器的个数 private int mContextWidth; //实际计算得到的控件的宽度 private float mOffset; //滑动偏移量 private ViewPager.OnPageChangeListener mPageChangeListener; public IndicatorView(Context context) { this(context,null); } public IndicatorView(Context context, AttributeSet attrs) { this(context, attrs,0); } public IndicatorView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context,attrs,defStyleAttr); } private void init(Context context, AttributeSet attrs, int defStyleAttr) { //设置指示器大小的默认值 mIndicatorSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,16,getResources().getDisplayMetrics()); //设置指示器间距的默认值 mDefaultMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,5,getResources().getDisplayMetrics()); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.IndicatorView,defStyleAttr,0); mIndicator = typedArray.getDrawable(R.styleable.IndicatorView_indicatorIcon); mMargin = (int) typedArray.getDimension(R.styleable.IndicatorView_indicatorMargin,mDefaultMargin); mSmooth = typedArray.getBoolean(R.styleable.IndicatorView_indicatorSmooth,true); mIndicatorSize = (int) typedArray.getDimension(R.styleable.IndicatorView_indicatorSize,mIndicatorSize); typedArray.recycle(); mIndicator.setBounds(0,0,mIndicatorSize,mIndicatorSize); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec)); } /** * 测量宽 * @param widthMeasureSpec * @return */ private int measureWidth(int widthMeasureSpec) { final int mode = MeasureSpec.getMode(widthMeasureSpec); final int size = MeasureSpec.getSize(widthMeasureSpec); int width; //计算整个控件的宽度,其中 mCount 是获取到的ViewPager的页数 int desired = getPaddingLeft() + mIndicatorSize * mCount + mMargin * (mCount - 1) + getPaddingRight(); mContextWidth = desired; if (mode == MeasureSpec.EXACTLY) { //如果是match_parent,则选取屏幕宽度和计算得到的宽度中较大的那个作为控件的宽度 width = Math.max(desired,size); }else { if (mode == MeasureSpec.AT_MOST) { //如果是wrap_parent,则选取屏幕宽度和计算得到的宽度中较小的那个作为控件的宽度 width = Math.min(desired,size); }else { //还有一种情况是 UNSPECIFIED ,一般是系统内部测量的过程中使用,这种情况也可以忽略掉 width = desired; } } mWidth = width; return width; } /** * 测量高 * @param heightMeasureSpec * @return */ private int measureHeight(int heightMeasureSpec) { final int mode = MeasureSpec.getMode(heightMeasureSpec); final int size = MeasureSpec.getSize(heightMeasureSpec); int height; int desired = getPaddingTop() + mIndicatorSize + getPaddingBottom(); if (mode == MeasureSpec.EXACTLY) { height = size; }else { if (mode == MeasureSpec.AT_MOST) { height = Math.min(desired,size); }else { height = desired; } } return height; } @Override protected void onDraw(Canvas canvas) { canvas.save(); /** * 这里是计算绘制控件的x轴上的起点位置 * 当wrap_content并且控件宽度小于屏幕宽度时,计算得到的宽度和实际宽度相同,mWidth/2 - mContextWidth/2 = 0,这种情况很好理解 * 再看当match_content的情况,控件的计算宽度小于屏幕宽度,则mWidth > mContextWidth,则计算得到的left是计算得到的控件距离屏幕左端的距离, * 控件将绘制在屏幕水平方向的中间,不清楚可以在纸上画下,其他情况也类似 */ int left = mWidth/2 - mContextWidth/2 + getPaddingLeft(); canvas.translate(left,getPaddingTop()); //依次绘制指示器 for (int i = 0 ; i < mCount ; i++) { mIndicator.setState(EMPTY_STATE_SET); mIndicator.draw(canvas); canvas.translate(mIndicatorSize + mMargin,0); } canvas.restore(); /** * 下面是绘制选中的指示器的样式 */ float leftDraw = ( mIndicatorSize + mMargin ) * ( mSelectedItem + mOffset ); canvas.translate(left,getPaddingTop()); canvas.translate(leftDraw,0); mIndicator.setState(SELECTED_STATE_SET); mIndicator.draw(canvas); } public void setViewPager(ViewPager viewPager) { if (viewPager == null) { return; } PagerAdapter pagerAdapter = viewPager.getAdapter(); if (pagerAdapter == null) { throw new RuntimeException("请先设置viewpager的adaper"); } mCount = pagerAdapter.getCount(); viewPager.addOnPageChangeListener(this); mSelectedItem = viewPager.getCurrentItem(); invalidate(); } /** * 因为自定义控件注册了viewpager的滑动监听, * 为了外部能够同样监听到滑动事件,所以提供该接口 * @param pageChangeListener */ public void setOnPageChangeListener(ViewPager.OnPageChangeListener pageChangeListener) { this.mPageChangeListener = pageChangeListener; } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if (mSmooth){ //允许显示滑动效果 mSelectedItem = position; mOffset = positionOffset; //通知view重绘 invalidate(); } if (mPageChangeListener != null) { mPageChangeListener.onPageScrolled(position,positionOffset,positionOffsetPixels); } } @Override public void onPageSelected(int position) { //当有page被选中后通知view重绘 mSelectedItem = position ; invalidate(); if(mPageChangeListener != null){ mPageChangeListener.onPageSelected(position); } } @Override public void onPageScrollStateChanged(int state) { if(mPageChangeListener != null){ mPageChangeListener.onPageScrollStateChanged(state); } } }
使用方式
這時候就可以在activity_main.xml內使用Indicator了<com.hello.kaiser.viewpager.IndicatorView android:id="@+id/id_view_indicator" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_gravity="center_horizontal|bottom" android:layout_marginBottom="32dp" app:indicatorSmooth="false" custom:indicatorIcon="@drawable/indicator_selector" custom:indicatorMargin="5dp" custom:indicatorSize="9dp" />這裡要注意的事,如果你是用複製貼上的方式在看這篇BLOG
你會發現會報錯,因為你寫的IndicatorView位置和我的並不一樣
我寫在com.hello.kaiser.viewpager.IndicatorView內,你必須指定你自己的IndicatorView才有辦法使用
在MainActivity.java內,設定IndicatorView的ViewPager吧
IndicatorView indicatorView = (IndicatorView) findViewById(R.id.id_view_indicator); indicatorView.setViewPager(mViewPager);
DEMO
https://github.com/nikeru8/ViewPagerDemo/tree/AddIndicator
沒有留言:
張貼留言