Android自定义View-流向图

整体效果

在这里插入图片描述

思路

1、该流向图需要实现四个位置,顺时针逆时针都可以流动所以需要做八种判断(左上顺时针,左上逆时针,右上顺时针等)
2、该View的颜色要可配置
3、流动速度要可配置
考虑用Path类先定义出底部的路径,Paint类实现颜色的替换。根据Path播放属性动画,在动画播放过程中,用画笔实时更新点的位置实现流动效果
1、首先创建相应属性,在styleable.xml中创建
<declare-styleable name="Direction">
        <attr name="line_color" format="reference" />
        <attr name="direction">//
            <enum name="left_top" value="0" />
            <enum name="left_bottom" value="1" />
            <enum name="right_top" value="2" />
            <enum name="right_bottom" value="3" />
        </attr>
        <attr name="animation_time" format="integer" />
        <attr name="flow">
            <enum name="pass" value="0" />//顺时针
            <enum name="inverse" value="1" />//逆时针
        </attr>
    </declare-styleable>
2、自定义一个view,在构造函数中获取到自定义的属性值
public class DirectionView extends View {
    public final static Integer LEFT_TOP = 0;
    public final static Integer LEFT_BOTTOM = 1;
    public final static Integer RIGHT_TOP = 2;
    public final static Integer RIGHT_BOTTOM = 3;
    public final static Integer PASS = 0;
    public final static Integer INVERSE = 1;
    private int mTotalWidth, mTotalHeight;
    private Paint linePaint;
    private Paint pointPaint;
    private float cirle = 60;
    private float[] mCurrentPosition = new float[2];
    private int padding = 10;
    private int color;
    private int direction;
    private int animationTime;
    private int flowDirection;

    public DirectionView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

    }

    public DirectionView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.Direction);
        color = ta.getColor(R.styleable.Direction_line_color, Color.GREEN);
        direction = ta.getInt(R.styleable.Direction_direction, 0);
        animationTime = ta.getInt(R.styleable.Direction_animation_time, 5);
        flowDirection = ta.getInt(R.styleable.Direction_flow, 0);
        //初始化画笔
        initPaint();
    }

    private void initPaint() {
        //画线画笔
        linePaint = new Paint();
        //抗锯齿
        linePaint.setAntiAlias(true);
        // 防抖动
        linePaint.setDither(true);
        // 开启图像过滤,对位图进行滤波处理。
        linePaint.setFilterBitmap(true);
        linePaint.setColor(color);
        //空心圆
        linePaint.setStyle(Paint.Style.STROKE);
        linePaint.setStrokeWidth(5);

        //圆点画笔
        pointPaint = new Paint();
        //抗锯齿
        pointPaint.setAntiAlias(true);
        // 防抖动
        pointPaint.setDither(true);
        // 开启图像过滤,对位图进行滤波处理。
        pointPaint.setFilterBitmap(true);
        pointPaint.setColor(color);
        //空心圆
        pointPaint.setStyle(Paint.Style.STROKE);
        pointPaint.setStrokeWidth(10);
    }

    private PathMeasure pathMeasure;
    private Path linePath;

    private void initPath() {
        linePath = new Path();
        if (direction == LEFT_TOP) {
            if (flowDirection == INVERSE) {
                linePath.moveTo(padding, mTotalHeight);
                linePath.lineTo(mTotalWidth - cirle, mTotalHeight);
                RectF oval = new RectF(mTotalWidth - 2 * cirle, mTotalHeight - 2 * cirle, mTotalWidth, mTotalHeight);
                //画一个90度的圆 用addArc会中断动画 要用arcTo
                linePath.arcTo(oval, 90, -90);
                linePath.lineTo(mTotalWidth, padding);
            } else if (flowDirection == PASS) {
                linePath.moveTo(mTotalWidth, padding);
                linePath.lineTo(mTotalWidth, mTotalHeight - cirle);
                RectF oval = new RectF(mTotalWidth - 2 * cirle, mTotalHeight - 2 * cirle, mTotalWidth, mTotalHeight);
                linePath.arcTo(oval, 0, 90);
                linePath.lineTo(padding, mTotalHeight);
            }
        } else if (direction == LEFT_BOTTOM) {
            if (flowDirection == INVERSE) {
                linePath.moveTo(mTotalWidth, mTotalHeight);
                linePath.lineTo(mTotalWidth, cirle + padding);
                RectF oval = new RectF(mTotalWidth - 2 * cirle, padding, mTotalWidth, padding + 2 * cirle);
                linePath.arcTo(oval, 0, -90);
                linePath.lineTo(padding, padding);
            } else if (flowDirection == PASS) {
                linePath.moveTo(padding, padding);
                linePath.lineTo(mTotalWidth - cirle, padding);
                RectF oval = new RectF(mTotalWidth - 2 * cirle, padding, mTotalWidth, padding + 2 * cirle);
                linePath.arcTo(oval, 270, 90);
                linePath.lineTo(mTotalWidth, mTotalHeight);
            }
        } else if (direction == RIGHT_TOP) {
            if (flowDirection == INVERSE) {
                linePath.moveTo(padding, padding);
                linePath.lineTo(padding, mTotalHeight - cirle);
                RectF oval = new RectF(padding, mTotalHeight - 2 * cirle, padding + 2 * cirle, mTotalHeight);
                linePath.arcTo(oval, 180, -90);
                linePath.lineTo(mTotalWidth, mTotalHeight);
            } else if (flowDirection == PASS) {
                linePath.moveTo(mTotalWidth, mTotalHeight);
                linePath.lineTo(padding + cirle, mTotalHeight);
                RectF oval = new RectF(padding, mTotalHeight - 2 * cirle, padding + 2 * cirle, mTotalHeight);
                linePath.arcTo(oval, 90, 90);
                linePath.lineTo(padding, padding);
            }
        } else if (direction == RIGHT_BOTTOM) {
            if (flowDirection == INVERSE) {
                linePath.moveTo(mTotalWidth, padding);
                linePath.lineTo(padding + cirle, padding);
                RectF oval = new RectF(padding, padding, padding + 2 * cirle, padding + 2 * cirle);
                linePath.arcTo(oval, 270, -90);
                linePath.lineTo(padding, mTotalHeight);
            } else if (flowDirection == PASS) {
                linePath.moveTo(padding, mTotalHeight);
                linePath.lineTo(padding, padding + cirle);
                RectF oval = new RectF(padding, padding, padding + 2 * cirle, padding + 2 * cirle);
                linePath.arcTo(oval, 180, 90);
                linePath.lineTo(mTotalWidth, padding);
            }
        }
        pathMeasure = new PathMeasure(linePath, false);
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, pathMeasure.getLength());
        //设置动画时长
        valueAnimator.setDuration(animationTime * 1000);
        //加入差值器
        valueAnimator.setInterpolator(new LinearInterpolator());
        //设置无限次循环
        valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
        //添加监听
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                //获取当前位置
                float value = (float) animation.getAnimatedValue();
                pathMeasure.getPosTan(value, mCurrentPosition, null);
                //打印当前坐标
                Log.i("zyq", mCurrentPosition[0] + "    " + mCurrentPosition[1]);
                //设置视图坐标
                invalidate();
            }
        });
        valueAnimator.start();
    }

    @Override
    protected void onDraw(final Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawPath(linePath, linePaint);
        canvas.drawCircle(mCurrentPosition[0], mCurrentPosition[1], 5, pointPaint);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mTotalWidth = w - padding;
        mTotalHeight = h - padding;
        initPath();
    }

}
3、在xml中使用并更换属性
<LinearLayout
            android:layout_marginTop="@dimen/space_300"
            android:layout_width="match_parent"
            android:layout_height="@dimen/space_200"
            android:orientation="vertical">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:orientation="horizontal">

                <com.aiswei.storage.customview.DirectionView
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_gravity="center_horizontal"
                    android:layout_marginTop="@dimen/space_20"
                    android:layout_weight="1"
                    android:padding="@dimen/space_5"
                    app:direction="left_top"
                    app:flow="inverse" />

                <com.aiswei.storage.customview.DirectionView
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_gravity="center_horizontal"
                    android:layout_marginTop="@dimen/space_20"
                    android:layout_weight="1"
                    android:padding="@dimen/space_5"
                    app:direction="right_top"
                    app:flow="pass" />
            </LinearLayout>

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="1"
                android:orientation="horizontal">

                <com.aiswei.storage.customview.DirectionView
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_gravity="center_horizontal"
                    android:layout_marginTop="@dimen/space_20"
                    android:layout_weight="1"
                    android:padding="@dimen/space_5"
                    app:direction="left_bottom"
                    app:flow="pass" />

                <com.aiswei.storage.customview.DirectionView
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_gravity="center_horizontal"
                    android:layout_marginTop="@dimen/space_20"
                    android:layout_weight="1"
                    android:padding="@dimen/space_5"
                    app:direction="right_bottom"
                    app:flow="inverse" />
            </LinearLayout>
        </LinearLayout>

欢迎指教和询问

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值