首先来看下效果^_^

实现原理并不复杂,这里我们通过自定义的方式通过位移偏差重绘线段的坐标使之有机连接起来,话不多说,代码走你┏ (゜ω゜)=☞

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() {@Overridepublic 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

更多相关文章

  1. Android滑动效果入门篇———ViewFlipper实现滑动效果
  2. android 画环形的资料
  3. ViewPager+PagerTabStrip实现页面选项卡滑动
  4. Android自定义view贝塞尔曲线
  5. 下拉刷新、上拉加载实战:带你理解自定义View整个过程
  6. android camera 拍照加图片处理
  7. 2014-7-23 Android(安卓)非常好用的组件或框架
  8. Android(安卓)OpenCV(四):绘制几何图形
  9. Android(安卓)彻底征服 ListView 一 (实用篇)

随机推荐

  1. android面试之线程、进程、Handler
  2. Android(安卓)异步任务使用分析
  3. 【源码分享下载】每日更新之高仿京东商城
  4. Android(安卓)Studio很卡,操作很不顺的点
  5. android 引入 iconv 库
  6. Android(安卓)Navigation Architecture C
  7. 4412 矩阵键盘
  8. Android(安卓)完全退出应用的四种方法
  9. 解决android logcat不打印信息
  10. Android中View绘制优化之一---- 优化布局