Android自定义View实现开关按钮

Android自定义View实现开关按钮,第1张

概述 前言:Android自定义View对于刚入门乃至工作几年的程序员来说都是非常恐惧的,但也是Android进阶学习的必经之路,平时项目中经常会有一些苛刻的需求,我们可以在GitHub上找到各种各样的效果,能用则用,不能用

 前言:AndroID自定义view对于刚入门乃至工作几年的程序员来说都是非常恐惧的,但也是AndroID进阶学习的必经之路,平时项目中经常会有一些苛刻的需求,我们可以在GitHub上找到各种各样的效果,能用则用,不能用自己花功夫改改也能草草了事。不过随着工作经验和工作性质,越来越觉得自定义view是时候有必要自己花点功夫研究一下。

一、经过这两天的努力,自己也尝试着写了一个Demo,效果很简单,就是开关按钮的实现。

可能有的人会说这效果so easy,找UI切三张图就完事了,何必大费周折自定义。你说的没错,不过这里只是用来学习自定义view来展示这么一个常见案例。

自定义控件

1.为什么自定义view?

AndroID自身带的控件不能满足需求,需要根据自己的需求定义控件.

2.AndroID 的界面绘制流程?

 

onMeasure()――onLayout()――onDraw()方法都在Activity生命周期的onResume()方法之后执行。

3.AndroID自定义view的方式?

集成VIEw:VIEw流程

onMeasure() (在这个方法里指定自己的宽高) -> onDraw() (绘制自己的内容)

集成VIEwGroup:VIEwGroup流程

onMeasure() (指定自己的宽高,所有子VIEw的宽高)-> onLayout() (摆放所有子VIEw) -> onDraw() (绘制内容)

自定义view实现开关按钮步骤:

写个类继承VIEw,

拷贝包含包名的全路径到xml中,

界面中找到该控件,设置初始信息,

根据需求绘制界面内容,

响应用户的触摸事件,

创建一个状态更新监听.

1.自定义ToggleVIEw集成VIEw,并且重新三个构造方法。

注意:构造方法为什么要重写三个?

ToggleVIEw(Context context)一个参数的构造方法是用于代码创建控件时调用的

ToggleVIEw(Context context,AttributeSet attrs)用于在xml里使用,可指定自定义属性

ToggleVIEw(Context context,AttributeSet attrs,int defStyle)用于在xml里使用,可指定自定义属性,如果指定了样式,则走此构造函数

我们在XML中定义了背景图片、开关按钮图片和开关默认状态,要获取在XML文件定义的属性就在包含三个参数的构造方法里用TypedArray类来获取。

在attrs.xml声明节点declare-styleable

<declare-styleable name="ToggleVIEw"><attr name="switch_background" format="reference" /><attr name="slIDe_button" format="reference" /><attr name="switch_state" format="boolean" /></declare-styleable>/*** 用于在xml里使用,则走此构造函数* @param context* @param attrs* @param defStyle*/public ToggleVIEw(Context context,int defStyle) {super(context,attrs,defStyle);// 获取配置的自定义属性TypedArray a = context.gettheme().obtainStyledAttributes(attrs,R.styleable.ToggleVIEw,defStyle,0);int switchBackgroundResource = a.getResourceID(R.styleable.ToggleVIEw_switch_background,-1);int slIDebuttonResource = a.getResourceID(R.styleable.ToggleVIEw_slIDe_button,-1);mSwitchState = a.getBoolean(R.styleable.ToggleVIEw_switch_state,false);//获取背景图片和开关图片后设置图片,便于在onMeasure()方法中设置VIEw宽和高,防止NullsetSwitchBackgroundResource(switchBackgroundResource);setSlIDebuttonResource(slIDebuttonResource);init();}

2.自定义ToggleVIEw集成VIEw后,在XML文件里不要忘记添加命名空间

“xmlns:cb=”http://schemas.androID.com/apk/res-auto””

然后将自定义view的完整路径粘贴到XML中,这点类似于AndroID v4包下的VIEwPager控件

以下便是demo中XML文件代码:

设置开关背景图片

- cb:switch_background=”@drawable/switch_background”

设置开关按钮图片

- cb:slIDe_button=”@drawable/slIDe_button”

设置开关默认状态

- cb:switch_state=”false”

3.界面中找到该控件,设置初始信息

在Activity中通过findVIEwByID方法找到自定义的VIEw控件,和系统的组件 *** 作没区别。

private ToggleVIEw toggleVIEw;@OverrIDeprotected voID onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentVIEw(R.layout.activity_main);toggleVIEw = (ToggleVIEw) findVIEwByID(R.ID.toggleVIEw);// toggleVIEw.setSwitchBackgroundResource(R.drawable.switch_background);// toggleVIEw.setSlIDebuttonResource(R.drawable.slIDe_button);// toggleVIEw.setSwitchState(true);// // 设置开关更新监听toggleVIEw.setonSwitchStateUpdateListener(new ToggleVIEw.OnSwitchStateUpdateListener(){@OverrIDepublic voID onStateUpdate(boolean state) {Toast.makeText(getApplicationContext(),"state: " + state,Toast.LENGTH_SHORT).show();}});}

4.根据需求绘制界面内容

已经通过onMeasure()方法设置了VIEw的宽度和高度,下面开始绘制的 *** 作就全部在onDraw()方法中进行,onDraw(Canvas canvas) 方法中canvas参数:画布,画板. 在上边绘制的内容都会显示到界面上.

// 根据开关状态boolean,直接设置图片位置if(mSwitchState){// 开int newleft = switchBackgroupBitmap.getWIDth() - slIDebuttonBitmap.getWIDth();canvas.drawBitmap(slIDebuttonBitmap,newleft,paint);}else {// 关canvas.drawBitmap(slIDebuttonBitmap,paint);}

开关打开时,开关按钮的位置在开关背景中的位置计算:

int newleft = switchBackgroupBitmap.getWIDth() - 

slIDebuttonBitmap.getWIDth(); 背景的宽度-按钮的宽度就是当前开关按钮所在的X轴上的位置点

开关关闭时,当前开关按钮所在的X轴上的位置点=0

5.响应用户的触摸事件

在完成以上3步 *** 作后,你会发现,只有在第一次进入后XML初始化默认开关状态的boolean值才会有变化,此后点击是没有任何效果的,这个时候我们就要想办法监听手势事件,重写ontouchEvent(MotionEvent event)方法,相信大多数朋友对这个方法并不陌生。

MotionEvent有三种状态:

MotionEvent.ACTION_DOWN: //按下屏幕
MotionEvent.ACTION_MOVE: //手指在屏幕上移动
MotionEvent.ACTION_UP //离开屏幕

当前需要考虑的问题是:

当手指按下屏幕后MotionEvent.ACTION_DOWN(在当前开关背景VIEw中)开关的X轴位置应该移动到手指按下的位置;

当手指在屏幕上移动MotionEvent.ACTION_MOVE(在当前开关背景VIEw中)开关按钮X轴应该随着手指移动的位置改变;

当手指离开屏幕后MotionEvent.ACTION_UP(在当前开关背景VIEw中)开关按钮应该判断手指离开的位置是否是当前背景的一半位置,如果X轴位置大于VIEw背景宽度的1/2、那么应该处于打开状态,如果X轴位置小于VIEw背景宽度的1/2,那么应该处于关闭状态。

如图所示:

private OnSwitchStateUpdateListener onSwitchStateUpdateListener;// 重写触摸事件,响应用户的触摸.@OverrIDepublic boolean ontouchEvent(MotionEvent event) {switch (event.getAction()) {case MotionEvent.ACTION_DOWN:istouchMode = true;System.out.println("event: ACTION_DOWN: " + event.getX());currentX = event.getX();break;case MotionEvent.ACTION_MOVE:System.out.println("event: ACTION_MOVE: " + event.getX());currentX = event.getX();break;case MotionEvent.ACTION_UP:istouchMode = false;System.out.println("event: ACTION_UP: " + event.getX());currentX = event.getX();float center = switchBackgroupBitmap.getWIDth() / 2.0f;// 根据当前按下的位置,和控件中心的位置进行比较. boolean state = currentX > center;// 如果开关状态变化了,通知界面. 里边开关状态更新了.if(state != mSwitchState && onSwitchStateUpdateListener != null){// 把最新的boolean,状态传出去了onSwitchStateUpdateListener.onStateUpdate(state);}mSwitchState = state;break;default:break;}// 重绘界面invalIDate(); // 会引发onDraw()被调用,里边的变量会重新生效.界面会更新return true; // 消费了用户的触摸事件,才可以收到其他的事件.}

注意:

以上监听ontouchEvent(MotionEvent

event)方法后还存在一个问题,不知道大家有没有发现,我们没有设置开关按钮的边界值,什么意思呢?就是手指滑动的时候左边和右边可以画出当前背景之外。

所以这里需要对左右两边的X轴位置进行处理:

// Canvas 画布,画板. 在上边绘制的内容都会显示到界面上.@OverrIDeprotected voID onDraw(Canvas canvas) {// 1. 绘制背景canvas.drawBitmap(switchBackgroupBitmap,paint);// 2. 绘制滑块if(istouchMode){// 根据当前用户触摸到的位置画滑块// 让滑块向左移动自身一半大小的位置float newleft = currentX - slIDebuttonBitmap.getWIDth() / 2.0f;int maxleft = switchBackgroupBitmap.getWIDth() - slIDebuttonBitmap.getWIDth();// 限定滑块范围if(newleft < 0){newleft = 0; // 左边范围}else if (newleft > maxleft) {newleft = maxleft; // 右边范围}canvas.drawBitmap(slIDebuttonBitmap,paint);}else {// 根据开关状态boolean,paint);}}}

6.创建一个状态更新监听.

基本上所以工作已经完成,这样我们一个自定义view已经大功告成了,当你完成这个效果后,你可能会发现有点类似于CheckBox。既然类似于CheckBox,我们知道当CheckBox点击选中和取消选中的时候都会有ischecked()方法来获取选中状态,所以我们这个自定义的开关按钮自然不能少这个功能,否则我们在界面上只有效果展示,却没有逻辑处理的地方。

public interface OnSwitchStateUpdateListener{// 状态回调,把当前状态传出去voID onStateUpdate(boolean state);}public voID setonSwitchStateUpdateListener(OnSwitchStateUpdateListener onSwitchStateUpdateListener) {this.onSwitchStateUpdateListener = onSwitchStateUpdateListener;}

代码很简单,写一个接口,然后定义一个回调方法返回开关状态,需要注意的是,在手指离开屏幕的时候,我们需要判断此次 *** 作是否改变了开关的状态,如果没有变化我们不做 *** 作,如果跟上次状态不同,则通知Activity状态更改!

case MotionEvent.ACTION_UP:istouchMode = false;System.out.println("event: ACTION_UP: " + event.getX());currentX = event.getX(); float center = switchBackgroupBitmap.getWIDth() / 2.0f;// 根据当前按下的位置,状态传出去了onSwitchStateUpdateListener.onStateUpdate(state);}mSwitchState = state;break;

Activity中回调也是非常简单的,类似于AndroID系统为我们提供的setonClickListener回调接口,之前一直用系统定义的监听接口,这次通过简单的一个自定义view,我们也可以给自己的VIEw写回调接口了。是不是觉得是件很开心的事情呢?

// 设置开关更新监听toggleVIEw.setonSwitchStateUpdateListener(new ToggleVIEw.OnSwitchStateUpdateListener(){@OverrIDepublic voID onStateUpdate(boolean state) {Toast.makeText(getApplicationContext(),Toast.LENGTH_SHORT).show();}});

以上所述是小编给大家介绍的AndroID自定义view实现开关按钮,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对编程小技巧网站的支持!

总结

以上是内存溢出为你收集整理的Android自定义View实现开关按钮全部内容,希望文章能够帮你解决Android自定义View实现开关按钮所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1147826.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存