Android 抽奖转盘的实现

** 本篇文章已授权公众号 guolin_blog (郭霖)独家发布 **

序言

最近需要实现一个抽奖的控件,我简单搜索了下,感觉要不很多细节地方没有处理,要么,根本就不能用。索性想自己实现个,从千图网搜了下,挑选了个自己比较喜欢的出来,psd打开后效果如下:


这里写图片描述



最终实现效果如下:


点击Go按钮自动滚动:


这里写图片描述



随手势滚动:


这里写图片描述



实现的效果还不错,因为是模拟器加录制,画面可能会有些卡顿,真机其实蛮顺畅的,下面简单的讲讲实现的步骤。


实现

  • 1,绘制。

    首先第一个我们要它给画出来,但是要注意的就是Android所对应的坐标系的问题。


    这里写图片描述

   for(int i= 0;i<6;i++){
            if(i%2 == 0){
                canvas.drawArc(rectF,angle,60,true,dPaint);
            }else
            {
                canvas.drawArc(rectF,angle,60,true,sPaint);
            }
            angle += 60;
        }

        for(int i=0;i<6;i++){
            drawIcon(width/2, height/2, radius, InitAngle, i, canvas);
            InitAngle += 60;
        }

        for(int i=0;i<6;i++){
            drawText(InitAngle+30,strs[i], 2*radius, textPaint, canvas,rectF);
            InitAngle += 60;
        }

其中有两个地方需要注意下,第一个就是画弧的地方第一个角度是起始角度,第二个是弧的角度,并不是结束的角度,所以是固定值60。第二个地方就是计算具体的x,y的值的时候要根据弧度去计算,不能根据角度。

  • 2.使用属性动画让其自动旋转。

如果用SurfaceView去进行重绘旋转存在一些问题,比如旋转的角度不好控制,旋转的速度不好控制。但是用属性动画,这个问题就很好解决了。

  ValueAnimator animtor = ValueAnimator.ofInt(InitAngle,DesRotate);
        animtor.setInterpolator(new AccelerateDecelerateInterpolator());
        animtor.setDuration(time);
        animtor.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int updateValue = (int) animation.getAnimatedValue();
                InitAngle = (updateValue % 360 + 360) % 360;
                ViewCompat.postInvalidateOnAnimation(RotatePan.this);
            }
        });
        animtor.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);

                int pos = InitAngle / 60;

                if(pos >= 0 && pos <= 3){
                    pos = 3 - pos;
                }else{
                    pos = (6-pos) + 3;
                }

                if(l != null)
                    l.endAnimation(pos);
            }
        });
        animtor.start();

用动画最重要的就是,如何计算出结束动画后的位置,那么把最终旋转的总角度%360°就得到最后一圈实际旋转的角度,再除以60就得到了到底选择了几个位置,因为一个位置占据60°,这应该不难理解。

    @Override
        public void onAnimationEnd(Animation animation) {
            int pos = startDegree % 360 / 60;

            if(pos >= 0 && pos <= 3){
                pos = 3 - pos;
            }else{
                pos = (6-pos) + 3;
            }

            if(l != null)
                l.endAnimation(pos);
        }

但是问题又来了,Android所对应的坐标系,0的位置应该是最底下,而指针的位置是在最上面,所以,我们结合上面的坐标系来看,还需要处理下,如上面的代码所示。

  • 3.利用Scroller和GestureDetector对手势进行处理。

    触摸事件的处理,最后到底允不允许转盘随手势滑动呢?其实貌似做成这样也就可以了,但是最后还是实现了下,用到了GestureDetector 和 Scroller这个类。其实做法有很多,首先获取我们的滑动的距离,Math.sqrt(dx * dx + dy * dy),然后无非就是把这个距离转换成我们需要的角度,你可以把这个距离当作我们的周长来处理,也可以把这个距离当作我们总的旋转的角度来处理。之后就是随着时间的流逝,不断的刷新我们的界面了。

 @Override
    public boolean onTouchEvent(MotionEvent event) {

        boolean consume = mDetector.onTouchEvent(event);
        if(consume)
        {
            getParent().requestDisallowInterceptTouchEvent(true);
            return true;
        }

        return super.onTouchEvent(event);
    }


    public void setRotate(int rotation){
        rotation = (rotation % 360 + 360) % 360;
        InitAngle = rotation;
        ViewCompat.postInvalidateOnAnimation(this);
    }


    @Override
    public void computeScroll() {

        if(scroller.computeScrollOffset()){
            setRotate(scroller.getCurrY());
        }

        super.computeScroll();
    }

    private class RotatePanGestureListener extends GestureDetector.SimpleOnGestureListener{

        @Override
        public boolean onDown(MotionEvent e) {
            return super.onDown(e);
        }

        @Override
        public boolean onDoubleTap(MotionEvent e) {
            return false;
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            float centerX = (RotatePan.this.getLeft() + RotatePan.this.getRight())*0.5f;
            float centerY = (RotatePan.this.getTop() + RotatePan.this.getBottom())*0.5f;

            float scrollTheta = vectorToScalarScroll(distanceX, distanceY, e2.getX() - centerX, e2.getY() -
                    centerY);
            int rotate = InitAngle -
                    (int) scrollTheta / FLING_VELOCITY_DOWNSCALE;

            setRotate(rotate);
            return true;
        }

        @Override
        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
            float centerX = (RotatePan.this.getLeft() + RotatePan.this.getRight())*0.5f;
            float centerY = (RotatePan.this.getTop() + RotatePan.this.getBottom())*0.5f;

            float scrollTheta = vectorToScalarScroll(velocityX, velocityY, e2.getX() - centerX, e2.getY() -
                    centerY);
            scroller.abortAnimation();
            scroller.fling(0, InitAngle , 0, (int) scrollTheta / FLING_VELOCITY_DOWNSCALE,
                    0, 0, Integer.MIN_VALUE, Integer.MAX_VALUE);
            return true;
        }

    }

    private float vectorToScalarScroll(float dx, float dy, float x, float y) {
        // get the length of the vector
        float l = (float) Math.sqrt(dx * dx + dy * dy);

        // decide if the scalar should be negative or positive by finding
        // the dot product of the vector perpendicular to (x,y).
        float crossX = -y;
        float crossY = x;

        float dot = (crossX * dx + crossY * dy);
        float sign = Math.signum(dot);

        return l * sign;
    }


  • 4.剩余问题处理

    还存在个问题,如果没有手势去操作转盘,那我们很容易判断它所旋转的角度,但是有手势的参与,我们很容易旋转到转盘中两个分片中间的位置,那么,我们在让它旋转之前,要简单处理下,避免这种事情发生。

   //TODO 为了每次都能旋转到转盘的中间位置
        int offRotate = DesRotate % 360 % 60;
        DesRotate -= offRotate;
        DesRotate += 30;

这样不管手势怎么操作,我最终都是旋转到分片的中间位置了。


  • 5.转动到指定某个区域
   /**
     * 开始转动
     * @param pos 如果 pos = -1 则随机,如果指定某个值,则转到某个指定区域
     */
    public void startRotate(int pos){

        int lap = (int) (Math.random()*12) + 4;

        int angle = 0;
        if(pos < 0){
            angle = (int) (Math.random() * 360);
        }else{
            int initPos  = queryPosition();
            if(pos > initPos){
                angle = (pos - initPos)*60;
                lap -= 1;
                angle = 360 - angle;
            }else if(pos < initPos){
                angle = (initPos - pos)*60;
            }else{
                //nothing to do.
            }
        }

好多人加我QQ,问我怎么转动到指定位置,所以更新了下代码,上传到github上去了,这里做个日志。如果传的是 -1 则随机转动,如果传的是大于0,则转动到指定位置。


  • 6.改变转盘数量
   <com.hr.nipuream.luckpan.view.RotatePan
        android:id="@+id/rotatePan"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="78dp"
        android:layout_centerHorizontal="true"
        luckpan:pannum="8"
        luckpan:names="@array/names"
        luckpan:icons="@array/icons"
        />


        <resources>
            <string-array name="names">
                <item>action</item>
                <item>adventure</item>
                <item>combat</item>
                <item>moba</item>
                <item>other</item>
                <item>role</item>
                <item>sports</item>
                <item>words</item>
            </string-array>

            <string-array name="icons">
                <item>action</item>
                <item>adventure</item>
                <item>combat</item>
                <item>moba</item>
                <item>other</item>
                <item>role</item>
                <item>sports</item>
                <item>words</item>
            </string-array>
        </resources>

将pannum改为你想要的数量,然后names和icons定义在arrays.xml文件中, 其中arrays.xml中的数量要和转盘的数量一致。理论上可以改为转盘数量为N的情况,但是综合来看还是6个和8个转盘数量最适宜,而且很多人也只问我怎么改成8个转盘,所以对这两种情况做了适配,如果后期还有别的需求在加吧,github上代码已经更新,请重新下载。

代码

最后,代码已经上传到github上去了。地址:https://github.com/Nipuream/LuckPan         欢迎Star

LuckPan.apk        apk下载地址

  • 17
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 30
    评论
### 回答1: 抽奖转盘是一种常见的游戏玩法,可以在Android Studio中实现。可以使用Canvas绘制转盘,然后使用动画实现旋转效果。还可以使用随机数生成器来确定抽奖结果。需要注意的是,抽奖转盘的设计应该符合用户体验,避免过于复杂或难以理解。 ### 回答2: Android Studio抽奖转盘是一种基于安卓开发平台的应用程序,它通过转盘的方式来实现抽奖的目的。抽奖转盘是功能相对简单,但现实生活中广泛应用的一个互动游戏。在实现抽奖转盘的应用中,可以通过安卓工具集成一系列功能实现开发过程,同时也可以结合一些开源库来实现更多的功能。 要实现抽奖转盘,我们需要一个圆形界面,可以在程序中通过绘制圆形实现。在圆形中间需要添加一个指针,指针在转动的时候与圆盘进行交互。在转盘的表面上,需要添加几段不同角度的弧形,每段弧形都可以对应一个中奖奖品。当程序启动后,转盘开始旋转,可以通过设置旋转速度和旋转时间的属性来实现这一功能。随着指针的旋转,最终会停在一个中奖区域内,这时程序会弹出提示框展示中奖的信息。 为了实现抽奖转盘的功能,需要注意以下几个方面: 首先,在制作过程中需要采用合适的布局,尽可能使得控件之间的关系清晰明了,从而方便后续开发和使用。 其次,在指针的旋转过程中,需要注意速度和转角的获取。这些数据的获取需要与程序中的计时器相关联,从而实现精准的控制。 最后,在展示中奖信息的过程中,需要考虑多种展示方式。这样可以让中奖结果得以灵活地展示,提高用户体验。 总之,抽奖转盘是一种非常有趣的互动方式,通过安卓工具和开源库的结合,我们可以轻松开发出一个功能强大的抽奖应用程序。 ### 回答3: Android Studio抽奖转盘是一款运行在Andriod Studio平台上的应用程序,其功能主要是模拟抽奖过程,让用户在其中获得激动人心的体验。抽奖转盘运用了许多Android Studio平台提供的强大工具和技术,例如可以使用Android代码编写软件界面、响应用户交互以及处理后端数据等。 抽奖转盘实现需要考虑几个方面,其中包括:转盘的转动效果、奖品与转盘的匹配关系、奖品信息的显示、中奖结果的提示等。 对于转盘的转动效果,可以使用Android Studio提供的动画效果和视图动作等工具进行实现,这样可以使转盘具有更加真实的旋转效果。同时,为了保证抽奖过程的公平性,还需要进行奖品池的随机抽取,以及防止用户通过欺骗方式获得大量奖品。 在奖品与转盘的匹配关系方面,开发者可以使用数据库或者其他的数据存储工具,将奖品与对应的转盘区块进行关联,这样可以在转盘旋转过程中快速地获取结果并向用户展示。 与此同时,还需要对抽奖过程进行监控和分析,例如记录用户中奖记录、统计用户抽奖次数等。这样可以为后续的运营策略提供依据,同时还可以对抽奖过程进行改进和优化。 最后,为了使用户获得更加良好的体验,需要在中奖结果的提示方面进行创新,例如可以加入音乐、动画等元素,让用户感受到惊喜和快乐。除此之外,还需要在用户界面设计、交互方式等方面进行深入优化,以提升用户参与度和满意度。 总之,Android Studio抽奖转盘是一款内容和技术相对较为复杂的应用程序,需要开发者具备较强的技术水平和创新意识,并且需要在多个方面进行综合考虑和优化,才能够为用户提供优质的抽奖体验。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值