弄过android开发的都知道,系统有一个默认的ToggleButton,但很多人都觉得他很难看,当然也包括我。如果你感觉他不难看,那你就继续使用系统的吧,这篇文章对你来说是多余的了。

今天来写一个模仿微信的ToggleButton控件,是啊,模仿都是模仿"大家之作",腾讯、360等等,也确实,他们设计出来的东西确实好看。

下面看效果图打开状态,关闭状态

先奉献上三张图片的素材,打开背景图,关闭背景图,滑块背景图。这里背景图高度是小于前两个背景图2个像素的。这样才能出现最终的打开和关闭后的滑块上下出现1个像素背景的效果。

开始我们的控件代码,这里我们自定义一个ToggleButton控件,当然要继承自View,下面贴出我的代码

  1. public class ToggleButton extends View {
  2.  
  3. private Bitmap onBackgroundImage;
  4. private Bitmap offBackgroundImage;
  5. private Bitmap blockImage;
  6. private ToggleState state = ToggleState.Open;
  7. private int currentX;
  8. private int mouseDownX = -1;
  9. private boolean isMove = false;
  10. private ToggleState preState;
  11. private boolean isInvalidate = true;
  12. public ToggleButton(Context context) {
  13. super(context);
  14. // TODO Auto-generated constructor stub
  15. }
  16.  
  17. private OnClickListener clickListener = null;
  18.  
  19. /*
  20. * 其实应该是stateChange事件,懒得改了
  21. */
  22. public void SetOnClickListener(OnClickListener listener) {
  23. if (listener != null)
  24. clickListener = listener;
  25. }
  26.  
  27. @Override
  28. protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
  29. // TODO Auto-generated method stub
  30. super.onLayout(changed, left, top, right, bottom);
  31. //layout方法是在ondraw方法之前执行,
  32. //在这个做判断是防止用户在设置Image之前setState,
  33. //这样Image为null,系统无法获取block宽度,也就无法设置currentX坐标,
  34. //系统将采用默认Open状态,会出现即使用户设置状态为close,而界面显示仍未open状态,
  35. //在这里判断,是因为当前image和state肯定已经设置完毕了
  36. if (state == ToggleState.Open) {
  37. currentX = 2;
  38. } else {
  39. if (blockImage == null || offBackgroundImage == null || onBackgroundImage == null)
  40. return;
  41. currentX=offBackgroundImage.getWidth()-blockImage.getWidth()-2;
  42. }
  43. }
  44. public enum ToggleState {
  45. Open, Close
  46. }
  47.  
  48. public ToggleButton(Context context, AttributeSet attrs) {
  49. super(context, attrs);
  50. // TODO Auto-generated constructor stub
  51. }
  52.  
  53. public void setOnBackgroundResource(int id) {
  54.  
  55. onBackgroundImage = BitmapFactory.decodeResource(getResources(), id);
  56. }
  57.  
  58. public void setOffBackgroundResource(int id) {
  59. offBackgroundImage = BitmapFactory.decodeResource(getResources(), id);
  60.  
  61. }
  62.  
  63. public void setState(ToggleState state) {
  64. preState = this.state = state;
  65. if (state == ToggleState.Open) {
  66. currentX = 2;
  67. } else {
  68. if (blockImage != null && offBackgroundImage != null)
  69. currentX = offBackgroundImage.getWidth() - blockImage.getWidth() - 2;
  70. }
  71. invalidate();
  72. }
  73.  
  74. public ToggleState getState() {
  75. return state;
  76. }
  77.  
  78. public void setBlockResource(int id) {
  79. blockImage = BitmapFactory.decodeResource(getResources(), id);
  80. }
  81.  
  82. @Override
  83. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  84. // TODO Auto-generated method stub
  85. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  86. setMeasuredDimension(onBackgroundImage.getWidth(), onBackgroundImage.getHeight());
  87.  
  88. }
  89.  
  90. @Override
  91. protected void onDraw(Canvas canvas) {
  92. // TODO Auto-generated method stub
  93. super.onDraw(canvas);
  94. // 有效性判断,如果其中有一个为空,拒绝绘制,继续绘制也没有意义
  95. if (blockImage == null || offBackgroundImage == null || onBackgroundImage == null)
  96. return;
  97. if ((currentX + blockImage.getWidth() / 2) > onBackgroundImage.getWidth() / 2) {
  98. canvas.drawBitmap(offBackgroundImage, 0, 0, null);
  99. } else {
  100. canvas.drawBitmap(onBackgroundImage, 0, 0, null);
  101. }
  102. canvas.drawBitmap(blockImage, currentX, 1, null);
  103. }
  104.  
  105. @Override
  106. public boolean onTouchEvent(MotionEvent event) {
  107. currentX = (int) event.getX();
  108. switch (event.getAction()) {
  109. case MotionEvent.ACTION_DOWN:
  110. // 记录鼠标按下时的x
  111. mouseDownX = (int) event.getX();
  112. // 按下的时候不进行重绘
  113. isInvalidate = false;
  114. break;
  115. case MotionEvent.ACTION_MOVE:
  116. // 记录鼠标发生了移动
  117. isMove = true;
  118. break;
  119. case MotionEvent.ACTION_UP:
  120. // 判断鼠标按下和抬起过程中是否发生了移动
  121. // 鼠标抬起时,判断当前x坐标位置
  122. if (isMove) {
  123. // 发生了移动,判断当前位置
  124. if ((currentX + blockImage.getWidth() / 2) > onBackgroundImage.getWidth() / 2) {
  125. // 背景图的后半段
  126. currentX = onBackgroundImage.getWidth() - blockImage.getWidth();
  127. state = ToggleState.Close;
  128. } else {
  129. // 背景图的前半段
  130. currentX = 2;
  131. state = ToggleState.Open;
  132. }
  133. } else {
  134. // 没有发生移动,即为点击事件,更改状态,同时改变滑块位置
  135. if (state == ToggleState.Open) {
  136. state = ToggleState.Close;
  137. currentX = onBackgroundImage.getWidth() - blockImage.getWidth() - 2;
  138. } else {
  139. state = ToggleState.Open;
  140. currentX = 2;
  141. }
  142. }
  143. // 复位,以免影响下一次的触摸事件
  144. isMove = false;
  145. if (preState != state && clickListener != null) {
  146. clickListener.onClick(this);
  147. preState = state;
  148. }
  149. break;
  150. }
  151. if (currentX < 2)
  152. currentX = 2;
  153. if (currentX + blockImage.getWidth() >= onBackgroundImage.getWidth())
  154. currentX = onBackgroundImage.getWidth() - blockImage.getWidth() - 2;
  155. // 通知控件绘制
  156. if (isInvalidate)
  157. invalidate();
  158. else
  159. isInvalidate = true;
  160. return true;
  161. }
  162.  
  163. }

代码里的注释够多吧,哈哈,所以就不进行讲解了。

接下来我们在activity布局文件里面使用这个ToggleButton 控件

  1. <包名.ToggleButton
  2. android:layout_width="wrap_content"
  3. android:layout_height="wrap_content"
  4. android:id="@+id/testToggleButton"
  5. />

我们在activity里面使用吧

  1. final ToggleButton toggle=(ToggleButton) findViewById(R.id.testToggleButton);
  2. toggle.setState(ToggleState.Close);
  3. toggle.setOnBackgroundResource(R.drawable.on);
  4. toggle.setOffBackgroundResource(R.drawable.off);
  5. toggle.setBlockResource(R.drawable.block);
  6.  
  7. toggle.SetOnClickListener(new OnClickListener() {
  8. @Override
  9. public void onClick(View v) {
  10. // TODO Auto-generated method stub
  11. String state=(toggle.getState()==ToggleState.Open?"打开":"关闭");
  12. Toast.makeText(MainActivity.this, "当前状态:"+state, Toast.LENGTH_SHORT).show();
  13. }
  14. });

注意啊,我上面的一行代码的位置

toggle.setState(ToggleState.Close);

设置状态这行代码放在了设置图片之前,但效果仍然是关闭状态。

但ToggleButton里面如果不重写OnLayout方法,显示出来的状态就只能是打开状态。

自定义控件(模仿微信ToggleButton控件)的更多相关文章

  1. Qt编写自定义控件31-面板仪表盘控件

    一.前言 在Qt自定义控件中,仪表盘控件是数量最多的,写仪表盘都写到快要吐血,可能是因为各种工业控制领域用的比较多吧,而且仪表盘又是比较生动直观的,这次看到百度的echart中有这个控件,所以也来模仿 ...

  2. WPF自定义控件(一)の控件分类

    一.什么是控件(Controls) 控件是指对数据和方法的封装.控件可以有自己的属性和方法,其中属性是控件数据的简单访问者,方法则是控件的一些简单而可见的功能.控件创建过程包括设计.开发.调试(就是所 ...

  3. Android自定义控件之自定义组合控件

    前言: 前两篇介绍了自定义控件的基础原理Android自定义控件之基本原理(一).自定义属性Android自定义控件之自定义属性(二).今天重点介绍一下如何通过自定义组合控件来提高布局的复用,降低开发 ...

  4. android自定义控件(五) 自定义组合控件

    转自http://www.cnblogs.com/hdjjun/archive/2011/10/12/2209467.html 代码为自己编写 目标:实现textview和ImageButton组合, ...

  5. ToggleButton控件

    ToggleButton 两种状态 ·状态button     -继承自CompoundButton ·主要属性:-Android:textOn    -Android:textOff ·主要方法: ...

  6. WPF自定义控件第二 - 转盘按钮控件

    继之前那个控件,又做了一个原理差不多的控件.这个控件主要模仿百度贴吧WP版帖子浏览界面左下角那个弹出的按钮盘.希望对大家有帮助. 这个控件和之前的也差不多,为了不让大家白看,文章最后发干货. 由于这个 ...

  7. Windows phone自定义控件(无外观控件)——FlipPanel

    编码前 无外观自定义控件的定义在上一篇中已经有了,至于这一篇的自定义控件,比之前多加入了状态的变化,就像默认的Button具有Pressed.Normal等状态.在状态转变的同时可以加上一些动画,可以 ...

  8. Qt编写自定义控件48-面板窗体控件

    一.前言 很多时候需要有一个控件,能够替代容器控件,自动容纳多个widget,自适应宽高,然后提供滚动条功能,这就必然需要用到QScrollArea控件,可设置各个子面板的间距等,也在很多系统中用到, ...

  9. Qt编写自定义控件47-面板区域控件

    一.前言 在很多web网页上,经常可以看到一个设备对应一个面板,或者某种同等类型的信息全部放在一个面板上,该面板还可以拖来拖去的,这个控件首次用在智能访客管理平台中,比如身份证信息一个面板,访客信息一 ...

随机推荐

  1. c# 匿名对象增加动态属性

    在开发过程中碰到了一个需求,需要动态创建对象及其动态属性.在尝试几种方法后,最后完成了需求,记录下过程,给园友参考下 1.动态创建对象一:匿名对象 ",Birthday =DateTime. ...

  2. MongoDB - MongoDB CRUD Operations, Query Documents

    Query Method MongoDB provides the db.collection.find() method to read documents from a collection. T ...

  3. JQuery Mobile + Cordova 实战一

    好的,今天给大家继续讲解 JQM 和 Cordova 的结合吧.Cordova 和 Phonegap 反正是一个东西,大家就当做一个是旧版(Phonegap)的一个是新版(Cordova)的就行.不同 ...

  4. Part 48 to 51 Talking about Access Modifiers in C#

    Part 48 Difference between Types and Type Members Part 49 Access Modifiers in C# Part 50 Internal an ...

  5. AJAX之JSON

    AJAX=Asynchronous JavaScript and XML(异步的JavaScript 和XML). AJAX不是新的编程语言,而是一种是用现代标准的新方法,用于创建快速动态网页的技术. ...

  6. .NET使用NPOI读取Word模板并替换关键字并下载

    NPOI 是 POI 项目的 .NET 版本.POI是一个开源的Java读写Excel.WORD等微软OLE2组件文档的项目. 使用 NPOI 你就可以在没有安装 Office 或者相应环境的机器上对 ...

  7. Windows Management Instrumentation 服务无法启动 解决办法

    Win7下 Windows Management Instrumentation 服务无法启动 解决办法: 1. 以管理员身份运行cmd.exe 2. sc config Winmgmt depend ...

  8. Linux中RM快速删除大量文件/文件夹方法

    昨天遇到一个问题,在Linux中有一个文件夹里面含有大量的Cache文件(夹),数量级可能在百万级别,使用rm -rf ./* 删除时间慢到不可接受.Google了一下,查到了一种方法,试用了下确实比 ...

  9. Linux密码更改

    男孩儿的灰色 QQ:936779899 2014.2.9 1. 进入单用户模式 2. 编辑passwd文件,删除X 3. 然后保存推出并编辑shadow文件 4. 删除标色部分,其他保持不变 5. 最 ...

  10. 7款外观迷人的HTML5/CSS3 3D按钮特效

    1.CSS3超酷3D弹性按钮 按钮实现非常简单 今天我又要向大家分享一款实现超级简单的CSS3 3D弹性按钮,它在鼠标按下时不仅从视觉上感受到3D立体的效果,而且更有弹性的动画特效,非常可爱. 在线演 ...