Android圆环选择View

本文详细介绍了如何在Android中实现一个圆环选择视图,包括绘制圆环、确定圆点位置、设置旋转角度、背景圆环同步旋转、颜色过渡以及ImageView动画等步骤。通过实例代码和思路解析,帮助开发者理解这一复杂UI组件的实现方法。
摘要由CSDN通过智能技术生成

Android圆环选择View

无奈产品喜欢在APP中加入各种动画,再加上UI小姐姐的奇思妙想,然后就设计出了一大堆动画,前两周才把动画写完,故有了此篇博客来记录一下当时所遇到的坑,效果图镇楼,如下:

注:点击哪个那个旋转到最下面,旋转到最下面的为选中状态。

哇,当时看到这效果,真的是有辞职的冲动,但是转眼一想,哎,反正动画也不太熟悉,那就把这个做出来吧,我们先不加动画,就先实现静态的,如下图:

后面的圆环是怎么画出来的?

先实现简单的吧,我们可以看到后面有个圆环,我们都知道圆环嘛,大圆套小圆,就可以实现,但是现在的圆环颜色是不一样的,我们若还是用普通的圆来实现的话,就有点儿困难了。既然直接画圆不行,那就用圆弧把这个圆拼出来吧,代码如下:

    //画大圆
    private void drawBackRound(Canvas canvas) {
        initPaint();
        roundWidth = Math.min(width, height) / 2 * 0.15f;
        rectF = new RectF(
                (width > height ? Math.abs(width - height) / 2 : 0) + roundWidth / 2,
                (height > width ? Math.abs(height - width) / 2 : 0) + roundWidth / 2,
                width - (width > height ? Math.abs(width - height) / 2 : 0) - roundWidth / 2,
                height - (height > width ? Math.abs(height - width) / 2 : 0) - roundWidth / 2);
        paint.setStrokeWidth(roundWidth * 0.8f);
        paint.setStyle(Paint.Style.STROKE);
        paint.setColor(getResources().getColor(R.color.white_0));
        paint.setAntiAlias(true);
        canvas.drawArc(rectF, 0, 360, false, paint);
    }

额……若是RectF不知道是什么玩意儿的话…简单总结RectF、Rect 和Matrix ,还有Paint的使用方法这篇博客里面有讲。同理啦,圆环是大圆套小圆的,那么画小圆的方法和这个类似,只是改变了paint的颜色罢了,这就不贴代码啦,继续解决下一个问题~

圆环上的圆位置是怎么确定的?

在看完成的整体效果时,想必大家若是看到过鸿洋大神的Android 打造炫目的圆形菜单 秒秒钟高仿建行圆形菜单那这个位置解决的就soeasy啦~,其实我就是借鉴的鸿洋大神的这个例子,主要的代码如下:


    /**
     * 设置menu item的位置
     */
    @Override
    protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
        int layoutRadius = mRadius;
        float angleDelay;
        final int childCount = getChildCount();
        int left, top;
        // menu item 的尺寸
        int cWidth = (int) (layoutRadius * RADIO_DEFAULT_CHILD_DIMENSION);
        // 找到中心的view
        View cView = findViewById(R.id.id_circle_menu_item_center);
        // 根据menu item的个数,计算角度
        if (cView != null) {
            //如果中心View存在item个数减1
            angleDelay = 360 / (getChildCount() - 1);
            // 设置center item位置
            //居中
            int cl = layoutRadius / 2 - cView.getMeasuredWidth() / 2;
            int cr = cl + cView.getMeasuredWidth();
            cView.layout(cl, cl, cr, cr);
        } else {
            angleDelay = 360 / (getChildCount());
        }
        // 遍历去设置menuitem的位置
        for (int j = 0; j < childCount; j++) {
            final View child = getChildAt(j);
            if (child.getId() == R.id.id_circle_menu_item_center)
                continue;
            if (child.getVisibility() == GONE) {
                continue;
            }
            mStartAngle %= 360;
            // 计算,中心点到menu item中心的距离
            float tmp = layoutRadius / 2f - cWidth / 2;
            // tmp cosa 即menu item中心点的横坐标
            left = layoutRadius
                    / 2
                    + (int) Math.round(tmp
                    * Math.cos(Math.toRadians(mStartAngle)) - 1 / 2f
                    * cWidth);
            // tmp sina 即menu item的纵坐标
            top = layoutRadius
                    / 2
                    + (int) Math.round(tmp
                    * Math.sin(Math.toRadians(mStartAngle)) - 1 / 2f
                    * cWidth);

            child.layout(left, top, left + cWidth, top + cWidth);
            // 叠加尺寸
            mStartAngle += angleDelay;
        }
    }

没错,就是重写onLayout来确定圆环上圆的位置,每当要旋转时就调用requestLayout()重新确定圆的位置~

圆环上的圆旋转的角度是怎么确定的?

在效果图上可以看到,点击哪个哪个就旋转到最下面,这中间有个问题,就是:我们人眼是可以看到那个在最上面,那个在左,在右,但是,在代码中实现的时候,我们并不知道它们的位置是在哪里,这就有个问题了,我们不知道位置,所以就没办法确定到底要旋转多少度,不知道要旋转的角度,那么这个效果就无法实现了。
后来想了半天,其实我们可以这样想,如下图:

首先我们可以获取的资源如下:当前选中的ImageView,要选中的ImageView,当前ImageView的在数组中的下标和要选中的ImageView在数组中的下标。这些我们是可以知道的,假如,现在选中了下标为1的ImageView,那么我不管是要选中下标为2还是0的都旋转90度,那么这个角度就可以用要选中的ImageView的下标来减去当前选中 X 90就好了,然后顺时针转还是逆时针转就看需求啦~但是还有一个要解决的,就是,当前选中的下标为0,那么左右的下标就是3和1了,这样(3-0) X 90要旋转的角度就不是我们想要的角度了,所以,在0和3的时候要有个判断,分析结束,主要的代码如下:


 cl_group.setOnMenuItemClickListener(new CircleMenuLayout.OnMenuItemClickListener() {
            @Override
            public void itemClick(View iv, int pos, View tv) {
                //旋转动画
                groupRotating(pos);
                ...
                oldElect = pos;
            }
        });

    //旋转
    private void groupRotating(int pos) {
        ValueAnimator anim = new ValueAnimator();
        int newElect = pos;
        int angle = 0;
        if (oldElect == 0 && newElect == 3) {
            //当前选中的是0,未来选中的是3,则顺时针旋转180
            angle = ROTATION_ANGLE;
        } else if (oldElect == 3 && newElect == 0) {
            //当前选中的是3,未来选中的是0,则逆时针旋转180
            angle = -ROTATION_ANGLE;
        } else {
            //若不属于以上两种情况,按普通的处理
            angle = (oldElect - newElect) * ROTATION_ANGLE;
        }
        //范围,基准角度----基准角度+要旋转的角度
        anim = ValueAnimator.ofInt(tag, tag + angle);
        anim.setDuration(SUCCESSFUL_ANIM_TIME).start();
        anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                int angle = (int) valueAnimator.getAnimatedValue();
                cl_group.setmStartAngle(angle);
            }
        });
        tag = tag + angle;
    }

ok,这里用了ValueAnimator属性动画,主要就是通过返回的数值,达到旋转的效果~

实现背景圆环同步旋转

我们在效果图上可以看到后面的圆环也是可以同步旋转的哈,其实要实现同步旋转很简单,在RingView加属性动画,然后外部调用就好啦!如下:

 public void setRingRotating(int nowAngle, int futureAngle) {
        ObjectAnimator.ofFloat(this, "rotation", nowAngle, futureAngle).setDuration(animTime).start();
    }

外部调用:

//旋转
    private void groupRotating(int pos) {
        ...
        //背景圆环同步旋转
        rl_view.setRingRotating(tag, tag + angle);
        tag = tag + angle;
    }

圆环颜色美化

在效果图上,可以看到当圆环在旋转的时候之前选中的颜色渐渐显示,要选中的渐渐隐藏,这个其实很简单,还是借助ValueAnimator属性动画就可以实现啦,同样的我们在外部调用它,在RingView里面添加如下代码:


    //设置颜色要显示还是隐藏的参数
    public void setElect(int oldElect, int newElect, int oldValue, int newValue) {
        this.oldElect = oldElect;
        this.newElect = newElect;
        //当前的
        int currentValue = 0;
        //将来的
        int futureValue = 0;
        currentValue = (oldElect % 2 == 0) ? oldValue : newValue;
        futureValue = (newElect % 2 == 0) ? oldValue : newValue;
        //动画
        ValueAnimator oldAnim = ValueAnimator.ofInt(0, currentValue);
        ValueAnimator newAnim = ValueAnimator.ofInt(futureValue, 0);
        oldAnim.setDuration(animTime).start();
        newAnim.setDuration(animTime).start();
        //显示
        oldAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                oldAlpha = (int) valueAnimator.getAnimatedValue();
                invalidate();
            }
        });
        //隐藏
        newAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                newAlpha = (int) valueAnimator.getAnimatedValue();
                invalidate();
            }
        });
    }

由于每段圆弧的颜色透明度不一样,所以要判断下当前的圆环和要选择的圆环的透明度。而它们的条件就是是否可以被2整除!
外部调用如下:

        cl_group.setOnMenuItemClickListener(new CircleMenuLayout.OnMenuItemClickListener() {
            @Override
            public void itemClick(View iv, int pos, View tv) {
               ...
                //背景圆弧换颜色动画
                rl_view.setElect(oldElect, pos, 80, 50);
               ...
                oldElect = pos;
            }
        });

圆弧上的ImageView美化

效果图上,可以看到圆弧上的ImageView是带有动画的,其实加动画也不难,主要的就是在点击后,有个缩放动画,主要代码如下:


    private void zoomImageViewAnim(Bean oldBean, Bean newBean, final int width, final int height) {
        //当前选中的缩小,换背景,字体换颜色
        //将来选中的缩小,换背景,字体换颜色
        final TextView oldTv = oldBean.getTv();
        final ImageView oldIv = oldBean.getIv();

        final TextView newTv = newBean.getTv();
        final ImageView newIv = newBean.getIv();

        ValueAnimator shrinkImageAnimWidth = new ValueAnimator();
        ValueAnimator shrinkImageAnimHeight = new ValueAnimator();
        shrinkImageAnimWidth = ValueAnimator.ofInt(width, 0);
        shrinkImageAnimHeight = ValueAnimator.ofInt(height, 0);
        //缩小
        AnimatorSet set = new AnimatorSet();
        set.playTogether(shrinkImageAnimWidth, shrinkImageAnimHeight);
        set.setDuration(500).start();
        shrinkImageAnimWidth.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int w = (int) animation.getAnimatedValue();
                FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) oldIv.getLayoutParams();
                params.width = w;
                //改变宽度
                oldIv.setLayoutParams(params);
                newIv.setLayoutParams(params);
            }
        });
        shrinkImageAnimHeight.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int h = (int) animation.getAnimatedValue();
                FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) oldIv.getLayoutParams();
                params.height = h;
                //改变高度
                oldIv.setLayoutParams(params);
                newIv.setLayoutParams(params);
            }
        });
        shrinkImageAnimWidth.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {

            }

            @Override
            public void onAnimationEnd(Animator animation) {
                //缩小动画,完成,开启放大动画
                //文字换颜色
                oldTv.setTextColor(Color.BLACK);
                newTv.setTextColor(Color.WHITE);
                //ImageView换背景
                oldIv.setBackgroundResource(R.drawable.shape_circle_yellow_50);
                newIv.setBackgroundResource(R.drawable.shape_circle_ring_yellow);

                AnimatorSet set1 = new AnimatorSet();
                ValueAnimator bigImageAnimWidth = new ValueAnimator();
                ValueAnimator bigImageAnimHeight = new ValueAnimator();
                bigImageAnimWidth = ValueAnimator.ofInt(0, width);
                bigImageAnimHeight = ValueAnimator.ofInt(0, height);

                set1.playTogether(bigImageAnimWidth, bigImageAnimHeight);
                set1.setDuration(500).start();

                bigImageAnimWidth.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        int w = (int) animation.getAnimatedValue();
                        FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) oldIv.getLayoutParams();
                        params.width = w;
                        //改变宽度
                        oldIv.setLayoutParams(params);
                        newIv.setLayoutParams(params);
                    }
                });

                bigImageAnimHeight.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        int h = (int) animation.getAnimatedValue();
                        FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) oldIv.getLayoutParams();
                        params.height = h;
                        //改变高度
                        oldIv.setLayoutParams(params);
                        newIv.setLayoutParams(params);
                    }
                });
            }

            @Override
            public void onAnimationCancel(Animator animation) {

            }

            @Override
            public void onAnimationRepeat(Animator animation) {

            }
        });
    }

就不多做解释啦,里面都注释了。

END

其实最主要的是借鉴了,鸿洋大神的Android 打造炫目的圆形菜单 秒秒钟高仿建行圆形菜单,写之前感觉好难,写之后感觉,也就是一层窗户纸的事儿,思路最重要,哈哈……
源码链接

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 要在Android中创建自定义圆环,可以使用Canvas和Paint类来绘制。以下是一个简单的例子: 1. 在你的XML布局文件中,添加一个自定义View: ```xml <com.example.myapp.MyCircleView android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 2. 创建一个自定义View类,并覆盖onDraw方法: ```java public class MyCircleView extends View { private Paint paint; private RectF rectF; private float strokeWidth = 20; //圆环宽度 private float progress = 0; //进度 public MyCircleView(Context context, AttributeSet attrs) { super(context, attrs); paint = new Paint(); paint.setAntiAlias(true); paint.setStyle(Paint.Style.STROKE); paint.setStrokeWidth(strokeWidth); paint.setColor(Color.BLUE); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); rectF = new RectF(strokeWidth / 2, strokeWidth / 2, getWidth() - strokeWidth / 2, getHeight() - strokeWidth / 2); canvas.drawArc(rectF, -90, progress, false, paint); } //设置圆环进度 public void setProgress(float progress) { this.progress = progress; invalidate(); } } ``` 3. 在Activity或Fragment中使用自定义View: ```java MyCircleView myCircleView = findViewById(R.id.my_circle_view); myCircleView.setProgress(120); ``` 在此示例中,我们使用了paint对象来绘制圆环。我们还使用RectF类来确定圆环的大小和位置。最后,我们在onDraw方法中使用canvas对象来绘制圆环。我们还添加了setProgress方法,用于设置进度。 希望这可以帮助到你! ### 回答2: Android中可以通过自定义继承自View的类来实现圆环的绘制。首先,在自定义View类的构造方法中初始化画笔,并设置画笔的属性,如颜色、宽度等。然后,在自定义View类的onDraw()方法中调用canvas的drawCircle()方法,传入圆心坐标和半径参数,即可绘制出一个圆。 此外,要实现圆环的效果,可以在drawCircle()方法之前先绘制一个实心圆,再绘制一个较大的同心空心圆。可以通过设置画笔的样式为STROKE,即只画圆边缘的方式,来实现空心圆的效果。 在绘制圆环的过程中,可以利用onMeasure()方法来获取View的宽高,并动态计算圆心坐标和半径,以适应不同的屏幕尺寸。 另外,如果需要显示进度效果,可以通过设置画笔的样式为FILL,并利用drawArc()方法在圆环内部绘制一个扇形,根据进度值设置绘制的角度,来显示进度条。 最后,在使用自定义圆环的时候,可以在XML布局文件中引用该自定义View,并设置相应的属性,如颜色、宽度、进度值等。 ### 回答3: 在Android中,可以通过自定义控件来实现圆环的效果。 首先,我们可以创建一个自定义的View类,继承自View类。在自定义类中,我们需要重写onDraw()方法来绘制圆环。 在onDraw()方法中,我们可以通过Canvas类提供的drawArc()方法来绘制圆弧,从而实现圆环的效果。drawArc()方法需要指定圆弧的矩形区域、起始角度、扫过的角度和是否包含中心点。 为了实现一个圆形的圆环效果,我们可以通过计算得出圆弧的矩形区域,起始角度设置为0,扫过的角度设置为360,表示一个完整的圆环。我们还可以设置画笔的宽度、颜色等属性来控制圆环的样式。 在自定义View类中,我们还可以通过重写onMeasure()方法来控制View的大小。在该方法中,我们可以根据需求设置View的宽度和高度,使得圆环显示出来。 最后,在使用自定义圆环的布局文件中,我们可以直接将自定义View类添加到布局中。可以通过设置布局文件中的宽度、高度,以及其他属性设置来调整圆环的样式和位置。 总结起来,Android中可以通过自定义View类和重写onDraw()方法来实现圆环效果。通过计算矩形区域、设置起始角度和扫过的角度,以及设置画笔的属性,可以实现不同样式的圆环效果。最后,在布局中使用该自定义View类来显示圆环
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值