@xujun94
2016-08-21T08:41:47.000000Z
字数 7808
阅读 1735
转载请注明原博客地址:
本篇博客主要讲解怎样自定义一个circleIndicator控件?
下一遍博客主要讲解怎样更改ViewPager切换的效果, 预计明天晚上之前更新。





其实很简单,值需要两个步骤
1) 在xml布局文件里面
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><android.support.v4.view.ViewPagerandroid:layout_below="@id/rl_header"android:id="@+id/viewPager"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_marginTop="20dp"></android.support.v4.view.ViewPager><com.xujun.administrator.customviewspecif.view.CirclePageIndicatorandroid:id="@+id/circle_indicator"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_marginBottom="20dp"></com.xujun.administrator.customviewspecif.view.CirclePageIndicator></RelativeLayout>
2)在代码里面
mViewPager = (ViewPager) findViewById(R.id.viewPager);mCirclePageIndicator = (CirclePageIndicator) findViewById(R.id.circle_indicator);//注意下面初始化的顺序不可以调换mFragemntAdapter = new BaseFragemntAdapter(getSupportFragmentManager(), mFragments);mViewPager.setAdapter(mFragemntAdapter);//将mCirclePageIndicator与我们的mViewPager绑定在一起mCirclePageIndicator.setViewPager(mViewPager);
1)在xml布局里面更改我们的样式
xmlns:app="http://schemas.android.com/apk/res-auto"//例如更改我们移动小圆点的颜色app:fillColor="#fff"//其他属性的更改请参考以下我们自定义的属性<declare-styleable name="CirclePageIndicator"><!-- Whether or not the indicators should be centered. --><attr name="centered" /><!-- Color of the filled circle that represents the current page. --><attr name="fillColor" format="color" /><!-- Color of the filled circles that represents pages. --><attr name="pageColor" format="color" /><!-- Orientation of the indicator. --><attr name="android:orientation"/><!-- Radius of the circles. This is also the spacing between circles. --><attr name="radius" format="dimension" /><!-- Whether or not the selected indicator snaps to the circles. --><attr name="snap" format="boolean" /><!-- Color of the open circles. --><attr name="strokeColor" format="color" /><!-- Width of the stroke used to draw the circles. --><attr name="strokeWidth" /><!-- View background --><attr name="android:background"/></declare-styleable>
2)在Java代码里面动态更改
// 设置滑动的时候移动的小圆点是否跳跃mCirclePageIndicator.setSnap(false);//设置小圆点的半径mCirclePageIndicator.setRadius(10 * density);// 设置页面小圆点的颜色mCirclePageIndicator.setPageColor(0x880000FF);// 设置移动的小圆点的颜色mCirclePageIndicator.setFillColor(0xFF888888);// 设置外边框的颜色mCirclePageIndicator.setStrokeColor(0xFF000000);//设置外表框的宽度mCirclePageIndicator.setStrokeWidth(2 * density);
大概可以分为以下几个步骤
public CirclePageIndicator(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);if (isInEditMode()) return;//初始化自定义属性final Resources res = getResources();final int defaultPageColor = res.getColor(R.color.default_circle_indicator_page_color);final int defaultFillColor = res.getColor(R.color.default_circle_indicator_fill_color);final int defaultOrientation = res.getInteger(R.integer.default_circle_indicator_orientation);在这里省略了若干方法a.recycle();final ViewConfiguration configuration = ViewConfiguration.get(context);mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);}
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {if (mOrientation == HORIZONTAL) {setMeasuredDimension(measureLong(widthMeasureSpec), measureShort(heightMeasureSpec));} else {setMeasuredDimension(measureShort(widthMeasureSpec), measureLong(heightMeasureSpec));}}/*** Determines the width of this view** @param measureSpec A measureSpec packed into an int* @return The width of the view, honoring constraints from measureSpec*/private int measureLong(int measureSpec) {int result;int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);if ((specMode == MeasureSpec.EXACTLY) || (mViewPager == null)) {//We were told how big to beresult = specSize;} else {//Calculate the width according the views countfinal int count = mViewPager.getAdapter().getCount();result = (int) (getPaddingLeft() + getPaddingRight()+ (count * 2 * mRadius) + (count - 1) * mRadius + 1);//Respect AT_MOST value if that was what is called for by measureSpecif (specMode == MeasureSpec.AT_MOST) {result = Math.min(result, specSize);}}return result;}/*** Determines the height of this view** @param measureSpec A measureSpec packed into an int* @return The height of the view, honoring constraints from measureSpec*/private int measureShort(int measureSpec) {int result;int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);if (specMode == MeasureSpec.EXACTLY) {//We were told how big to beresult = specSize;} else {//Measure the heightresult = (int) (2 * mRadius + getPaddingTop() + getPaddingBottom() + 1);//Respect AT_MOST value if that was what is called for by measureSpecif (specMode == MeasureSpec.AT_MOST) {result = Math.min(result, specSize);}}return result;}
@Overridepublic void setViewPager(ViewPager view) {if (mViewPager == view) {return;}if (mViewPager != null) {mViewPager.addOnPageChangeListener(null);}if (view.getAdapter() == null) {throw new IllegalStateException("ViewPager does not have adapter instance.");}mViewPager = view;mViewPager.addOnPageChangeListener(this);invalidate();}
里面主要的逻辑简单来说就是判断我们的ViewPager是否已经设置adapter,没有的话抛出异常,接着监听ViewPager的PageChangListener事件。
调用invalidate()方法重新绘制CirclePagerIndicator
@Overridepublic void onPageScrollStateChanged(int state) {mScrollState = state;if (mListener != null) {mListener.onPageScrollStateChanged(state);}}@Overridepublic void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {mCurrentPage = position;mPageOffset = positionOffset;invalidate();if (mListener != null) {mListener.onPageScrolled(position, positionOffset, positionOffsetPixels);}}@Overridepublic void onPageSelected(int position) {if (mSnap || mScrollState == ViewPager.SCROLL_STATE_IDLE) {mCurrentPage = position;mSnapPage = position;invalidate();}if (mListener != null) {mListener.onPageSelected(position);}}
@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);if (mViewPager == null) {return;}final int count = mViewPager.getAdapter().getCount();if (count == 0) {return;}if (mCurrentPage >= count) {setCurrentItem(count - 1);return;}int longSize;int longPaddingBefore;int longPaddingAfter;int shortPaddingBefore;// 根据方向的不同初始化各个变量if (mOrientation == HORIZONTAL) {longSize = getWidth();longPaddingBefore = getPaddingLeft();longPaddingAfter = getPaddingRight();shortPaddingBefore = getPaddingTop();} else {longSize = getHeight();longPaddingBefore = getPaddingTop();longPaddingAfter = getPaddingBottom();shortPaddingBefore = getPaddingLeft();}final float threeRadius = mRadius * 3;final float shortOffset = shortPaddingBefore + mRadius;float longOffset = longPaddingBefore + mRadius;/*** 居中显示的时候*/if (mCentered) {longOffset += ((longSize - longPaddingBefore - longPaddingAfter) / 2.0f)- ((count * threeRadius) / 2.0f);}float dX;float dY;float pageFillRadius = mRadius;if (mPaintStroke.getStrokeWidth() > 0) {pageFillRadius -= mPaintStroke.getStrokeWidth() / 2.0f;}//Draw stroked circlesfor (int iLoop = 0; iLoop < count; iLoop++) {float drawLong = longOffset + (iLoop * threeRadius);if (mOrientation == HORIZONTAL) {dX = drawLong;dY = shortOffset;} else {dX = shortOffset;dY = drawLong;}// Only paint fill if not completely transparentif (mPaintPageFill.getAlpha() > 0) {canvas.drawCircle(dX, dY, pageFillRadius, mPaintPageFill);}// Only paint stroke if a stroke width was non-zeroif (pageFillRadius != mRadius) {canvas.drawCircle(dX, dY, mRadius, mPaintStroke);}}//下面绘制移动的实心圆float cx = (mSnap ? mSnapPage : mCurrentPage) * threeRadius;//根据移动的实心圆是否跳跃计算偏移量if (!mSnap) {cx += mPageOffset * threeRadius;}if (mOrientation == HORIZONTAL) {dX = longOffset + cx;dY = shortOffset;} else {dX = shortOffset;dY = longOffset + cx;}canvas.drawCircle(dX, dY, mRadius, mPaintFill);}
其实核心就是根据方向的不同绘制我们的小圆点,那些偏移量是一些数学运算而已,不过别小看这些,计算这些偏移量还是挺繁琐的。
到此我们的源码分析为止
如果各位觉得还行的话,欢迎在github上面 star或者 fork,谢谢 ,github项目地址ViewPagerTabIndicator
关于页面导航器的,可以查看我的这一篇博客仿网易新闻的顶部导航指示器
源码github参考库ViewPagerIndicator:
转载请注明原博客地址: