Android自定义View基础篇(二)

近来在win10上面看到了加载做得很酷炫,自己也想在Android上实现类似的效果,来看下效果图:

blue

green

是不是有点类似,首先先讲两种使用方法

使用方法一

和基础控件一样:

    <com.github.ws.loadingview.app.LoadingView xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/lv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="visible"
        app:ballColor="#00ff00"
        app:ballNumber="6" />

app:ballColor表示小球的颜色,app:ballNumber表示小球的个数。

使用方法二

        LoadingDialog dialog=new LoadingDialog(this);
        dialog.show();

实现过程

我们看完了使用方法,接下来我们深入到代码中去了解如何实现的。后面我会附上源码及下载地址。

values文件夹新建attrs:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="LoadingView">
        <attr name="ballColor" format="color"></attr>
        <attr name="ballNumber" format="integer" />
    </declare-styleable>

</resources>

自定义属性:

        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.LoadingView, defStyleAttr, 0);
        ballColor = ta.getColor(R.styleable.LoadingView_ballColor, Color.parseColor("#ff0000"));
        ballNumber = ta.getInteger(R.styleable.LoadingView_ballNumber, 6);
        ta.recycle();

onMeasure方法:

        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(getDefaultMeasureSpec()[0], getDefaultMeasureSpec()[1]);
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(getDefaultMeasureSpec()[0], heightSpecSize);
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSpecSize, getDefaultMeasureSpec()[1]);
        }else {
            setMeasuredDimension(widthSpecSize, heightSpecSize);
        }

我们需要需要绘制,重写onDraw()方法,我们先来看一张草图,自己画的,不是很清晰凑合着看:

caotu

小圆的坐标:

float x = (float) (centerPointX + loadRadius * Math.cos(Math.toRadians(a)));

float y = (float) (centerPointY + loadRadius * Math.sin(Math.toRadians(a)));

开始a的角度是-90度,可以从效果图中看到小球运动一圈后会隐藏0.5秒,一圈的角度刚好是360度,当然这是一个小球的角度,当我们有6个小球的情况呢?

   if ((angle + BALLS_DISTANCE * (i - (ballNumber - 1))) > CIRCLE_ANGLE) {       //第一个小球运动了360度隐藏,最后一个小球运动了360度 ,第一个小球又显示出来。 
                mPaint.setAlpha(0);
                if (angle > CIRCLE_ANGLE + BALLS_DISTANCE * (ballNumber - 1)) {
                    handler.sendEmptyMessageDelayed(MSG_BALL_GONE, 500);
                }
            } else {
                mPaint.setAlpha(255);
            }

sendEmptyMessageDelayed发送了消息,0.5秒后重置小球的位置状态:

if (msg.what == MSG_BALL_GONE) {
                isShow = true;
                angle = 0;
                handler.removeMessages(MSG_BALL_GONE);
                invalidate();
            }

然后就是对isShow的一个状态处理:

            if (isShow) {
                if (angle + BALLS_DISTANCE * i < BALLS_DISTANCE * (ballNumber - 1)) {
                    mPaint.setAlpha(0);
                }
                if (angle > BALLS_DISTANCE * (ballNumber - 1)) {
                    isShow = false;
                }
            }

源码到这里就解析的差不多了,可能你有更好的办法来实现,我只是通过LoadingView来讲解自定义View的基础过程。如果你有更好的建议可以给我留言。下面是源码:

package com.github.ws.loadingview.app;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.WindowManager;

import com.github.ws.loadingview.R;

/**
 * Created by Administrator on 3/18 0018.
 */
public class LoadingView extends View {

    private Paint mPaint;
    private int ballColor;
    private int angle = 0;//旋转角度  0~360

    private int centerPointX;
    private int centerPointY;
    private int ballNumber; //默认小球数量为6

    private int loadRadius;
    private int ballRadius;

    private boolean isShow = false;

    private static final float LOADING_RADIUS = 1 / 16F;
    private static final float POINT_RADIUS = 1 / 8F;
    private static final int MOVE_ANGLE = 8;

    private static final int MSG_BALL_GONE = 2;
    private static final int MSG_BALL_VISIBLE = 1;

    private static final int BALLS_DISTANCE = 32;
    private static final int CIRCLE_ANGLE = 360;

    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (msg.what == MSG_BALL_VISIBLE) {
                angle += MOVE_ANGLE;
                handler.removeMessages(MSG_BALL_VISIBLE);
                invalidate();
            } else if (msg.what == MSG_BALL_GONE) {
                isShow = true;
                angle = 0;
                handler.removeMessages(MSG_BALL_GONE);
                invalidate();
            }
        }
    };

    public LoadingView(Context context) {
        super(context);
        init(context);
    }

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

    public LoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.LoadingView, defStyleAttr, 0);
        ballColor = ta.getColor(R.styleable.LoadingView_ballColor, Color.parseColor("#ff0000"));
        ballNumber = ta.getInteger(R.styleable.LoadingView_ballNumber, 6);
        ta.recycle();
        init(context);
    }

    private void init(Context context) {
        this.setBackgroundColor(Color.parseColor("#6f000000"));
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(ballColor);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(getDefaultMeasureSpec()[0], getDefaultMeasureSpec()[1]);
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(getDefaultMeasureSpec()[0], heightSpecSize);
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSpecSize, getDefaultMeasureSpec()[1]);
        }

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        loadRadius = (int) (Math.min(w, h) * LOADING_RADIUS);
        ballRadius = (int) (loadRadius * POINT_RADIUS);
        centerPointX = w / 2;
        centerPointY = h / 2;
        super.onSizeChanged(w, h, oldw, oldh);
    }


    private int[] getDefaultMeasureSpec() {
        WindowManager wm = (WindowManager) getContext().getSystemService(
                Context.WINDOW_SERVICE);
        DisplayMetrics outMetrics = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(outMetrics);
        return new int[]{outMetrics.widthPixels, outMetrics.heightPixels};
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int i = 0; i < ballNumber; i++) {
            //90度 开始转动
            float x = (float) (centerPointX + loadRadius * Math.cos(Math.toRadians(90 + angle + BALLS_DISTANCE * (i - (ballNumber - 1)))));
            float y = (float) (centerPointY + loadRadius * Math.sin(Math.toRadians(90 + angle + BALLS_DISTANCE * (i - (ballNumber - 1)))));
            if ((angle + BALLS_DISTANCE * (i - (ballNumber - 1))) > CIRCLE_ANGLE) {
                mPaint.setAlpha(0);
                if (angle > CIRCLE_ANGLE + BALLS_DISTANCE * (ballNumber - 1)) {
                    handler.sendEmptyMessageDelayed(MSG_BALL_GONE, 500);
                }
            } else {
                mPaint.setAlpha(255);
            }
            if (isShow) {
                if (angle + BALLS_DISTANCE * i < BALLS_DISTANCE * (ballNumber - 1)) {
                    mPaint.setAlpha(0);
                }
                if (angle > BALLS_DISTANCE * (ballNumber - 1)) {
                    isShow = false;
                }
            }
            canvas.drawCircle(x, y, ballRadius, mPaint);
        }
        handler.sendEmptyMessageDelayed(MSG_BALL_VISIBLE, 15);  //旋转快慢  合适自己调整
    }

    /**
     * 小球颜色
     *
     * @param ballColor
     */
    public void setBallColor(int ballColor) {
        this.ballColor = ballColor;
        if (mPaint != null) {
            mPaint.setColor(ballColor);
        }
    }

    /**
     * 小球的数量
     *
     * @param ballNumber
     */
    public void setBallNumber(int ballNumber) {
        this.ballNumber = ballNumber;
    }


}

分享给大家github地址

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值