Android 扇形网络控件 - 无网络视图(动画)

前言

一般在APP没有网络的情况下,我们都会用一个无网络的提示图标,在提示方面为了统一app的情况,我们一般使用简单的提示图标,偶尔只需要改变一下图标的颜色就一举两得,而不需要让PS来换一次颜色。当然app有图标特殊要求的就另当别论了。

效果图

这里写图片描述

当你第一眼看到这样的图,二话不说直接让UI给你切一张图标来的快对吧,我其实开始也是这么想的,但是到了做的app越来越多的时候,你就会发现就算是用这个简单的图标,随着主题颜色的改变,我们的图标颜色也会发生相应的改变,我们需要的就随意改变这个图标颜色,那么改变图标颜色就难了,还是自己着手自己写一个控件吧,看着这个控件我有点愣住了,因为有平滑效果,怎么弄呢。我问了一下UI他们怎么绘制的,结果他给我说这个图标他们是用圆来完成的。从中得到启发的我,开始着手来完成这样一个控件。

控件分析

这里写图片描述

使用

(1)将下面的源码复制到自己项目中,本人不提倡用Git依赖,依赖多了打包慢,写代码卡。

(2)以下是可以用到的方法

    /**
     * 设置扇形高度
     *
     * @param arcStrokeWidth
     */
    public void setStrokeWidth(float arcStrokeWidth)

    /**
     * 设置扇形的半径
     *
     * @param radius
     */
    public void setRadius(float radius)

    /**
     * 设置扇形的间距
     *
     * @param arcPadding
     */
    public void setArcPadding(float arcPadding)

    /**
     * 设置扇形数量
     *
     * @param arcCount
     */
    public void setArcCount(int arcCount)
    /**
     * 设置开始角度
     *
     * @param startAngle
     */
    public void setStartAngle(float startAngle)

    /**
     * 设置颜色
     *
     * @param color
     */
    public void setColor(int color) 
    /**
     * 设置动画时间
     *
     * @param duration
     */
    public void setDuration(int duration)

    /**
     * 开始动画
     */
    public void startAnimation()

    /**
     * 停止动画
     */
    public void stopAnimation()

布局xml

    <com.android.widget.ArcNetView
        android:layout_width="match_parent"
        android:layout_height="wrap_content" 
        app:arc_color="@color/colorAccent"
        app:arc_animation="true"/>

注:包名请修改成自己项目的包名(ArcNetView复制到自己项目中);

attr.xml

    <declare-styleable name="ArcNetView">
        <attr name="arc_count" format="integer" />
        <attr name="arc_color" format="color" />
        <attr name="arc_radius" format="dimension" />
        <attr name="arc_stroke_width" format="dimension" />
        <attr name="arc_padding" format="dimension" />
        <attr name="arc_animation" format="boolean" />

ArcNetView

import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;

import com.android.R;

/**
 * Created by Relin
 * on 2018-09-11.
 * 扇形网络控件
 */
public class ArcNetView extends View {

    private Canvas canvas;
    private Paint paint;
    private float arcStrokeWidth = dpToPx(24);
    private float radius = dpToPx(28);
    private float arcPadding = dpToPx(20);
    private int arcCount = 3;
    private float width;
    private float height;
    private float startAngle = -135;
    private float sweepAngle = Math.abs(startAngle + 90) * 2;
    private float circleY;
    private float circleX;
    private ValueAnimator animator;
    private boolean isStart;
    private int duration = 600;
    private int arcColor = Color.parseColor("#005F91");

    public ArcNetView(Context context) {
        super(context);
        init(context, null);
    }

    public ArcNetView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public ArcNetView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    /**
     * 初始化
     *
     * @param context
     * @param attrs
     */
    private void init(Context context, AttributeSet attrs) {
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.NetworkView);
        arcCount = array.getColor(R.styleable.NetworkView_arc_count, arcCount);
        arcColor = array.getColor(R.styleable.NetworkView_arc_color, arcColor);
        radius = array.getDimension(R.styleable.NetworkView_arc_radius, radius);
        arcStrokeWidth = array.getDimension(R.styleable.NetworkView_arc_stroke_width, arcStrokeWidth);
        arcPadding = array.getDimension(R.styleable.NetworkView_arc_padding, arcPadding);
        isStart = array.getBoolean(R.styleable.NetworkView_arc_animation, isStart);
        array.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int w = widthSpecSize;
        int h = heightSpecSize;
        int needHeight = (int) (radius * arcCount + arcStrokeWidth / 2 + arcPadding * arcCount);
        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
            w = needHeight;
            h = needHeight;
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            w = needHeight;
            h = heightSpecSize;
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            w = widthSpecSize;
            h = needHeight;
        }
        setMeasuredDimension(w, h);
        //获取最终的宽高
        width = getMeasuredWidth();
        height = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
        radius -= getPaddingLeft() - getPaddingRight();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        this.canvas = canvas;
        //初始化画笔
        paint = new Paint();
        paint.setColor(arcColor);
        paint.setStrokeWidth(arcStrokeWidth);
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        circleX = width / 2;
        circleY = height / 2 + radius * arcCount / 2 + arcStrokeWidth / (arcCount * 2);
        //类型绘制
        if (!isStart) {
            for (int i = 0; i < arcCount; i++) {
                drawArcView(canvas, i);
            }
            //绘制最底部圆
            drawCircle(canvas, arcColor, circleX, circleY, arcStrokeWidth / 2);
        } else {
            animationDraw();
        }
    }

    /**
     * 动态绘制
     */
    private void animationDraw() {
        animator = ValueAnimator.ofInt(-1, arcCount + 1);// -1 0 1 2 3
        animator.setDuration(duration);
        animator.setInterpolator(new LinearInterpolator());
        animator.setRepeatCount(-1);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                if (value == arcCount) {
                    canvas.restore();
                }
                if (value == -1 || value == arcCount) {
                    drawCircle(canvas, arcColor, circleX, circleY, arcStrokeWidth / 2);
                }
                if (value > -1) {
                    drawArcView(canvas, value);
                }
                invalidate();
                Log.e("RRL", "value:" + value);
            }
        });
        animator.start();
    }

    /**
     * 绘制上部分
     *
     * @param canvas   画布
     * @param acrIndex 绘制下标[第几个扇形]
     */
    private void drawArcView(Canvas canvas, int acrIndex) {
        //绘制扇形
        float arcRadius = radius + (arcStrokeWidth / 2 + arcPadding) * acrIndex;
        RectF rectF = new RectF(circleX - arcRadius, circleY - arcRadius, circleX + arcRadius, circleY + arcRadius);
        canvas.drawArc(rectF, startAngle, sweepAngle, false, paint);
        //绘制两端圆点
        double angle = (sweepAngle / 2) * Math.PI / 180;
        float x = (float) (Math.sin(angle) * arcRadius);
        float y = (float) (Math.cos(angle) * arcRadius);
        //左边圆点
        drawCircle(canvas, arcColor, circleX - x, circleY - y, arcStrokeWidth / 2);
        //右边圆点
        drawCircle(canvas, arcColor, circleX + x, circleY - y, arcStrokeWidth / 2);
    }

    /**
     * 绘制圆
     *
     * @param canvas  画布
     * @param color   颜色
     * @param circleX 圆中心X
     * @param circleY 圆中心Y
     * @param radius  圆半径
     */
    public void drawCircle(Canvas canvas, int color, float circleX, float circleY, float radius) {
        Paint circlePaint = new Paint();
        circlePaint.setColor(color);
        circlePaint.setStyle(Paint.Style.FILL);
        circlePaint.setAntiAlias(true);
        canvas.drawCircle(circleX, circleY, radius, circlePaint);
    }

    public static float dpToPx(float dp) {
        return dp * getScreenDensity();
    }

    public static float getScreenDensity() {
        return Resources.getSystem().getDisplayMetrics().density;
    }

    /**
     * 设置扇形高度
     *
     * @param arcStrokeWidth
     */
    public void setStrokeWidth(float arcStrokeWidth) {
        this.arcStrokeWidth = dpToPx(arcStrokeWidth);
    }


    /**
     * 设置扇形的半径
     *
     * @param radius
     */
    public void setRadius(float radius) {
        this.radius = dpToPx(radius);
    }

    /**
     * 设置扇形的间距
     *
     * @param arcPadding
     */
    public void setArcPadding(float arcPadding) {
        this.arcPadding = dpToPx(arcPadding);
    }

    /**
     * 设置扇形数量
     *
     * @param arcCount
     */
    public void setArcCount(int arcCount) {
        this.arcCount = arcCount;
    }

    /**
     * 设置开始角度
     *
     * @param startAngle
     */
    public void setStartAngle(float startAngle) {
        this.startAngle = startAngle;
        this.sweepAngle = Math.abs(startAngle + 90) * 2;
    }

    /**
     * 设置颜色
     *
     * @param color
     */
    public void setColor(int color) {
        this.arcColor = color;
    }

    /**
     * 设置动画时间
     *
     * @param duration
     */
    public void setDuration(int duration) {
        this.duration = duration;
    }

    /**
     * 开始动画
     */
    public void startAnimation() {
        isStart = true;
        invalidate();
    }

    /**
     * 停止动画
     */
    public void stopAnimation() {
        isStart = false;
        if (animator != null) {
            animator.removeAllUpdateListeners();
            animator = null;
        }
        invalidate();
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值