Android之——史上最简单自定义开关按钮的实现

转载请注明出处:http://blog.csdn.net/l1028386804/article/details/48102871

很多时候,我们在很多无论是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>

三、运行效果

四、温馨提示

大家可以到链接http://download.csdn.net/detail/l1028386804/9063331下载Android自定义开关按钮实现示例完整源代码

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

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-10 08:53:54

Android之——史上最简单自定义开关按钮的实现的相关文章

Android之——史上最简单旋转菜单实现效果

转载请注明出处:http://blog.csdn.net/l1028386804/article/details/48048323 由于身体原因,前几天没有给大家更新博客,那么,今天我们就来一起实现一个非常酷炫的旋转菜单效果吧.在很多APP中,不难发现,人家把菜单效果设计的那叫一个酷炫啊,其中一种设计就是将菜单设计成旋转的效果.好了,那么这么酷炫的菜单效果是如何实现的呢?下面,就让我们一起来实现这个酷炫的菜单效果吧. 一.原理 老规矩,还是先唠叨下原理级别的东西. 这个示例的实现原理很简单,利用

【Android】史上最简单,一步集成侧滑(删除)菜单,高仿QQ、IOS

本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 转载请标明出处: http://blog.csdn.net/zxt0601/article/details/53157090 本文出自:[张旭童的博客](http://blog.csdn.net/zxt0601) 代码传送门:喜欢的话,随手点个star.多谢 https://github.com/mcxtzhang/SwipeDelMenuLayout 重要的话 开头说,not for the RecyclerView or L

Android之——史上最简单最酷炫的3D图片浏览效果的实现

转载请注明出处:http://blog.csdn.net/l1028386804/article/details/48052709 如今,Android开发已经成为移动互联开发领域中一支不可或缺的力量,那么Android中要实现3D的效果那也就是合情合理的事情了.那么,如何在Android中实现像IOS中那样的3D图片浏览效果呢?下面,鄙人将重磅推出今天的重点博文,和大家一起在Android中实现酷炫的3D图片浏览效果. 一.原理 老规矩,还是要来啰嗦下原理的东西. 整体实现是以手机屏幕的正中间

Android之——史上最简单图片轮播广告效果实现

转载请注明出处:http://blog.csdn.net/l1028386804/article/details/48049913 如今的Android开发需求越来越来多,实现效果越来越酷炫,很多Android APP都要实现PC网站上那样的图片轮播效果,那么,这些图片的轮播效果是如何实现的呢?下面,我就跟大家一起来实现这些酷炫的功能. 一.原理 首先,将这些要轮播的图片和一些文本分别放置在不同的数据集合中,程序启动的时候默认显示一组图片和文本数据,然后启动一个定时器,每隔一段时间便替换掉显示的

强烈推荐:Android史上最强大的自定义任务软件Tasker

强烈推荐:Android史上最强大的自定义任务软件Taskerhttp://bbs.mumayi.com/thread-28387-1-1.html(出处: 木蚂蚁手机乐园) Android上的Tasker绝对称得上是Android系统的神器之一,与Auto Memory Manager不同,Tasker不是加速型的软件,而是系统增强型的软件,由于有众多系统状态可控制,故使得Tasker一跃成为Android系统中最闪亮的明星.但Tasker也无疑是最难使用的软件,由于可以控制的地方太多,反而让

Android 自定义控件打造史上最简单的侧滑菜单

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39185641 ,本文出自[张鸿洋的博客] 侧滑菜单在很多应用中都会见到,最近QQ5.0侧滑还玩了点花样~~对于侧滑菜单,一般大家都会自定义ViewGroup,然后隐藏菜单栏,当手指滑动时,通过Scroller或者不断的改变leftMargin等实现:多少都有点复杂,完成以后还需要对滑动冲突等进行处理~~今天给大家带来一个简单的实现,史上最简单有点夸张,但是的确是我目前遇到过的最

Android滑动菜单特效实现,仿人人客户端侧滑效果,史上最简单的侧滑实现

本文首发于CSDN博客,转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/8714621 人人客户端有一个特效还是挺吸引人的,在主界面手指向右滑动,就可以将菜单展示出来,而主界面会被隐藏大部分,但是仍有左侧的一小部分同菜单一起展示. 据说人人客户端的这个特效是从facebook客户端模仿来的,至于facebook是不是又从其它地方模仿来的就不得而知了.好,今天我们就一起来实现这个效果,总之我第一次看到这个特效是在人人客户端看到的,我

开源框架】Android之史上最全最简单最有用的第三方开源库收集整理,有助于快速开发

[原][开源框架]Android之史上最全最简单最有用的第三方开源库收集整理,有助于快速开发,欢迎各位... 时间 2015-01-05 10:08:18 我是程序猿,我为自己代言 原文  http://blog.csdn.net/caoyouxing/article/details/42418591 主题 开源 安卓开发 http://www.tuicool.com/articles/jyA3MrU Android开源库 自己一直很喜欢Android开发,就如博客签名一样, 我是程序猿,我为自

史上最简单的个人移动APP开发入门--jQuery Mobile版跨平台APP开发

书是人类进步的阶梯. ——高尔基 习大大要求新新人类要有中国梦,鼓励大学生们一毕业就创业.那最好的创业途径是什么呢?就是APP.<构建跨平台APP-jQuery Mobile移动应用实战>就是一本写给没钱没身份没资历的创业小白看的APP书,看完这本书你可以拥有自己的一个APP,不用花钱就能移植到其他移动平台,支持iOS,Android,Windows Phone!!!!!!!!找个最便宜的来练手吧!  小白APP交流Q群:  348632872 清华大学出版社推出的<构建跨平台APP:j