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 美食大转盘内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 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自定义转盘菜单效果

    最近由于公司项目需要,需要开发一款转盘菜单,费了好大功夫搞出来了,下面分享下 样图 具体功能如下: 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使用surfaceView自定义抽奖大转盘

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

  • android实现简单的活动转盘

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

  • 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实现可点击的幸运大转盘

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

  • Android实现指针刻度转盘

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

  • Android 美食大转盘详解流程

    目录 效果视频 前言 美食大转盘 初始化SurfaceView 测量 绘制 绘制盘块 开始旋转转盘 停止旋转转盘 自定义转盘等份 控件引用 沉浸式体验 效果图 Reveal Animator 效果视频 自定义转盘代码 XML布局代码 Activity代码 代码下载地址 效果视频 前言 你还在为明天吃什么而烦恼嘛 美食大赏帮你解决选择困难症 帮你做出最佳的选择 做吃货,我们是认真的 美食大转盘 本示例使用SurfaceView绘制而成,接下来逐步分析, 文末会贴出全部代码``文末会贴出全部代码``

  • Android实现动态添加数据与堆叠折线图详解流程

    目录 效果视频 引用 描述 导包 代码分析 初始化 动态添加数据 温度数据 湿度数据 光照数据 动态添加X轴时间值 初始化 自动刷新时间实现 尾言 效果视频 引用 描述 本示例采用的是非常.非常.非常好用的一款第三方SDK--helloCharts 传送门 导包 第一步 :导入maven maven { url 'https://jitpack.io' } 第二步:导入依赖 implementation 'com.github.lecho:hellocharts-library:1.5.8@aa

  • Android自定义View实现体重表盘详解流程

    目录 效果视频 分析 起始角度 圆弧 指针 代码 初始化属性 画布 绘制内圆弧 绘制外圆弧 绘制中间指针 绘制中间文字 绘制左右两边文字 动画 全部代码 下载链接 效果视频 分析 起始角度 如下图所示,起点角度为150,终点角度为240 圆弧 白色圆弧为整个圆弧范围,蓝色圆弧为根据数据变动而覆盖白色圆弧,蓝色圆弧比白色圆弧大一点,突出显示 InnerArcPaint.setStrokeWidth( Width * (float)0.1 ); OuterArcPaint.setStrokeWidt

  • Android实现MVVM架构数据刷新详解流程

    目录 效果图 示例结构图 代码解析 导入dataBinding 实体类 xml视图 VM 绑定视图与数据层 效果图 示例结构图 代码解析 导入dataBinding dataBinding{ enabled = true } 实体类 继承BaseObservable public class Sensor extends BaseObservable 为字段添加@Bindable @Bindable public String getTmpValue() { return tmpValue; }

  • Android usb设备权限查询及自动获取详解流程

    看到当上面的对话框弹出时,可以使用命令查看顶层的活动窗口 adb shell dumpsys window | findstr mCurrentFocus mCurrentFocus=Window{41ab0ee0 u0 com.android.systemui/com.android.systemui.usb.UsbPermissionActivity} 这就是应用的位置,当然我们也可以是用grep命令来查找这个对话框的.xml文件,进入android源码然后输入命令: grep '默认情况下

  • Android MVVM架构实现RecyclerView列表详解流程

    目录 效果图 导入引用 导入Recyclerview依赖 导入dataBinding引用 代码解析 建立实体类 建立RecyclerView子项 适配器 建立适配器 设置子项点击事件 adapter全部代码 建立VM层 子项点击事件的使用 VM层代码 数据与视图交互 效果图 导入引用 导入Recyclerview依赖 implementation 'androidx.recyclerview:recyclerview:1.1.0' 导入dataBinding引用 dataBinding { en

  • Android Canvas和Bitmap结合绘图详解流程

    目录 Rect/RectF Matrix Canvas Bitmap Rect/RectF 存储四个值的矩形类:左侧.顶部.右侧和底部.可用于直接在画布上绘制或仅用于存储要绘制的对象的大小.Rect和RectF类之间的区别在于 RectF 存储浮点值,而Rect类存储整数. private static Bitmap createDrawableBitmap(Drawable drawable) { int width = drawable.getIntrinsicWidth(); int he

  • Android Studio真机无线连接USB设备调试运行详解流程

    前言 一般情况下,多数移动开发者使用的是数据线连接电脑,进行各种移动设备的调试,更有胜者,非常迷恋模拟器,模拟器它好不好,答案是好,因为直接运行在电脑上,直接操作,调试,确实方便.尤其是ios开发小伙伴,多数app通过模拟器基本上都能开发好,再用真机验证就可以了.但对于android,就比较恼火了,模拟器一直不好用,卡.慢都是影响撸码的心情.另外android设备随便弄个便宜的都是容易的,基本上是真机开发.那么非一般情况呢,例如我们使用了NDK也就是C层的代码时,由于架构匹配方面,需要用真机开发

  • Android 滑动小圆点ViewPager的两种设置方法详解流程

    第一种方法: 一.测试如下,直接设置小圆点不是图标 二.准备工作 1.在drawable创建dot.xml,设置小圆点,比较方便 <?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_selected="t

  • Android 控制车载蓝牙播放音乐详解流程

    需求:手机端音乐暂停和播放状态从服务端告诉客户端.设备端实现暂停.播放.上一首.下一首等功能 代码路径: packages/apps/Bluetooth/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java packages/apps/Bluetooth/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java pack

随机推荐