接上一篇自己定义开关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. mysql 登录与权限

    一.mysql 登录方式 1.1 格式:mysql -u用户名 -p密码 -h ip -P 端口 -S 套接字 mysql -uvagrant -pvagrant -h 127.0.0.1 -P 33 ...

  2. codeforces 712A. Memory and Crow

    2019-05-18 08:48:27 加油,加油,坚持!!! 这道题我没有想出公式推导,只是按照模拟题来做,第5个样例超时 样例超时,方法错误 https://www.cnblogs.com/ECJ ...

  3. golang iris html/temple

    在使用golang的模板语法的过程中遇见自动转义问题(或者以我的理解下发的富文本html代码不是template.html类型,而是string类型),需要强制转型 func unescaped(x ...

  4. 爬虫之Urllib库的基本使用

    官方文档地址:https://docs.python.org/3/library/urllib.html 什么是Urllib Urllib是python内置的HTTP请求库包括以下模块urllib.r ...

  5. C#解除某类警告。。。。。。。。。。

    C#预处理器指令取消不必要的警告 今天将自己写的一个类库生成一个DLL后,想把注释也加进去.... 方法:在属性->生成选项卡->XML文档文件(勾选)(生成的文件名不能修改,使用时必须跟 ...

  6. 第5章分布式系统模式 Broker(代理程序)

    许多复杂的软件系统运行在多个处理器或分布式计算机上.将软件分布在多台计算机上的原因有多种,例如: 分布式系统可以利用多个 CPU 或一群低成本计算机的计算能力. 某个软件可能仅在特定计算机上可用. 出 ...

  7. Android ExpandableListView group的item有间距child间隔不变

    <ExpandableListView android:id="@+id/lv" android:layout_width="fill_parent" a ...

  8. ssh 免密码登入远程服务器

    生成ssh密钥,将公钥上传至远程服务器~/.ssh目录下面(没有的话就建一个): ssh-keygen -t rsa scp ~/.ssh/id_rsa.pub root@yourserver.com ...

  9. 位姿检索PoseRecognition:LSH算法.p稳定哈希

    位姿检索使用了LSH方法,而不使用PNP方法,是有一定的来由的.主要的工作会转移到特征提取和检索的算法上面来,有得必有失.因此,放弃了解析的方法之后,又放弃了优化的方法,最后陷入了检索的汪洋大海. 0 ...

  10. auto_ftp_sh

    #!/usr/bin/env python # -*- coding:utf-8 -*-   import paramiko import time   mydate = time.strftime( ...