现在所有的手机绝大部分已经都是触屏的,固这些手机都会对手指对屏幕的操作进行监控。android提供了手势识别器来对手势操作进行了监听:
GestureDetector gestureDetector =new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {}
其中可以重写其方法有如下几种:
抛掷(onFling): 手指在触摸屏上迅速移动,并松开的动作。
public boolean onFling(MotionEvent event1, MotionEvent event2,
float velocityX, float velocityY) {
Log.d(DEBUG_TAG, "onFling: " + event1.toString()+event2.toString());
return true;
}
按下(onDown): 刚刚手指接触到触摸屏的那一刹那,就是触的那一下。
<span style="font-size:18px;">public boolean onDown(MotionEvent e) {
// TODO Auto-generated method stub
return super.onDown(e);
}</span>
长按(onLongPress): 手指按在持续一段时间,并且没有松开。
<span style="font-size:18px;">public void onLongPress(MotionEvent e) {
// TODO Auto-generated method stub
super.onLongPress(e);
}</span>
滑动(onScroll): 手指在触摸屏上滑动。
<span style="font-size:18px;">public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
// TODO Auto-generated method stub
return super.onScroll(e1, e2, distanceX, distanceY);
}</span>
<span style="font-size:18px;">public boolean onSingleTapUp(MotionEvent e) {
// TODO Auto-generated method stub
return super.onSingleTapUp(e);
}</span>
按住(onShowPress): 手指按在触摸屏上,它的时间范围在按下起效,在长按之前。
<span style="font-size:18px;">public void onShowPress(MotionEvent e) {
// TODO Auto-generated method stub
super.onShowPress(e);
}</span>
双击( onDoubleTap):双击的时候触发
<span style="font-size:18px;">public boolean onDoubleTap(MotionEvent e) {
// TODO Auto-generated method stub
return super.onDoubleTap(e);
}</span>
使用OnGestureListener接口,这样需要重载OnGestureListener接口所有的方法,适合监听所有的手势。相比
另一个onTouchEvent(MotionEvent event)直接去识别优势就很明显,该方法只能提供比较简单的事件:
public boolean onTouchEvent(MotionEvent event){
int action = MotionEventCompat.getActionMasked(event);
switch(action) {
case (MotionEvent.ACTION_DOWN) :
Log.d(DEBUG_TAG,"Action was DOWN");
return true;
case (MotionEvent.ACTION_MOVE) :
Log.d(DEBUG_TAG,"Action was MOVE");
return true;
case (MotionEvent.ACTION_UP) :
Log.d(DEBUG_TAG,"Action was UP");
return true;
case (MotionEvent.ACTION_CANCEL) :
Log.d(DEBUG_TAG,"Action was CANCEL");
return true;
case (MotionEvent.ACTION_OUTSIDE) :
Log.d(DEBUG_TAG,"Movement occurred outside bounds " +
"of current screen element");
return true;
default :
return super.onTouchEvent(event);
}
}
所以一般都是使用手势识别器,简单而强大为什么不用。
例子:
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
public abstract class BaseSetupActivity extends Activity {
private Intent intent;
private GestureDetector gestureDetector;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 创建手势识别器
gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
@Override
// 重写手势识别器中的方法
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// e1 起始点
// e2 抬起点
if (e1.getRawX() - e2.getRawX() > 50) {
showNextPage();
//关闭动画
overridePendingTransition(0, 0);
}
if (e2.getRawX() - e1.getRawX() > 50) {
showPrePage();
//关闭动画
overridePendingTransition(0, 0);
}
return super.onFling(e1, e2, velocityX, velocityY);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (e1.getRawX() - e2.getRawX() > 500) {
showNextPage();
//关闭动画
overridePendingTransition(0, 0);
}
if (e2.getRawX() - e1.getRawX() > 500) {
showPrePage();
//关闭动画
overridePendingTransition(0, 0);
}
return super.onFling(e1, e2, distanceX, distanceY);
}
});
}
// 抽象方法,去定义调转到下一页的方法
public abstract void showNextPage();
// 抽象方法,去定义调转到上一页的方法
public abstract void showPrePage();
// 统一处理跳转界面 下一页 交给子类处理 ,点击和滑动聚合在一起了
public void nextPage(View v) {
showNextPage();
// 开启平移动画
overridePendingTransition(R.anim.next_in_anim, R.anim.next_out_anim);
}
// 统一处理跳转界面 上一页 交给子类处理 , 点击和滑动聚合在一起了
public void prePage(View v) {
showPrePage();
// 开启平移动画
overridePendingTransition(R.anim.next_in_anim, R.anim.next_out_anim);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// 通过手势识别器去识别不同的事件类型
gestureDetector.onTouchEvent(event);
return super.onTouchEvent(event);
}
}
通过投掷和滑动的手势进行界面的跳转(跳转的代码没有写出)
事件的分发:
当Android在监听到屏幕上的手势事件后,会根据布局视图的层级进行分发,从结构最外层的ViewGroug往下面传递,
一直到中途被某一层级的View截断而消费掉,不在往下传递,属于消费事件。要先了解事件分发的函数:
View中有两个方法参与到Touch事件分发
onDispatchTouchEvent(MotionEvent event)和onTouch(MotionEvent event)
ViewGroup有三个方法参与到Touch事件分发
onDispatchTouchEvent(MotionEvent event),onInterceptTouchEvent(MotionEvent ev),onTouch(MotionEvent event)
了解三个函数的含义:
1、dispatchTouchEvent
dispatch是否分发事件,整个事件的驱动都在这个方法中,他会先调用自己的onInterceptTouchEvent,再递归调用child的dispatchTouchEvent,这样就可以将事件一直传下去,如果我们覆写了这个方法,而且没有调用super.dispatchTouchEvent(),也就是我们将事件调用的递归过程在这里截断了,child就不会收到该事件的传递,所以这里可以对事件的进行第一次拦截,这里返回true,表示该View已经消费了这个事件,事件传递结束;返回false,表示没有消费,事件返回父控件onTouchEven进行处理。
2、onInterceptTouchEvent
intercept意思是拦截、截断,所以该方法的作用就是判断是否要将这个事件断下来。返回true,事件截断,进入onTouchEvent消费事件;返回false,不进行拦截,事件继续向下传递,进入到子view的dispatchTouchEvent。
3、onTouchEvent
代表是否该view消费了该事件,也就是是否对该事件的发生进行了相应的处理,返回true一个事件就到这里结束了,返回false说明事件没有被消费,事件向上返回到父控件的onTouchEvent方法。
一个简单的例子:
写一个button按钮的监听:
<span style="font-size:18px;">button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Log.d("TAG", "onClick execute");
}
}); </span>
再写button的onTouch的监听:
<span style="font-size:18px;">button.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Log.d("TAG", "onTouch execute, action " + event.getAction());
return false;
}
}); </span>
当点击了button后,这里肯定会触发onTouch的ACTION_DOWN和ACTION_UP事件,可能你按的时候抖了一下就产生ACTION_MOVE事件
固这里打印会有:
onTouch execute,action 0
onTouch execute,action 1
onClick execute
首先:源码中只要你触摸到了任何一个控件,就一定会调用该控件的dispatchTouchEvent方法。那当我们去点击按钮的时候,就会去调用Button类里的dispatchTouchEvent方法,可是你会发现Button类里并没有这个方法,那么就到它的父类TextView里去找一找,你会发现TextView里也没有这个方法,那没办法了,只好继续在TextView的父类View里找一找,这个时候你终于在View里找到了这个方法(super回调父类方法),示意图如下:
这里就简单描叙下:http://files.cnblogs.com/files/sunzn/PRE_andevcon_mastering-the-android-touch-system.pdf
事件分发大神的分析路径:
Android事件分发机制完全解析,带你从源码的角度彻底理解(上)
http://blog.csdn.net/guolin_blog/article/details/9097463
Android事件分发机制完全解析,带你从源码的角度彻底理解(下)
http://blog.csdn.net/guolin_blog/article/details/9153761
直接看代码:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#CCCCCC"
android:endColor="#CCCCCC"
android:angle="45"/>
<padding android:left="5dp"
android:top="5dp"
android:right="5dp"
android:bottom="5dp" />
<corners android:radius="16dp" />
</shape>
这里设置的为16dp的圆角矩形,在代码中通过R.drawable.name 进行访问,在xml中通过@ drawable/filename进行访问
属性介绍:
android:startColor="#CCCCCC" 渐变色的开始颜色
android:endColor 颜色值 结束颜色
android:centerColor 整型 渐变中间颜色,即开始颜色与结束颜色之间的颜色
android:angle 整型 渐变角度(PS:当angle=0时,渐变色是从左向右。 然后逆时针方向转,当angle=90时为从下往上。angle必须为45的整数倍)
android:type 渐变类型: linear 线性渐变,这是默认设置 radial 放射性渐变,以开始色为中心。 sweep 扫描线式的渐变。
android:centerX 整型 渐变中心X点坐标的相对位置
android:centerY 整型 渐变中心Y点坐标的相对位置
android:left="5dp" 整型 左内边距
android:top ="5dp" 整型 上内边距
android:right ="5dp" 整型 右内边距
android:bottom ="5dp" 整型 下内边距 (设置padding时,不要再次去布局中设定,会产生冲突,drawable无效)
<corners android:radius="16dp" /> 设置圆角全局半径
android:topLeftRadius 整型 左上角半径
android:topRightRadius 整型 右上角半径
android:bottomLeftRadius 整型 左下角半径
android:bottomRightRadius 整型 右下角半径
<solid android:color="#00000000" />实心填充
<stroke
android:width="2dp"
android:color="#dcdcdc"
/>
描边: android:width 整型 描边的宽度
android:color 颜色值 描边的颜色
1)肯定我们不用再去托运添加实现findViews()init()setLinsteners()这些初始化方法了
2)我们不用再在每个Activity中手动调用这自定义共有方法,因为我们的BaseActivity已经为我们做好了封装。
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
public abstract class BaseSetupActivity extends Activity {
private Intent intent;
private GestureDetector gestureDetector;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 创建手势识别器
gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
@Override
// 重写手势识别器中的方法
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
// e1 起始点
// e2 抬起点
if (e1.getRawX() - e2.getRawX() > 50) {
showNextPage();
//关闭动画
overridePendingTransition(0, 0);
}
if (e2.getRawX() - e1.getRawX() > 50) {
showPrePage();
//关闭动画
overridePendingTransition(0, 0);
}
return super.onFling(e1, e2, velocityX, velocityY);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (e1.getRawX() - e2.getRawX() > 500) {
showNextPage();
//关闭动画
overridePendingTransition(0, 0);
}
if (e2.getRawX() - e1.getRawX() > 500) {
showPrePage();
//关闭动画
overridePendingTransition(0, 0);
}
return super.onFling(e1, e2, distanceX, distanceY);
}
});
}
// 抽象方法,去定义调转到下一页的方法
public abstract void showNextPage();
// 抽象方法,去定义调转到上一页的方法
public abstract void showPrePage();
// 统一处理跳转界面 下一页 交给子类处理 ,点击和滑动聚合在一起了
public void nextPage(View v) {
showNextPage();
// 开启平移动画
overridePendingTransition(R.anim.next_in_anim, R.anim.next_out_anim);
}
// 统一处理跳转界面 上一页 交给子类处理 , 点击和滑动聚合在一起了
public void prePage(View v) {
showPrePage();
// 开启平移动画
overridePendingTransition(R.anim.next_in_anim, R.anim.next_out_anim);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// 通过手势识别器去识别不同的事件类型
gestureDetector.onTouchEvent(event);
return super.onTouchEvent(event);
}
}
某个界面的基础与BaseSetupActivity 实现了抽象方法,直接看最下面的方法就行了,其他的不管
package com.itheima.mobilesafe74.activity;
import com.itheima.mobilesafe74.R;
import com.itheima.mobilesafe74.utils.ConstantVaule;
import com.itheima.mobilesafe74.utils.SpUtil;
import com.itheima.mobilesafe74.utils.ToastUtil;
import com.itheima.mobilesafe74.view.SettingItemView;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;
public class Setup2Activity extends BaseSetupActivity {
private SettingItemView siv_sim_bound;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.setup2);
intiUi();
}
private void intiUi() {
siv_sim_bound = (SettingItemView) findViewById(R.id.siv_sim_bound);
// 1,读取已有的绑定状态 用来回显 sp中是否存储了sim卡的序列号
String sim_number = SpUtil.getString(this, ConstantVaule.SIM_NUMBER, "");
// 2,判断是否为空""
if (TextUtils.isEmpty(sim_number)) {
siv_sim_bound.setCheck(false);
} else {
siv_sim_bound.setCheck(true);
}
siv_sim_bound.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 3,获取原有的状态
boolean isCheck = siv_sim_bound.isCheck();
// 4,将原有的状态取反
// 5,状态设置给当前条目
siv_sim_bound.setCheck(!isCheck);
if (!isCheck) {
// 6,存储(序列号)
// 6.1获取sim卡序列号TelephonyManager
TelephonyManager manager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
// 6.2拿到sim卡序列卡号
String simSerialNumber = manager.getSimSerialNumber();
Toast.makeText(getApplicationContext(), simSerialNumber, 1).show();
// 6.3存储
SpUtil.putString(getApplicationContext(), ConstantVaule.SIM_NUMBER, simSerialNumber);
} else {
// 7,将存储序列号的节点,从sp中删除
SpUtil.remove(getApplicationContext(), ConstantVaule.SIM_NUMBER);
}
}
});
}
//老版手机更换手机sim卡,监听在开机发送开启广播,一旦捕获到此广播
//就让其广播接收者的onReceiver中发送短信到安全号码
@Override
public void showNextPage() {
String SerialNumber = SpUtil.getString(this, ConstantVaule.SIM_NUMBER, "");
if (TextUtils.isEmpty(SerialNumber)) {
ToastUtil.show(this, "请绑定sim卡,亲");
} else {
Intent intent = new Intent(getApplicationContext(), Setup3Activity.class);
startActivity(intent);
finish();
}
}
@Override
public void showPrePage() {
Intent intent = new Intent(getApplicationContext(), Setup1Activity.class);
startActivity(intent);
finish();
}
}
总结:掌握shape的用法和属性,了解手势的分发机制,掌握手势管理器处理几种必用到的手势。学会BaseActivity的工程框架。