效果图:

定义一个类,取名为MySwitch.java,此类去继承View,为何是继承View而不是去继承ViewGroup呢,是因为自定义开关没有子控件,之需要操作自身绘制即可

  1. package custom.view.upgrade.my_switch;
  2.  
  3. import android.content.Context;
  4. import android.content.res.TypedArray;
  5. import android.graphics.Bitmap;
  6. import android.graphics.BitmapFactory;
  7. import android.graphics.Canvas;
  8. import android.graphics.Paint;
  9. import android.graphics.drawable.BitmapDrawable;
  10. import android.graphics.drawable.Drawable;
  11. import android.support.annotation.Nullable;
  12. import android.util.AttributeSet;
  13. import android.util.Log;
  14. import android.view.MotionEvent;
  15. import android.view.View;
  16.  
  17. import custom.view.R;
  18.  
  19. public class MySwitch extends View implements View.OnClickListener {
  20.  
  21. private static String TAG = MySwitch.class.getSimpleName();
  22.  
  23. private Paint mPaint;
  24.  
  25. /**
  26. * 让布局中来指定实例化,得到属性集合AttributeSet
  27. * @param context
  28. * @param attrs
  29. */
  30. public MySwitch(Context context, @Nullable AttributeSet attrs) {
  31. super(context, attrs);
  32.  
  33. initView(context, attrs);
  34. initListener();
  35. }
  36.  
  37. // 定义按钮背景图片
  38. private Bitmap bmSwitchBackground;
  39.  
  40. // 定义按钮拖动的图片
  41. private Bitmap bmSwitchDrag;
  42.  
  43. // 定义开关的状态 true || false , 默认是关闭状态
  44. private boolean switchStatus;
  45.  
  46. // 定义开关的临时记录状态
  47. private boolean tempSwitchStatus;
  48.  
  49. // 定义按钮拖动距离左边的距离
  50. private int dragLife = -1;
  51.  
  52. /**
  53. * 初始化工作
  54. */
  55. private void initView(Context context, AttributeSet attrs) {
  56. mPaint = new Paint();
  57. mPaint.setAntiAlias(true); // 抗锯齿
  58.  
  59. // 获取属性
  60. TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MySwitch);
  61. bmSwitchBackground = ((BitmapDrawable) typedArray.getDrawable(R.styleable.MySwitch_switch_background)).getBitmap();
  62. bmSwitchDrag = ((BitmapDrawable) typedArray.getDrawable(R.styleable.MySwitch_switch_drag)).getBitmap();
  63. switchStatus = typedArray.getBoolean(R.styleable.MySwitch_switch_status, false);
  64. }
  65.  
  66. public void setBmSwitchBackground(int switchBackground) {
  67. this.bmSwitchBackground = BitmapFactory.decodeResource(getResources() ,switchBackground);
  68. }
  69.  
  70. public void setBmSwitchDrag(int switchDrag) {
  71. this.bmSwitchDrag = BitmapFactory.decodeResource(getResources(), switchDrag);
  72. }
  73.  
  74. public void setSwitChStatus(boolean switchStatus) {
  75. this.switchStatus = switchStatus;
  76. dragLife = -1;
  77. }
  78.  
  79. /**
  80. * 初始化事件
  81. */
  82. private void initListener() {
  83. setOnClickListener(this);
  84. }
  85.  
  86. @Override
  87. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  88. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  89.  
  90. // 宽度是:按钮背景的宽度
  91. // 高度是:按钮背景的高度
  92. // 测量自身View
  93. setMeasuredDimension(bmSwitchBackground.getWidth(), bmSwitchBackground.getHeight());
  94. }
  95.  
  96. @Override
  97. protected void onDraw(Canvas canvas) {
  98. super.onDraw(canvas);
  99.  
  100. // 绘制按钮背景
  101. canvas.drawBitmap(bmSwitchBackground, 0, 0, mPaint);
  102.  
  103. if (dragLife != -1) {
  104. canvas.drawBitmap(bmSwitchDrag, dragLife, 0 , mPaint);
  105. } else if (dragLife == -1) {
  106. if (switchStatus) {
  107. // 打开状态
  108. // 滑动点向右就是开启状态
  109. int openDragLife = getLifeDragMaxValue();
  110. canvas.drawBitmap(bmSwitchDrag, openDragLife, 0, mPaint);
  111. moveEndX = openDragLife;
  112. } else {
  113. // 关闭状态
  114. canvas.drawBitmap(bmSwitchDrag, 0, 0, mPaint);
  115. moveEndX = 0;
  116. }
  117.  
  118. // 当开关的状态发生变化后,回调方法告诉用户,开关改变了
  119. if (null != onSwitchChangeListener && switchStatusChange) {
  120. if (tempSwitchStatus != switchStatus) {
  121. onSwitchChangeListener.onSwitchChange(switchStatus);
  122. }
  123. }
  124. }
  125. }
  126.  
  127. private float downX;
  128. private int moveEndX;
  129.  
  130. private float clickDown;
  131. private float clickMove;
  132.  
  133. // 开关状态是否发送了改变
  134. private boolean switchStatusChange;
  135.  
  136. @Override
  137. public boolean onTouchEvent(MotionEvent event) {
  138. super.onTouchEvent(event); // 必须要调用此方法,onClick点击事件方法才会生效
  139. switch (event.getAction()) {
  140. case MotionEvent.ACTION_DOWN:
  141. downX = event.getX();
  142. isClick = true;
  143. clickDown = event.getX();
  144. switchStatusChange = false;
  145. tempSwitchStatus = switchStatus;
  146. break;
  147. case MotionEvent.ACTION_MOVE:
  148. // Log.d(TAG, ">>>>>不加等于:" + (int) (event.getX() - downX));
  149. moveEndX += (int) (event.getX() - downX);
  150. Log.d(TAG,">>>>>>加等于:" + moveEndX);
  151.  
  152. if (moveEndX > getLifeDragMaxValue()) {
  153. moveEndX = getLifeDragMaxValue();
  154. } else if (moveEndX < 0){
  155. moveEndX = 0;
  156. }
  157.  
  158. dragLife = moveEndX;
  159. invalidate();
  160.  
  161. downX = event.getX();
  162.  
  163. clickMove = downX;
  164. if (Math.abs(clickMove - clickDown) > 5) {
  165. isClick = false;
  166. }
  167.  
  168. break;
  169. case MotionEvent.ACTION_UP:
  170. if (dragLife > (getLifeDragMaxValue() / 2)) {
  171. dragLife = -1;
  172. switchStatus = true;
  173. switchStatusChange = true;
  174. } else if (dragLife >= 0){
  175. dragLife = -1;
  176. switchStatus = false;
  177. switchStatusChange = true;
  178. } else {
  179. switchStatusChange = false;
  180. }
  181. invalidate();
  182. // upX = (int) event.getX();
  183. break;
  184. default:
  185. break;
  186. }
  187. return true;
  188. }
  189.  
  190. private int getLifeDragMaxValue() {
  191. return bmSwitchBackground.getWidth() - bmSwitchDrag.getWidth();
  192. }
  193.  
  194. /*@Override
  195. protected void onFinishInflate() {
  196. super.onFinishInflate();
  197.  
  198. if (switchStatus) {
  199. moveEndX = getLifeDragMaxValue();
  200. Log.d(TAG, ">>>>>>>>>>>>>>>>>onFinishInflate()............ getLifeDragMaxValue():" + getLifeDragMaxValue());
  201. }
  202. }*/
  203.  
  204. /**
  205. * 定义点击事件状态
  206. */
  207. private boolean isClick = true;
  208.  
  209. @Override
  210. public void onClick(View v) {
  211. Log.d(TAG, "onClick() isClick:" + isClick);
  212. if (isClick) {
  213. if (switchStatus) {
  214. switchStatus = false;
  215. switchStatusChange = true;
  216. } else {
  217. switchStatus = true;
  218. switchStatusChange = true;
  219. }
  220. // switchStatus = (switchStatus==true?false:true);
  221. dragLife = -1;
  222. invalidate();
  223. }
  224. }
  225.  
  226. private OnSwitchChangeListener onSwitchChangeListener;
  227.  
  228. /**
  229. * 用户设置的 状态监听
  230. * @param onSwitchChangeListener
  231. */
  232. public void setOnSwitchChangeListener(OnSwitchChangeListener onSwitchChangeListener) {
  233. this.onSwitchChangeListener = onSwitchChangeListener;
  234. }
  235. }

布局文件中去引用写好的自定义开关类

并设置自定义属性:

  1.      myswitch:switch_background="@mipmap/switch_background"
  2. myswitch:switch_drag="@mipmap/switch_drag"
  3. myswitch:switch_status="true"
  1. <!-- 自定义开关升级版 -->
  2. <RelativeLayout
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. xmlns:myswitch="http://schemas.android.com/apk/res-auto"
  5. xmlns:tools="http://schemas.android.com/tools"
  6. android:layout_width="match_parent"
  7. android:layout_height="match_parent"
  8. tools:context=".upgrade.MainActivity">
  9.  
  10. <!-- 使用wrap_content,是因为不知道按钮的背景有多大,更加按钮图片的改变而变化 -->
  11. <custom.view.upgrade.my_switch.MySwitch
  12. android:id="@+id/custom_myswitch"
  13. android:layout_width="wrap_content"
  14. android:layout_height="wrap_content"
  15. android:layout_centerInParent="true"
  16. myswitch:switch_background="@mipmap/switch_background"
  17. myswitch:switch_drag="@mipmap/switch_drag"
  18. myswitch:switch_status="true"
  19. />
  20.  
  21. </RelativeLayout>

自定义规则arrts.xml文件声明:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <resources>
  3.  
  4. <declare-styleable name="MySwitch">
  5.  
  6. <attr name="switch_status" format="boolean" />
  7.  
  8. <attr name="switch_background" format="reference" />
  9.  
  10. <attr name="switch_drag" format="reference" />
  11.  
  12. </declare-styleable>
  13.  
  14. </resources>

模拟用户来使用:

  1. MySwitch mySwitch = findViewById(R.id.custom_myswitch);
  2.  
  3. // 设置开关的背景图片
  4. mySwitch.setBmSwitchBackground(R.mipmap.switch_background);
  5.  
  6. // 设置开关拖动的图片
  7. mySwitch.setBmSwitchDrag(R.mipmap.switch_drag);
  8.  
  9. // 设置开关的状态,打开、关闭
  10. mySwitch.setSwitChStatus(false);
  11.  
  12. mySwitch.setOnSwitchChangeListener(new OnSwitchChangeListener() {
  13. @Override
  14. public void onSwitchChange(boolean switchChangeStatus) {
  15. String result;
  16. if (switchChangeStatus) {
  17. result = "打开";
  18. } else {
  19. result = "关闭";
  20. }
  21. Toast.makeText(MainActivity.this, "开关已" + result, Toast.LENGTH_SHORT).show();
  22. }
  23. });

Android-自定义开关(升级版)的更多相关文章

  1. Android 自定义 View 绘制

    在 Android 自定义View 里面,介绍了自定义的View的基本概念.同时在 Android 控件架构及View.ViewGroup的测量 里面介绍了 Android 的坐标系 View.Vie ...

  2. android 自定义动画

    android自定义动画注意是继承Animation,重写里面的initialize和applyTransformation,在initialize方法做一些初始化的工作,在applyTransfor ...

  3. Android自定义View 画弧形,文字,并增加动画效果

    一个简单的Android自定义View的demo,画弧形,文字,开启一个多线程更新ui界面,在子线程更新ui是不允许的,但是View提供了方法,让我们来了解下吧. 1.封装一个抽象的View类   B ...

  4. Android自定义View4——统计图View

    1.介绍 周末在逛慕课网的时候,看到了一张学习计划报告图,详细记录了自己一周的学习情况,天天都是0节课啊!正好在学习Android自定义View,于是就想着自己去写了一个,这里先给出一张慕课网的图,和 ...

  5. (转)[原] Android 自定义View 密码框 例子

    遵从准则 暴露您view中所有影响可见外观的属性或者行为. 通过XML添加和设置样式 通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 ...

  6. Android 自定义View合集

    自定义控件学习 https://github.com/GcsSloop/AndroidNote/tree/master/CustomView 小良自定义控件合集 https://github.com/ ...

  7. Android 自定义View (五)——实践

    前言: 前面已经介绍了<Android 自定义 view(四)-- onMeasure 方法理解>,那么这次我们就来小实践下吧 任务: 公司现有两个任务需要我完成 (1)监测液化天然气液压 ...

  8. Android 自定义 view(四)—— onMeasure 方法理解

    前言: 前面我们已经学过<Android 自定义 view(三)-- onDraw 方法理解>,那么接下我们还需要继续去理解自定义view里面的onMeasure 方法 推荐文章: htt ...

  9. Android 自定义 view(三)—— onDraw 方法理解

    前言: 上一篇已经介绍了用自己定义的属性怎么简单定义一个view<Android 自定义view(二) -- attr 使用>,那么接下来我们继续深究自定义view,下一步将要去简单理解自 ...

随机推荐

  1. (转)html中使用表单和input file上传图片

    本文转载自:http://hi.baidu.com/love_1210/item/120e452b42b2a854c38d59eb 客户端代码: <form name="form1&q ...

  2. Thinkphp 查询条件 and 和 or同时使用即复合查询

            thinkphp 3.2快捷查询OR查询&分割表示AND查询讲解         快捷查询方式是一种多字段查询的简化写法,可以进一步简化查询条件的写法,在多个字段之间用|分割表 ...

  3. 性能基准测试:KVM大战Xen

    编译自:http://major.io/2014/06/22/performance-benchmarks-kvm-vs-xen/作者: Major Hayden原创:LCTT https://lin ...

  4. Solr -- 查询语法/参数

    1. 常用查询参数 参数 描述 defType 指定用于处理查询语句(参数q的内容)的查询解析器,eg:defType=lucene sort 指定响应的排序方式:升序asc或降序desc.同时需要指 ...

  5. Jenkins是什么?

    Jenkins 是一个可扩展的持续集成引擎. 主要用于: l 持续.自动地构建/测试软件项目. l 监控一些定时执行的任务. Jenkins拥有的特性包括: l 易于安装-只要把jenkins.war ...

  6. vba截屏保存

    Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVa ...

  7. Python列表练习题

    1.创建一个空列表,命名为names,往里面添加 Lihua.Rain.Jack.Xiuxiu.Peiqi和Black元素. #!-*- coding:utf-8 -*- names = [" ...

  8. sqlserver job 执行时间

    select instance_id,jh.run_date,jh.job_id,jh.step_name, case jh.run_status then 'failed' then 'Succee ...

  9. JSP中系统Date的几点不符合中国时间观的地方

    正常调用系统时间的显示格式是Date date = new Date 显示出来的当前时间为Sun Nov 22 18:39:51 CST 2015 星期天的英文单词是Sun, 这个大家都是熟悉的, 这 ...

  10. eclipse-jee-mars-2-win32-x86_64安装activiti

    离线安装老是不行,只能在线安装了 选择Activiti BPMN Desisner,然后一直选择下一步,直到出现如下窗体: 幸亏这个插件很小,在线安装也不算慢