android 自定义View SpinnerLoader使用解析,让你摆脱系统难看的进度条

  1. 云栖社区>
  2. 博客>
  3. 正文

android 自定义View SpinnerLoader使用解析,让你摆脱系统难看的进度条

王亟亟 2015-08-19 10:18:00 浏览1187
展开阅读全文

转载请注明出处王亟亟的大牛之路

又一种进度条的实现,项目结构很小,轻松移植。

包结构
这里写图片描述

运行效果:
这里写图片描述

自定义View:(伸手党只要看一些final变量的注释就能自行修改)

public class SpinnerLoader extends View {
    //旋转的点的数量,默认为9(45度的情况下超过9也不显示,9以下会少点)
    private static final int POINTS_COUNT = 9;
    //小圆转动速度,数值越大越快
    private static final int STEP = 5;
    //等层圆转动速度,数值越大越快
    private static final int BIG_STEP = 1;
    private static final int DEFAULT_COLOR = Color.rgb(87, 247, 250);
    private static final float DEFAULT_RADUIS = 180;
    private static final float DEFAULT_CIRCLE_RADUIS = 40;
    private static final float DEAFULT_MOVE_RADUIS = 30;
    //底层圆之间的角度
    private static final int SPLIT_ANGLE = 45;
    //小圆与底层圆接触时的圆的大小,数值越大圆越大
    private static final int ADDITION_LENGTH = 6;
    private static final int FLAT_ANGLE = 180;

    /**
     * for save and restore instance of view.
     */
    private static final String INSTANCE_STATE = "saved_instance";
    private static final String ANGLE = "angle";
    private static final String BIGCIRCLECENTERX = "bigCircleCenterX";
    private static final String BIGCIRCLECENTERY = "bigCircleCenterY";
    private static final String RADUIS = "raduis";
    private static final String CIRCLERADUIS = "circleRaduis";
    private static final String MOVERADUIS = "moveRaduis";
    private static final String POINTCOLOR = "pointColor";
    private static final String STARTX1 = "startX1";
    private static final String STARTY1 = "startY1";
    private static final String ENDX1 = "endX1";
    private static final String ENDY1 = "endY1";
    private static final String STARTX2 = "startX2";
    private static final String STARTY2 = "startY2";
    private static final String ENDX2 = "endX2";
    private static final String ENDY2 = "endY2";
    private static final String CONTROLX1 = "controlX1";
    private static final String CONTROLY1 = "controlY1";
    private static final String BIGSTEP = "bigStep";

    private CirclePoint[] circlePoints = new CirclePoint[POINTS_COUNT];

    private int angle = 0;
    private int bigStep = BIG_STEP;
    float bigCircleCenterX;
    float bigCircleCenterY;
    float raduis;
    float circleRaduis;
    float moveRaduis;
    int pointColor;


    private float startX1;
    private float startY1;
    private float startX2;
    private float startY2;

    private float controlX1;
    private float controlY1;

    private float endX1;
    private float endY1;
    private float endX2;
    private float endY2;

    private Path path1;
    private Paint circlePaint;
    private Paint linePaint;

    private boolean isFirst = true;

    public SpinnerLoader(Context context) {
        this(context, null);
    }

    public SpinnerLoader(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

        final TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SpinnerLoader,
                defStyleAttr, 0);
        pointColor = attributes.getColor(R.styleable.SpinnerLoader_point_color, DEFAULT_COLOR);
        boolean isdynamic = attributes.getBoolean(R.styleable.SpinnerLoader_isdynamic, true);
        isDynamic(isdynamic);
        attributes.recycle();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(measure(widthMeasureSpec, true), measure(heightMeasureSpec, false));
    }

    private int measure(int measureSpec, boolean isWidth) {
        int result;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        int padding = isWidth ? getPaddingLeft() + getPaddingRight() : getPaddingTop() + getPaddingBottom();
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        } else {
            result = isWidth ? getSuggestedMinimumWidth() : getSuggestedMinimumHeight();
            result += padding;
            if (mode == MeasureSpec.AT_MOST) {
                if (isWidth) {
                    result = Math.max(result, size);
                } else {
                    result = Math.min(result, size);
                }
            }
        }
        return result;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (isFirst) {
            init();
            isFirst = false;
        }
        for (int i = 0; i < POINTS_COUNT - 1; i ++) {
            CirclePoint p = circlePoints[i];
            p.x = getPaddingLeft() + bigCircleCenterX + (float)Math.cos(Math.toRadians(p.currentAngle)) * raduis;
            p.y = getPaddingTop() + bigCircleCenterY + (float)Math.sin(Math.toRadians(p.currentAngle)) * raduis;
            p.currentAngle = p.currentAngle + bigStep;
            canvas.drawCircle(p.x, p.y, p.raduis, circlePaint);
        }
        calculateMoveingPoint(canvas);
        angle = angle + STEP;
        invalidate();
    }

    protected void init() {
        float temp = getHeight() > getWidth() ? getWidth() / 2 : getHeight() / 2;
        raduis = temp - temp / DEFAULT_RADUIS * DEFAULT_CIRCLE_RADUIS;
        circleRaduis = DEFAULT_CIRCLE_RADUIS / DEFAULT_RADUIS * raduis;
        moveRaduis = DEAFULT_MOVE_RADUIS / DEFAULT_RADUIS * raduis;
        bigCircleCenterX = getPaddingLeft() + getWidth() / 2;
        bigCircleCenterY = getPaddingTop() + getHeight() / 2;

        path1 = new Path();
        initializePaints();
        initializePoints();
    }

    /**
     * 计算动态的点
     * @param canvas
     */
    protected void calculateMoveingPoint(Canvas canvas) {
        CirclePoint p = circlePoints[POINTS_COUNT - 1];
        p.x = bigCircleCenterX + (float)Math.cos(Math.toRadians(angle)) * raduis;
        p.y = bigCircleCenterY + (float)Math.sin(Math.toRadians(angle)) * raduis;
        canvas.drawCircle(p.x, p.y, p.raduis, circlePaint);
        for (int i = 0; i < POINTS_COUNT - 1; i++) {
            CirclePoint biggerP1 = circlePoints[i];

            //是否相交
            if (isIntersect(p, biggerP1)) {
                canvas.drawCircle(biggerP1.x, biggerP1.y, biggerP1.raduis + ADDITION_LENGTH*(1-getDistanceRatio(p, biggerP1)), circlePaint);
            }

            if (isConnect(p, biggerP1)) {
                float headOffsetX1 = (float)(circleRaduis*Math.sin(Math.atan((p.y - biggerP1.y) / (p.x - biggerP1.x))));
                float headOffsetY1 = (float)(circleRaduis*Math.cos(Math.atan((p.y - biggerP1.y) / (p.x - biggerP1.x))));
                float footOffsetX1 = (float)(moveRaduis*Math.sin(Math.atan((p.y - biggerP1.y) / (p.x - biggerP1.x))));
                float footOffsetY1 = (float)(moveRaduis*Math.cos(Math.atan((p.y - biggerP1.y) / (p.x - biggerP1.x))));

                startX1 = biggerP1.x - headOffsetX1;
                startY1 = biggerP1.y + headOffsetY1;

                endX1 = biggerP1.x + headOffsetX1;
                endY1 = biggerP1.y - headOffsetY1;

                startX2 = p.x - footOffsetX1;
                startY2 = p.y + footOffsetY1;

                endX2 = p.x + footOffsetX1;
                endY2 = p.y - footOffsetY1;

                controlX1 = (biggerP1.x + p.x) / 2;
                controlY1 = (biggerP1.y + p.y) / 2;

                path1.reset();
                path1.moveTo(startX1, startY1);
                path1.quadTo(controlX1, controlY1, startX2, startY2);
                path1.lineTo(endX2, endY2);
                path1.quadTo(controlX1, controlY1, endX1, endY1);
                path1.lineTo(startX1, startY1);
                canvas.drawPath(path1, linePaint);
            }
        }
    }

    protected void initializePoints() {
        for (int i = 0; i < POINTS_COUNT; i++) {
            CirclePoint p = new CirclePoint();
            p.currentAngle = SPLIT_ANGLE * i;
            p.x = getPaddingLeft() + bigCircleCenterX + (float)Math.cos(Math.toRadians(p.currentAngle)) * raduis;
            p.y = getPaddingTop() + bigCircleCenterY + (float)Math.sin(Math.toRadians(p.currentAngle)) * raduis;
            p.color = pointColor;
            p.raduis = circleRaduis;
            if (i == POINTS_COUNT - 1) {
                p.raduis = moveRaduis;
            }
            circlePoints[i] = p;
        }
    }

    protected void initializePaints() {
        circlePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        circlePaint.setColor(pointColor);

        linePaint = new Paint();
        linePaint.setAntiAlias(true);
        linePaint.setStyle(Paint.Style.FILL_AND_STROKE);
        linePaint.setStrokeWidth(1);
        linePaint.setColor(pointColor);

    }

    private boolean isIntersect(CirclePoint a, CirclePoint b) {
        float distance = (float)Math.sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
        return distance < (a.raduis + b.raduis);
    }

    private boolean isConnect(CirclePoint a, CirclePoint b) {
        float distance = (float)Math.sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
        return distance < raduis * Math.cos(Math.toRadians((FLAT_ANGLE - SPLIT_ANGLE) / 2));
    }

    private float getDistanceRatio(CirclePoint a, CirclePoint b) {
        float distance = (float)Math.sqrt((a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y));
        return distance / (a.raduis + b.raduis);
    }

    public void setPointcolor(int color) {
        pointColor = color;
        if (linePaint != null) {
            linePaint.setColor(color);
        }
        if (circlePaint != null) {
            circlePaint.setColor(color);
        }

    }

    public void isDynamic(boolean dynamic) {
        if (dynamic) {
            bigStep = BIG_STEP;
        } else {
            bigStep = 0;
        }
    }

    protected float dp2px(float dp) {
        final float scale = getResources().getDisplayMetrics().density;
        return  dp * scale + 0.5f;
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        final Bundle bundle = new Bundle();
        bundle.putParcelable(INSTANCE_STATE,super.onSaveInstanceState());
        bundle.putInt(ANGLE, angle);
        bundle.putFloat(BIGCIRCLECENTERX, bigCircleCenterX);
        bundle.putFloat(BIGCIRCLECENTERY, bigCircleCenterY);
        bundle.putFloat(RADUIS, raduis);
        bundle.putFloat(CIRCLERADUIS, circleRaduis);
        bundle.putFloat(MOVERADUIS, moveRaduis);
        bundle.putFloat(STARTX1, startX1);
        bundle.putFloat(STARTY1, startY1);
        bundle.putFloat(ENDX1, endX1);
        bundle.putFloat(ENDY1, endY1);
        bundle.putFloat(STARTX2, startX2);
        bundle.putFloat(STARTY2, startY2);
        bundle.putFloat(ENDX2, endX2);
        bundle.putFloat(ENDY2, endY2);
        bundle.putFloat(CONTROLX1, controlX1);
        bundle.putFloat(CONTROLY1, controlY1);
        bundle.putInt(POINTCOLOR, pointColor);
        bundle.putInt(BIGSTEP, bigStep);
        return bundle;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if(state instanceof Bundle){
            final Bundle bundle = (Bundle)state;
            angle = bundle.getInt(ANGLE);
            bigCircleCenterX = bundle.getFloat(BIGCIRCLECENTERX);
            bigCircleCenterY = bundle.getFloat(BIGCIRCLECENTERY);
            raduis = bundle.getFloat(RADUIS);
            circleRaduis = bundle.getFloat(CIRCLERADUIS);
            moveRaduis = bundle.getFloat(MOVERADUIS);
            startX1 = bundle.getFloat(STARTX1);
            startY1 = bundle.getFloat(STARTY1);
            endX1 = bundle.getFloat(ENDX1);
            endY1 = bundle.getFloat(ENDY1);
            startX2 = bundle.getFloat(STARTX2);
            startY2 = bundle.getFloat(STARTY2);
            endX2 = bundle.getFloat(ENDX2);
            endY2 = bundle.getFloat(ENDY2);
            controlX1 = bundle.getFloat(CONTROLX1);
            controlY1 = bundle.getFloat(CONTROLY1);
            pointColor = bundle.getInt(POINTCOLOR);
            bigStep = bundle.getInt(BIGSTEP);
            init();
            super.onRestoreInstanceState(bundle.getParcelable(INSTANCE_STATE));
            return;
        }
        super.onRestoreInstanceState(state);
    }

    static class CirclePoint {
        public int currentAngle;
        public float raduis;
        public float x;
        public float y;
        public int color;
    }

}

MainActivity(可以无视)

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

布局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">

    <customdialog.wjj.com.customdialog.SpinnerLoader
        android:id="@+id/one"
        android:layout_marginTop="30dp"
        android:layout_centerHorizontal="true"
        app:point_color="#FF6347"
        app:isdynamic="false"
        android:layout_width="40dp"
        android:layout_height="40dp"/>

    <customdialog.wjj.com.customdialog.SpinnerLoader
        android:id="@+id/two"
        android:layout_marginTop="10dp"
        android:layout_centerHorizontal="true"
        android:layout_below="@+id/one"
        android:layout_width="90dp"
        android:layout_height="90dp"
        app:point_color="#FF00FF"
        />

    <customdialog.wjj.com.customdialog.SpinnerLoader
        android:id="@+id/three"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="10dp"
        android:layout_below="@+id/two"
        android:layout_width="120dp"
        android:layout_height="120dp"
        app:point_color="#D2691E"
        />

</RelativeLayout>

所用到的attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="SpinnerLoader">
        <attr name="point_color" format="color"/>
        <attr name="isdynamic" format="boolean"/>
    </declare-styleable>

</resources>

伸手党们可以下载就用,简单粗暴,为了生活
源码地址:http://yunpan.cn/cdUai7ndmFhHQ 访问密码 9491

网友评论

登录后评论
0/500
评论
王亟亟
+ 关注