一个带阴影效果的圆形控制的View,可以上下左右中进行控制

@[TOC] 一个带阴影效果的圆形控制的View,可以上下左右中进行控制

先看一下效果图

控制View的效果图
效果图

用法


  <!--   app:center_text="@string/tv"  中间的文本
        app:center_icon="@drawable/ok_tv_normal" 中间的图标 两个都设置优先使用图标
        app:center_is_can_click="true"   中间是否可以点击 默认可以
        app:control_radius="85dp"  圆的半径 设置这个来控制view的大小,设置宽高没有用的
        app:first_icon="@drawable/up_tv" //第一个图标 图标顺序为顺时针 上 右 下 左
        app:four_icon="@drawable/left_tv" 第四个图标
        app:icon_num="4" //设置图标的个数不包括中间的
        app:second_icon="@drawable/right_tv" 第二个图标
        app:third_icon="@drawable/down_tv" 第三个图标
        -->

<at.smarthome.infraredcontrol.views.CommonControlView
        android:id="@+id/cv_tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:center_icon="@drawable/ok_tv_normal"
        app:center_is_can_click="true"
        app:control_radius="85dp"
        app:first_icon="@drawable/up_tv"
        app:four_icon="@drawable/left_tv"
        app:icon_num="3"
        app:second_icon="@drawable/right_tv"
        app:third_icon="@drawable/down_tv" />

本圆形控制View,支持中间小圆里可以放文字或者图标,圆边上的图标最多支持4个图标,可以通过xml自定义属性动态配置1-4个图标。还可以为其点击区域添加点击事件。非常灵活。

设计原理

怎么来绘制这样的一个View呢,首先需要画一个带阴影效果的圆,并在大圆内画一个小圆,然后根据点击区域画点击效果,画中间图标或者文字,再根据配置来画其他的图标。最后我们需要根据手指触摸的位置添加点击事件。
所涉及的主要知识点,利用BlurMaskFilter来绘制阴影效果,利用Region来判断手指触摸的位置是否在Region创建的对象中,利用Path来绘制不规则扇形。

主要代码具体实现

1.Region区域的数据初始化 和Path的设置

private void initRegin() {
        //绘制中间圆的Path
        //raidus为半径 innerRaidus为内圆半径
        centPath.addCircle(raidus, raidus, innerRaidus, Path.Direction.CW);
        //中间Region对象
        center.setPath(centPath, new Region((int) (raidus - innerRaidus), (int) (raidus - innerRaidus), (int) (raidus + innerRaidus), (int) (raidus + innerRaidus)));
        //miconNum为图标个数除中间的图标外
        //默认开始角度为 private int startAngel = 225;  private int sweepAngel = 90;
         //只有为第三个的时候才有改变,等下要计算图标的Region和Path
        if (miconNum == 3) {
        
            startAngel = 210;
            sweepAngel = 120;
        }
        for (int i = 0; i < miconNum; i++) {
           //因为要有阴影效果才有padding
            RectF rect = new RectF(padding, padding, raidus + raidus - padding, raidus + raidus - padding);

            RectF rect1 = new RectF(raidus - innerRaidus, raidus - innerRaidus, raidus + innerRaidus, raidus + innerRaidus);
            Path path = new Path();
            path.moveTo(raidus, raidus);
            path.arcTo(rect, startAngel, sweepAngel);

            Path path1 = new Path();
            path1.moveTo(raidus, raidus);
            path1.arcTo(rect1, startAngel, sweepAngel);

            if (miconNum == 2) {
                startAngel += 2 * sweepAngel;
            } else {
                startAngel += sweepAngel;
            }
            RectF rectF = new RectF();

            RectF rectF1 = new RectF();

            path.computeBounds(rectF, true);
            path.close();

            path1.computeBounds(rectF1, true);
            path1.close();

            Path pathReal = new Path();

            //取公共部分 Path.Op.DIFFERENCE
            pathReal.op(path, path1, Path.Op.DIFFERENCE);

           //存储Path的List
            mPaths.add(pathReal);

            Region region = new Region();

            region.setPath(path, new Region((int) rectF.left, (int) rectF.top, (int) rectF.right, (int) rectF.bottom));

            Region region1 = new Region();
            region1.setPath(path1, new Region((int) rectF1.left, (int) rectF1.top, (int) rectF1.right, (int) rectF1.bottom));

            Region region2 = new Region();
            //取公共部分 Path.Op.DIFFERENCE
            region2.op(region, region1, Region.Op.DIFFERENCE);
            //存储Region的List
            regions.add(region2);
        }

    }

到现在为止我们就做好前期工作把Regoin和Path弄好了,接下来看如何绘制和判断点击事件
1.绘制代码

@Override
    protected void onDraw(Canvas canvas) {
        //绘制圆有阴影效果
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setMaskFilter(blurMaskFilter);
        //阴影颜色
        mPaint.setColor(getResources().getColor(R.color.color_black10));
        canvas.drawCircle(raidus, raidus, raidus - padding, mPaint);
        mPaint.setMaskFilter(null);
        //再把圆填充为白色,就周围才会有阴影了
        mPaint.setColor(getResources().getColor(R.color.white));
        canvas.drawCircle(raidus, raidus, raidus - padding, mPaint);

        //绘制内圆
        mPaint.setColor(getResources().getColor(R.color.line_grey));
        mPaint.setStyle(Paint.Style.STROKE);
        canvas.drawCircle(raidus, raidus, innerRaidus, mPaint);
      
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(0);
        mPaint.setColor(getResources().getColor(R.color.line_grey));
         //判断当前点击区域绘制点击效果
        if (isDown) {
            if (currentSelect != -1 && currentSelect != 4 && currentSelect < mPaths.size()) {
            
                drawRegion(canvas, mPaths.get(currentSelect), mPaint);
            } else if (currentSelect == 4) {
                drawRegion(canvas, centPath, mPaint);
            }

        }
    
    //绘制中间的图标或者文字
        if (mCenterDrawables != null) {
            canvas.drawBitmap(mCenterDrawables, raidus - mCenterDrawables.getWidth() / 2.0f, raidus - mCenterDrawables.getHeight() / 2.0f, null);
        } else {
            if (!TextUtils.isEmpty(centerText)) {
                mPaint.setColor(getResources().getColor(R.color.color666666));
                mPaint.setTextSize(DensityUtils.dip2px(getContext(), 14));
                float textWidth = mPaint.measureText(centerText);
                Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
                float y = raidus + (Math.abs(fontMetrics.ascent) - fontMetrics.descent) / 2;
                canvas.drawText(centerText, raidus - textWidth / 2, y, mPaint);
            }
        }

        canvas.save();
//绘制其他图标
        for (Bitmap mDrawable : mDrawables) {
            canvas.drawBitmap(mDrawable, raidus - (mDrawable.getWidth() / 2.0f), (raidus - innerRaidus - mDrawable.getHeight() + padding) / 2, null);
            if (miconNum == 2) {
                canvas.rotate(180, raidus, raidus);
            } else if (miconNum == 3) {
                canvas.rotate(120, raidus, raidus);
            } else if (miconNum == 4) {
                canvas.rotate(90, raidus, raidus);
            }

        }
        canvas.restore();

    }

//绘制阴影效果
    private void drawRegion(Canvas canvas, Path rgn, Paint paint) {
        //用Region方法来绘制图会有很明显的锯齿问题
        canvas.drawPath(rgn, paint);

    }

2.点击区域判断

//主要根据Region来判断,这里就不做过多解释
 @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (isEnabled()) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    //按下
                    lastX = event.getX();
                    lastY = event.getY();

                    if (isCenterTop((int) event.getX(), (int) event.getY()) && isCenterCanClick) {


                        isDown = true;
                        currentSelect = 4;
                        invalidate();
                    } else if (isDownTop((int) event.getX(), (int) event.getY())) {

                        isDown = true;
                        currentSelect = 0;
                        invalidate();
                    } else if (isDownLeft((int) event.getX(), (int) event.getY())) {
                        isDown = true;

                        currentSelect = 3;

                        invalidate();
                    } else if (isDownRight((int) event.getX(), (int) event.getY())) {

                        isDown = true;
                        currentSelect = 1;
                        invalidate();

                    } else if (isDownBottom((int) event.getX(), (int) event.getY())) {
                        isDown = true;
                        currentSelect = 2;
                        invalidate();
                    }
                    break;
                case MotionEvent.ACTION_UP:
                case MotionEvent.ACTION_CANCEL:
                    //弹起


                    if (isCenterCanClick && isCenterTop((int) event.getX(), (int) event.getY()) && currentSelect == 4 && isClick(lastX, lastY, event.getX(), event.getY())) {
                        if (callBack != null) {
                            callBack.centerClick();
                        }
                    } else if (isDownTop((int) event.getX(), (int) event.getY()) && currentSelect == 0 && isClick(lastX, lastY, event.getX(), event.getY())) {

                        if (callBack != null) {
                            callBack.oneClick();
                        }
                    } else if (isDownLeft((int) event.getX(), (int) event.getY()) && currentSelect == 3 && isClick(lastX, lastY, event.getX(), event.getY())) {

                        if (callBack != null) {
                            callBack.fourClick();
                        }
                    } else if (isDownRight((int) event.getX(), (int) event.getY()) && currentSelect == 1 && isClick(lastX, lastY, event.getX(), event.getY())) {

                        if (callBack != null) {
                            callBack.twoClick();
                        }
                    } else if (isDownBottom((int) event.getX(), (int) event.getY()) && currentSelect == 2 && isClick(lastX, lastY, event.getX(), event.getY())) {

                        if (callBack != null) {
                            callBack.threeClick();
                        }
                    }
                    isDown = false;
                    currentSelect = -1;
                    invalidate();
                    break;
                default:
                    break;
            }

            return true;
        } else {
            return super.onTouchEvent(event);
        }
    }

到这里我们就把主要代码实现讲完了。最后我会附上整个代码的链接。

资料

代码下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值