小六边形组成的加载动画

先分析一下:

注释都在代码里了:

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

/**
 * 六边形的加载动画
 * 中间对应着代码中的最后一个,最右边的为第一个,逆时针依次
 */
public class LoadingHexagon extends View {

    private Context context;

    /**
     * 六边形之间的间距值
     */
    private float space;

    /**
     * 每个小六边形的半径
     */
    private float hexagonRadius;


    private Paint paint;

    /**
     * 存放7个六边形的中点坐标
     */
    private List<HexagonPoint> centrePointList = new ArrayList<>();


    /**
     * 渐显的状态 false
     * 渐暗的状态 true
     */
    private boolean animationStart = false;
    //当前执行动画的索引
    private int currentPoint = 0;
    /**
     * 控制动画
     */
    private Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if(animationStart){
                //慢慢都显示
                if(centrePointList.get(currentPoint).scale == 1){
                    currentPoint ++;
                }
                //Log.e("tag","逐渐显示" + currentPoint);
            }else{
                //逐渐隐藏
                if(centrePointList.get(currentPoint).scale == 0){
                    currentPoint ++;
                }
                //Log.e("tag","逐渐隐藏" + currentPoint);
            }
            if(currentPoint == centrePointList.size()){
                animationStart = !animationStart;
                currentPoint = 0;
                //Log.e("tag","转换状态");
            }
            flush();
            handler.sendEmptyMessageDelayed(0,animDelayedTime);
        }
    };

    /**
     * 一个小六边形一个变化的时间
     */
    private int animDelayedTime = 15;

    /**
     * 动画是否在执行
     */
    private boolean isExecute = false;

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

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

    public LoadingHexagon(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        initData();
    }

    private void initData() {
        space = dp2px(context,1);//水平与垂直间隙为1dp
        hexagonRadius = dp2px(context,15);//半径

        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL);
        paint.setStrokeWidth(3);
        paint.setColor(Color.parseColor("#6699ff"));
        //圆角处理
        paint.setPathEffect(new CornerPathEffect(hexagonRadius * 0.1f));

        initCentrePoint();//确定中心坐标

        initPeakPoint();//确定顶点坐标
    }


    /**
     * 确定7个六边形的中心坐标(没有缩放时的坐标)
     * 最右边为第一个,后面的按顺时针方向增加
     */
    private void initCentrePoint() {
        //大半径R,每个小六边形中心点到原点的距离 = 2 * r * cos(30度) + 水平间距
        float radius = (float) (2 * hexagonRadius*Math.cos(2*Math.PI/360*30) + space);
        centrePointList.removeAll(centrePointList);
        for (int i=0;i<6;i++){
            centrePointList.add(new HexagonPoint(getCosX(60*i,radius),getSinY(60*i,radius)));
        }
        //最后一个原点坐标
        centrePointList.add(new HexagonPoint(0,0));
    }

    /**
     * 确定每个小六边形的顶点坐标
     * 因为每个小六边形的半径都是相等的,并且相对中心点来说与顶点的关系都是一样的
     * 所以中心坐标加上对应的值就可以了
     */
    private void initPeakPoint() {
        //外围六个
        for (int i=0;i<6;i++){
            HexagonPoint hexagonPoint = centrePointList.get(i);
            //确定对应的六个顶点坐标
            hexagonPoint.peakPointList.removeAll(hexagonPoint.peakPointList);
            for(int j=0;j<6;j++){
                float x = hexagonPoint.point_x + getCosX(30+j*60,hexagonPoint.scale*hexagonRadius);
                float y = hexagonPoint.point_y + getSinY(30+j*60,hexagonPoint.scale*hexagonRadius);
                hexagonPoint.peakPointList.add(new PeakPoint(x,y));
            }
        }
        //中间的小六边形的六个顶点
        centrePointList.get(6).peakPointList.removeAll(centrePointList.get(6).peakPointList);
        for(int j=0;j<6;j++){
            float x = 0 + getCosX(30+j*60,centrePointList.get(6).scale*hexagonRadius);
            float y = 0 + getSinY(30+j*60,centrePointList.get(6).scale*hexagonRadius);
            centrePointList.get(6).peakPointList.add(new PeakPoint(x,y));
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //平移坐标原点到中心位置
        canvas.translate(getWidth()/2,getHeight()/2);

        initPeakPoint();//获取顶点坐标

        Path path = new Path();
        //画外围六个小六边形中心坐标组成的大六边形
        /*path.moveTo(centrePointList.get(0).point_x,centrePointList.get(0).point_y);
        for (int i = 1; i < centrePointList.size()-1; i++) {
            path.lineTo(centrePointList.get(i).point_x,centrePointList.get(i).point_y);
        }
        //封闭
        path.close();
        paint.setColor(Color.parseColor("#ff0000"));
        paint.setStyle(Paint.Style.STROKE);
        canvas.drawPath(path,paint);*/


        //画外围六个六边形
        //paint.setColor(Color.parseColor("#6699ff"));
        //paint.setStyle(Paint.Style.FILL);
        for (int i = 0; i < centrePointList.size()-1; i++) {
            path.reset();
            for (int j = 0; j < 6; j++) {
                if(j==0){
                    path.moveTo(centrePointList.get(i).peakPointList.get(j).point_x,centrePointList.get(i).peakPointList.get(j).point_y);
                }else{
                    path.lineTo(centrePointList.get(i).peakPointList.get(j).point_x,centrePointList.get(i).peakPointList.get(j).point_y);
                }
            }
            path.close();
            paint.setAlpha(centrePointList.get(i).alpha);
            canvas.drawPath(path,paint);
        }
        //画在中心的六边形
        path.reset();
        for (int j = 0; j < 6; j++) {
            if(j == 0){
                path.moveTo(centrePointList.get(6).peakPointList.get(j).point_x,centrePointList.get(6).peakPointList.get(j).point_y);
            }else{
                path.lineTo(centrePointList.get(6).peakPointList.get(j).point_x,centrePointList.get(6).peakPointList.get(j).point_y);
            }
        }
        path.close();
        paint.setAlpha(centrePointList.get(6).alpha);
        canvas.drawPath(path,paint);
    }


    /**
     * 刷新数据
     */
    private void flush() {
        if (animationStart) {//逐个显示出来
            for (int i = 0; i < centrePointList.size(); i++) {
                if(currentPoint == i){
                    //在缩放的小六边形
                    centrePointList.get(i).addStart();
                    break;
                }
            }
        } else {//逐个消失
            for (int i = 0; i < centrePointList.size(); i++) {
                if(currentPoint == i){
                    //在缩放的小六边形
                    centrePointList.get(i).subtractStart();
                    break;
                }
            }
        }
        invalidate();
    }


    /**
     * 根据角度值获得X坐标
     * @param angle  角度值(0-360)
     * @param radius  半径
     * @return 该角度在X轴对应的坐标值
     */
    private float getCosX(int angle,float radius){
        double jiaodu = (2*Math.PI / 360) * angle;//要偏移的角度,弧度与角度的转换
        return (float) (radius*Math.cos(jiaodu));
    }

    /**
     * 根据角度值获得Y坐标
     * @param angle
     * @return
     */
    private float getSinY(int angle,float radius){
        double jiaodu = (2*Math.PI / 360) * angle;
        return (float) (radius*Math.sin(jiaodu));
    }


    /**
     * dp转换为px
     */
    public float dp2px(Context context , int dpVal){
        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dpVal,context.getResources().getDisplayMetrics());
    }

    /**
     * 六边形的坐标点
     */
    private class HexagonPoint{
        /**
         * X轴坐标
         */
        public float point_x;

        /**
         * Y轴坐标
         */
        public float point_y;

        /**
         * 缩放值 0-1f
         */
        public float scale = 1;

        /**
         * 透明度 0-255  对应透明度的10次,所以每次步进值为25.5
         */
        public int alpha = 255;
        //透明度每次改变量 变化范围为[0,255]    这里分15次为一个周期
        private int alpahChange = 26;
        private float scaleChange = 1.0f/10;

        /**
         * 存放每个小六边形对应的六个顶点坐标
         */
        public List<PeakPoint> peakPointList;

        public HexagonPoint(float point_x, float point_y) {
            this.point_x = point_x;
            this.point_y = point_y;
            peakPointList = new ArrayList<PeakPoint>();
        }

        /**
         * 大小与透明度的增加
         */
        public void addStart(){
            alpha = alpha + alpahChange;
            if(alpha >=255){
                alpha = 255;
            }
            scale = scale + scaleChange;
            if(scale >= 1){
                scale = 1;
            }
        }

        /**
         * 大小与透明度的减少
         */
        public void subtractStart(){
            alpha = alpha - alpahChange;
            if(alpha <= 0){
                alpha = 0;
            }
            scale = scale - scaleChange;
            if(scale <= 0){
                scale = 0;
            }
        }
    }

    /**
     * 顶点坐标
     */
    private class PeakPoint{
        /**
         * X轴坐标
         */
        public float point_x;

        /**
         * Y轴坐标
         */
        public float point_y;

        public PeakPoint(float point_x, float point_y) {
            this.point_x = point_x;
            this.point_y = point_y;
        }
    }

    /**
     * 停止动画
     */
    public void stopAnim(){
        handler.removeMessages(0);
        isExecute = false;//记录状态
    }

    /**
     * 重新开始动画
     */
    public void startAnim(){
        if(!isExecute){
            handler.removeMessages(0);
            isExecute = true;
            //接着暂停的状态开始
            initCentrePoint();
            initPeakPoint();
            currentPoint = 0;
            handler.sendEmptyMessageDelayed(0,animDelayedTime);
        }
    }

    /**
     * 接着暂停的状态开始执行
     */
    public void continueAnim(){
        if(!isExecute){
            handler.removeMessages(0);
            isExecute = true;
            handler.sendEmptyMessageDelayed(0,animDelayedTime);
        }
    }

    /**
     * 设置小六边形的半径
     * @param hexagonRadius
     */
    public void setHexagonRadius(float hexagonRadius){
        this.hexagonRadius = hexagonRadius;
        initCentrePoint();
        invalidate();
    }

    /**
     * 设置小六边形之间的间隙
     * @param space 间隙值(水平方向与垂直方向的间隙相等)
     */
    public void setHexagonSpace(float space){
        this.space = space;
        initCentrePoint();
        invalidate();
    }

    /**
     * 设置小六边形的颜色
     * @param color
     */
    public void setHexagonColor(int color){
        paint.setColor(color);
        invalidate();
    }

    /**
     * 设置一个小六边形动画一个动作的时间
     * @param animDelayedTime  默认为15ms
     */
    public void setHexagonAnimTime(int animDelayedTime){
        this.animDelayedTime = animDelayedTime;
    }
}

使用:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
    android:orientation="vertical"
    tools:context="com.tsgithub.MainActivity">

    <Button
        android:id="@+id/btn_start"
        android:text="重新开始"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <Button
        android:id="@+id/btn_continue"
        android:text="接着暂停的状态开始"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <Button
        android:id="@+id/btn_stop"
        android:text="暂停"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <com.tsgithub.view.LoadingHexagon
        android:id="@+id/loadingHexagon"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>
View Code
public class MainActivity extends AppCompatActivity {

    private LoadingHexagon loadingHexagon;

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

        loadingHexagon = (LoadingHexagon) findViewById(R.id.loadingHexagon);

        //设置小六边形的颜色 间距 半径
        /*loadingHexagon.setHexagonColor(Color.parseColor("#ffcccc"));
        loadingHexagon.setHexagonSpace(20);
        loadingHexagon.setHexagonRadius(100);*/

        loadingHexagon.setHexagonAnimTime(10);//设置一个动画的时间,默认15

        findViewById(R.id.btn_start).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                loadingHexagon.startAnim();
            }
        });
        findViewById(R.id.btn_stop).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                loadingHexagon.stopAnim();
            }
        });
        findViewById(R.id.btn_continue).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                loadingHexagon.continueAnim();
            }
        });

    }

    @Override
    protected void onDestroy() {
        //需要停止动画
        loadingHexagon.stopAnim();
        super.onDestroy();
    }
}

效果图:

 

posted @ 2017-05-04 10:54  ts-android  阅读(492)  评论(0编辑  收藏  举报