引导页圆点指示器
来源:互联网 发布:免激活windows xp 编辑:程序博客网 时间:2024/05/16 14:03
布局代码
<com.godinsec.privacy.guidepage.BezierViewPager android:id="@+id/mViewPager" android:layout_width="fill_parent" android:layout_height="fill_parent" />...... <com.godinsec.privacy.guidepage.BezierRoundView android:id="@+id/bezRound" android:layout_marginBottom="33.333dp" android:layout_gravity="bottom|center" android:layout_width="90dp" android:layout_height="wrap_content" />
attrs.xml
<resources> <declare-styleable name="BezierRoundView"> <attr name="color_bez" format="color"/> <attr name="color_touch" format="color"/> <attr name="color_stroke" format="color"/> <attr name="time_animator" format="integer"/> <attr name="round_count" format="integer"/> <attr name="radius" format="dimension"/> </declare-styleable></resources>
BezierRoundView bezRound = (BezierRoundView) findViewById(R.id.bezRound);
bezRound.attach2ViewPage(mViewPager);
BezierRoundView
package com.godinsec.privacy.guidepage;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Matrix;import android.graphics.Paint;import android.graphics.Path;import android.graphics.PointF;import android.graphics.PorterDuff;import android.graphics.PorterDuffXfermode;import android.graphics.Xfermode;import android.support.v4.view.ViewPager;import android.util.AttributeSet;import android.util.Log;import android.util.TypedValue;import android.view.View;import android.view.WindowManager;public class BezierRoundView extends View implements ViewPager.OnPageChangeListener { public BezierRoundView(Context context) { this(context, null); } public BezierRoundView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public BezierRoundView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5, getResources().getDisplayMetrics()); //默认设置15dp TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.BezierRoundView, defStyleAttr, 0); int n = array.getIndexCount(); for (int i = 0; i < n; i++) { int attr = array.getIndex(i); if (attr == R.styleable.BezierRoundView_color_bez) { color_bez = array.getColor(i, color_bez); } else if (attr == R.styleable.BezierRoundView_color_touch) { color_touch = array.getColor(i, color_touch); } else if (attr == R.styleable.BezierRoundView_color_stroke) { color_stroke = array.getColor(i, color_stroke); } else if (attr == R.styleable.BezierRoundView_time_animator) { time_animator = array.getInteger(i, time_animator); } else if (attr == R.styleable.BezierRoundView_round_count) { default_round_count = array.getInteger(i, default_round_count); } else if (attr == R.styleable.BezierRoundView_radius) { mRadius = array.getDimensionPixelSize(attr, mRadius); } } array.recycle(); init(); } private final String TAG = "QDX"; private int time_animator = 500; //动画时间 private Matrix matrix_bounceL; //将向右弹的动画改为向左 private int color_bez = 0xffffffff; private int color_touch = 0xffffffff; private int color_stroke = Color.parseColor("#393d56"); private void init() { DEFAULT_HEIGHT = mRadius * 3; mBezPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mBezPaint.setColor(color_bez);//默认QQ糖的颜色为粉红色 mBezPaint.setStyle(Paint.Style.FILL); mRoundStrokePaint = new Paint(Paint.ANTI_ALIAS_FLAG); mRoundStrokePaint.setColor(color_stroke); mRoundStrokePaint.setStyle(Paint.Style.STROKE); mRoundStrokePaint.setStrokeWidth(2); mTouchPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mTouchPaint.setColor(color_touch);//默认触摸反馈颜色也为粉红色 mTouchPaint.setStyle(Paint.Style.FILL); mTouchPaint.setXfermode(clearXfermode); mBezPath = new Path(); //y轴一样 p5 = new PointF(mRadius * bezFactor, mRadius); p6 = new PointF(0, mRadius); p7 = new PointF(-mRadius * bezFactor, mRadius); //y轴一样 p0 = new PointF(0, -mRadius); p1 = new PointF(mRadius * bezFactor, -mRadius); p11 = new PointF(-mRadius * bezFactor, -mRadius); //x轴一样 p2 = new PointF(mRadius, -mRadius * bezFactor); p3 = new PointF(mRadius, 0); p4 = new PointF(mRadius, mRadius * bezFactor); //x轴一样 p8 = new PointF(-mRadius, mRadius * bezFactor); p9 = new PointF(-mRadius, 0); p10 = new PointF(-mRadius, -mRadius * bezFactor); matrix_bounceL = new Matrix(); matrix_bounceL.preScale(-1, 1); } private int DEFAULT_WIDTH; private int DEFAULT_HEIGHT; private int default_round_count = 4; //默认圆球的数量 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (DEFAULT_WIDTH == 0) { WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); DEFAULT_WIDTH = wm.getDefaultDisplay().getWidth(); } int width = measureSize(1, DEFAULT_WIDTH, widthMeasureSpec); int height = measureSize(1, DEFAULT_HEIGHT, heightMeasureSpec); setMeasuredDimension(width, height); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWidth = w; mHeight = h; initCountPos(); } /** * 测绘measure * * @param specType 1为宽, 其他为高 * @param contentSize 默认值 */ private int measureSize(int specType, int contentSize, int measureSpec) { int result; //获取测量的模式和Size int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { result = Math.min(contentSize, specSize); } else { result = contentSize; if (specType == 1) { // 根据传人方式计算宽 result += (getPaddingLeft() + getPaddingRight()); } else { // 根据传人方式计算高 result += (getPaddingTop() + getPaddingBottom()); } } return result; } private Paint mBezPaint; private Paint mRoundStrokePaint; private Paint mTouchPaint; private int mWidth; private int mHeight; private int mRadius; private final float bezFactor = 0.551915024494f; private Xfermode clearXfermode = new PorterDuffXfermode(PorterDuff.Mode.CLEAR); private Path mBezPath; private PointF p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11; private float rRadio = 1; //P2,3,4 x轴倍数 private float lRadio = 1; //P8,9,10倍数 private float tbRadio = 1; //y轴缩放倍数 private float boundRadio = 0.55f; //进入另一个圆的回弹效果 /** * 离开圆的阈值 */ private float disL = 0.5f; /** * 最大值的阈值 */ private float disM = 0.8f; /** * 到达下个圆的阈值 */ private float disA = 0.9f; private float[] bezPos; //记录每一个圆心x轴的位置 private float[] xPivotPos; //根据圆心x轴+mRadius,划分成不同的区域 ,主要为了判断触摸x轴的位置 private int curPos = 0; //当前圆的位置 private int nextPos = 0; //圆要到达的下一个位置 private BezierViewPager mViewPage; /** * 关联ViewPager,监听scroll进行改变bezRound */ public void attach2ViewPage(BezierViewPager vPage) { vPage.addOnPageChangeListener(this); this.mViewPage = vPage; this.default_round_count = vPage.getAdapter().getCount(); initCountPos(); } private void initCountPos() { bezPos = new float[default_round_count]; xPivotPos = new float[default_round_count]; for (int i = 0; i < default_round_count; i++) { bezPos[i] = mWidth / (default_round_count + 1) * (i + 1); xPivotPos[i] = mWidth / (default_round_count + 1) * (i + 1) + mRadius; } } //展示QQ糖动画 private float animatedValue; private boolean isAniming = false; @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.translate(0, mHeight / 2); mBezPath.reset(); for (int i = 0; i < default_round_count; i++) { canvas.drawCircle(bezPos[i], 0, mRadius - 2, mRoundStrokePaint); //绘制圆框 } if (animatedValue == 1) { canvas.drawCircle(bezPos[nextPos], 0, mRadius, mBezPaint); return; } canvas.translate(bezPos[curPos], 0); if (0 < animatedValue && animatedValue <= disL) { rRadio = 1f + animatedValue * 2; // [1,2] lRadio = 1f; tbRadio = 1f; } if (disL < animatedValue && animatedValue <= disM) { rRadio = 2 - range0Until1(disL, disM) * 0.5f; // [2,1.5] lRadio = 1 + range0Until1(disL, disM) * 0.5f; // [1,1.5] tbRadio = 1 - range0Until1(disL, disM) / 3; // [1 , 2/3] } if (disM < animatedValue && animatedValue <= disA) { rRadio = 1.5f - range0Until1(disM, disA) * 0.5f; // [1.5,1] lRadio = 1.5f - range0Until1(disM, disA) * (1.5f - boundRadio); //反弹效果,进场 内弹boundRadio tbRadio = (range0Until1(disM, disA) + 2) / 3; // [ 2/3,1] } if (disA < animatedValue && animatedValue <= 1f) { rRadio = 1; tbRadio = 1; lRadio = boundRadio + range0Until1(disA, 1) * (1 - boundRadio); //反弹效果,饱和 } if (animatedValue == 1 || animatedValue == 0) { //防止极其粗暴的滑动 rRadio = 1f; lRadio = 1f; tbRadio = 1f; } boolean isTrans = false; float transX = (nextPos - curPos) * (mWidth / (default_round_count + 1)); if (disL <= animatedValue && animatedValue <= disA) { isTrans = true; transX = transX * (animatedValue - disL) / (disA - disL); } if (disA < animatedValue && animatedValue <= 1) { isTrans = true; } if (isTrans) { canvas.translate(transX, 0); } bounce2RightRound(); if (!direction) { mBezPath.transform(matrix_bounceL); } canvas.drawPath(mBezPath, mBezPaint); if (isTrans) { canvas.save(); } } /** * 通过 path 将向右弹射的动画绘制出来 * 如果要绘制向左的动画,只要设置path的transform(matrix)即可 */ private void bounce2RightRound() { mBezPath.moveTo(p0.x, p0.y * tbRadio); mBezPath.cubicTo(p1.x, p1.y * tbRadio, p2.x * rRadio, p2.y, p3.x * rRadio, p3.y); mBezPath.cubicTo(p4.x * rRadio, p4.y, p5.x, p5.y * tbRadio, p6.x, p6.y * tbRadio); mBezPath.cubicTo(p7.x, p7.y * tbRadio, p8.x * lRadio, p8.y, p9.x * lRadio, p9.y); mBezPath.cubicTo(p10.x * lRadio, p10.y, p11.x, p11.y * tbRadio, p0.x, p0.y * tbRadio); mBezPath.close(); } /** * 将animatedValue值域转化为[0,1] * * @param minValue 大于等于 * @param maxValue 小于等于 * @return 根据当前 animatedValue,返回 [0,1] 对应的数值 */ private float range0Until1(float minValue, float maxValue) { return (animatedValue - minValue) / (maxValue - minValue); } private boolean direction; //方向 , true是位置向右(0->1) /** * @param position 当前cur位置,如果当前是1,手指右滑(vPage向左滑动)那就是0, 左滑至下一个位置才为2 * @param positionOffset [0,1) ,到达下一个pos就置为0 */ @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if (isAniming) //如果是手动选择pos,就用animatorValue去计算 0-->3 return; Log.w(TAG, "onPageScrolled positionOffset==" + positionOffset); animatedValue = positionOffset; direction = ((position + positionOffset) - curPos > 0); //运动方向。 true为右边(手往左滑动) nextPos = direction ? curPos + 1 : curPos - 1; //右 +1 左 -1 if (!direction) //如果是向左 animatedValue = 1 - animatedValue; //让 animatedValue 不管是左滑还是右滑,都从[0,1)开始计算 if (positionOffset == 0) { curPos = position; nextPos = position; } //快速滑动的时候,positionOffset有可能不会置于0 if (direction && position + positionOffset > nextPos) { //向右,而且 curPos = position; nextPos = position + 1; } else if (!direction && position + positionOffset < nextPos) { curPos = position; nextPos = position - 1; } invalidate(); } @Override public void onPageSelected(int position) { onPageSelected.onPageSelected(direction,position); } @Override public void onPageScrollStateChanged(int state) { } OnPageSelected onPageSelected; public void setOnPageSelected(OnPageSelected onPageSelected){ this.onPageSelected = onPageSelected; } public void setAnimDuration(int time) { time_animator = time; } /** * 设置圆的半径 */ public void setRadius(int radius) { this.mRadius = radius; init(); } /** * 设置bez 圆的数量,默认4个 */ public void setRoundCount(int count) { this.default_round_count = count; initCountPos(); } /** * 设置bez 圆的颜色,默认粉红色 */ public void setBezRoundColor(int roundcolor) { color_bez = roundcolor; mBezPaint.setColor(roundcolor); } /** * 触摸效果颜色,默认粉红色 */ public void setTouchColor(int touchColor) { color_touch = touchColor; mTouchPaint.setColor(touchColor); } /** * 圆框的颜色 */ public void setStrokeColor(int strokeColor) { color_stroke = strokeColor; mRoundStrokePaint.setColor(strokeColor); }}
0 0
- 引导页圆点指示器
- 引导页指示器
- 安卓引导页指示器实现
- App引导页的实现带圆点指示器
- 一个带指示器的引导页(ViewPager)
- 用ViewPager实现引导页,带页面指示器
- Android之ViewPager引导页(动态加载指示器)
- Android启动引导页及圆点指示器详解
- Android 引导页动态添加圆点指示器
- Android-实现引导页的圆点指示器
- 指示器
- 指示器
- 指示器
- 引导
- 引导
- 引导
- STL指示器
- 索引指示器
- 实训1/6 Python基本语法、变量、运算符
- Java集合
- python遍历小写英文字母
- selectCursor 和事物
- 【深入Java虚拟机】之三:类初始化
- 引导页圆点指示器
- jQuery篇一
- Module State
- Android Native 使用初识
- android之职责清晰的MVP设计模式
- 移动app测试之多任务和意外情况处理办法
- oracle导入dmp数据文件问题
- OC 重点block-------1-------基本使用
- 【深入Java虚拟机】之四:类加载机制