Android:自定义View实现随滑动由箭头变对勾的指示按钮
2015-07-20 23:31
866 查看
首先来看下效果^_^
实现原理并不复杂,这里我们通过自定义的方式通过位移偏差重绘线段的坐标使之有机连接起来,话不多说,代码走你┏ (゜ω゜)=☞
接下来对代码进行详细分析:
上面数字对应mPoint对应的点
比如我们刚开始绘制的箭头,代码为
其实就是点2连3、点3连6、点3连7,就出来了箭头效果
由箭头随偏差变成对勾的过程如下:
可以看到2和7是固定的,①23趋势是向左下滑动致27,②73是3滑动到2分之3半径处,③63是两点均移动:6移动到1,3移动到2分之3半径处,当它们滑动完成后便变为了对勾;反之对勾变为箭头逆向过程而已。
需要注意的是线移动的过程中的点是对应一定数学关系的,23的3点移动的过程是一直沿着最原始的73这条线滑动的,这里用直角关系可方便求出当前坐标点。
完整demo资源地址:http://download.csdn.net/detail/oushangfeng123/8917893
实现原理并不复杂,这里我们通过自定义的方式通过位移偏差重绘线段的坐标使之有机连接起来,话不多说,代码走你┏ (゜ω゜)=☞
public class MagicButton extends View { // 八个点,用于绘制线段 private PointF mPoint0; private PointF mPoint1; private PointF mPoint2; private PointF mPoint3; private PointF mPoint4; private PointF mPoint5; private PointF mPoint6; private PointF mPoint7; // 箭头到对勾对应的四个点 private PointF mArrawToTickPoint1, mArrawToTickPoint2, mArrawToTickPoint3, mArrawToTickPoint4; // 对勾到箭头对应的四个点 private PointF mTickToArrawPoint1, mTickToArrawPoint2, mTickToArrawPoint3, mTickToArrawPoint4; // 角度,对应于位移偏差,用于动态改变线段的落点 private float mProgress = 0; // view的宽高 private int mViewWidth; private int mViewHeight; // view的中心 private int mViewCenterX; private int mViewCenterY; // 半径 private int mRadius; // 是否绘制箭头标志位 private boolean mIsDrawArrow = true; /** * 画实心圆 */ private Paint mCirclePaint; /** * 画符号 */ private Paint mSymbolPaint; public MagicButton(Context context) { super(context); } public MagicButton(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { // 画背景圆 mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG); mCirclePaint.setColor(Color.parseColor("#ccaaaaaa")); // 画符号 mSymbolPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mSymbolPaint.setColor(getResources().getColor(android.R.color.white)); mSymbolPaint.setStyle(Paint.Style.STROKE); mSymbolPaint.setStrokeCap(Paint.Cap.ROUND); mSymbolPaint.setStrokeJoin(Paint.Join.ROUND); } public MagicButton(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public MagicButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { mViewWidth = measureWidth(widthMeasureSpec); mViewHeight = measureHeight(heightMeasureSpec); mViewCenterX = mViewWidth / 2; mViewCenterY = mViewHeight / 2; mRadius = mViewCenterX <= mViewCenterY ? mViewCenterX : mViewCenterY; mSymbolPaint.setStrokeWidth(mRadius * 2 / 18); setMeasuredDimension(mViewWidth, mViewHeight); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { if (mViewWidth != 0) { // 为八个点定坐标 mPoint0 = new PointF(mRadius / 2, mRadius / 2); mPoint1 = new PointF(mRadius / 2 * 3, mRadius / 2); mPoint2 = new PointF(mRadius / 2, mRadius / 2 * 2); mPoint3 = new PointF(mRadius / 2 * 3, mRadius / 2 * 2); mPoint4 = new PointF(mRadius / 2, mRadius / 2 * 3); mPoint5 = new PointF(mRadius / 2 * 3, mRadius / 2 * 3); mPoint6 = new PointF(mRadius / 2 * 2, mRadius / 2); mPoint7 = new PointF(mRadius / 2 * 2, mRadius / 2 * 3); } super.onSizeChanged(w, h, oldw, oldh); } private int measureHeight(int measureSpec) { int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); int result = 500; if (specMode == MeasureSpec.AT_MOST) { result = specSize; } else if (specMode == MeasureSpec.EXACTLY) { result = specSize; } return result; } private int measureWidth(int measureSpec) { int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); int result = 500; if (specMode == MeasureSpec.AT_MOST) { result = specSize; } else if (specMode == MeasureSpec.EXACTLY) { result = specSize; } return result; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 先绘制背景 canvas.drawCircle(mViewCenterX, mViewCenterY, mRadius, mCirclePaint); if (mIsDrawArrow) { if (mTickToArrawPoint1 == null) { // 刚开始直接绘制箭头 drawLine(canvas, mPoint2, mPoint3); drawLine(canvas, mPoint3, mPoint6); drawLine(canvas, mPoint3, mPoint7); } else { // 偏移过程绘制对勾到箭头过程 drawLine(canvas, mPoint2, mTickToArrawPoint1); drawLine(canvas, mPoint7, mTickToArrawPoint2); drawLine(canvas, mTickToArrawPoint3, mTickToArrawPoint4); } } else { // 偏移过程绘制箭头到对勾过程 drawLine(canvas, mPoint2, mArrawToTickPoint1); drawLine(canvas, mPoint7, mArrawToTickPoint2); drawLine(canvas, mArrawToTickPoint3, mArrawToTickPoint4); } } // 计算对勾到箭头过程对应落点的坐标值 private void tickToArraw() { mProgress = 1 - mProgress; double length = Math.sqrt(2) / 2 * mRadius - mProgress * Math.sqrt(2) / 2 * mRadius; mTickToArrawPoint1 = new PointF((float) (mRadius + (mRadius / 2 - length * Math.sqrt(2) / 2)), (float) (mRadius * 3 / 2 - (mRadius / 2 - length * Math.sqrt(2) / 2))); mTickToArrawPoint2 = new PointF((float) (mRadius * 5 / 4 + mRadius / 4 * mProgress), (float) mRadius); mTickToArrawPoint3 = new PointF((float) (mRadius * 3 / 2 - mRadius / 2 * mProgress), (float) mRadius / 2); mTickToArrawPoint4 = new PointF((float) (mRadius * 5 / 4 + mRadius / 4 * mProgress), (float) mRadius); } // 计算箭头到对勾过程对应落点的坐标值 private void arrawToTick() { double length = mProgress * Math.sqrt(2) / 2 * mRadius; mArrawToTickPoint1 = new PointF((float) (mRadius * 3 / 2 - length * Math.sqrt(2) / 2), (float) (mRadius + length * Math.sqrt(2) / 2)); mArrawToTickPoint2 = new PointF((float) (mRadius * 3 / 2 - mRadius / 4 * mProgress), (float) mRadius); mArrawToTickPoint3 = new PointF((float) (mRadius + mRadius / 2 * mProgress), (float) mRadius / 2); mArrawToTickPoint4 = new PointF((float) (mRadius * 3 / 2 - mRadius / 4 * mProgress), (float) mRadius); } /** * 设置button图标变化的进度 * * @param isDrawArrow 绘制监听 * @param progress 进度 */ public void setProgress(boolean isDrawArrow, float progress) { mIsDrawArrow = isDrawArrow; mProgress = progress; if (isDrawArrow) { tickToArraw(); } else { arrawToTick(); } invalidate(); } // 绘制线段 private void drawLine(Canvas canvas, PointF start, PointF end) { if (start.x != 0 && end.x != 0) { canvas.drawLine(start.x, start.y, end.x, end.y, mSymbolPaint); } } }
接下来对代码进行详细分析:
上面数字对应mPoint对应的点
比如我们刚开始绘制的箭头,代码为
drawLine(canvas, mPoint2, mPoint3); drawLine(canvas, mPoint3, mPoint6); drawLine(canvas, mPoint3, mPoint7);
其实就是点2连3、点3连6、点3连7,就出来了箭头效果
由箭头随偏差变成对勾的过程如下:
可以看到2和7是固定的,①23趋势是向左下滑动致27,②73是3滑动到2分之3半径处,③63是两点均移动:6移动到1,3移动到2分之3半径处,当它们滑动完成后便变为了对勾;反之对勾变为箭头逆向过程而已。
需要注意的是线移动的过程中的点是对应一定数学关系的,23的3点移动的过程是一直沿着最原始的73这条线滑动的,这里用直角关系可方便求出当前坐标点。
//使用十分方便 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <android.support.v4.view.ViewPager android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="match_parent"> </android.support.v4.view.ViewPager> <test.oubowu.com.magicbutton.MagicButton android:id="@+id/button" android:layout_width="70dp" android:layout_height="70dp" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:layout_margin="10dp"/> </RelativeLayout>
mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (mViewPager.getCurrentItem() == mCount - 1) { // 导航完成 Toast.makeText(MainActivity.this, "导航完毕", Toast.LENGTH_LONG).show(); } else if (mViewPager.getCurrentItem() == mCount - 2) { SCROLL_STATE = SCROLL_RIGHT; mViewPager.setCurrentItem(mViewPager.getCurrentItem() + 1); } else { mViewPager.setCurrentItem(mViewPager.getCurrentItem() + 1); } } }); mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { switch (SCROLL_STATE) { case SCROLL_RIGHT: //向右滑动说明是从倒数第一页滑动倒数第二页,调用mButton.setProgress(true, positionOffset),true为由对勾绘制箭头,将偏移量传回来绘制 if (position == mCount - 2) { mButton.setProgress(true, positionOffset); } break; case SCROLL_LEFT: //向左滑动说明是从倒数第二页滑动倒数第一页,调用mButton.setProgress(false, positionOffset),false为由箭头绘制对勾,将偏移量传回来绘制 if (position == mCount - 2) { mButton.setProgress(false, positionOffset); } break; case SCROLL_NONE: break; } } @Override public void onPageSelected(int i) { } @Override public void onPageScrollStateChanged(int i) { } });
完整demo资源地址:http://download.csdn.net/detail/oushangfeng123/8917893
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories