接上一篇自己定义开关button(一)的内容继续。上一次实现了一个开关button的基本功能。即自己定义了一个控件。开关button,实现了点击切换开关状态的功能。今天我们想在此基础之上。进一步实现触摸拖拽开关滑块来实现开关的功能。还是一样先来看看效果,这里因为要显示拖拽。我打开了开发人员选项中的显示触摸操作,会在屏幕上显示一个圆圈表示触摸位置:

在这里,我们的主要工作就是在原有代码的基础上,添加一个重写的onTouchEvent方法。刚加入上来的时候是这个样子的:

@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}

对于触摸事件来说。一般返回值为true的话,那么就代表在这里消费掉本次触摸。而返回false的话,就在当前位置对本次触摸不做处理或者不能全然处理,还须要继续将本次事件分发给兴许view或者viewgroup响应,对于这里。我们定义的开关button已经是子view了。所以这里返回true就能够,如今看起来我们直接将return
super.onTouchEvent(event);
这一句删掉。然后加上return true;就能够了,会不会产生什么问题呢?我们临时先这样处理。

之后的工作就比較简单了。我们须要依据event.getAction()的类型来做对应的处理,即我们基本上每天都在使用的:MotionEvent.ACTION_DOWN、MotionEvent.ACTION_MOVE和MotionEvent.ACTION_UP

因为在ACTION_MOVE的时候。我们想让滑块随着我们手指的位置的移动而移动。这里因为仅仅是水平移动。所以我们仅仅须要记录x方向的位置,须要两个值:int
firstX
int secondXfirstX负责记录上一次ACTION_MOVE时候的x值,secondX负责记录本次ACTION_MOVE时候的x值。然后这两个值相减。则能够得到手指在两次ACTION_MOVE触发的时间里移动的距离。得到这个距离之后还须要将firstX调整为当前的值,以便下次使用。

然后将这个距离差值与我们滑块的位置进行求和。这样就能够调整我们滑块的位置随手指移动了。当然滑块的位置是有一个范围的,这里应该是[0,MAX_LEFT_DISTANCE]MAX_LEFT_DISTANCE
= backgroundBitmap.getWidth()
- slideButton.getWidth();,所以我们须要做一个推断,来限制滑块,不让其划出边界。好了。基本逻辑到这里。看看代码:

@Override
public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN:
// 当按下的时候
firstX = secondX = (int) event.getX();
break;
case MotionEvent.ACTION_MOVE:
// 当移动的时候
// 计算手指在屏幕上移动的距离
int disX = (int) (event.getX() - secondX);
secondX = (int) event.getX(); // 将本次的位置。设置给lastX
slideBtn_left = slideBtn_left + disX; // 做一个推断,防止滑块划出边界,滑块的范围应该是在[0,MAX_LEFT_DISTANCE];
if (slideBtn_left < 0) {
slideBtn_left = 0;
} else {
if (slideBtn_left > MAX_LEFT_DISTANCE) {
slideBtn_left = MAX_LEFT_DISTANCE;
}
} break;
case MotionEvent.ACTION_UP:
// 当抬起的时候 // 抬起的时候,推断松开的位置是哪里,来由此来决定开关的状态是打开还是关闭
if (slideBtn_left < MAX_LEFT_DISTANCE / 2) {
currentState = false;
} else if (slideBtn_left >= MAX_LEFT_DISTANCE / 2) {
currentState = true;
} // 由开关的状态标志。确定应该是打开还是关闭状态
flushState();
break;
} // 依据状态标志来刷新相应的滑块停止位置,从而实现打开或者关闭效果
flushView(); // 返回true意味着消费掉本次事件。不让其它控件还能够接收到这个事件
return true;
}

写成如今这样会导致仅仅有拖动效果,而点击效果不起作用了。

。。

问题在哪里呢,实际上对于一个view来说。当点击事件传入的时候是先会调用onTouchEvent方法。然后才是调用onClickonLongClick等方法。可是我们也没有看到这种方法里面可以怎样调用onClick方法啊?事实上,秘密就在我们之前删掉的return
super.onTouchEvent(event);
这一句里面。

我们最好还是进入到super.onTouchEvent(event);里面一探到底,找到switch(event.getAction()){}这一块核心代码:

 if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
...
if (!post(mPerformClick)) {
performClick();
}
...
break;
case MotionEvent.ACTION_DOWN:
...
else {
// Not inside a scrolling container, so show the feedback right away
setPressed(true);
checkForLongClick(0);
}
...
break;
...
}
...
}

而在performClick()之中我们能够发现:

public boolean performClick() {
...
li.mOnClickListener.onClick(this);
...
}

到这里基本上就清楚了。onClickonLongClick是在super.onTouchEvent方法里被调用的。onClick是在ACTION_UP的时候可能被调用。而onLongClick是在ACTION_DOWN的时候可能被调用。所以在这里我们尽管去掉了 return
super.onTouchEvent(event);
这一句。可是super.onTouchEvent(event);是须要保留的。

我们能够将它放到onTouchEvent方法的第一句。

做完这一步之后我们会发现又能够响应到onClick方法了,可是还是有些不完美;我们希望在拖动之后就不要有点击操作。点击也不要有拖动效果(点击当然不会有拖动效果...),因为onClick点击的推断仅仅是单纯的检測ACTION_DOWN之后是否有一个ACTION_UP,假设有。那么就推断为一次点击事件,至于中间的过程是否滑动了,它却无论。所以在这里我们须要再加上一个boolean类型的推断标志isDrag。在ACTION_DOWN的时候将其设置为false,假设触发了ACTION_MOVE了,则将其置为true

然后在onClick方法里面,加上一个条件。假设isDrag为假才运行点击的相应操作,否则就跳过。这样就比較完美了。

。。来看看终于的onTouchEventonClick的写法:

@Override
public boolean onTouchEvent(MotionEvent event) { super.onTouchEvent(event);// 这一句不能少,否则无法触发onclick事件 switch (event.getAction()) { case MotionEvent.ACTION_DOWN:
// 当按下的时候
isDrag = false;
firstX = secondX = (int) event.getX();
break;
case MotionEvent.ACTION_MOVE:
// 当移动的时候
isDrag = true;
// 计算手指在屏幕上移动的距离
int disX = (int) (event.getX() - secondX);
secondX = (int) event.getX(); // 更新slideBtn_left的大小
slideBtn_left = slideBtn_left + disX; // 做一个推断。防止滑块划出边界,滑块的范围应该是在[0,MAX_LEFT_DISTANCE];
if (slideBtn_left < 0) {
slideBtn_left = 0;
} else {
if (slideBtn_left > MAX_LEFT_DISTANCE) {
slideBtn_left = MAX_LEFT_DISTANCE;
}
} break;
case MotionEvent.ACTION_UP:
// 当抬起的时候 // 抬起的时候,推断松开的位置是哪里,来由此来决定开关的状态是打开还是关闭
if (slideBtn_left < MAX_LEFT_DISTANCE / 2) {
currentState = false;
} else if (slideBtn_left >= MAX_LEFT_DISTANCE / 2) {
currentState = true;
} // 由开关的状态标志,确定应该是打开还是关闭状态
flushState();
break;
} // 依据状态标志来刷新相应的滑块停止位置。从而实现打开或者关闭效果
flushView(); // 返回true意味着消费掉本次事件,不让其它控件还能够接收到这个事件
return true;
}
@Override
public void onClick(View v) {
if (!isDrag) {
// 假设不是拖动事件。才进行点击事件的响应操作
currentState = !currentState;
flushState();
flushView();
} else {
// 假设是拖动事件,则不进行点击事件的响应操作
}
}

至此。关于自己定义开关的触摸事件就基本完毕了,事实上关于触摸点击的机制。Android是有一整套的流程的。。找时间会写一篇关于此的博文吧。

下一篇会解说怎样自己定义控件的自己定义属性。谢谢关注!

Android自己定义控件系列三:自己定义开关button(二)的更多相关文章

  1. Android自己定义控件系列五:自己定义绚丽水波纹效果

    尊重原创!转载请注明出处:http://blog.csdn.net/cyp331203/article/details/41114551 今天我们来利用Android自己定义控件实现一个比較有趣的效果 ...

  2. Android自己定义控件系列案例【五】

    案例效果: 案例分析: 在开发银行相关client的时候或者开发在线支付相关client的时候常常要求用户绑定银行卡,当中银行卡号一般须要空格分隔显示.最常见的就是每4位数以空格进行分隔.以方便用户实 ...

  3. Android自己定义控件系列二:自己定义开关button(一)

    这一次我们将会实现一个完整纯粹的自己定义控件,而不是像之前的组合控件一样.拿系统的控件来实现.计划分为三部分:自己定义控件的基本部分,自己定义控件的触摸事件的处理和自己定义控件的自己定义属性: 以下就 ...

  4. Androd自己定义控件(三)飞翔的小火箭

    在前面的自己定义控件概述中已经跟大家分享了Android开发其中自己定义控件的种类. 今天跟大家分享一个非主流的组合控件. 我们在开发其中,难免须要在不同的场合中反复使用一些控件的组合.而Java的最 ...

  5. 【Android】自己定义控件实现可滑动的开关(switch)

    ~转载请注明来源:http://blog.csdn.net/u013015161/article/details/46704745 介绍 昨天晚上写了一个Android的滑动开关, 即SlideSwi ...

  6. android自己定义控件系列教程----视图

    理解android视图 对于android设备我们所示区域事实上和它在底层的绘制有着非常大的关系,非常多时候我们都仅仅关心我们所示,那么在底层一点它究竟是怎么样的一个东西呢?让我们先来看看这个图. w ...

  7. android自己定义控件系列教程-----仿新版优酷评论剧集卡片滑动控件

    我们先来看看优酷的控件是怎么回事? 仅仅响应最后也就是最顶部的卡片的点击事件,假设点击的不是最顶部的卡片那么就先把它放到最顶部.然后在移动到最前面来.重复如次. 知道了这几条那么我们就非常好做了. 里 ...

  8. android动手写控件系列——老猪叫你写相机

    前记:Android这个开源而自由的系统,为我们带来开发便利,同时也埋下太多的深坑.例如调用系统自带的相机就会出现照片丢失,或者其他各种各样的问题.因此,看来自定义一个相机十分的必要. 要自定义相机我 ...

  9. Android自己定义控件系列一:Android怎样实现老版优酷client三级环形菜单

    转载请附上本文链接:http://blog.csdn.net/cyp331203/article/details/40423727 先来看看效果: 一眼看上去好像还挺炫的,感觉比較复杂...实际上并不 ...

随机推荐

  1. HDU2080 夹角有多大2

    2019-05-17 15:00:09 加油加油,fightting !!! 这道题不知道acos()函数,acos()返回的是弧度,转化成度数要 / PI * 180 也没有想到通过向量 但是想到了 ...

  2. 如何用SVG写一个环形进度条以及动画

    本次案例主要使用了svg的三个元素,分别为circle.text.path,关于svg的介绍大家可以看MDN上的相关教程,传送门 由于svg可以写到HTML中,所以这里我们就可以很方便的做进度条加载动 ...

  3. colab使用谷歌云中的文件

    colab使用谷歌云中文件 无法一劳永逸 Google Colab最大的不足就是使用虚拟机,这意味着我们自行安装的库虚拟机重启之后,就会被复原,比如keras,数据无法持久化.为了能够持久保存数据,我 ...

  4. js编写时间选择框

    效果图: 代码: 新建js:WdatePicker.js /* * My97 DatePicker 4.72 Release * License: http://www.my97.net/dp/lic ...

  5. C# DataTable常用方法总结

    https://blog.csdn.net/wangzhen209/article/details/51743118

  6. [hihocoder][Offer收割]编程练习赛48

    折线中点 #pragma comment(linker, "/STACK:102400000,102400000") #include<stdio.h> #includ ...

  7. EditPlus代码自动完成的设置

    EditPlus代码自动完成的设置保存在 *.acp 文件中,可以在“工具”->“首选项”->“文件”->“文件类型及语法”中(如下图) 其中“语法文件”保存着进行语法高亮的关键词, ...

  8. SQL Server数据库备份的几个建议

    1.定期进行数据备份(完备或差异备份)和日志备份. 2.使用压缩备份来减少磁盘空间占用和提高备份效率. 3.定期检查磁盘剩余空间和备份文件增长情况,以确保有足够空间进行下一次备份. 4.使用校验和(C ...

  9. 图像压缩Vs.压缩感知

    压缩感知科普文两则: 原文链接:http://www.cvchina.info/2010/06/08/compressed-sensing-2/ 这几天由于happyharry的辛勤劳动,大伙纷纷表示 ...

  10. 路飞学城Python-Day182

    Evernote Export 集群介绍 1.集群介绍 集群:将多个物理机器组成一个逻辑计算机,实现负载和容错 计算机集群简称集群,是一种计算机系统,它通过一组松散集成的计算机软件或硬件连接起来高度紧 ...