安卓自定义控件(五)触控基础MotionEvent
之前去面试,人家说,我这个事件拦截机制写得太少了,还有一个MotionEvent没写,这个确实也很重要,后来我考虑了一下,决定将这篇文章放到自己定义控件里。
先简单再提一下事件分发,事件分发和拦截主要涉及3个方法:
- interceptTouchEvent()这个方法,顾名思义是我们的拦截方法,返回true事件就不会向下传递;
- onTouchEvent()就是触摸事件,返回true就表明当前层消费了这次事件,它会阻止事件向上传递;
- 而dispatchTouchEvent()是事件分发的方法,它的super方法是事件分发的关键,我们不要去动他,它分发它的事件,我们拦我们的,逻辑上不要乱。
而接下来讲的MotionEvent,MotionEvent其实就是onTouchEvent()和onTouch()这两个方法的参数,要想消费掉事件,就要在这两个方法块里面编辑我们的代码。
常量
先来认识一下这些常量,看一下就好,大概记住一下。
常见的动作常量:
public static final int ACTION_DOWN = 0;单点触摸动作
public static final int ACTION_UP = 1;单点触摸离开动作
public static final int ACTION_MOVE = 2;触摸点移动动作
public static final int ACTION_CANCEL = 3;触摸动作取消
public static final int ACTION_OUTSIDE = 4;触摸动作超出边界
public static final int ACTION_POINTER_DOWN = 5;多点触摸动作
public static final int ACTION_POINTER_UP = 6;多点离开动作
以下是一些非touch事件
public static final int ACTION_HOVER_MOVE = 7;
public static final int ACTION_SCROLL = 8;
public static final int ACTION_HOVER_ENTER = 9;
public static final int ACTION_HOVER_EXIT = 10;
掩码常量
ACTION_MASK = 0X000000ff
动作掩码
ACTION_POINTER_INDEX_MASK = 0X0000ff00
触摸点索引掩码
ACTION_POINTER_INDEX_SHIFT = 8 获取触摸点索引需要移动的位数
加速球的实现
如何使用这个事件?我们就来做一下360加速球一样的东西吧,可以拖着那个加速球到处跑,加速球会自动旋转,此外还可以给他设置点击事件等等。
考虑一下我们的需求和设计方案:
- 首先就是旋转动画,360加速球是可以旋转的,旋转可以用Canvas、Matrix来做,不过我算法不是特别好,做出来老是跑飞了,就用帧动画了;
- 然后是拖拽这个控件,这个要在ACTION_MOVE里面处理,每次获取一下触摸的坐标,然后调用postInvalidate()重绘即可;
- 其中onTouchEvent和onClick是冲突的,因此点击事件得重新设计,我准备如果坐标没发生变化,就在松开手指的时候触发点击事件。
源码
/**
* 拖拽按钮,直接继承Button,可以省去很多代码,关于帧动画的代码我就不贴了,因为没什么技术含量
* Created by ChenSS on 2017/1/2.
*/
public class DragButton extends Button {
private Point point = new Point();
//记录之前的坐标
private Point pointBefore = new Point();
//记录之后的坐标
private Point pointAfter = new Point();
//将点击事件获取下来,在松开手指的时候调用
private OnClickListener listener;
public DragButton(Context context) {
this(context, null);
}
public DragButton(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.buttonStyle);
}
public DragButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void setOnTouchListener(OnTouchListener l) {
super.setOnTouchListener(l);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//图片旋转,我使用的是帧动画,其实直接通过onDraw直接重绘更好
AnimationDrawable animationDrawable = (AnimationDrawable) getBackground();
int eventAction = event.getAction();
int x = (int) event.getRawX();
int y = (int) event.getRawY();
Log.e("point", x + "=========" + y);
switch (eventAction) {
case MotionEvent.ACTION_DOWN:
//记录触摸时的坐标
pointBefore.x = getLeft();
pointBefore.y = getTop();
point.x = (int) event.getX();
point.y = (y - getTop());
//启动动画
animationDrawable.start();
break;
case MotionEvent.ACTION_MOVE:
//这一步主要就是获取触摸的坐标,重新绘制View
int left = x - point.x;
int top = y - point.y;
int right = left + getWidth();
int bottom = top + getHeight();
layout(left, top, right, bottom);
//重新绘制
postInvalidate();
break;
case MotionEvent.ACTION_UP:
pointAfter.x = getLeft();
pointAfter.y = getTop();
//我们要判断一下,松开手指的时候,坐标是不是和原先一样
//如果坐标变化了,说明用户在拖动按钮,这个时候就不要执行点击事件
//如果没变化,就说明用户点击了一下这个按钮
if (pointAfter.equals(pointBefore.x, pointBefore.y)) {
listener.onClick(this);
} else {
pointBefore.set(pointAfter.x, pointAfter.y);
}
//结束动画
animationDrawable.stop();
break;
default:
break;
}
return true;
}
@Override
public void setOnClickListener(OnClickListener listener) {
//这里就不要调用super了,因为我们的点击事件比较特殊,不能交给父类处理
this.listener = listener;
}
}
/**
* 我使用的是相对布局,然后就是上面的Button了
*/
public class TestActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
DragButton button = (DragButton) findViewById(R.id.test_btn);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 点击的时候,给一个弹窗
new AlertDialog.Builder(TestActivity.this)
.setNeutralButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
}
})
.setTitle("test button")
.setMessage("test test test!!!")
.show();
}
});
}
}
多点触摸————图片放大
多点触摸最常见的使用方式,我觉得应该是在下拉刷新。无聊的小伙伴应该都玩过微信或者QQ的下拉刷新,把它一直拉到屏幕底部,拉到不能再拉了,然后再松开。这个过程,我们一个手指肯定无法一次性将ListView拉到最底部,这个过程应当是:用一个手指一直拉,然后换另一个手指拉,然后再换别的手指。这个换的过程,需要捕捉多个触摸点。不过下拉刷新都被写烂了,我也不想再写了,来简单地实现以下图片放大的功能吧。
/**
* 代码我写得尽量精简,但是显得有些粗糙,用的还是帧布局,然后一个ImageView
*/
public class ZoomImgActivity extends AppCompatActivity implements View.OnTouchListener {
private ImageView myImageView;
//两个触点的间距
private float currentDistance = 0;
//旧的两个触点的间距
private float lastDistance = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_zoom_img);
myImageView = (ImageView) findViewById(R.id.activity_zoom_img);
myImageView.setOnTouchListener(this);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
ImageView myImageView = (ImageView) v;
switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
//获取LayoutParams
RelativeLayout.LayoutParams layoutParams =
(RelativeLayout.LayoutParams) myImageView.getLayoutParams();
switch (event.getPointerCount()) {
case 1:
break;
case 2:
//两个的话就放大或者缩小
float offsetX = event.getX(0) - event.getX(1);
float offsetY = event.getY(0) - event.getY(1);
currentDistance = (float) Math.sqrt(offsetX * offsetX + offsetY * offsetY);
//5px有误差
if (currentDistance - lastDistance > 5) {
//放大
layoutParams.width = (int) (1.01f * myImageView.getWidth());
layoutParams.height = (int) (1.01f * myImageView.getHeight());
myImageView.setLayoutParams(layoutParams);
} else if (lastDistance - currentDistance > 5) {
//缩小
layoutParams.width = (int) (0.99f * myImageView.getWidth());
layoutParams.height = (int) (0.99f * myImageView.getHeight());
myImageView.setLayoutParams(layoutParams);
}
lastDistance = currentDistance;
}
}
return true;
}
}
安卓自定义控件(五)触控基础MotionEvent的更多相关文章
- 多点触控之MotionEvent.ACTION_MASK作用
ACTION_MASK在Android中是应用于多点触摸操作,字面上的意思大概是动作掩码的意思吧. 在onTouchEvent(MotionEvent event)中,使用switch (event. ...
- android触控,先了解MotionEvent(一)
http://my.oschina.net/banxi/blog/56421 这是我个人的看法,要学好android触控,了解MotionEvent是必要,对所用的MotionEvent常用的API要 ...
- 【朝花夕拾】Android自定义View篇之(八)多点触控(上)MotionEvent简介
前言 在前面的文章中,介绍了不少触摸相关的知识,但都是基于单点触控的,即一次只用一根手指.但是在实际使用App中,常常是多根手指同时操作,这就需要用到多点触控相关的知识了.多点触控是在Android2 ...
- 安卓Tv开发(一)移动智能电视之焦点控制(触控事件)
前言:移动智能设备的发展,推动了安卓另一个领域,包括智能电视和智能家居,以及可穿戴设备的大量使用,但是这些设备上的开发并不是和传统手机开发一样,特别是焦点控制和用户操作体验风格上有很大的区别,本系列博 ...
- Android多点触控手势基础
处理多点触控手势 多点触控就是同时把一根以上的手指放在屏幕上. 再继续往下以前需要补充一些名词: 触控手势:就是把一根或者几根手指放在屏幕上做各种动作,其中包括保留一根手指的前提下,拿起或者放下其余的 ...
- android触控,先了解MotionEvent
MotionEvent源代码可以在ocs看到,当然你也可以在SDK中下载源代码,或者其他地方,如: https://github.com/CyanogenMod/android_frameworks_ ...
- (五)多点触控之兼容ViewPager
在上一篇文章中,自定义的ZoomImageView已经实现了自由缩放,自由移动以及双击放大与缩小的功能.已经可以投入使用这个控件了.下面我们就在ViewPager中使用这个控件.如果你还没读过上一篇文 ...
- (一)自定义ImageView,初步实现多点触控、自由缩放
真心佩服那些一直专注于技术共享的大神们,正是因为他们无私的分享精神,我才能每天都有进步.近日又算是仔细学了android的自定义控件技术,跟着大神的脚步实现了一个自定义的ImageView.里面涉及到 ...
- 【朝花夕拾】Android自定义View篇之(九)多点触控(下)实践出真知
前言 在上一篇文章中,已经总结了MotionEvent以及多点触控相关的基础理论知识和常用的函数.本篇将通过实现单指拖动图片,多指拖动图片的实际案例来进行练习并实现一些效果,来理解前面的理论知识.要理 ...
随机推荐
- IIS ApplicationPoolIdentity(配置IIS讀寫網站文件)
原创地址:http://www.cnblogs.com/jfzhu/p/4067297.html 转载请注明出处 从IIS 7.5开始,Application Pool Identity的Built- ...
- python中时间日期格式化符号:
%y 两位数的年份表示(00-99) %Y 四位数的年份表示(000-9999) %m 月份(01-12) %d 月内中的一天(0-31) %H 24小时制小时数(0-23) %I 12小时制小时数( ...
- JS鼠标滚轮事件详解
鼠标滚轮事件 //兼容性写法,该函数也是网上别人写的,不过找不到出处了,蛮好的,所有我也没有必要修改了 //判断鼠标滚轮滚动方向 if (window.addEventListener)//FF,火狐 ...
- php7+apache2.4配置
因为需要搭建一套discuz的程序,所以就扯到了php. 对于php完全没接触过,然后就百度了下php的容器,看到apache可以.机子上刚好有装,就干脆用它了. 测试环境:win10x64+php7 ...
- Tomcat 笔记-设置虚拟主机
通过作用虚拟主机,可以使多个不同域名的网站共存于一个Tomcat中 在tomcat的server.xml文件中添加主机名: <Host name="hostname" app ...
- MongoDB理解
1. 什么是MongoDB (1)MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统.旨在为 WEB 应用提供可扩展的高性能数据存储解决方案. (2)MongoDB 是一个 ...
- 【问题排查】fastjson线上排坑记
前言 版本上线时发现fastjson的toString方法的返回的字符串与与之前版本的toString方法返回的字符串不相同,这导致依赖toString进行md5计算所得到的结果不相同,更进一步导致其 ...
- px em rem的详解与区别
在前端项目开发中,px,em,以及rem都是页面布局常用的单位,虽然它们是长度单位,但是所含的意义不一样.通过复习和查阅,总结了以下知识. px像素(Pixel) 定义:相对长度单位.像素px是相对于 ...
- Hibernate映射类型
- Nodejs MSSQL详细解读
MSSQL 是Nodejs用于连接Microsoft SQL Server 的插件. 安装方法 npm install mssql 配置Config const config = { user: '. ...