今天来讲讲自定义单个控件,就拿开关按钮来讲讲,相信大家见了非常多这样的了,先看看效果:

我们可以看到一个很常见的开关按钮,那就来分析分析。

首先:

这是由两张图片构成:

①一张为有开和关的背景图片

②一张为控制开和关的滑动按钮

第一步:

写个类继承View,并重写几个方法:

第一个为构造函数,重写一个参数的函数和两个参数的函数就够了,因为两个参数的函数能够使用自定义属性

第二个为控制控件的大小–>protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {}

第三个为绘制控件的方法–>protected void onDraw(Canvas canvas) {}

第二步:

将用户指定的两张图片加载进来,这里使用自定义属性加载, 在values目录下新建attrs.xml,在xml文件中指定自定义属性名和自定义属性的字段及值类型(即背景图和滑块图)即可:

<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="switchView_attrs"><attr name="background" format="reference"></attr><attr name="slide" format="reference"></attr></declare-styleable>
</resources>

各个字段的含义我在这就不讲了,不懂的就去看看前几篇《android开发笔记之自定义组合控件》有讲过,写好就只需在构造函数中加载进来

public SwitchView(Context context, AttributeSet attrs) {super(context, attrs);//拿到自定义属性TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.switchView_attrs);//拿到自定义字段的值Drawable switchBackground = ta.getDrawable(R.styleable.switchView_attrs_background);Drawable switchView_slide = ta.getDrawable(R.styleable.switchView_attrs_slide);//把值设置到相应组件上backgroundBitmap = convertDrawable2BitmapSimple(switchBackground);switchSlide = convertDrawable2BitmapSimple(switchView_slide);}

不过要注意的是:

因为从自定义属性中取到的是Drawable对象,而我们要的是一个Bitmap对象,所以我们先得把Drawable对象转成Bitmap对象,其实有很多种方法来转,这里就介绍种最简单的方法,借助与BitmapDrawable类:

//将Drawable转成Bitmappublic Bitmap convertDrawable2BitmapSimple(Drawable drawable) {BitmapDrawable bd= (BitmapDrawable)drawable;return bd.getBitmap();}

第三步:

onMeasure方法来控制控件的大小,我们可以看到这个开关按钮的大小就跟背景的大小一样大,只需要设置为背景的大小:

//控制控件的大小@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {if (backgroundBitmap != null) {//控件大小设置为背景的大小setMeasuredDimension(backgroundBitmap.getWidth(), backgroundBitmap.getHeight());}else {super.onMeasure(widthMeasureSpec, heightMeasureSpec);}}

第四步:

既然这个开关按钮需要通过点击或移动来控制控件的开和关,所以就需要实现onTouchEvent方法,当然应该有三个事件会触发:按下、移动和抬起的时候,每次点击、移动或抬起都需要重绘,我们先来分析下滑块的状态有哪些(应该有四种状态)一开始默认状态为空:

1.点击的时候
2.移动的时候
3.抬起的时候
4.空的时候(即什么都没干的时候)

先分析下点击的时候的情况:

①当按下或移动的坐标大于滑块宽度一半时将滑块右移

②当按下或移动的坐标小于滑块宽度一半时滑块不动

注:防止滑块移至背景外面,最大是滑块右边和背景右边对齐(即最大离左边为背景宽度-滑块宽度)

再来看看移动的时候的情况应该是和点击的时候是一样的。

再来分析抬起的时候的情况:

①如果开关状态是打开的就将滑块移动至右边

②如果开关状态是关闭的就将滑块移动至左边

那怎么判断什么时候是打开状态和关闭状态呢??

①抬起的坐标大于背景宽度一半的时候设为打开状态

②抬起的坐标小于背景宽度坐标一 半的时候设为关闭状态

再来分析下空的时候,可以发现它和抬起的时候的情况是一样的。

第五步:

onDraw方法中将背景和滑块绘制出来。刚才分析了onTouchEvent方法,这次是一样的,滑块的四个状态分别处理,前面onTouchEvent方法中滑块的状态改变,然后通过invalidate()方法来通知系统重绘。

第六步:

我们做这个自定义控件是为了让用户使用的,现在这个是没有什么用的,用户用不了,所以可以通过设置监听器来对外提供接口。

/** * switchView开关监听接口*  *  */interface OnSwitchChangedListener {public void onSwitchChange(boolean isOpen);}/** * 设置 switchView状态监听 * */public void setOnChangeListener(OnSwitchChangedListener listener) {switchListener = listener;}

这个监听器中的boolean值需要赋值,那在什么时候赋值呢,应该是在抬起或空的状态的时候给它赋值,因为那个时候才真正确定开关按钮是打开的还是关闭的。

第七步:

到这一步就是来使用了,在布局文件中把自定义的这个控件定义出来

<com.example.custom.SwitchViewminguo:background="@drawable/switch_background"minguo:slide="@drawable/slide_button_background"android:id="@+id/switchView"android:layout_width="wrap_content"android:layout_height="wrap_content"/>

使用定义好的View应该都会用了,不会的去看看《android开发笔记之自定义组合控件》。

核心代码:

布局文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:minguo="http://schemas.android.com/apk/res/com.example.custom"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="com.example.custom.MainActivity" ><com.example.custom.SwitchViewminguo:background="@drawable/switch_background"minguo:slide="@drawable/slide_button_background"android:id="@+id/switchView"android:layout_width="wrap_content"android:layout_height="wrap_content"/>
</LinearLayout>

自定义View: SwitchView.java

/*** 自定义开关按钮* @author Administrator**/
public class SwitchView extends View {//背景图片和滑块图片private Bitmap backgroundBitmap,switchSlide;//画笔private Paint paint;//得到的x坐标(点击、移动、抬起)private float currentX;//判断开关是否打开的标记位private boolean isOpen = false;//开关打开与关闭的监听器private OnSwitchChangedListener switchListener;//滑块的四种状态public static final int STATE_DOWN = 1; //按下的时候public static final int STATE_MOVE = 2; //移动的时候public static final int STATE_UP = 3;   //抬起的时候public static final int STATE_NONE = 0; //空的时候(即什么都没干的时候)//标记状态(默认为空状态)private int state = STATE_NONE;public SwitchView(Context context) {super(context,null);}public SwitchView(Context context, AttributeSet attrs) {super(context, attrs);//拿到自定义属性TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.switchView_attrs);//拿到自定义字段的值Drawable switchBackground = ta.getDrawable(R.styleable.switchView_attrs_background);Drawable switchView_slide = ta.getDrawable(R.styleable.switchView_attrs_slide);//把值设置到相应组件上backgroundBitmap = convertDrawable2BitmapSimple(switchBackground);switchSlide = convertDrawable2BitmapSimple(switchView_slide);}//将Drawable转成Bitmappublic Bitmap convertDrawable2BitmapSimple(Drawable drawable) {BitmapDrawable bd = (BitmapDrawable)drawable;return bd.getBitmap();}//控制控件的大小@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {if (backgroundBitmap != null) {//控件大小设置为背景的大小setMeasuredDimension(backgroundBitmap.getWidth(), backgroundBitmap.getHeight());}else {super.onMeasure(widthMeasureSpec, heightMeasureSpec);}}//绘制控件的样式@Overrideprotected void onDraw(Canvas canvas) {//背景为空的时候才将背景绘制if (backgroundBitmap != null) {paint = new Paint();//第一个参数表示需要画的Bitmap//第二个参数表示Bitmap左边离控件的左边距//第三个参数表示Bitmap上边离控件的上边距//第四个参数表示画笔canvas.drawBitmap(backgroundBitmap, 0, 0, paint);}switch (state) {case STATE_DOWN:        //按下和移动的触发事件都一样,都是将滑块移动case STATE_MOVE://当按下或移动的坐标大于滑块宽度一半时将滑块右移if (currentX > switchSlide.getWidth()/2f) {//让滑块向右滑动(重新绘制滑块的位置)float left = currentX - switchSlide.getWidth()/2f;//防止滑块移至背景外面,最大是滑块右边和背景右边对齐(即最大离左边为背景宽度-滑块宽度)float maxLeft = backgroundBitmap.getWidth() - switchSlide.getWidth();if (left > maxLeft) {left = maxLeft;}canvas.drawBitmap(switchSlide, left, 0, paint);//当按下或移动的坐标小于滑块宽度一半时滑块不动}else if (currentX < switchSlide.getWidth()/2f) {//让滑块不动就可以了canvas.drawBitmap(switchSlide, 0, 0, paint);}break;case STATE_NONE:        //空或抬起的时候将滑块至于左边或右边case STATE_UP://如果是打开的将滑块移动至右边,并将打开状态传至监听器if (isOpen) {if (switchListener != null) {switchListener.onSwitchChange(true);}canvas.drawBitmap(switchSlide, backgroundBitmap.getWidth() - switchSlide.getWidth(), 0, paint);//如果是关闭的将滑块至于左边,并将关闭状态传至监听器}else {if (switchListener != null) {switchListener.onSwitchChange(false);}canvas.drawBitmap(switchSlide, 0, 0, paint);}break;default:break;}}@Overridepublic boolean onTouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:currentX = event.getX();//将标记位修改成按下的状态state = STATE_DOWN;//通知系统重新绘制界面invalidate();//在主线程
//          postInvalidate();//在子线程break;case MotionEvent.ACTION_MOVE:currentX = event.getX();//将标记位修改为移动状态state = STATE_MOVE;invalidate();break;case MotionEvent.ACTION_UP:currentX = event.getX();//将标记为修改为抬起状态state = STATE_UP;//抬起的坐标大于背景宽度一半的时候设为打开状态if (currentX > backgroundBitmap.getWidth()/2f) {//滑块在右边,开启isOpen = true;//抬起的坐标小于背景宽度坐标一 半的时候设为关闭状态}else if (currentX < backgroundBitmap.getWidth()) {//滑块在左边,关闭isOpen = false;}invalidate();break;}return true;}/** * switchView开关监听接口*  *  */interface OnSwitchChangedListener {public void onSwitchChange(boolean isOpen);}/** * 设置 switchView状态监听 * */public void setOnChangeListener(OnSwitchChangedListener listener) {switchListener = listener;}
}

MainActivity.java

public class MainActivity extends Activity {private SwitchView switchView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);switchView = (SwitchView) findViewById(R.id.switchView);switchView.setOnChangeListener(new OnSwitchChangedListener() {@Overridepublic void onSwitchChange(boolean isOpen) {if (isOpen) {//打开开关的时候的逻辑Toast.makeText(MainActivity.this, "开关打开了", Toast.LENGTH_LONG).show();}else {//关闭开关的时候的逻辑Toast.makeText(MainActivity.this, "开关关闭了", Toast.LENGTH_LONG).show();}}});}
}

大家看起来这么简单的一个写了这么多,其实我们学习这个不是为了写这个,比这个好的开源多的是,而是为了学习这种思路与思维。大家赶紧试试吧!

android开发笔记之自定义开关按钮相关推荐

  1. Android开发笔记(一百三十七)自定义行为Behavior

    协调布局CoordinatorLayout能够让内部的子控件互相配合着移动,这跟以往孤立的控件运动截然不同,协调运动的结果,意味着App画面更加流畅.自然,也更符合日常生活中的动作场景.如果你对Coo ...

  2. Android开发笔记(一百二十六)自定义音乐播放器

    MediaRecorder/MediaPlayer 在Android手机上面,音频的处理比视频还要复杂,这真是出人意料.在前面的博文< Android开发笔记(五十七)录像录音与播放>中, ...

  3. Android开发笔记(一百二十五)自定义视频播放器

    视频播放方式 在Android中播放视频的方式有两种: 1.使用MediaPlayer结合SurfaceView进行播放.其中通过SurfaceView显示视频的画面,通过MediaPlayer来设置 ...

  4. Android开发笔记(一百二十四)自定义相册

    画廊Gallery Gallery是一个早期的画廊控件,左右滑动手势可展示内嵌的图片列表,类似于一个平面的万花筒.虽然Android现在将Gallery标记为Deprecation(表示已废弃),建议 ...

  5. Android开发笔记(一百一十八)自定义悬浮窗

    WindowManager 在前面< Android开发笔记(六十六)自定义对话框>中,我们提到每个页面都是一个Window窗口,许多的Window对象需要一个管家来打理,这个管家我们称之 ...

  6. Android开发笔记(九十五)自定义Drawable

    Drawable Bitmap是Android对图像的定义描述,而Drawable则是对图像的展现描述,在View视图中显示图像都是通过Drawable来实现的.其中有关Bitmap的介绍参见< ...

  7. Android开发笔记(六十六)自定义对话框

    AlertDialog Android中最常用的对话框是AlertDialog,它可以完成常见的交互操作,如提示.确认.选择等等,然后就是进度对话框ProgressDialog(参见< Andr ...

  8. Android开发笔记(十一)自定义视图的构造方法

    自定义视图的用法 Android自带的视图常常不能满足实际开发的需求,这种情况下我们就得自定义视图(View). 首先在res\values目录下找到attrs.xml(如没有则创建之),在该属性定义 ...

  9. Android开发笔记(一百四十八)自定义输入法软键盘

    手机上输入文字,都是通过系统自带的软键盘,这个软键盘可以是Android自带的,也可以是第三方软键盘如搜狗输入法.多数情况下面,系统自带的软键盘已经够用了,可是总有少数情况,系统软键盘无法满足开发者的 ...

最新文章

  1. 康奈尔大学对博士生的四点要求
  2. 读《编程之道》-对于程序员的抽象描述
  3. python之穿越火线游戏代码_Python实现拼字游戏与代码重构
  4. Bug人生---超越bug杀手
  5. ORACLE经常使用系统查询
  6. gensim提取一个句子的关键词_聊一聊 NLPer 如何做关键词抽取
  7. Oracle数据库导入csv文件(sqlldr命令行)
  8. Wordpress固定链接伪静态
  9. 写博客是一种乐趣,一种需要培养的乐趣,Java程序员最大的悲哀是什么
  10. 排序算法之 插入排序
  11. [转载] python字符串查找的四种方法
  12. java dll源码查看工具_Java调用dll库源码
  13. 计算机机设应用基础,计算机机应用基础–word.ppt
  14. 3000款课件培训PPT模板免费下载网站
  15. pcf8591c语言编程,学51单片机-基于PCF8591的AD采样和DA输出
  16. 就是要让你搞懂Nginx,这篇就够了!
  17. 计算机不显示磁盘阵列,识别不到硬盘?bios中怎么改raid为ahci硬盘模式。
  18. 入行10年后,我总结了这份FPGA学习路线
  19. 物理PC机ping不通虚拟机解决方法(亲测可用)
  20. qt设置背景图片注意事项

热门文章

  1. 数据交换中心的方案设计
  2. altium designer多通道设计技巧
  3. 几款免费的监控服务器对比
  4. IE恶意修改之终极解决方案
  5. 源码安装mariadb10.4.24
  6. 空战小游戏(控制台应用) c/c++实现
  7. 数据团队如何写年度总结和计划?
  8. Sklearn到底是什么?
  9. Stata新命令-prodest:不再畏惧生产函数
  10. Backbone入门指南(二):依赖库Underscore