总体思想
我们可以把该控件的实现分成多个步骤进行:
(1)、实现开关的绘制,使开关能正确显示在手机上
(2)、实现开关的滑动
1、开关的绘制
首先在布局activity_main.xml中放置该开关,宽高选择包裹内容,实际的大小和图片我们将在java代码中指定
<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"
tools:context="cn.hugo.android.slidetoggle.MainActivity" >
<!--必须需要把自定义的控件的包名和类名写完整-->
<cn.hugo.android.slidetoggle.widget.SlideToggle
android:id="@+id/toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true" >
</cn.hugo.android.slidetoggle.widget.SlideToggle>
</RelativeLayout>
然后,在activity中设置该布局文件MainActivity.java
package cn.hugo.android.slidetoggle;
import android.app.Activity;
import android.os.Bundle;
import cn.hugo.android.slidetoggle.widget.SlideToggle;
public class MainActivity extends Activity {
private SlideToggle toggle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toggle = (SlideToggle) findViewById(R.id.toggle);
toggle.setToggleState(true); //设置开关状态为打开
}
}
最后定义一个继承view的滑动开关控件,SlideToggle.java
package cn.hugo.android.slidetoggle.widget;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
import cn.hugo.android.slidetoggle.R;
public class SlideToggle extends View {
private Bitmap slideButton;
private Bitmap switchButton;
private boolean mState; // 开关状态
public SlideToggle(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlideToggle(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
slideButton = BitmapFactory.decodeResource(getResources(),
R.drawable.slide_button_background);
switchButton = BitmapFactory.decodeResource(getResources(),
R.drawable.switch_background);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 把开关的背景画到画布上
canvas.drawBitmap(switchButton, 0, 0, null);
// 根据开关状态把可移动的背景描绘在画布上
if (mState) {
canvas.drawBitmap(slideButton, switchButton.getWidth()
- slideButton.getWidth(), 0, null);
}
else {
canvas.drawBitmap(slideButton, 0, 0, null);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 设置控件的宽和高,为背景图片的宽高
setMeasuredDimension(switchButton.getWidth(), switchButton.getHeight());
}
/**
* 设置开关状态
*
* @param b
* true:开;false:关
*/
public void setToggleState(boolean b) {
this.mState = b;
}
}
Canvas.drawBitmap(Bitmap bitmap,float left, float top,Paint paint) 方法,是在该控件中绘制图画,其中bitmap是需
要绘制的图,left和top是把该图画在距离该控件左边、上边的距离的位置。
现在,安装该应用后,显示效果如下图:
2、实现滑动
我们此时需要覆盖View的public boolean onTouchEvent(MotionEvent event);方法,对手指的按下、移动、移开操作进行处理。有些细节需要进行特殊处理,比如滑块滑出控件,开关状态的处理等。
package cn.hugo.android.slidetoggle.widget;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import cn.hugo.android.slidetoggle.R;
public class SlideToggle extends View {
private Bitmap slideButton;
private Bitmap switchButton;
private boolean mState; // 开关状态
private float mCurrentX; // 记录手指按下的位置
private boolean isSliding; // 是否正在滑动
public SlideToggle(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SlideToggle(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
slideButton = BitmapFactory.decodeResource(getResources(),
R.drawable.slide_button_background);
switchButton = BitmapFactory.decodeResource(getResources(),
R.drawable.switch_background);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 把开关的背景画到画布上
canvas.drawBitmap(switchButton, 0, 0, null);
if (isSliding) { // 正在对开关进行滑动
float left = mCurrentX - slideButton.getWidth() / 2; // 使滑块处于手指的中间位置
// 避免滑块滑出控件范围
if (left < 0) {
left = 0;
}
else if (left > switchButton.getWidth() - slideButton.getWidth()) {
left = switchButton.getWidth() - slideButton.getWidth();
}
canvas.drawBitmap(slideButton, left, 0, null);
}
else {
// 根据开关状态把可移动的背景描绘在画布上
if (mState) {
canvas.drawBitmap(slideButton, switchButton.getWidth()
- slideButton.getWidth(), 0, null);
}
else {
canvas.drawBitmap(slideButton, 0, 0, null);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
mCurrentX = event.getX(); // 记录按下时相对控件的x轴位置,event.getRawX是相对整个屏幕的x位置
isSliding = true;
break;
}
case MotionEvent.ACTION_UP: {
isSliding = false;
// 判断当前状态属于何种状态,把开关设置为相应状态
mState = mCurrentX > switchButton.getWidth() / 2; // 移开滑块的x位置大于开关背景一半,则视为打开状态
break;
}
case MotionEvent.ACTION_MOVE: {
mCurrentX = event.getX();
break;
}
default:
break;
}
invalidate(); // 使控件调用一次onDraw()方法
return true; // 消耗触摸事件,事件不再传递
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 设置控件的宽和高,为背景图片的宽高
setMeasuredDimension(switchButton.getWidth(), switchButton.getHeight());
}
/**
* 设置开关状态
*
* @param b
* true:开;false:关
*/
public void setToggleState(boolean b) {
this.mState = b;
}
}
总的来说还是比较简单的。
源码链接:
我是源码链接