android开关按钮背景图,Android自定义控件之开关按钮学习笔记分享

今天来讲讲自定义单个控件,就拿开关按钮来讲讲,相信大家见了非常多这样的了,先看看效果:

b9ad76260c16bedab076ce9e28a099f7.gif

我们可以看到一个很常见的开关按钮,那就来分析分析。

首先:

这是由两张图片构成:

①一张为有开和关的背景图片

②一张为控制开和关的滑动按钮

第一步:

写个类继承View,并重写几个方法:

第一个为构造函数,重写一个参数的函数和两个参数的函数就够了,因为两个参数的函数能够使用自定义属性

第二个为控制控件的大小–>protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {}

第三个为绘制控件的方法–>protected void onDraw(Canvas canvas) {}

第二步:

将用户指定的两张图片加载进来,这里使用自定义属性加载, 在values目录下新建attrs.xml,在xml文件中指定自定义属性名和自定义属性的字段及值类型(即背景图和滑块图)即可:

各个字段的含义我在这就不讲了,不懂的就去看看前几篇《Android开发笔记之自定义组合控件》有讲过,写好就只需在构造函数中加载进来

public SwitchView(Context context, AttributeSet attrs) {

super(context, attrs);

//拿到自定义属性

TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.switchView_attrs);

//拿到自定义字段的值

Drawable switchBackground = ta.getDrawable(R.styleable.switchView_attrs_background);

Drawable switchView_slide = ta.getDrawable(R.styleable.switchView_attrs_slide);

//把值设置到相应组件上

backgroundBitmap = convertDrawable2BitmapSimple(switchBackground);

switchSlide = convertDrawable2BitmapSimple(switchView_slide);

}

不过要注意的是:

因为从自定义属性中取到的是Drawable对象,而我们要的是一个Bitmap对象,所以我们先得把Drawable对象转成Bitmap对象,其实有很多种方法来转,这里就介绍种最简单的方法,借助与BitmapDrawable类:

//将Drawable转成Bitmap

public Bitmap convertDrawable2BitmapSimple(Drawable drawable) {

BitmapDrawable bd= (BitmapDrawable)drawable;

return bd.getBitmap();

}

第三步:

onMeasure方法来控制控件的大小,我们可以看到这个开关按钮的大小就跟背景的大小一样大,只需要设置为背景的大小:

//控制控件的大小

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

if (backgroundBitmap != null) {

//控件大小设置为背景的大小

setMeasuredDimension(backgroundBitmap.getWidth(), backgroundBitmap.getHeight());

}else {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

}

}

第四步:

既然这个开关按钮需要通过点击或移动来控制控件的开和关,所以就需要实现onTouchEvent方法,当然应该有三个事件会触发:按下、移动和抬起的时候,每次点击、移动或抬起都需要重绘,我们先来分析下滑块的状态有哪些(应该有四种状态)一开始默认状态为空:

1.点击的时候

2.移动的时候

3.抬起的时候

4.空的时候(即什么都没干的时候)

先分析下点击的时候的情况:

①当按下或移动的坐标大于滑块宽度一半时将滑块右移

②当按下或移动的坐标小于滑块宽度一半时滑块不动

注:防止滑块移至背景外面,最大是滑块右边和背景右边对齐(即最大离左边为背景宽度-滑块宽度)

3fac58cc6918641956ce986c0a9cf432.png

再来看看移动的时候的情况应该是和点击的时候是一样的。

再来分析抬起的时候的情况:

①如果开关状态是打开的就将滑块移动至右边

②如果开关状态是关闭的就将滑块移动至左边

那怎么判断什么时候是打开状态和关闭状态呢??

①抬起的坐标大于背景宽度一半的时候设为打开状态

②抬起的坐标小于背景宽度坐标一 半的时候设为关闭状态

75ffa903f98832d47950157a2931860b.png

再来分析下空的时候,可以发现它和抬起的时候的情况是一样的。

第五步:

在onDraw方法中将背景和滑块绘制出来。刚才分析了onTouchEvent方法,这次是一样的,滑块的四个状态分别处理,前面onTouchEvent方法中滑块的状态改变,然后通过invalidate()方法来通知系统重绘。

第六步:

我们做这个自定义控件是为了让用户使用的,现在这个是没有什么用的,用户用不了,所以可以通过设置监听器来对外提供接口。

/**

* switchView开关监听接口

*

* */

interface OnSwitchChangedListener {

public void onSwitchChange(boolean isOpen);

}

/**

* 设置 switchView状态监听

* */

public void setOnChangeListener(OnSwitchChangedListener listener) {

switchListener = listener;

}

这个监听器中的boolean值需要赋值,那在什么时候赋值呢,应该是在抬起或空的状态的时候给它赋值,因为那个时候才真正确定开关按钮是打开的还是关闭的。

第七步:

到这一步就是来使用了,在布局文件中把自定义的这个控件定义出来

minguo:background="@drawable/switch_background"

minguo:slide="@drawable/slide_button_background"

android:id="@+id/switchView"

android:layout_width="wrap_content"

android:layout_height="wrap_content"/>

cf20113a1e992bde31ce20e2b12de285.png

使用定义好的View应该都会用了,不会的去看看《android开发笔记之自定义组合控件》。

核心代码:

布局文件

xmlns:minguo="http://schemas.android.com/apk/res/com.example.custom"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

tools:context="com.example.custom.MainActivity" >

minguo:background="@drawable/switch_background"

minguo:slide="@drawable/slide_button_background"

android:id="@+id/switchView"

android:layout_width="wrap_content"

android:layout_height="wrap_content"/>

自定义View: SwitchView.java

/**

* 自定义开关按钮

* @author Administrator

*

*/

public class SwitchView extends View {

//背景图片和滑块图片

private Bitmap backgroundBitmap,switchSlide;

//画笔

private Paint paint;

//得到的x坐标(点击、移动、抬起)

private float currentX;

//判断开关是否打开的标记位

private boolean isOpen = false;

//开关打开与关闭的监听器

private OnSwitchChangedListener switchListener;

//滑块的四种状态

public static final int STATE_DOWN = 1; //按下的时候

public static final int STATE_MOVE = 2; //移动的时候

public static final int STATE_UP = 3; //抬起的时候

public static final int STATE_NONE = 0; //空的时候(即什么都没干的时候)

//标记状态(默认为空状态)

private int state = STATE_NONE;

public SwitchView(Context context) {

super(context,null);

}

public SwitchView(Context context, AttributeSet attrs) {

super(context, attrs);

//拿到自定义属性

TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.switchView_attrs);

//拿到自定义字段的值

Drawable switchBackground = ta.getDrawable(R.styleable.switchView_attrs_background);

Drawable switchView_slide = ta.getDrawable(R.styleable.switchView_attrs_slide);

//把值设置到相应组件上

backgroundBitmap = convertDrawable2BitmapSimple(switchBackground);

switchSlide = convertDrawable2BitmapSimple(switchView_slide);

}

//将Drawable转成Bitmap

public Bitmap convertDrawable2BitmapSimple(Drawable drawable) {

BitmapDrawable bd = (BitmapDrawable)drawable;

return bd.getBitmap();

}

//控制控件的大小

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

if (backgroundBitmap != null) {

//控件大小设置为背景的大小

setMeasuredDimension(backgroundBitmap.getWidth(), backgroundBitmap.getHeight());

}else {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

}

}

//绘制控件的样式

@Override

protected void onDraw(Canvas canvas) {

//背景为空的时候才将背景绘制

if (backgroundBitmap != null) {

paint = new Paint();

//第一个参数表示需要画的Bitmap

//第二个参数表示Bitmap左边离控件的左边距

//第三个参数表示Bitmap上边离控件的上边距

//第四个参数表示画笔

canvas.drawBitmap(backgroundBitmap, 0, 0, paint);

}

switch (state) {

case STATE_DOWN: //按下和移动的触发事件都一样,都是将滑块移动

case STATE_MOVE:

//当按下或移动的坐标大于滑块宽度一半时将滑块右移

if (currentX > switchSlide.getWidth()/2f) {

//让滑块向右滑动(重新绘制滑块的位置)

float left = currentX - switchSlide.getWidth()/2f;

//防止滑块移至背景外面,最大是滑块右边和背景右边对齐(即最大离左边为背景宽度-滑块宽度)

float maxLeft = backgroundBitmap.getWidth() - switchSlide.getWidth();

if (left > maxLeft) {

left = maxLeft;

}

canvas.drawBitmap(switchSlide, left, 0, paint);

//当按下或移动的坐标小于滑块宽度一半时滑块不动

}else if (currentX < switchSlide.getWidth()/2f) {

//让滑块不动就可以了

canvas.drawBitmap(switchSlide, 0, 0, paint);

}

break;

case STATE_NONE: //空或抬起的时候将滑块至于左边或右边

case STATE_UP:

//如果是打开的将滑块移动至右边,并将打开状态传至监听器

if (isOpen) {

if (switchListener != null) {

switchListener.onSwitchChange(true);

}

canvas.drawBitmap(switchSlide,

backgroundBitmap.getWidth() - switchSlide.getWidth(), 0, paint);

//如果是关闭的将滑块至于左边,并将关闭状态传至监听器

}else {

if (switchListener != null) {

switchListener.onSwitchChange(false);

}

canvas.drawBitmap(switchSlide, 0, 0, paint);

}

break;

default:

break;

}

}

@Override

public boolean onTouchEvent(MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

currentX = event.getX();

//将标记位修改成按下的状态

state = STATE_DOWN;

//通知系统重新绘制界面

invalidate();//在主线程

// postInvalidate();//在子线程

break;

case MotionEvent.ACTION_MOVE:

currentX = event.getX();

//将标记位修改为移动状态

state = STATE_MOVE;

invalidate();

break;

case MotionEvent.ACTION_UP:

currentX = event.getX();

//将标记为修改为抬起状态

state = STATE_UP;

//抬起的坐标大于背景宽度一半的时候设为打开状态

if (currentX > backgroundBitmap.getWidth()/2f) {

//滑块在右边,开启

isOpen = true;

//抬起的坐标小于背景宽度坐标一 半的时候设为关闭状态

}else if (currentX < backgroundBitmap.getWidth()) {

//滑块在左边,关闭

isOpen = false;

}

invalidate();

break;

}

return true;

}

/**

* switchView开关监听接口

*

* */

interface OnSwitchChangedListener {

public void onSwitchChange(boolean isOpen);

}

/**

* 设置 switchView状态监听

* */

public void setOnChangeListener(OnSwitchChangedListener listener) {

switchListener = listener;

}

}

MainActivity.java

public class MainActivity extends Activity {

private SwitchView switchView;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

switchView = (SwitchView) findViewById(R.id.switchView);

switchView.setOnChangeListener(new OnSwitchChangedListener() {

@Override

public void onSwitchChange(boolean isOpen) {

if (isOpen) {

//打开开关的时候的逻辑

Toast.makeText(MainActivity.this, "开关打开了", Toast.LENGTH_LONG).show();

}else {

//关闭开关的时候的逻辑

Toast.makeText(MainActivity.this, "开关关闭了", Toast.LENGTH_LONG).show();

}

}

});

}

}

大家看起来这么简单的一个写了这么多,其实我们学习这个不是为了写这个,比这个好的开源多的是,而是为了学习这种思路与思维,大家赶紧试试吧!

谢谢大家的阅读,也希望大家可以继续关注脚本之家的更多精彩内容。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值