接上一篇自己定义开关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. C#操作QQ邮箱发送电子邮件原来这么简单。。。。

    在贴代码之前,首先需要给QQ邮箱开服务IMAP/SMTP服务,详细开通方法见 "开通方法"(可能需要发送收费短信,所以只要开通这一个服务就好了). 这边主要就是为了一个服务的授权码 ...

  2. website robots.txt 防爬虫 措施

    robots.txt文件用法举例: 1. 允许所有的robot访问 User-agent: * Allow: / 或者 User-agent: * Disallow: 2. 禁止所有搜索引擎访问网站的 ...

  3. html中<frameset>标签,框架结构各窗口的父级菜单子级菜单关系

    这个问题搞得我头大,并且在查过百度后各位大佬给出的解释我都不能理解,应该是我太白的原因,希望我写的能好理解. 下面文章窗口1.2.3,在代码里分别为chuangkou.chuangkou1.chuan ...

  4. React 16 服务端渲染的新特性

    React 16 服务端渲染的新特性 React 16 中关于服务端渲染的新特性 快速介绍React 16 服务端渲染的新特性,包括数组.性能.流等 React 16 终于来了!

  5. Android RecyclerView、ListView实现单选列表的优雅之路.

    一 概述: 这篇文章需求来源还是比较简单的,但做的优雅仍有值得挖掘的地方. 需求来源:一个类似饿了么这种电商优惠券的选择界面: 其实就是 一个普通的列表,实现了单选功能, 效果如图:  (不要怪图渣了 ...

  6. CV与IP:基础,经典以及最近发展

    原文链接:http://blog.csdn.net/dcraw/article/details/7617891 原文编写层层递进,逻辑清晰: 不过看这篇转载可以少点击鼠标:http://blog.cs ...

  7. VFS文件系统结构分析 与socket

    本文乃fireaxe原创,使用GPL发布,可以自由拷贝,转载.但转载请保持文档的完整性,并注明原作者及原链接.内容可任意使用,但对因使用该内容引起的后果不做任何保证. 作者:fireaxe_hq@ho ...

  8. gulp打包压缩代码以及图片

    1.首先全局安装gulp 全局安装就不做介绍了 初学gulp,终于把常用的配置,api,语法弄明白了! gulp插件地址:http://gulpjs.com/plugins gulp官方网址:http ...

  9. sklearn学习2-----LogisticsRegression

    1.官网地址: http://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.htm ...

  10. JavaScript对原始数据类型的拆装箱操作

    JS中的基本类型: boolean null undefined string number symbol 每当读取一个基本类型的时候,会隐式的自动进行装箱操作(即:将一个基本类型变成对象): var ...