zoukankan      html  css  js  c++  java
  • 自定义控件:滑动开关按钮(自定义属性)

         

    【主要步骤】

    1、自定义类MyToggleButton继承自view。

    2、重写onMeasure方法,指定控件大小。

    3、重写onDraw方法,绘制控件内容。

    4、重写onTouchEvent方法,对touch事件进行解析。

    【为新控件添加自定义的属性】

    1、在attrs.xml文件中声明属性,有属性名:name和格式:format=如:

    <declare-styleable name="MyToggleBtn">

            <attr name="curr_state" format="boolean" />

    </declare-styleable>

    2、在布局文件中使用新属性,使用之前必须先声明命名空间,如:

    xmlns:heihei=http://schemas.android.com/apk/res/com.example.testdemo

    说明:xmlns是XML name space的缩写;

             heihei 可以任意写;

             http://schemas.android.com/apk/res/   此为android固定格式;

             com.example.testdemo   此应用的包名,如manifest配置文件中一致。

    3、在自定义view的构造方法当中,通过解析AttributeSet对象,获得所需要的属性值。

      

     1 <?xml version="1.0" encoding="utf-8"?>
     2 <!-- res/values/attrs.xml -->
     3 <resources>
     4     
     5     <!-- 声名属性集的名称 -->
     6     <declare-styleable name="MyToggleBtn">
     7 
     8         <!-- 声名一个属性  name是my_background   类型为 引用类型      引用资源ID -->
     9         <attr name="my_background" format="reference" />
    10 
    11         <!-- 声名一个属性  name是my_slide_btn   类型为 引用类型      引用资源ID -->
    12         <attr name="my_slide_btn" format="reference" />
    13 
    14         <!-- 声名一个属性  name是curr_state   类型为 boolean 类型-->
    15         <attr name="curr_state" format="boolean" />
    16         
    17     </declare-styleable>
    18     
    20 </resources>
     1 <RelativeLayout 
     2     xmlns:android="http://schemas.android.com/apk/res/android"
     3     xmlns:heihei="http://schemas.android.com/apk/res/com.example.testdemo"
     4     xmlns:tools="http://schemas.android.com/tools"
     5     android:layout_width="match_parent"
     6     android:layout_height="match_parent" >
     8     <com.example.testdemo.MyToggleButton
     9         android:id="@+id/my_toggle_btn"
    10         android:layout_width="wrap_content"
    11         android:layout_height="wrap_content"
    12         android:layout_centerHorizontal="true"
    13         android:layout_centerVertical="true"
    14         heihei:my_background="@drawable/switch_background"   开关图片
    15         heihei:my_slide_btn="@drawable/slide_button"  灰色图片
    16         heihei:curr_state="false"
    17         testAttrs="@drawable/ic_launcher" />
    19 </RelativeLayout>
      1 package com.example.testdemo;
      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.util.AttributeSet;
     10 import android.util.Log;
     11 import android.view.MotionEvent;
     12 import android.view.View;
     13 import android.view.View.OnClickListener;
     14 
     15 public class MyToggleButton extends View implements OnClickListener {
     16     public static final String TAG = "MyToggleButton";
     17 
     18     // 做为背景的图片
     19     private Bitmap backgroundBitmap;
     21     // 可以滑动的图片
     22     private Bitmap slideBtn;
     23     private Paint paint;
     25     // 滑动按钮的左边届
     26     private float slideBtn_left;
     28     // 背景图的资源ID
     29     private int backgroundId;
     31     // 滑动图片的资源ID
     32     private int slideBtnId; 
     34     // 当前开关的状态 true 为开
     35     private boolean currState = false;
     37     // 判断是否发生拖动, 如果拖动了,就不再响应 onclick 事件
     38     private boolean isDrag = false;
     40     // down 事件时的x值
     41     private int firstX;
     43     // touch 事件的上一个x值
     44     private int lastX;
     45 
     46     // 在代码里面创建对象的时候,使用此构造方法
     47     public MyToggleButton(Context context) {
     48         super(context);
     49     }
     50 
     51     /**
     52      * 在布局文件中声名的view,创建时由系统自动调用。
     53      * 
     54      * @param context
     55      *            上下文对象
     56      * @param attrs
     57      *            属性集
     58      */
     59     public MyToggleButton(Context context, AttributeSet attrs) {
     60         super(context, attrs);
     61        //-------------------
     62         int count = attrs.getAttributeCount();
     63         for (int i = 0; i < count; i++) {
     64             String name = attrs.getAttributeName(i);
     65             String value = attrs.getAttributeValue(i);
     66             Log.i(TAG, "name:" + name+ "value:" + value);
     67         }
     68        //-------------------
     69 
     70         // 无命名空间测试
     71         String testAttrs = attrs.getAttributeValue(null, "testAttrs");
     72 
     73         System.out.println("testAttrs===:" + testAttrs);
     74 
     75         // 获得自定义的属性
     76         // (比如)第一个参数:原材料,第二个参数:图纸。
     77         // TypedArray相当于是一个加工厂,把原材料加工成图纸上面说的。
     78         TypedArray ta = context.obtainStyledAttributes(attrs,
     79                 R.styleable.MyToggleBtn);
     80 
     81         int N = ta.getIndexCount();
     82         for (int i = 0; i < N; i++) {
     83             // 获得某个属性的ID值
     84             int itemId = ta.getIndex(i);
     85             switch (itemId) {
     86             case R.styleable.MyToggleBtn_curr_state:
     87                 currState = ta.getBoolean(itemId, false);
     88 
     89                 break;
     90             case R.styleable.MyToggleBtn_my_background:
     91                 backgroundId = ta.getResourceId(itemId, -1);
     92                 if (backgroundId == -1) {
     93                     throw new RuntimeException("请设置背景图片");
     94                 }
     95                 backgroundBitmap = BitmapFactory.decodeResource(getResources(),
     96                         backgroundId);
     97 
     98                 break;
     99             case R.styleable.MyToggleBtn_my_slide_btn:
    100                 slideBtnId = ta.getResourceId(itemId, -1);
    101                 slideBtn = BitmapFactory.decodeResource(getResources(),
    102                         slideBtnId);
    103 
    104                 break;
    105 
    106             default:
    107                 break;
    108             }
    109 
    110         }
    111         initView();
    112     }
    113 
    114     /**
    115      * 初始化
    116      */
    117     private void initView() {
    118         // 初始化图片
    119         // backgroundBitmap = BitmapFactory.decodeResource(getResources(),
    120         // R.drawable.switch_background);
    121         // slideBtn = BitmapFactory.decodeResource(getResources(),
    122         // R.drawable.slide_button);
    123 
    124         // 初始化 画笔
    125         paint = new Paint();
    126         // 打开抗矩齿
    127         paint.setAntiAlias(true);
    128         // 添加onclick事件监听
    129         setOnClickListener(this);
    130 
    131         flushState();
    132     }
    133 
    134     /*
    135      * view 对象显示的屏幕上,有几个重要步骤: 
    136      * 1、构造方法 创建 对象。 
    137      * 2、测量view的大小。 onMeasure(int,int);
    138      * 3、确定view的位置 ,view自身有一些建议权,决定权在 父view手中。 onLayout(); 
    139      * 4、绘制 view 的内容 。
    140      * onDraw(Canvas)
    141      */
    142 
    143     @Override
    144     // 测量尺寸时的回调方法
    145     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    146         // super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    147 
    148         // 设置当前view的大小 width :view的宽度 height :view的高度 (单位:像素)
    149         setMeasuredDimension(backgroundBitmap.getWidth(),
    150                 backgroundBitmap.getHeight());
    151     }
    152 
    153     // 确定位置的时候调用此方法
    154     // 自定义view的时候,作用不大
    155     // @Override
    156     // protected void onLayout(boolean changed, int left, int top, int right,
    157     // int bottom) {
    158     // super.onLayout(changed, left, top, right, bottom);
    159     // }
    160 
    161     @Override
    162     /**
    163      * 绘制当前view的内容
    164      */
    165     protected void onDraw(Canvas canvas) {
    166         // super.onDraw(canvas);
    167 
    168         // 绘制 背景
    169         /*
    170          * backgroundBitmap 要绘制的图片 left 图片的左边届 top 
    171          * 图片的上边届 paint 绘制图片要使用的画笔
    172          */
    173         canvas.drawBitmap(backgroundBitmap, 0, 0, paint);
    174 
    175         // 绘制 可滑动的按钮
    176         canvas.drawBitmap(slideBtn, slideBtn_left, 0, paint);
    177     }
    178 
    179     @Override
    180     /**
    181      * onclick 事件在View.onTouchEvent 中被解析。
    182      * 系统对onclick 事件的解析,过于简陋,只要有down 事件  up 事件,
    183      * 系统即认为 发生了click 事件
    184      * 
    185      */
    186     public void onClick(View v) {
    187         /*
    188          * 如果没有拖动,才执行改变状态的动作
    189          */
    190         if (!isDrag) {
    191             currState = !currState;
    192             flushState();
    193         }
    194     }
    195 
    196     @Override
    197     public boolean onTouchEvent(MotionEvent event) {
    198         super.onTouchEvent(event);
    199 
    200         switch (event.getAction()) {
    201         case MotionEvent.ACTION_DOWN:
    202             firstX = lastX = (int) event.getX();
    203             isDrag = false;
    204 
    205             break;
    206         case MotionEvent.ACTION_MOVE:
    207 
    208             // 判断是否发生拖动
    209             if (Math.abs(event.getX() - firstX) > 5) {
    210                 isDrag = true;
    211             }
    212 
    213             // 计算 手指在屏幕上移动的距离
    214             int dis = (int) (event.getX() - lastX);
    215 
    216             // 将本次的位置 设置给lastX
    217             lastX = (int) event.getX();
    218 
    219             // 根据手指移动的距离,改变slideBtn_left 的值
    220             slideBtn_left = slideBtn_left + dis;
    221             break;
    222         case MotionEvent.ACTION_UP:
    223 
    224             // 在发生拖动的情况下,根据最后的位置,判断当前开关的状态
    225             if (isDrag) {
    226                 // slideBtn左边届最大值
    227                 int maxLeft = backgroundBitmap.getWidth() - slideBtn.getWidth();
    228                 // 根据 slideBtn_left 判断,当前应是什么状态
    229                 // 此时应为 打开的状态
    230                 if (slideBtn_left > maxLeft / 2) {
    231                     currState = true;
    232                 } else {
    233                     currState = false;
    234                 }
    235 
    236                 flushState();
    237             }
    238             break;
    239         }
    240 
    241         flushView();
    242 
    243         return true;
    244     }
    245 
    246     /**
    247      * 刷新当前状态
    248      */
    249     private void flushState() {
    250         if (currState) {
    251             slideBtn_left = backgroundBitmap.getWidth() - slideBtn.getWidth();
    252         } else {
    253             slideBtn_left = 0;
    254         }
    255 
    256         flushView();
    257     }
    258 
    259     /**
    260      * 刷新当前视力
    261      */
    262     private void flushView() {
    263         /*
    264          * 对 slideBtn_left 的值进行判断 ,确保其在合理的位置 即 0<=slideBtn_left <= maxLeft
    265          * slideBtn左边届最大值
    266          */
    267         int maxLeft = backgroundBitmap.getWidth() - slideBtn.getWidth();
    268 
    269         // 确保 slideBtn_left >= 0
    270         slideBtn_left = (slideBtn_left > 0) ? slideBtn_left : 0;
    271 
    272         // 确保 slideBtn_left <=maxLeft
    273         slideBtn_left = (slideBtn_left < maxLeft) ? slideBtn_left : maxLeft;
    274 
    275         // 刷新当前视图 导致 执行onDraw执行
    276         invalidate();
    277     }
    278 
    279 }

    DEMO下载地址:http://pan.baidu.com/s/1jGsrvM2

  • 相关阅读:
    Vue.js中学习使用Vuex详解
    vuex存储和本地存储(localstorage、sessionstorage)的区别
    Java 编译与反编译
    Vue导航守卫beforeRouteEnter,beforeRouteUpdate,beforeRouteLeave详解
    Vue生命周期简介和钩子函数
    微信开发----被动回复用户消息
    C#4.0 System.Dynamic
    Mvc5 控制器,视图简单说明
    JQuery 禁用后退按钮
    防止用户多次点击
  • 原文地址:https://www.cnblogs.com/androidsj/p/3995206.html
Copyright © 2011-2022 走看看