Android 美食大转盘详解流程

目录
  • 效果视频
  • 前言
    • 美食大转盘
      • 初始化SurfaceView
      • 测量
      • 绘制
      • 绘制盘块
      • 开始旋转转盘
      • 停止旋转转盘
      • 自定义转盘等份
      • 控件引用
    • 沉浸式体验
      • 效果图
    • Reveal Animator
      • 效果视频
  • 自定义转盘代码
  • XML布局代码
  • Activity代码
  • 代码下载地址

效果视频

前言

你还在为明天吃什么而烦恼嘛
美食大赏帮你解决选择困难症
帮你做出最佳的选择
做吃货,我们是认真的

美食大转盘

本示例使用SurfaceView绘制而成,接下来逐步分析,
文末会贴出全部代码``文末会贴出全部代码``文末会贴出全部代码

初始化SurfaceView

    private void init() {
        mSurfaceHolder = this.getHolder();
        // 管理SurfaceView的生命周期
        mSurfaceHolder.addCallback(this);
        this.setFocusable(true);
        this.setFocusableInTouchMode(true);
        this.setKeepScreenOn(true);
    }

测量

通过获取高、宽,然后减去mPadding 的长度获取到控件中心点,然后存储测量的宽、高

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = Math.min(getMeasuredWidth(), getMeasuredHeight());
        mPadding = getPaddingLeft();
        mRadius = width - mPadding * 2;
        // 中心点
        mCenter = width / 2;
        setMeasuredDimension(width, width);
    }

绘制

绘制圆盘背景

设置背景图片

private Bitmap mBgBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.turntable_bgcolor );

绘制背景突出部分

private void drawBgColor() {
        mCanvas.drawColor(0xFFFFFFFF);
        mCanvas.drawBitmap(mBgBitmap, null, new Rect(mPadding / 2, mPadding / 2, getMeasuredWidth() - mPadding / 2, getMeasuredWidth() - mPadding / 2), null);
    }

绘制盘块

 private void drawArea() {
        // 起始角度
        float tempAngle = mStartAngle;
        // 每个盘块绘制的角度
        float sweepAngele = 360 / mItemCount;
        for (int i = 0; i < mItemCount; i++) {
            mArcPaint.setColor(itemColors[i]);
            // 绘制盘块
            mCanvas.drawArc(mRange, tempAngle, sweepAngele, true, mArcPaint);
            // 绘制文本
            drawText(tempAngle, sweepAngele, itemCharString[i]);
            // 绘制图标
            drawIcon(tempAngle, mBitmaps[i]);

            tempAngle += sweepAngele;
        }
        mStartAngle += mSpeed;

        // 如果需要停止,让转速逐渐变小直到0
        if (isShouldEnd) {
            mSpeed -= mDifferSpeed;
        }
        if (mSpeed <= 0) {
            mSpeed = 0;
            isShouldEnd = false;
        }

    }

绘制盘块内文字

 private void drawText(float tempAngle, float sweepAngele, String itemTextStr) {
        Path path = new Path();
        path.addArc(mRange, tempAngle, sweepAngele);
        // 利用水平偏移量让文字居中
        float textWidth = mTextPaint.measureText(itemTextStr);
        int hOffset = (int) (mRadius * Math.PI / mItemCount / 2 - textWidth / 2);
        // 利用垂直偏移量让文字向圆心靠拢
        int vOffset = mRadius / 2 / 6;
        mCanvas.drawTextOnPath(itemTextStr, path, hOffset, vOffset, mTextPaint);
    }

绘制盘块内图标

private void drawIcon(float tempAngle, Bitmap bitmap) {
        // 约束图片的宽度,为直径的1/8,可以作为可变参数设置
        int imgWidth = mRadius / 8;
        // 获取弧度值
        float angle = (float) ((tempAngle + 360 / mItemCount / 2) * Math.PI / 180);
        // 约定图片位置在直径1/4处
        int x = (int) (mCenter + mRadius / 4 * Math.cos(angle));
        int y = (int) (mCenter + mRadius / 4 * Math.sin(angle));
        // 确定图片位置
        Rect rect = new Rect(x - imgWidth / 2, y - imgWidth / 2, x + imgWidth / 2, y + imgWidth / 2);
        mCanvas.drawBitmap(bitmap, null, rect, null);
    }

开始旋转转盘

   public void Start(int index) {
        if (isStart()) {
            return;
        }
        if (index < 0) {
            mSpeed = 50 * (1 + Math.random() * (0.5));
            isShouldEnd = false;
            return;
        }
    }

停止旋转转盘

    public void Stop() {
        if (isShouldEnd()) {
            return;
        }
        // 将初始角度重置
        mStartAngle = 0;
        isShouldEnd = true;
    }

自定义转盘等份

因为我们要根据需要制作不同等份的转盘,需要跟用户产生交互,所有需要暴露一个方法供用户选择

public void InitNumber(int number){
        if (number <= 0){
            return;
        }
        /**
         * 确保为偶数*/
        if (number % 2 == 0){
            InitArea(number);
        }
    }

本示例以4,6,8等份为例

private void InitArea(int number){
        switch (number){
            case 4:
                fourParts();
                break;
            case 6:
                sixParts();
                break;
            case 8:
                eightParts();
                break;
                default:
                    sixParts();
        }
    }

每一次选择转盘等份,都需要对View进行重绘,需要多次改变图片数量,所以我们将图片设置成一个公共的方法,避免内存浪费

private void fourParts(){
        mItemCount = 4;
        itemCharString = new String[]{"粉条", "面条", "米饭", "粥",};
        itemImages = new int[]{R.drawable.fen, R.drawable.mian, R.drawable.rice, R.drawable.tang};
        itemColors = new int[]{0xffffc300, 0xfff17e01, 0xffffc300, 0xfff17e01};
        InitImage(mItemCount,itemImages);
    }
private void InitImage(int count,int[] item){
        mBitmaps = new Bitmap[mItemCount];
        for (int i = 0; i < count; i++) {
            mBitmaps[i] = BitmapFactory.decodeResource(getResources(), item[i]);
        }
    }

控件引用

布局引用

XML布局文件内引用如下,其中中间的指针为图片类型

<com.franzliszt.foodturntable.Turntable
        android:id="@+id/TurnTable"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="20dp"
        android:layout_margin="10dp"
        android:layout_centerInParent="true"/>

        <ImageView
        android:id="@+id/StartAndEnd"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_centerInParent="true"
        android:src="@drawable/start"
        android:onClick="Start"/>

Activity 引用

默认设置8等份,如果自定义多种等份,默认必须为最大的等份,不然会出现区域空白

turntable.InitNumber( 8 );

图标切换

对开始与暂停图标进行切换,根据点击次数进行切换

 public void Start(View view) {
        count++;
        /*暂停*/
        if (count % 2 == 0) {
            turntable.Stop();
            StartIcon();
        } else {
            /*开始*/
            turntable.Start( -1 );
            StopIcon();
        }
    }

更换转盘等份

首先提供三个RadioButton供用户选择,然后通过SharedPreferences进行存储数据,有关SharedPreferences封装的知识,请移步到另一篇博文Android SharedPreferences存取操作以及封装详解

private void SelectNumber() {
        RG.setOnCheckedChangeListener( new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                switch (checkedId) {
                    case R.id.fourParts:
                        sp.PutData( context, "num", 4 );
                        break;
                    case R.id.sixParts:
                        sp.PutData( context, "num", 6 );
                        break;
                    case R.id.eightParts:
                        sp.PutData( context, "num", 8 );
                        break;
                }
            }
        } );
    }

然后取出数据,并回调等份数值即可

public void Confirm(View view) {
        RevealAnim( view );
        loadingView.setVisibility( View.VISIBLE );
        startLoading();
        startPercentMockThread();
        int num = (int) sp.GetData( context, "num", 0 );
        turntable.InitNumber( num );
    }

沉浸式体验

效果图

沉浸式体验即标题栏与系统状态栏主题一致我们分为以下几个步骤完成以上效果

建立一个样式

首先我们需要建立一个标题栏为空的样式,我们有两种方式去实现
第一种,使用系统提供样式

<style name="NotActionBar_1" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

第二种,直接使用标志进行设置
其实第一种的源码就是第二种,(废话文学)

<style name="NotActionBar" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
       <item name="windowNoTitle">true</item>
        <item name="windowActionBar">false</item>
    </style>

引用空标题栏样式

我们需要在我们要隐藏标题栏的Activity中引用如下代码android:theme="@style/NotActionBar"
配置图如下,地址:清单文件中进行配置

自定义标题栏

我们隐藏了标题栏之后,我们需要自定义一个自己喜欢的风格的标题栏
推荐使用ToolBar,本示例为了简单,就直接使用了TextView,代码如下

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:orientation="horizontal"
        android:background="#cc00cc"
        android:gravity="center"
        android:paddingTop="20dp">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="吃货大赏"
            android:textColor="#ffffff"
            android:textSize="17sp"
            android:padding="10dp"
            />
    </LinearLayout>

合二为一

终于到了最后一步,我们需要在设置Activity对系统栏进行设置
首先需要对版本进行一个判定,防止版本不兼容
其次获取DecorView实例
然后使用标志符对系统状态进行设置,其中以下两个系统标志符为全屏和隐藏系统状态栏
重中之重,以下代码必须在setContentView( R.layout.activity_main );之前执行
重中之重,以下代码必须在setContentView( R.layout.activity_main );之前执行
重中之重,以下代码必须在setContentView( R.layout.activity_main );之前执行

View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN和View.SYSTEM_UI_FLAG_LAYOUT_STABLE
 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate( savedInstanceState );
        if (Build.VERSION.SDK_INT >= 21) {
            View decorView = getWindow().getDecorView();
            decorView.setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE );
            getWindow().setStatusBarColor( Color.TRANSPARENT );
        }
        setContentView( R.layout.activity_main );
        InitView();
        SelectNumber();
    }

Reveal Animator

效果视频

建立一个圆形样式

首先我们需要建立一个圆形样式,在res->drawable下建立一个oval.xml文件
代码如下:

<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
    <solid android:color="#cc00cc"/>
</shape>

动画设置

在XML文件中配置我们建立的圆形样式

 android:background="@drawable/oval"

然后再Activity中进行动画设置,其中createCircularReveal()方法的五个参数分别为:控件,动画开始的中心的X,动画开始的中心的Y,动画开始的半径,动画结束的半径

 private void RevealAnim(View view) {
        Animator animator = ViewAnimationUtils.createCircularReveal(
                view, view.getWidth() / 2, view.getHeight() / 2, view.getWidth(), 0
        );
        animator.setInterpolator( new AccelerateDecelerateInterpolator() );
        animator.setDuration( 2000 );
        animator.start();
    }

自定义转盘代码

public class Turntable extends SurfaceView implements SurfaceHolder.Callback, Runnable {

    private SurfaceHolder mSurfaceHolder;
    private Canvas mCanvas;
    /**
     * 用于SurfaceView绘制的子线程
     */
    private Thread mThread;
    /**
     * 控制子线程开关
     */
    private boolean isRunning;
    /**
     * 字样*/
    private String[] itemCharString;
    /**
     * 图片*/
    private int[] itemImages;
    private Bitmap[] mBitmaps;

    /**
     * 背景
     */
    private Bitmap mBgBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.turntable_bgcolor );
   /**
    * 色块*/
    private int[] itemColors;
   /**
    * 默认等份*/
    private int mItemCount = 8;

    /**
     * 整个盘块的范围
     */
    private RectF mRange = new RectF();
    /**
     * 整个盘块的直径
     */
    private int mRadius;
    /**
     * 绘制盘块的画笔
     */
    private Paint mArcPaint;
    /**
     * 绘制文本的画笔
     */
    private Paint mTextPaint;
    /**
     * 字体大小*/
    private float mTextSize = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_SP, 20, getResources().getDisplayMetrics());
    /**
     * 盘块滚动的速度
     */
    private double mSpeed = 0;

    /**
     * 转盘的中心位置
     */
    private int mCenter;
    /**
     * 这里我们的padding直接取paddingLeft
     */
    private int mPadding;

    /**
     * volatile保证线程间的可见性
     */
    private volatile float mStartAngle = 0;

    /**
     * 判断是否点击了停止按钮
     */
    private boolean isShouldEnd = false;

    /**
     * 设置单次绘制最低时间,如果在该时间内绘制完成,让子线程sleep到改时间结束
     * 这样防止了线程绘制频繁,先消耗性能的问题
     */
    private long mOneTimeMinMillionSeconds = 50;

    private int mDifferSpeed = 1;// 调用停止后递减的速度差值 要大于0

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

    public Turntable(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public Turntable(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
    public void InitNumber(int number){
        if (number <= 0){
            return;
        }
        /**
         * 确保为偶数*/
        if (number % 2 == 0){
            InitArea(number);
        }
    }
    private void InitArea(int number){
        switch (number){
            case 4:
                fourParts();
                break;
            case 6:
                sixParts();
                break;
            case 8:
                eightParts();
                break;
                default:
                    sixParts();
        }
    }
    private void fourParts(){
        mItemCount = 4;
        itemCharString = new String[]{"粉条", "面条", "米饭", "粥",};
        itemImages = new int[]{R.drawable.fen, R.drawable.mian, R.drawable.rice, R.drawable.tang};
        itemColors = new int[]{0xffffc300, 0xfff17e01, 0xffffc300, 0xfff17e01};
        InitImage(mItemCount,itemImages);
    }
    private void sixParts(){
        mItemCount = 6;
        itemCharString = new String[]{"火锅", "汉堡", "巧克力", "奶茶", "蛋糕", "炸鸡"};
        itemImages = new int[]{R.drawable.huoguo, R.drawable.hanbao, R.drawable.qiaokeli, R.drawable.naicha, R.drawable.dangao, R.drawable.zhaji1};
        itemColors = new int[]{0xffffc300, 0xfff17e01, 0xffffc300, 0xfff17e01, 0xffffc300, 0xfff17e01};
        InitImage(mItemCount,itemImages);
    }
    private void eightParts(){
        mItemCount = 8;
        itemCharString = new String[]{"苹果", "香蕉", "榴莲", "西瓜", "葡萄", "火龙果","芒果","草莓"};
        itemImages = new int[]{R.drawable.apple, R.drawable.xaingjiao, R.drawable.liulian, R.drawable.xigua, R.drawable.putao, R.drawable.huolongguo,R.drawable.mangguo,R.drawable.caomei};
        itemColors = new int[]{0xffffc300, 0xfff17e01, 0xffffc300, 0xfff17e01, 0xffffc300, 0xfff17e01,0xffffc300,0xfff17e01};
        InitImage(mItemCount,itemImages);
    }
    private void init() {
        mSurfaceHolder = this.getHolder();
        // 管理SurfaceView的生命周期
        mSurfaceHolder.addCallback(this);
        // 能够获取焦点
        this.setFocusable(true);
        this.setFocusableInTouchMode(true);
        // 保持常亮
        this.setKeepScreenOn(true);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = Math.min(getMeasuredWidth(), getMeasuredHeight());
        mPadding = getPaddingLeft();
        mRadius = width - mPadding * 2;
        // 中心点
        mCenter = width / 2;
        setMeasuredDimension(width, width);
    }
    private void InitImage(int count,int[] item){
        mBitmaps = new Bitmap[mItemCount];
        for (int i = 0; i < count; i++) {
            mBitmaps[i] = BitmapFactory.decodeResource(getResources(), item[i]);
        }
    }
    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
        // 初始化盘块画笔
        mArcPaint = new Paint();
        mArcPaint.setAntiAlias(true);
        mArcPaint.setDither(true);
        // 初始化文字画笔
        mTextPaint = new Paint();
        mTextPaint.setColor(0xffffffff);
        mTextPaint.setTextSize(mTextSize);
        // 初始化盘块绘制范围
        mRange = new RectF(mPadding, mPadding, mPadding + mRadius, mPadding + mRadius);
        // 初始化图片
        InitImage(mItemCount,itemImages);
        isRunning = true;
        mThread = new Thread(this);
        mThread.start();
    }

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
        isRunning = false;
    }

    @Override
    public void run() {
        /**
         * 不断的进行绘制
         */
        while (isRunning) {
            long preMillions = System.currentTimeMillis();
            draw();
            long afterMillions = System.currentTimeMillis();
            long drawOnceTime = afterMillions - preMillions;
            if (drawOnceTime < mOneTimeMinMillionSeconds) {
                try {
                    Thread.sleep(mOneTimeMinMillionSeconds - drawOnceTime);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void draw() {
        try {
            mCanvas = mSurfaceHolder.lockCanvas();
            if (mCanvas != null) {
               /* 绘制背景颜色*/
                drawBgColor();
                /*绘制区域*/
                drawArea();
            }
        } catch (Exception e) {
           e.printStackTrace();
        } finally {
            if (mCanvas != null) {
                mSurfaceHolder.unlockCanvasAndPost(mCanvas);
            }
        }
    }

    /**
     * 绘制盘块
     */
    private void drawArea() {
        // 起始角度
        float tempAngle = mStartAngle;
        // 每个盘块绘制的角度
        float sweepAngele = 360 / mItemCount;
        for (int i = 0; i < mItemCount; i++) {
            mArcPaint.setColor(itemColors[i]);
            // 绘制盘块
            mCanvas.drawArc(mRange, tempAngle, sweepAngele, true, mArcPaint);
            // 绘制文本
            drawText(tempAngle, sweepAngele, itemCharString[i]);
            // 绘制图标
            drawIcon(tempAngle, mBitmaps[i]);

            tempAngle += sweepAngele;
        }
        mStartAngle += mSpeed;

        // 如果需要停止,让转速逐渐变小直到0
        if (isShouldEnd) {
            mSpeed -= mDifferSpeed;
        }
        if (mSpeed <= 0) {
            mSpeed = 0;
            isShouldEnd = false;
        }
    }

    /**
     * 绘制每个盘块的图标
     *
     * @param tempAngle
     * @param bitmap
     */
    private void drawIcon(float tempAngle, Bitmap bitmap) {
        // 约束图片的宽度,为直径的1/8,可以作为可变参数设置
        int imgWidth = mRadius / 8;
        // 获取弧度值
        float angle = (float) ((tempAngle + 360 / mItemCount / 2) * Math.PI / 180);
        // 约定图片位置在直径1/4处
        int x = (int) (mCenter + mRadius / 4 * Math.cos(angle));
        int y = (int) (mCenter + mRadius / 4 * Math.sin(angle));
        // 确定图片位置
        Rect rect = new Rect(x - imgWidth / 2, y - imgWidth / 2, x + imgWidth / 2, y + imgWidth / 2);
        mCanvas.drawBitmap(bitmap, null, rect, null);
    }

    /**
     * 绘制每个盘块的文本
     *
     * @param tempAngle
     * @param sweepAngele
     * @param itemTextStr
     */
    private void drawText(float tempAngle, float sweepAngele, String itemTextStr) {
        Path path = new Path();
        path.addArc(mRange, tempAngle, sweepAngele);
        // 利用水平偏移量让文字居中
        float textWidth = mTextPaint.measureText(itemTextStr);
        int hOffset = (int) (mRadius * Math.PI / mItemCount / 2 - textWidth / 2);
        // 利用垂直偏移量让文字向圆心靠拢
        int vOffset = mRadius / 2 / 6;
        mCanvas.drawTextOnPath(itemTextStr, path, hOffset, vOffset, mTextPaint);
    }

    /**
     * 绘制背景
     */
    private void drawBgColor() {
        mCanvas.drawColor(0xFFFFFFFF);
        mCanvas.drawBitmap(mBgBitmap, null, new Rect(mPadding / 2, mPadding / 2, getMeasuredWidth() - mPadding / 2, getMeasuredWidth() - mPadding / 2), null);
    }

    /**
     * 启动转盘
     * 能够控制到具体某个index范围内停止
     */
    public void Start(int index) {
        if (isStart()) {
            return;
        }
        if (index < 0) {
            mSpeed = 50 * (1 + Math.random() * (0.5));
            isShouldEnd = false;
            return;
        }
    }

    /**
     * 停止转盘
     */
    public void Stop() {
        if (isShouldEnd()) {
            return;
        }
        // 将初始角度重置
        mStartAngle = 0;
        isShouldEnd = true;
    }

    /**
     * 转盘是否在旋转
     *
     * @return
     */
    public boolean isStart() {
        return mSpeed != 0;
    }

    /**
     * 是否停止状态(但可能处于旋转减速到停止)
     *
     * @return
     */
    public boolean isShouldEnd() {
        return isShouldEnd;
    }
}

XML布局代码

<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"
    android:background="#ffffff">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:orientation="horizontal"
        android:background="#cc00cc"
        android:gravity="center"
        android:paddingTop="20dp">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="吃货大赏"
            android:textColor="#ffffff"
            android:textSize="17sp"
            android:padding="10dp"
            />
    </LinearLayout>
    <com.franzliszt.foodturntable.Turntable
        android:id="@+id/TurnTable"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="20dp"
        android:layout_margin="10dp"
        android:layout_centerInParent="true"/>
    <ImageView
        android:id="@+id/StartAndEnd"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_centerInParent="true"
        android:src="@drawable/start"
        android:onClick="Start"/>
    <com.franzliszt.foodturntable.AnimatedCircleLoadingView
        android:id="@+id/loadingView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#000000"
        android:alpha="0.9"
        android:layout_centerInParent="true"
        app:animCircleLoadingView_mainColor="#cc00cc"
        app:animCircleLoadingView_secondaryColor="#ff0000"
        app:animCircleLoadingView_textColor="@android:color/white"
        android:visibility="gone"
        />
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/TurnTable"
        android:orientation="vertical"
        android:layout_marginTop="20dp"
        >
        <RadioGroup
            android:id="@+id/RG"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_gravity="center"
            android:gravity="center"
            >
            <RadioButton
                android:id="@+id/fourParts"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="主食"
                android:layout_marginRight="10dp"
                />
            <RadioButton
                android:id="@+id/sixParts"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="外卖"
                android:layout_marginRight="10dp"
                android:layout_marginLeft="10dp"/>
            <RadioButton
                android:id="@+id/eightParts"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="水果"
                android:layout_marginLeft="10dp"/>
        </RadioGroup>
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="center"
            android:layout_marginTop="20dp">

            <Button
                android:id="@+id/ConfirmSelection"
                android:layout_width="40dp"
                android:layout_height="50dp"
                android:layout_alignParentLeft="true"
                android:layout_alignParentBottom="true"
                android:layout_marginRight="20dp"
                android:background="@drawable/oval"
                android:elevation="4dp"
                android:onClick="Confirm"
                android:text="Confirm"
                android:textAllCaps="false"
                android:textColor="#ffffff"
                android:textSize="12sp" />
            <Button
                android:elevation="10dp"
                android:layout_width="50dp"
                android:layout_height="50dp"
                android:text="Reset"
                android:textSize="12sp"
                android:textAllCaps="false"
                android:textColor="#ffffff"
                android:layout_alignParentRight="true"
                android:layout_alignParentBottom="true"
                android:onClick="Reset"
                android:background="@drawable/oval"
                android:layout_marginLeft="20dp"/>
        </LinearLayout>
    </LinearLayout>
</RelativeLayout>

Activity代码

public class MainActivity extends AppCompatActivity {
    private AnimatedCircleLoadingView loadingView;
    private Turntable turntable;
    private int count = 0;
    private ImageView ChangeStatus;
    private RadioGroup RG;
    private SP sp;
    private Context context = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate( savedInstanceState );
        if (Build.VERSION.SDK_INT >= 21) {
            View decorView = getWindow().getDecorView();
            decorView.setSystemUiVisibility( View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE );
            getWindow().setStatusBarColor( Color.TRANSPARENT );
        }
        setContentView( R.layout.activity_main );
        InitView();
        SelectNumber();
    }

    private void InitView() {
        turntable = findViewById( R.id.TurnTable );
        loadingView = findViewById( R.id.loadingView );
        ChangeStatus = findViewById( R.id.StartAndEnd );
        RG = findViewById( R.id.RG );
        /*默认设置8等份*/
        turntable.InitNumber( 8 );
        if (context == null) {
            context = MainActivity.this;
        }
        sp = new SP( context );
    }

    public void Start(View view) {
        count++;
        /*暂停*/
        if (count % 2 == 0) {
            turntable.Stop();
            StartIcon();
        } else {
            /*开始*/
            turntable.Start( -1 );
            StopIcon();
        }
    }

    private void StartIcon() {
        ChangeStatus.setImageDrawable( getResources().getDrawable( R.drawable.start ) );
    }

    private void StopIcon() {
        ChangeStatus.setImageDrawable( getResources().getDrawable( R.drawable.stop ) );
    }

    public void Confirm(View view) {
        RevealAnim( view );
        loadingView.setVisibility( View.VISIBLE );
        startLoading();
        startPercentMockThread();
        int num = (int) sp.GetData( context, "num", 0 );
        turntable.InitNumber( num );
    }

    public void Reset(View view) {
        RevealAnim( view );
        loadingView.setVisibility( View.GONE );
        resetLoading();
    }

    private void startLoading() {
        loadingView.startIndeterminate();
    }

    private void startPercentMockThread() {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep( 500 );
                    for (int i = 0; i <= 100; i++) {
                        Thread.sleep( 40 );
                        changePercent( i );
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        new Thread(runnable).start();
    }

    private void changePercent(final int percent) {
        runOnUiThread( new Runnable() {
            @Override
            public void run() {
                loadingView.setPercent( percent );
            }
        } );
    }

    public void resetLoading() {
        runOnUiThread( new Runnable() {
            @Override
            public void run() {
                loadingView.resetLoading();
            }
        } );
    }

    private void RevealAnim(View view) {
        Animator animator = ViewAnimationUtils.createCircularReveal(
                view, view.getWidth() / 2, view.getHeight() / 2, view.getWidth(), 0
        );
        animator.setInterpolator( new AccelerateDecelerateInterpolator() );
        animator.setDuration( 2000 );
        animator.start();

    }

    private void SelectNumber() {
        RG.setOnCheckedChangeListener( new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                switch (checkedId) {
                    case R.id.fourParts:
                        sp.PutData( context, "num", 4 );
                        break;
                    case R.id.sixParts:
                        sp.PutData( context, "num", 6 );
                        break;
                    case R.id.eightParts:
                        sp.PutData( context, "num", 8 );
                        break;
                }
            }
        } );
    }
}

代码下载地址

gitee下载地址

到此这篇关于Android 美食大转盘详解流程的文章就介绍到这了,更多相关Android 美食大转盘内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

时间: 2021-11-15

Android使用surfaceView自定义抽奖大转盘

使用surfaceView自定义抽奖大转盘 话不多说,先上效果图 完整代码地址欢迎start 实现思路以及过程 1.首先了解SurfaceView的基本用法,它跟一般的View不太一样,采用的双缓存机制,可以在子线程中绘制View,不会因为绘制耗时而失去流畅性,这也是选择使用SurfaceView去自定义这个抽奖大转盘的原因,毕竟绘制这个转盘的盘块,奖项的图片和文字以及转动都是靠绘制出来的,是一个比较耗时的绘制过程. 2.使用SurfaceView的一般模板样式 一般会用到的成员变量 priva

Android实现可点击的幸运大转盘

之前的项目有一个幸运大转盘的功能,在网上找了很久,都没有合适的方法. 这是效果图,实现目标:十二星座的图片可点击切换选中效果,根据选择不同的星座,实现不同的 方法.之前网上的都是带有指针的,或者可点击改变效果,但是并不知道选择的到底是哪个,即虚拟选择. 实现该功能的主要代码如下: 1.自定义一个布局,存放图片,实现圆形布局. /** * * * CircleMenuLayout.java * * @author wuxiaosu * */ public class CircleMenuLayou

android实现简单的活动转盘

本文实例为大家分享了android实现简单活动转盘的具体代码,供大家参考,具体内容如下 页面 public class CircleTurntableActivity extends AppCompatActivity { private Animation mStartAnimation; private ImageView mLuckyTurntable; private boolean isRunning; private boolean mIsLucky = false; @Overri

Android实现指针刻度转盘

本文实例为大家分享了Android实现指针刻度转盘的具体代码,供大家参考,具体内容如下 一. 先上个效果图,实现如图所示刻度转盘和2个文本的绘制,最后1个刻度绘制的比较长一些(后期会添加动画效果,未完待续-): 二. 话不多说,上代码,Timber可使用Log代替,也可根据自身需求将配置属性放到attrs.xml中去: package com.landleaf.householdtype.widget; import android.content.Context; import android

Android自定义转盘菜单效果

最近由于公司项目需要,需要开发一款转盘菜单,费了好大功夫搞出来了,下面分享下 样图 具体功能如下: import android.graphics.Color; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentPagerAdapter; import android.support.v7.app.AppCompatActivity; im

Android自定义View实现抽奖转盘

本文实例为大家分享了Android自定义View实现抽奖转盘的具体代码,供大家参考,具体内容如下 public class LuckCircle extends SurfaceView implements SurfaceHolder.Callback,Runnable { private SurfaceHolder mHolder; private Canvas mCanvas; //用于绘制的线程 private Thread mThread; //线程开关的控制 private boole

Android自定义view制作抽奖转盘

本文实例为大家分享了Android自定义view制作抽奖转盘的具体代码,供大家参考,具体内容如下 效果图 TurntableActivity package com.bawei.myapplication.turntable; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.MotionEvent; import android.view.View; im

Android自定义view制作绚丽的验证码

废话不多说了,先给大家展示下自定义view效果图,如果大家觉得还不错的话,请继续往下阅读. 怎么样,这种验证码是不是很常见呢,下面我们就自己动手实现这种效果,自己动手,丰衣足食,哈哈~ 一. 自定义view的步骤 自定义view一直被认为android进阶通向高手的必经之路,其实自定义view好简单,自定义view真正难的是如何绘制出高难度的图形,这需要有好的数学功底(后悔没有好好学数学了~),因为绘制图形经常要计算坐标点及类似的几何变换等等.自定义view通常只需要以下几个步骤: 写一个类继承

android自定义view制作圆形进度条效果

还是我们自定View的那几个步骤: 1.自定义View的属性 2.在View的构造方法中获得我们自定义的属性 [ 3.重写onMesure ] 4.重写onDraw 1.自定义属性: <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="CustomTitleView"> <attr name="m

Android自定义View制作仪表盘界面

前言 最近我跟自定义View杠上了,甚至说有点上瘾到走火入魔了.身为菜鸟的我自然要查阅大量的资料,学习大神们的代码,这不,前两天正好在郭神在微信公众号里推送一片自定义控件的文章--一步步实现精美的钟表界面.正适合我这种菜鸟来学习,闲着没事,我就差不多依葫芦画瓢也写了一个自定义表盘View,现在纯粹最为笔记记录下来.先展示下效果图: 下面进入正题 自定义表盘属性 老规矩,先在attrs文件里添加表盘自定义属性 <declare-styleable name="WatchView"&

Android自定义View制作动态炫酷按钮实例解析

普通按钮也就那么几种样式,看着都审美疲劳,先放效果图: 你会不会以为这个按钮是集结了很多动画的产物,我告诉你,并没有.所有的实现都是基于自定义View,采用最底层的onDraw一点一点的画出来的.没有采用一丁点的动画.虽然演示时间很短,但是要完成这么多变化,还是挺吃力. 首先讲解用法: public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState)

Android自定义View实现圆环带数字百分比进度条

分享一个自己制作的Android自定义View.是一个圆环形状的反映真实进度的进度条,百分比的进度文字跟随已完成进度的圆弧转动.以下是效果图: 这个自定义View可以根据需要设定圆环的宽度和百分比文字的大小. 先说一下思路:这个View一共分为三部分:第一部分也就是灰色的圆环部分,代表未完成的进度:第二部分是蓝色的圆弧部分,代表已经完成的进度:第三部分是红色的百分比的数字百分比文本,显示当前确切的完成进度. 下面是View的编写思路: ①:定义三个画笔,分别画灰色圆环,蓝色圆弧,红色文字: ②:

Android自定义view实现阻尼效果的加载动画

效果: 需要知识: 1. 二次贝塞尔曲线 2. 动画知识 3. 基础自定义view知识 先来解释下什么叫阻尼运动 阻尼振动是指,由于振动系统受到摩擦和介质阻力或其他能耗而使振幅随时间逐渐衰减的振动,又称减幅振动.衰减振动.[1] 不论是弹簧振子还是单摆由于外界的摩擦和介质阻力总是存在,在振动过程中要不断克服外界阻力做功,消耗能量,振幅就会逐渐减小,经过一段时间,振动就会完全停下来.这种振幅随时间减小的振动称为阻尼振动.因为振幅与振动的能量有关,阻尼振动也就是能量不断减少的振动.阻尼振动是非简谐运

Android 自定义View 密码框实例代码

暴露您view中所有影响可见外观的属性或者行为. •通过XML添加和设置样式 •通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 效果图展示: 支持的样式 可以通过XML定义影响外边和行为的属性如下 边框圆角值,边框颜色,分割线颜色,边框宽度,密码长度,密码大小,密码颜色 <declare-styleable name="PasswordInputView"> <attr name="borde

Android自定义View详解

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/24252901 很多的Android入门程序猿来说对于Android自定义View,可能都是比较恐惧的,但是这又是高手进阶的必经之路,所有准备在自定义View上面花一些功夫,多写一些文章.先总结下自定义View的步骤: 1.自定义View的属性 2.在View的构造方法中获得我们自定义的属性 [ 3.重写onMesure ] 4.重写onDraw 我把3用[]标出了,所以说3不一