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

美工图如下:

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

转载请注明。

项目源码:

自定义三档半圆开关控件相关推荐

  1. Qt工作笔记-自定义开关控件

    1.自定义开关控件: 2.点击有动画效果: 3.在动画效果中,不再响应信号: 运行截图如下: 输出响应信号: 源码如下: myonoff.h #ifndef MYONOFF_H #define MYO ...

  2. 【Qt】qt自定义开关控件

    基于QWidget实现一个简单的开关控件,左右动画效果切换,效果如下 样式在paintEvent中绘制 void RdSwitchButton::paintEvent(QPaintEvent *eve ...

  3. Android自己写的三款实用开关控件

    2019独角兽企业重金招聘Python工程师标准>>> 自定义开关控件,代码简单,比较实用. http://www.see-source.com/androidwidget/list ...

  4. Android 开关控件Switch

    扣扣技术交流群:460189483 目录:     1.应用场景与概述     2.常用属性     3.简单使用     4.更改默认Switch的样式     5.自定义Switch      1 ...

  5. Android 基础 View 系列之 仿IPhone 开关控件

    极力推荐Android 开发大总结文章:欢迎收藏程序员Android 力荐 ,Android 开发者需要的必备技能 自定义View 是Android中常用的方法之一,本章实现类似于IPhone 开关控 ...

  6. 自定义窗体设计器-控件测试

    自定义窗体设计器-控件测试 基于.net2的自定义窗体设计器控件(类似visual studio的vb,c#设计器,其实就是vs2005的设计器) 控件由四部分组成:工具栏,工具箱,绘图设计区,属性框 ...

  7. Qt QWidget实现开关控件SwithButton(SlipButton)

    前言 Qt做界面的时候常常会用到开关控件,类似于CheckButton有两种状态,只是界面表现形式不一样而已.本文通过QWidget类来实现一个开关控件SwitchBtn(有些平台上又称为SlipBu ...

  8. weui-switch开关控件,表单提交后如何取值

    最近在学习weui这个框架,做了一些小的试验,发现weui-switch控件直接提交不能获取到表单信息,在segmentfault上发现也有人提了这个问题,有人说可以设置一个隐含标签来捕获开关的状态, ...

  9. asp.net读取用户控件,自定义加载用户控件

    1.自定义加载用户控件 ceshi.aspx页面 <html><body> <div id="divControls" runat="ser ...

最新文章

  1. 生产者消费者--TestPC.java
  2. SGI STL 学习笔记二 vector
  3. 用代码查看SAP Spartacus购物车内的行项目
  4. python网络框架生产环境_配置Django框架为生产环境的注意事项(DEBUG=False)
  5. 浅谈href=#与href=javascript:void(0)的区别
  6. vue手势滚动_vue + any-touch实现一个iscroll 实现拖拽和滑动动画效果
  7. 【SpringBoot】项目打成 jar 包后关于配置文件的外部化配置
  8. Android使用BaseAdapter绑定ListView实现不同item的TextView多种文字变色
  9. PDF如何转换成jpg图片
  10. 腾讯会议中用PPT放视频,视频没有声音
  11. linux操作系统课程内容,《linux操作系统及应用》课程标准
  12. 金彩教育:如何提升自然流量
  13. 计算机毕业设计PHP基于微信小程序寸金校园租车平台(源码+程序+uni+lw+部署)
  14. python小助手_如何用python写个人专属群聊提醒小助手?
  15. Brenda-利用SOAP API访问Brenda及本地保存
  16. 我是怎么薅的双十一羊毛
  17. 判断数组相同数c语言_单片机常用的14个C语言算法,看过的都成了大神!
  18. Spring Boot 注解原理
  19. 【数据库】数据分析专项练习题库-SQL试卷一
  20. MATLAB 相机标定中标定板角点像素坐标系到世界坐标系的转换

热门文章

  1. Spring-AOP表达式execution()详解
  2. 常用视频编辑软件简介--
  3. 阿里云CDN API推送
  4. python 新媒体素材_新媒体运营日常工作是怎样的?
  5. 搭上pr更新这辆快车,开展网站联合纵横
  6. 用Python演奏《太阳照常升起》
  7. python 读取鼠标选中文本_送书 | 选择文本的神技
  8. 在系统启动时至少一个服务或驱动程序产生错误 hwinfo32
  9. python实现淘宝秒杀_python实现简单淘宝秒杀功能
  10. Java、JSP电子健康档案管理系统