zoukankan      html  css  js  c++  java
  • 自己定义三档半圆开关控件

    项目中须要一个多档的开关。依据美工的做图来开,可能得用自己定义控件来实现,正好之前学习做了一个卫星菜单自己定义控件,打算尝试自己自己定义这个半圆控件。

    美工图例如以下:


    1.考虑自己定义控件所需属性

    依据美工图来看。我认为须要三个属性,开关所处于档位level(说是3档)。指示器颜色indicatorColor,内圆半径radius
    于是在values目录下新建attrs.xml文件,内容例如以下

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    
        <attr name="level" format="integer" />
        <attr name="indicatorColor" format="color" />
    
    
        <declare-styleable name="FanBtn">
            <attr name="level" />
            <attr name="indicatorColor" />
            <attr name="radius" />
        </declare-styleable>
    </resources>
    FanBtn是我自己定义控件的名称(风扇开关)

    format属性表示属性值的类型,一共同拥有:string,color,demension,integer,enum,reference,float,boolean,fraction,flag几种


    2.创建自己定义控件类FanBtn

    public FanBtn(Context context) {
        this(context, null);
    }
    
    public FanBtn(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    
    public FanBtn(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    
        /**
         * 获得我们所定义的自己定义样式属性
         */
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.FanBtn, defStyleAttr, 0);
    
        level = a.getInt(R.styleable.FanBtn_level, 1);//默认1档
        indicatorColor = a.getColor(R.styleable.FanBtn_indicatorColor, Color.RED);
        radius = (int) a.getDimension(R.styleable.FanBtn_radius, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, getResources().getDisplayMetrics()));
        Log.e("log", "level   " + level + "indicatorColor " + indicatorColor + "  radius  " + radius);
    
        a.recycle();
    
    
    }
    控件有1參数。2參数。3參数的构造方法,为了简化代码,进行例如以下设置,当调用1參数方法时。实际调用2參数方法,而2參数方法又会调用3參数方法,所以我们能够仅仅把方法写到3參数方法中就可以。

    然后构造方法中主要内容就是获取自己定义属性的值,获取完自己定义属性后,一定调用recycle()方法回收

    3.进行内容绘制

    自己定义控件一般须要重写的方法有onMeasure,onLayout,onDraw,只是并非每一个方法都须要重写

    一般来说假设是一个控件view的话。一般仅仅用onMeasure,onDraw就可以

    假设是viewgroup的话。须要onMeasure,onLayout来布置viewgroup中子控件的位置,假设不须要自绘控件,能够不用又一次ondraw方法

    本次的话由于没有现成控件。所以通过ondraw来绘制自己定义view

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            paint = new Paint();
            /**
             * 画最外层的大圆环
             */
            int center = getWidth() / 2; //获取圆心的x坐标
            paint.setColor(Color.WHITE); //设置圆环的颜色
    //        paint.setStyle(Paint.Style.STROKE); //设置空心
    //        paint.setStrokeWidth(roundWidth); //设置圆环的宽度
            paint.setAntiAlias(true);  //消除锯齿
            canvas.drawCircle(center, center, radius, paint); //画出圆环
    
            /**
             * 画圆弧 ,画圆环的进度
             */
            int margin = 10 * getResources().getDisplayMetrics().densityDpi / 160;
            Log.e("log", "margin   " + margin);
            paint.setStrokeWidth(8); //设置圆环的宽度
    
            RectF oval = new RectF(center - radius - margin, center - radius - margin, center
                    + radius + margin, center + radius + margin);  //用于定义的圆弧的形状和大小的界限
    
            paint.setStyle(Paint.Style.STROKE);
            canvas.drawArc(oval, 120, 300, false, paint);  //依据进度画圆弧
            paint.setColor(indicatorColor);  //设置进度的颜色
            canvas.drawArc(oval, 120, 100*level, false, paint);  //依据进度画圆弧
    
           /**
    
           * 画圆点
           */
            int point_x=(int)(center+(radius-magrin)* Math.cos( Math.PI *4/3-Math.PI*level*5/9 ));
    
    int point_y=(int)(center-(radius-magrin)* Math.sin( Math.PI *4/3-Math.PI*level*5/9 )); canvas.drawPoint(point_x,point_y, paint); }
    1.首先创建画笔。准备绘制

    2.绘制中心的圆。首先确定圆心坐标,当中getWidth()方法会获取view本身设置的宽度。除2获取中间的位置

    然后设置画笔颜色,之后使用drawCircle方法进行画圆,这里用上了之前获取的radius 圆半径

    3.画完中心的圆后,接下来准备绘制外圈的圆弧,这块就须要计算下角度什么的了。。。

    因为外圈圆弧与圆有一定距离,设置个偏移量magin。后边那一长段主要是我想把dp转换为px。。。

    之后设置圆环宽度,然后使用创建一个RectF。我理解的是创建一个与圆弧外切的矩形。用来限制圆弧的范围

    最后使用drawArc方来绘制圆弧。第一个參数即刚才创建的用来限制圆弧的范围,第二个參数是圆弧開始的角度。第三个參数是圆弧转过的度数

    当中第二个參数经过測试,当为0时。是在圆的最右边開始,然后顺时针旋转,详细例如以下图所看到的:


    为了美观,把下边空白的弧角度定位60度,剩下每档100度。然后通过复杂的计算。

    。。

    圆弧的開始角度为120度,所以開始画弧

    先画个白弧作为背景,開始角度120,转过角度300度

    之后画红弧指示,開始角度120度。转过角度100*level度

    然后圆弧完毕

    3.画指示器点

    美工图上在中心圆边缘有个红点。。。别把它忘了使用drawPoint方法画点就可以,仅仅须要算出点的坐标就可以,只是还真是不好算。

    详细公式如上,过程不说了,画半天图怎么也能算出来

    4.设置点击事件

    如今执行一下,能够看到基本样子有了

    然后须要实现点击效果。眼下决定点一下提升一档,到三档后,点一下减一档,然后重复

    自己定义控件的点击方法须要通过 onTouchEvent方法自己处理,通过监听触摸事件推断是单击还是其它。

    代码例如以下

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
    
            case MotionEvent.ACTION_DOWN:
    
                break;
            case MotionEvent.ACTION_UP:
    
                level=level+levelFlag;
                if(level>=3)
                    levelFlag=-1;
                if(level<=1)
                    levelFlag=1;
                invalidate();  //重绘
    
                mDown.OnDown();
    
                break;
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_CANCEL:
                break;
            default:
                break;
        }
    
        return true;
    }


    能够监听按下事件和抬起事件。

    我这里简单的认定手指抬起后即完毕了一次点击事件,所以把要触发的效果写在ACTION_UP内

    逻辑大家应该都能看懂。不多说了。

    然后后边使用invalidate()来又一次调用onDraw方法重绘布局

    用来调用onDraw的方法除了invalidate()外还有postInvalidate()。两种方法都能够重绘布局。两种方法的差别。个人理解。主要在于能发在线程中使用。Invalidate的方法仅仅能在主线程中调用,不能在子线程中调用。就像不能在子线程中使用setText方法来给Textview赋值一样,要使用的话须要用Handler来进行消息传递。

    而postInvalidate方法能够在子线程中使用。

    本次的话,用两个方法都能够。

    mDown.OnDown方法后边再提。如今先不写,能够先凝视了

    然后要主要最后return的值,须要设备true,假设设为了false了就无法接受到UP事件了。原理的话。大概是,设为true的话说明本次事件到此为止,不再向下传递,并且之后的UP事件也会发给次监听器,而设为false的话,本次事件会继续往下传。然后接受不到UP事件。。。

    从网上找了个更具体的解释:


    setOnTouchListener 单独使用的时候返回值须要为true,这样才干保证移动的时候能后获取对应的监
    听,而非一次监听(即每次仅仅有一个按下的事件)
    setOnTouchListener 和 setOnClickListener 同一时候使用时。onTouch 的返回值要设为 false,这样既可
    以保证按下移动抬起事件能够被监听,而且点击事件也会被监听。


    之后能够执行下看看效果,是不是点击后能够切换档位了


    5.到此该控件基本样子实现了,本以为控件应该完毕了。然后到主activity绑定了控件之后。设了OnClick方法,输出个Toast,结果执行完事之后发现没有不论什么反应,仅仅是控件在自己变换。。。本来以为是那个return true的原因,改了下发现还是一样。

    后来发现应该自己定义监听事件

    // 为每一个接口设置监听器
        public void setOnDownActionListener(OnDownActionListener down) {
            mDown = down;
        }
    
    
        // 定义三个接口
        public interface OnDownActionListener {
            public void OnDown();
        }
    首先创建一个监听器OnDownActionListener接口。创建监听器时须要重写OnDown方法

    然后提供一个设置监听器的方法,setOnDownActionListener方法

    之后则仅仅须要在Action_up下加入mDown.OnDown 就可以在点击时,触发监听器中的OnDown方法


    创建完成后再主程序中

    fanBtn.setOnDownActionListener(new FanBtn.OnDownActionListener() {
        @Override
        public void OnDown() {
            Toast.makeText(MainActivity.this,"Nihiao",Toast.LENGTH_LONG).show();
        }
    });
    然后測试,发现控件切换档位时,也会输出Toast,至此。自己定义控件完毕


    參考:http://blog.csdn.net/lmj623565791/article/details/24252901


    转载请注明。

    项目源代码


  • 相关阅读:
    Ubuntu开发环境配置
    win7和Ubuntu16.04之间相互远程控制
    QT学习之usb摄像头采集(Opencv+QT)[cvCapture,IplImage,QImage]
    Opencv 图像畸变矫正(after 相机标定, 获得内参和畸变参数)
    opencv角点检测、棋盘格检测、亚像素cvFindCornerSubPix()
    开始学习机加工钣金加工
    今夜的硬件之旅
    关于做实验
    机器学习部分题目
    ubuntu18.10配置git和github
  • 原文地址:https://www.cnblogs.com/zsychanpin/p/7264963.html
Copyright © 2011-2022 走看看