欢迎访问移动开发之家(rcyd.net),关注移动开发教程。移动开发之家  移动开发问答|  每日更新
页面位置 : > > > 内容正文

Android实现史上最简单自定义开关按钮的方法,

来源: 开发者 投稿于  被查看 24315 次 评论:263

Android实现史上最简单自定义开关按钮的方法,


目录
  • 前言
  • 一、原理
  • 二、实现
    • 1、自定义View类MyToggle
      • 1)属性字段
      • 2)覆写View类的构造方法
      • 3)创建init方法
      • 4)手指触摸事件回调方法onTouch
      • 5)界面重绘方法onDraw
      • 6)计算开关的宽高
      • 7)设置图片资源信息
      • 8)设置开关按钮的状态
      • 9)自定义开关状态监听器
      • 10)设置开关监听器
      • 11)MyToggle完整代码如下:
    • 2、MainActivity
      • 3、布局文件activity_main.xml
        • 4、AndroidManifest.xml
        • 三、运行效果
          • 四、温馨提示

            前言

            很多时候,我们在很多无论是Android还是IOS的APP中都会遇到这样的一种效果,有一个按钮,我们点击一下,便会滑动一下,一会显示“开”,一会显示“关”,这便是开关按钮了,比如:很多Android手机的设置功能里,就有很多功能是用开关按钮实现的,那么这些开关按钮时如何实现的呢?下面,就让我们一起来实现这个功能吧。

            一、原理

            我们在界面的某一个区域里放置一个背景图A,这个图片一边为“开”,一边为“关”,在这个图片上放置一个图片B,图B大约为图A的一半,恰好可以覆盖掉图A上的“开”或者“关”,当我们手指点击图片的时候,图B在图A上滑动,相应的覆盖“开”或者“关”,这样就实现了开关按钮的效果。

            二、实现

            1、自定义View类MyToggle

            这个类继承自View类同时实现了OnTouchListener接口,这个类实现的功能比较多,我们分解来看这个类。

            1)属性字段

            这个类中定义了不少的属性字段,每个属性字段的具体含义详见代码注释

            具体实现代码如下:

            //开关开启的背景图片  
            private Bitmap bkgSwitchOn;  
            //开关关闭的背景图片  
            private Bitmap bkgSwitchOff;  
            //开关的滚动图片  
            private Bitmap btnSlip;  
            //当前开关是否为开启状态  
            private boolean toggleStateOn;  
            //开关状态的监听事件  
            private OnToggleStateListener toggleStateListener;  
            //记录开关·当前的状态  
            private boolean isToggleStateListenerOn;  
            //手指按下屏幕时的x坐标  
            private float proX;  
            //手指滑动过程中当前x坐标  
            private float currentX;  
            //是否处于滑动状态  
            private boolean isSlipping;  
            //记录上一次开关的状态  
            private boolean proToggleState \= true;  
            //开关开启时的矩形  
            private Rect rect\_on;  
            //开关关闭时的矩形  
            private Rect rect\_off;

            2)覆写View类的构造方法

            我们在构造方法里完成的操作就是调用我们自己创建的init()方法

            具体实现代码如下:

            public MyToggle(Context context) {  
              super(context);  
              init(context);  
            }  
              
            public MyToggle(Context context, AttributeSet attrs) {  
              super(context, attrs);  
              init(context);  
            }

            3)创建init方法

            这个方法中实现的操作就是设置触摸事件。

            具体实现代码如下:

            //初始化方法  
            private void init(Context context) {  
              setOnTouchListener(this);  
              
            }

            4)手指触摸事件回调方法onTouch

            这个方法是当手指操作手机屏幕时,Android自动回调的方法,我们在这个方法中,监听手指的按下、移动和抬起事件,记录手指当前的X坐标来判断图片B的移动方向,从而实现图片B在图片A上的移动来达到按钮开和关的效果。

            具体实现代码如下:

            @Override  
            public boolean onTouch(View v, MotionEvent event) {  
              switch (event.getAction()) {  
              case MotionEvent.ACTION\_DOWN:  
                //记录手指按下时的x坐标  
                proX \= event.getX();   
                currentX \= proX;  
                //将滑动标识设置为true  
                isSlipping \= true;  
                break;  
              
              case MotionEvent.ACTION\_MOVE:  
                //记录手指滑动过程中当前x坐标  
                currentX \= event.getX();  
                break;  
              
              case MotionEvent.ACTION\_UP:  
                //手指抬起时将是否滑动的标识设置为false  
                isSlipping \= false;  
                //处于关闭状态  
                if(currentX < bkgSwitchOn.getWidth() / 2 ){  
                  toggleStateOn \= false;  
                } else { // 处于开启状态  
                  toggleStateOn \= true;  
                }  
                  
                // 如果使用了开关监听器,同时开关的状态发生了改变,这时使用该代码  
                if(isToggleStateListenerOn && toggleStateOn != proToggleState){  
                  proToggleState \= toggleStateOn;  
                  toggleStateListener.onToggleState(toggleStateOn);  
                }  
                break;  
              }  
              invalidate();//重绘  
              return true;  
            }

            5)界面重绘方法onDraw

            这个方法主要实现的是界面的重绘操作。

            只要的思路是:

            画背景图A:

                当前手指滑动X坐标currentX大于图A宽度的一般时,按钮背景为开启状态;

                当前手指滑动X坐标currentX小于图A宽度的一般时,按钮背景为关闭状态;

            记录滑块B的X坐标:

            B滑动时:

               当前手指滑动X坐标currentX大于背景图A的宽度,则B坐标为图A宽度减去图B宽度

               当前手指滑动X坐标currentX小于背景图A的宽度,则B坐标为当前X坐标currentX减去滑块宽度的一半

            B静止:

               当按钮处于“开”状态,则B坐标为“开”状态的最左边X坐标

               当按钮处于“关”状态,则B坐标为“关”状态的最左边X坐标

            具体实现代码如下:

            @Override  
            protected void onDraw(Canvas canvas) {  
              super.onDraw(canvas);  
              //用来记录我们滑动块的位置  
              int left\_slip \= 0;   
              Matrix matrix \= new Matrix();  
              Paint paint \= new Paint();  
              if(currentX < bkgSwitchOn.getWidth() / 2){  
                //在画布上绘制出开关状态为关闭时的  背景图片  
                canvas.drawBitmap(bkgSwitchOff, matrix, paint);  
              }else{  
                //在画布上绘制出开关状态为开启时的  背景图片  
                canvas.drawBitmap(bkgSwitchOn, matrix, paint);  
              }  
              if(isSlipping){//开关是否处于滑动状态  
                // 滑动块 是否超过了整个滑动按钮的宽度   
                if(currentX \> bkgSwitchOn.getWidth()){  
                  //指定滑动块的位置  
                  left\_slip \= bkgSwitchOn.getWidth() \- btnSlip.getWidth();  
                } else {  
                  //设置当前滑动块的位置  
                  left\_slip \= (int) (currentX \- btnSlip.getWidth() /2);  
                }  
              } else {//开关是否处于   不滑动状态   
                if(toggleStateOn){  
                  left\_slip \= rect\_on.left;  
                } else {  
                  left\_slip \= rect\_off.left;  
                }  
              }  
                
              if(left\_slip < 0){  
                left\_slip \= 0;  
              } else if( left\_slip \> bkgSwitchOn.getWidth() \- btnSlip.getWidth()){  
                left\_slip \= bkgSwitchOn.getWidth() \- btnSlip.getWidth();  
              }  
              //绘制图像  
              canvas.drawBitmap(btnSlip, left\_slip, 0, paint);  
            }

            6)计算开关的宽高

            这里我通过覆写onMeasure来计算开关的宽度和高度

            具体实现代码如下:

            //计算开关的宽高  
            @Override  
            protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
              setMeasuredDimension(bkgSwitchOn.getWidth(), bkgSwitchOn.getHeight());  
            }

            7)设置图片资源信息

            这个方法主要是供外界调用,向本类提供图片资源。

            具体代码实现如下:

            /\*\*  
             \* 设置图片资源信息  
             \* @param bkgSwitch\_on  
             \* @param bkgSwitch\_off  
             \* @param btn\_Slip  
             \*/  
            public void setImageRes(int bkgSwitch\_on, int bkgSwitch\_off, int btn\_Slip) {  
              bkgSwitchOn \= BitmapFactory.decodeResource(getResources(), bkgSwitch\_on);  
              bkgSwitchOff \= BitmapFactory.decodeResource(getResources(),bkgSwitch\_off);  
              btnSlip \= BitmapFactory.decodeResource(getResources(), btn\_Slip);  
              rect\_on \= new Rect(bkgSwitchOn.getWidth() \- btnSlip.getWidth(), 0,bkgSwitchOn.getWidth(), btnSlip.getHeight());  
              rect\_off \= new Rect(0, 0, btnSlip.getWidth(), btnSlip.getHeight());  
            }

            8)设置开关按钮的状态

            通过传递一个boolean类型的状态,我们在这个方法中将这个状态标识记录下来。

            具体实现代码如下:

            /\*\*  
             \* 设置开关按钮的状态  
             \* @param state  
             \*/  
            public void setToggleState(boolean state) {  
              toggleStateOn \= state;  
            }

            9)自定义开关状态监听器

            我在这个类中定义了一个开关状态监听器接口OnToggleStateListener,里面有一个onToggleState方法来执行按钮的状态变化监听操作。

            具体代码实现如下:

            /\*\*  
             \* 自定义开关状态监听器  
             \* @author liuyazhuang  
             \*  
             \*/  
            interface OnToggleStateListener {  
              abstract void onToggleState(boolean state);  
            }

            10)设置开关监听器

            创建setOnToggleStateListener方法,传递一个OnToggleStateListener监听器对象,通过外界创建OnToggleStateListener对象,并将OnToggleStateListener对象传递进来,我们只需要将外界传递过来的OnToggleStateListener对象记录下来,同时当我们调用OnToggleStateListener接口中的onToggleState方法时,便实现了回调外界OnToggleStateListener实现类中的onToggleState方法。

            具体代码实现如下:

            //设置开关监听器并将是否设置了开关监听器设置为true  
            public void setOnToggleStateListener(OnToggleStateListener listener) {  
              toggleStateListener \= listener;  
              isToggleStateListenerOn \= true;  
            }

            11)MyToggle完整代码如下:

            package com.lyz.slip.toggle;  
              
            import android.content.Context;  
            import android.graphics.Bitmap;  
            import android.graphics.BitmapFactory;  
            import android.graphics.Canvas;  
            import android.graphics.Matrix;  
            import android.graphics.Paint;  
            import android.graphics.Rect;  
            import android.util.AttributeSet;  
            import android.view.MotionEvent;  
            import android.view.View;  
            import android.view.View.OnTouchListener;  
              
            /\*\*  
             \* 自定义开关类  
             \* @author liuyazhuang  
             \*  
             \*/  
            public class MyToggle extends View implements OnTouchListener {  
              //开关开启的背景图片  
              private Bitmap bkgSwitchOn;  
              //开关关闭的背景图片  
              private Bitmap bkgSwitchOff;  
              //开关的滚动图片  
              private Bitmap btnSlip;  
              //当前开关是否为开启状态  
              private boolean toggleStateOn;  
              //开关状态的监听事件  
              private OnToggleStateListener toggleStateListener;  
              //记录开关·当前的状态  
              private boolean isToggleStateListenerOn;  
              //手指按下屏幕时的x坐标  
              private float proX;  
              //手指滑动过程中当前x坐标  
              private float currentX;  
              //是否处于滑动状态  
              private boolean isSlipping;  
              //记录上一次开关的状态  
              private boolean proToggleState \= true;  
              //开关开启时的矩形  
              private Rect rect\_on;  
              //开关关闭时的矩形  
              private Rect rect\_off;  
              
              public MyToggle(Context context) {  
                super(context);  
                init(context);  
              }  
                
              public MyToggle(Context context, AttributeSet attrs) {  
                super(context, attrs);  
                init(context);  
              }  
                
              //初始化方法  
              private void init(Context context) {  
                setOnTouchListener(this);  
              
              }  
              
              @Override  
              public boolean onTouch(View v, MotionEvent event) {  
                switch (event.getAction()) {  
                case MotionEvent.ACTION\_DOWN:  
                  //记录手指按下时的x坐标  
                  proX \= event.getX();   
                  currentX \= proX;  
                  //将滑动标识设置为true  
                  isSlipping \= true;  
                  break;  
              
                case MotionEvent.ACTION\_MOVE:  
                  //记录手指滑动过程中当前x坐标  
                  currentX \= event.getX();  
                  break;  
              
                case MotionEvent.ACTION\_UP:  
                  //手指抬起时将是否滑动的标识设置为false  
                  isSlipping \= false;  
                  //处于关闭状态  
                  if(currentX < bkgSwitchOn.getWidth() / 2 ){  
                    toggleStateOn \= false;  
                  } else { // 处于开启状态  
                    toggleStateOn \= true;  
                  }  
                    
                  // 如果使用了开关监听器,同时开关的状态发生了改变,这时使用该代码  
                  if(isToggleStateListenerOn && toggleStateOn != proToggleState){  
                    proToggleState \= toggleStateOn;  
                    toggleStateListener.onToggleState(toggleStateOn);  
                  }  
                  break;  
                }  
                  
                invalidate();//重绘  
                return true;  
              }  
              
              @Override  
              protected void onDraw(Canvas canvas) {  
                super.onDraw(canvas);  
                //用来记录我们滑动块的位置  
                int left\_slip \= 0;   
                Matrix matrix \= new Matrix();  
                Paint paint \= new Paint();  
                if(currentX < bkgSwitchOn.getWidth() / 2){  
                  //在画布上绘制出开关状态为关闭时的  背景图片  
                  canvas.drawBitmap(bkgSwitchOff, matrix, paint);  
                }else{  
                  //在画布上绘制出开关状态为开启时的  背景图片  
                  canvas.drawBitmap(bkgSwitchOn, matrix, paint);  
                }  
                if(isSlipping){//开关是否处于滑动状态  
                  // 滑动块 是否超过了整个滑动按钮的宽度   
                  if(currentX \> bkgSwitchOn.getWidth()){  
                    //指定滑动块的位置  
                    left\_slip \= bkgSwitchOn.getWidth() \- btnSlip.getWidth();  
                  } else {  
                    //设置当前滑动块的位置  
                    left\_slip \= (int) (currentX \- btnSlip.getWidth() /2);  
                  }  
                } else {//开关是否处于   不滑动状态   
                  if(toggleStateOn){  
                    left\_slip \= rect\_on.left;  
                  } else {  
                    left\_slip \= rect\_off.left;  
                  }  
                }  
                  
                if(left\_slip < 0){  
                  left\_slip \= 0;  
                } else if( left\_slip \> bkgSwitchOn.getWidth() \- btnSlip.getWidth()){  
                  left\_slip \= bkgSwitchOn.getWidth() \- btnSlip.getWidth();  
                }  
                //绘制图像  
                canvas.drawBitmap(btnSlip, left\_slip, 0, paint);  
              }  
              //计算开关的宽高  
              @Override  
              protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
                setMeasuredDimension(bkgSwitchOn.getWidth(), bkgSwitchOn.getHeight());  
              }  
                
              /\*\*  
               \* 设置图片资源信息  
               \* @param bkgSwitch\_on  
               \* @param bkgSwitch\_off  
               \* @param btn\_Slip  
               \*/  
              public void setImageRes(int bkgSwitch\_on, int bkgSwitch\_off, int btn\_Slip) {  
                bkgSwitchOn \= BitmapFactory.decodeResource(getResources(), bkgSwitch\_on);  
                bkgSwitchOff \= BitmapFactory.decodeResource(getResources(),bkgSwitch\_off);  
                btnSlip \= BitmapFactory.decodeResource(getResources(), btn\_Slip);  
                rect\_on \= new Rect(bkgSwitchOn.getWidth() \- btnSlip.getWidth(), 0,bkgSwitchOn.getWidth(), btnSlip.getHeight());  
                rect\_off \= new Rect(0, 0, btnSlip.getWidth(), btnSlip.getHeight());  
              }  
              
              /\*\*  
               \* 设置开关按钮的状态  
               \* @param state  
               \*/  
              public void setToggleState(boolean state) {  
                toggleStateOn \= state;  
              }  
              
              /\*\*  
               \* 自定义开关状态监听器  
               \* @author liuyazhuang  
               \*  
               \*/  
              interface OnToggleStateListener {  
                abstract void onToggleState(boolean state);  
              }  
              //设置开关监听器并将是否设置了开关监听器设置为true  
              public void setOnToggleStateListener(OnToggleStateListener listener) {  
                toggleStateListener \= listener;  
                isToggleStateListenerOn \= true;  
              }  
            }

            2、MainActivity

            这个类实现很简单,主要的功能就是加载界面布局,初始化界面控件,调用MyToggle类中的方法实现按钮的开关效果

            具体代码实现如下:

            package com.lyz.slip.toggle;  
              
            import android.app.Activity;  
            import android.os.Bundle;  
            import android.widget.Toast;  
              
            import com.lyz.slip.toggle.MyToggle.OnToggleStateListener;  
              
            /\*\*  
             \* 程序主入口  
             \* @author liuyazhuang  
             \*  
             \*/  
            public class MainActivity extends Activity {  
                //自定义开关对象  
                private MyToggle toggle;  
                @Override  
                public void onCreate(Bundle savedInstanceState) {  
                    super.onCreate(savedInstanceState);  
                    setContentView(R.layout.activity\_main);  
                    toggle \= (MyToggle) findViewById(R.id.toggle);  
                    //设置开关显示所用的图片  
                    toggle.setImageRes(R.drawable.bkg\_switch, R.drawable.bkg\_switch, R.drawable.btn\_slip);  
                    //设置开关的默认状态    true开启状态  
                    toggle.setToggleState(true);  
                    //设置开关的监听  
                    toggle.setOnToggleStateListener(new OnToggleStateListener() {  
                        @Override  
                        public void onToggleState(boolean state) {  
                            // TODO Auto-generated method stub  
                            if(state){  
                                Toast.makeText(getApplicationContext(), "开关开启", 0).show();  
                            } else {  
                                Toast.makeText(getApplicationContext(), "开关关闭", 0).show();  
                            }  
                        }  
                    });  
                }  
            }

            3、布局文件activity_main.xml

            这里我引用了自己定义的View类MyToggle。

            具体代码实现如下:

            <RelativeLayout xmlns:android\="http://schemas.android.com/apk/res/android"  
                xmlns:tools\="http://schemas.android.com/tools"  
                android:layout\_width\="match\_parent"  
                android:layout\_height\="match\_parent" \>  
              
                <com.lyz.slip.toggle.MyToggle  
                    android:id\="@+id/toggle"  
                    android:layout\_width\="wrap\_content"  
                    android:layout\_height\="wrap\_content"  
                    android:layout\_centerInParent\="true"/>  
            </RelativeLayout\>

            4、AndroidManifest.xml

            具体代码如下:

            <?xml version="1.0" encoding="utf-8"?>  
            <manifest xmlns:android\="http://schemas.android.com/apk/res/android"  
                package\="com.lyz.slip.toggle"  
                android:versionCode\="1"  
                android:versionName\="1.0" \>  
              
                <uses-sdk  
                    android:minSdkVersion\="10"  
                    android:targetSdkVersion\="18" />  
              
                <application  
                    android:allowBackup\="true"  
                    android:icon\="@drawable/ic\_launcher"  
                    android:label\="@string/app\_name"  
                    android:theme\="@style/AppTheme" \>  
                    <activity  
                        android:name\="com.lyz.slip.toggle.MainActivity"  
                        android:label\="@string/app\_name" \>  
                        <intent-filter\>  
                            <action android:name\="android.intent.action.MAIN" />  
              
                            <category android:name\="android.intent.category.LAUNCHER" />  
                        </intent-filter\>  
                    </activity\>  
                </application\>  
            </manifest\>

            三、运行效果

            四、温馨提示

            大家可以到链接下载Android自定义开关按钮实现示例完整源代码

            本实例中,为了方面,我把一些文字直接写在了布局文件中和相关的类中,大家在真实的项目中要把这些文字写在string.xml文件中,在外部引用这些资源,切记,这是作为一个Android程序员最基本的开发常识和规范,我在这里只是为了方便直接写在了类和布局文件中。

            到此这篇关于Android实现史上最简单自定义开关按钮的文章就介绍到这了,更多相关Android自定义开关按钮内容请搜索3672js教程以前的文章或继续浏览下面的相关文章希望大家以后多多支持3672js教程!

            您可能感兴趣的文章:
            • Android动画 实现开关按钮动画(属性动画之平移动画)实例代码
            • Android自定义实现开关按钮代码
            • Android开发之开关按钮用法示例
            • Android自定义View实现开关按钮
            • Android开发之开关按钮控件ToggleButton简单用法示例

            用户评论