自定义控件Android 自定义view自定义控件

Android超简单实现自定义抽奖转盘效果

2019-03-14  本文已影响274人  itfitness

目录

目录

效果展示

前言

●之前有一个项目需要用到转盘效果,但是那个时候思路不是很清晰,所以引用了一下别人的控件但是别人的源码还是比较复杂的,后来经过分析后使用一种简单的方式写出了之前的效果。
●如果有小伙伴想实现九宫格抽奖效果的话请看我的另一篇文章《Android超简单实现九宫格抽奖》

实现步骤

1.画转盘

public class LuckPan extends View {
    private Paint mPaintArc;//转盘扇形画笔
    private float mRadius;//圆盘的半径
    private RectF rectFPan;//构建转盘的矩形
    private String[] mItemStrs = {"俯卧撑30个","波比跳15个","卷腹30个","高抬腿30下","深蹲30下","开合跳30下"};
    private float mItemAnge;
    public LuckPan(Context context) {
        this(context,null);
    }

    public LuckPan(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

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

    private void init() {
        mPaintArc = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaintArc.setStyle(Paint.Style.FILL);
    }
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mRadius = Math.min(w,h)/2*0.9f;
        //这里是将(0,0)点作为圆心
        rectFPan = new RectF(-mRadius,-mRadius,mRadius,mRadius);
        //每一个Item的角度
        mItemAnge = 360 / mItemStrs.length;
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(getWidth()/2,getHeight()/2);//为了操作方便将画布中心点设置为(0,0)
        drawPanItem(canvas);//画转盘
    }
    private void drawPanItem(Canvas canvas) {
        float startAng = 0;//扇形开始的角度
        for (int x = 1;x<= mItemStrs.length;x++){
        //这里我们通过判断奇数偶数来给转盘设置不同的颜色
            if(x%2 == 1){
                //是奇数
                mPaintArc.setColor(Color.WHITE);
            }else {
                //偶数
                mPaintArc.setColor(Color.parseColor("#F8864A"));
            }
            canvas.drawArc(rectFPan,startAng,mItemAnge,true,mPaintArc);//画扇形
            startAng+=mItemAnge;//每画完一次增加开始角度
        }
    }
}
public class LuckPan extends View {
    //...省略部分
    private Paint mPaintItemStr;//转盘文字画笔
    private ArrayList<Path> mArcPaths;
    private RectF rectFStr;//构建文字圆盘的矩形
    private float mTextSize = 20;//文字大小
    private String[] mItemStrs = {"俯卧撑30个","波比跳15个","卷腹30个","高抬腿30下","深蹲30下","开合跳30下"};//绘制的文字数组
    public LuckPan(Context context) {
        this(context,null);
    }

    public LuckPan(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

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

    private void init() {
        //...省略部分代码
        mPaintItemStr = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaintItemStr.setColor(Color.parseColor("#ED2F2F"));//设置文字颜色
        mPaintItemStr.setStrokeWidth(3);
        mPaintItemStr.setTextAlign(Paint.Align.CENTER);//设置文字水平居中对齐
        mArcPaths = new ArrayList<>();
    }
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        rectFStr = new RectF(-mRadius/7*5,-mRadius/7*5,mRadius/7*5,mRadius/7*5);//构建文字路径的矩形半径为圆盘的五分之七
        mTextSize = mRadius/9;//根据圆盘的半径设置文字大小
        mPaintItemStr.setTextSize(mTextSize);//设置文字大小
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(getWidth()/2,getHeight()/2);//画布中心点设置为(0,0)
        drawPanItem(canvas);
        drawText(canvas);
    }
    //画文字
    private void drawText(Canvas canvas) {
        for(int x = 0;x<mItemStrs.length;x++){
            Path path = mArcPaths.get(x);
            canvas.drawTextOnPath(mItemStrs[x],path,0,0,mPaintItemStr);
        }
    }

    private void drawPanItem(Canvas canvas) {
        float startAng = 0;//扇形开始的角度
        for (int x = 1;x<= mItemStrs.length;x++){
            if(x%2 == 1){
                //是奇数
                mPaintArc.setColor(Color.WHITE);
            }else {
                //偶数
                mPaintArc.setColor(Color.parseColor("#F8864A"));
            }
            //以下是添加文字绘制路径的代码
            Path path = new Path();
            path.addArc(rectFStr,startAng,mItemAnge);//文字的路径圆形比盘的小
            mArcPaths.add(path);
            //==========================
            canvas.drawArc(rectFPan,startAng,mItemAnge,true,mPaintArc);
            startAng+=mItemAnge;
        }
    }
}

3.设置动画实现转动

public class LuckPan extends View {
    //省略部分代码....
    private int mRepeatCount = 4;//转几圈
    private int mLuckNum = 2;//最终停止的位置
    private ObjectAnimator objectAnimator;
    public LuckPan(Context context) {
        this(context,null);
    }

    public LuckPan(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public LuckPan(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
    //省略部分代码....
    public void startAnim(){
        if(objectAnimator!=null){
            objectAnimator.cancel();
        }
        objectAnimator = ObjectAnimator.ofFloat(this, "rotation", 0, mRepeatCount*360+mLuckNum*mItemAnge);
        objectAnimator.setDuration(4000);
        objectAnimator.start();
    }
}
@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(getWidth()/2,getHeight()/2);//画布中心点设置为(0,0)
        canvas.rotate(-90);//将画布旋转-90度
        drawPanItem(canvas);
        drawText(canvas);
    }
public class LuckPan extends View {
    private float mOffsetAngle = 0;//圆盘偏移角度(我们需要添加此属性)
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mItemAnge = 360 / mItemStrs.length;
        mOffsetAngle = mItemAnge/2;//设置偏移角度
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(getWidth()/2,getHeight()/2);//画布中心点设置为(0,0)
        canvas.rotate(-90-mOffsetAngle);//将画布旋转(-90-mOffsetAngle)度
        drawPanItem(canvas);
        drawText(canvas);
    }
}
public class LuckPan extends View {
    private float mStartAngle = 0;//存储圆盘开始的位置
    private ObjectAnimator objectAnimator;
    public void startAnim(){
        if(objectAnimator!=null){
            objectAnimator.cancel();
        }
        float v = mItemAnge*mLuckNum-mStartAngle%360;//如果转过一次了那下次旋转的角度就需要减去上一次多出的,否则结束的位置会不断增加的
        objectAnimator = ObjectAnimator.ofFloat(this, "rotation", mStartAngle, mStartAngle+mRepeatCount*360+v);
        objectAnimator.setDuration(4000);
        objectAnimator.start();
        mStartAngle += mRepeatCount*360+v;//将上一次的角度加进来以达到下次开始就是上次结束的位置
    }
}
//这里其实就是将角度计算那部分+变成-
public void startAnim(){
        if(objectAnimator!=null){
            objectAnimator.cancel();
        }
        float v = mItemAnge*mLuckNum+mStartAngle%360;//如果转过一次了那下次旋转的角度就需要减去上一次多出的,否则结束的位置会不断增加的
        objectAnimator = ObjectAnimator.ofFloat(this, "rotation", mStartAngle, mStartAngle-mRepeatCount*360-v);
        objectAnimator.setDuration(4000);
        objectAnimator.start();
        mStartAngle -= mRepeatCount*360+v;
    }
 /**
     * 设置转盘数据
     * @param items
     */
    public void setItems(String[] items){
        mItemStrs = items;
        mOffsetAngle=0;
        mStartAngle=0;
        mOffsetAngle = 360/items.length/2;//根据item的数量动态调整圆盘偏移角度
        invalidate();
    }
    /**
     * 设置转盘数据
     */
    public void setLuckNumber(int luckNumber){
        mLuckNum = luckNumber;
    }
    

布局代码:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.itfitness.luckpan.MainActivity">
    <com.itfitness.luckpan.widget.LuckPan
        android:id="@+id/pan"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
    <ImageView
        android:id="@+id/img_start"
        android:src="@mipmap/ic_luckdrawstart"
        android:layout_centerInParent="true"
        android:layout_width="130dp"
        android:layout_height="130dp" />
</RelativeLayout>

Avtivity代码:

public class MainActivity extends AppCompatActivity {
    private LuckPan pan;
    private ImageView imgStart;
    private String[] mItemStrs = {"123","撒大声道1","撒大声道2","撒旦说","撒大声道3","哥哥哥","对应效果","对应代码"};
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        pan = (LuckPan) findViewById(R.id.pan);
        imgStart = (ImageView) findViewById(R.id.img_start);
        pan.setItems(mItemStrs);
        pan.setLuckNumber(2);
        imgStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                pan.startAnim();
            }
        });
    }
}
public interface LuckPanAnimEndCallBack {
    void onAnimEnd(String str);
}

LuckPan代码:这里为了方便省略部分代码

public class LuckPan extends View {
    private ObjectAnimator objectAnimator;
    private LuckPanAnimEndCallBack luckPanAnimEndCallBack;

    public LuckPanAnimEndCallBack getLuckPanAnimEndCallBack() {
        return luckPanAnimEndCallBack;
    }

    public void setLuckPanAnimEndCallBack(LuckPanAnimEndCallBack luckPanAnimEndCallBack) {
        this.luckPanAnimEndCallBack = luckPanAnimEndCallBack;
    }

    public void startAnim(){
//        mLuckNum = random.nextInt( mItemStrs.length);//随机生成结束位置
        if(objectAnimator!=null){
            objectAnimator.cancel();
        }
        float v = mItemAnge*mLuckNum+mStartAngle%360;//如果转过一次了那下次旋转的角度就需要减去上一次多出的,否则结束的位置会不断增加的
        objectAnimator = ObjectAnimator.ofFloat(this, "rotation", mStartAngle, mStartAngle-mRepeatCount*360-v);
        objectAnimator.setDuration(4000);
        //在动画的监听事件中增加我们实现的回调函数
        objectAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                if(luckPanAnimEndCallBack!=null){
                    luckPanAnimEndCallBack.onAnimEnd(mItemStrs[mLuckNum]);
                }
            }
        });
        objectAnimator.start();
        mStartAngle -= mRepeatCount*360+v;
    }
}

Activity代码:

public class MainActivity extends AppCompatActivity {
    private LuckPan pan;
    private ImageView imgStart;
    private String[] mItemStrs = {"123","撒大声道1","撒大声道2","撒旦说","撒大声道3","哥哥哥","对应效果","对应代码"};
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        pan = (LuckPan) findViewById(R.id.pan);
        imgStart = (ImageView) findViewById(R.id.img_start);
        pan.setItems(mItemStrs);
        pan.setLuckNumber(2);
        pan.setLuckPanAnimEndCallBack(new LuckPanAnimEndCallBack() {
            @Override
            public void onAnimEnd(String str) {
                Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
            }
        });
        imgStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                pan.startAnim();
            }
        });
    }
}

项目源码:https://github.com/myml666/LuckPan

上一篇下一篇

猜你喜欢

热点阅读