我学习Android都是结合源代码去学习,这样比较直观,非常清楚的看清效果,觉得很好,今天的学习源码是网上找的个CityList 源码 百度搜就知道很多下载的地方
本节学习接上篇布局学习(六) 地址:http://blog.csdn.net/u014737138/article/details/40557335
本节主要掌握的就是 对于上篇文章说道的26个字母列表实现触摸点击事件的处理
学习之前我们先需要知道一个知识点或者叫原理:
首先,Android事件处理机制是基于Listener实现的,比如触摸屏相关的事件,就是通过onTouchListener实现; 其次,所有View的子类都可以通过setOnTouchListener()、setOnKeyListener()等方法来添加对某一类事件的Listener; 第三,Listener一般会以Interface的方式来提供,其中包含一个或多个abstract方法,我们需要实现这些方法来完成 onTouch()、onKey()等操作。这样,程序便可以在特定的事件被dispatch到该view的时候,通过callback函数给予适当的响 应。
1.首先需要写个接口
代码语言:javascript复制 public interface OnTouchingLetterChangedListener
{
public void onTouchingLetterChanged(String s);
}
看这个名字就知道了,这个是自定义的接口,触摸字母变化监听器 ,
里面的函数的作用是:
2.定义完这个接口我们现在要做的就是在我们实现的类中声明这个变量 ,并写出它的设置函数
代码语言:javascript复制public class MyLetterListView extends View {
OnTouchingLetterChangedListener onTouchingLetterChangedListener;
public void setOnTouchingLetterChangedListener(OnTouchingLetterChangedListener onTouchingLetterChangedListener)
{
this.onTouchingLetterChangedListener = onTouchingLetterChangedListener;
}
}
3.我们的类是继承View的 我们必须实现一个方法 onTouchEvent
@Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); }
手机屏幕事件的处理方法onTouchEvent。该方法在View类中的定义,并且所有的View子类全部重写了该方法,
应用程序可以通过该方法处理手机屏幕的触摸事件
参数event:参数event为手机屏幕触摸事件封装类的对象,其中封装了该事件的所有信息,
例如触摸的位置、触摸的类型以及触摸的时间等。该对象会在用户触摸手机屏幕时被创建。
触摸的类型为:MotionEvent.ACTION_DOWN,MotionEvent.ACTION_MOVE,MotionEvent.ACTION_UP
返回值:该方法的返回值机理与键盘响应事件的相同,同样是当已经完整地处理了该事件且不希望其他回调方法再次处理时返回true,否则返回false。
该方法并不只处理一种事件,一般情况下三种触摸类型情况的事件全部由onTouchEvent方法处理:
MotionEvent.ACTION_DOWN:屏幕被按下:当屏幕被按下时,会自动调用该方法来处理事件,此时MotionEvent.getAction()的值
为MotionEvent.ACTION_DOWN,只需重新该回调方法,然后在方法中进行动作的判断即可
MotionEvent.ACTION_MOVE:在屏幕中拖动:该方法还负责处理触控笔在屏幕上滑动的事件,同样是调用MotionEvent.getAction()方法来判
断动作值是否为MotionEvent.ACTION_MOVE再进行处理。
MotionEvent.ACTION_UP:屏幕被抬起:当触控笔离开屏幕时触发的事件,该事件同样需要onTouchEvent方法来捕捉,然后在方法中进行
动作判断。当MotionEvent.getAction()的值为MotionEvent.ACTION_UP时,表示是屏幕被抬起的事件
原理讲到这里呢我们就需要明白这样一个道理:
我们可以通过MotionEvent的getAction()方法来获取Touch事件的类型,包括 ACTION_DOWN(按下触摸屏), ACTION_MOVE(按下触摸屏后移动受力点), ACTION_UP(松开触摸屏)和ACTION_CANCEL(不会由用户直接触发)。借助对于用户不同操作的判断,结合getRawX()、 getRawY()、getX()和getY()等方法来获取坐标后,我们可以实现诸如拖动某一个按钮,拖动滚动条,显示触摸的内容等等功能。
4.重载触摸事件的调度处理函数:
1)public boolean dispatchTouchEvent(MotionEvent ev) 这个方法用来分发TouchEvent
@Override public boolean dispatchTouchEvent(MotionEvent event) { final int action = event.getAction();// 首先获得触摸事件的类型 final float y = event.getY();//获得当前触摸位置的Y坐标 final int oldChoose = choose;//这个变量在上篇文章说道是表示该字母有没有被选中,它是一个flag变量,这里记录它的上一步状态 final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener;//监听对象 final int c = (int) (y / getHeight() * b.length);//这个函数的作用就是获取当前触摸的位置是哪个字母的索引
//首先,getHeight()获得的是当前的View的高度,y/getHerght()得到就是一个比例值,这个比例值肯定小于1
//其次,用上面的比例值乘以总长度,不就是得到一个索引值吗?y/getHeight()*b.length(),它的意思就是占据这个数据多长
//将上面的float值强制性转换为int类型值,得到的就是一个整数,这个整数就是在数组中的索引值,通过这个整数我们就可以获得字母 switch (action) {//触摸事件的分发调度了,如果返回的是一个true值,将让onTouchEvent()进行处理 case MotionEvent.ACTION_DOWN://按下事件处理 showBkg = true; if (oldChoose != c && listener != null) {//如果当前按下的位置的字母和上一步状态选择的字母不同,并且当前的监听事件处于活跃状态 if (c > 0 && c < b.length) {//索引值合法 listener.onTouchingLetterChanged(b[c]);//处理这个字母 显示 choose = c;//记住当前被选择中的字母 invalidate();//更新View,在UI线程自身中使用 } } break; case MotionEvent.ACTION_MOVE://按下之后拖动事件 if (oldChoose != c && listener != null) { if (c > 0 && c < b.length) { listener.onTouchingLetterChanged(b[c]); choose = c; invalidate(); } } break; case MotionEvent.ACTION_UP://触摸放开事件 showBkg = false;//不需要调用onTouchEvent了 choose = -1;//没有字母被选择中 invalidate();//更新View break; } return true; }
至此 整个实现就基本处理完了,但是有人在这里可能又会问到 View的触摸事件处理的原理还是没有搞懂,他们之间的调用是怎么样的啊
他们之间的执行顺序是什么样的啊?那么我们来在代码中加入输出日志 看看它是怎么执行的:
首先我贴上这个类的代码(加了输出跟踪日志):
代码语言:javascript复制import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class MyLetterListView extends View {
OnTouchingLetterChangedListener onTouchingLetterChangedListener;
String[] b = { "#", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" };
int choose = -1;
Paint paint = new Paint();
boolean showBkg = false;
public MyLetterListView(Context context) {
super(context);
// TODO Auto-generated constructor stub
System.out.println("构造函数一:参数只有一个上下文Context");
}
public MyLetterListView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
System.out.println("构造函数二:参数Context context, AttributeSet attrs, int defStyle");
}
public MyLetterListView(Context context, AttributeSet attrs)
{
super(context, attrs);
System.out.println("构造函数三:参数Context context, AttributeSet attrs");
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
System.out.println("onDraw()");
super.onDraw(canvas);
{
canvas.drawColor(Color.parseColor("#40000000"));
}
int height = getHeight();
int width = getWidth();
int singleHeight = height / b.length;
for (int i = 0; i < b.length; i )
{
paint.setColor(Color.WHITE);
paint.setTypeface(Typeface.DEFAULT_BOLD);
paint.setAntiAlias(true);
if (i == choose)
{
paint.setColor(Color.parseColor("#3399ff"));
paint.setFakeBoldText(true);
}
float xPos = width / 2 - paint.measureText(b[i]) / 2;
float yPos = singleHeight * i singleHeight;
canvas.drawText(b[i], xPos, yPos, paint);
paint.reset();
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent event)
{
System.out.println("dispatchTouchEvent");
final int action = event.getAction();
final float y = event.getY();
final int oldChoose = choose;
final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener;
final int c = (int) (y / getHeight() * b.length);
switch (action) {
case MotionEvent.ACTION_DOWN:
showBkg = true;
if (oldChoose != c && listener != null)
{
if (c > 0 && c < b.length)
{
listener.onTouchingLetterChanged(b[c]);
choose = c;
invalidate();
}
}
System.out.println("MotionEvent.ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
if (oldChoose != c && listener != null)
{
if (c > 0 && c < b.length)
{
listener.onTouchingLetterChanged(b[c]);
choose = c;
invalidate();
}
}
System.out.println("MotionEvent.ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
showBkg = false;
choose = -1;
invalidate();
System.out.println("MotionEvent.ACTION_UP");
break;
}
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
System.out.println("onTouchEvent");
return super.onTouchEvent(event);
}
public void setOnTouchingLetterChangedListener(OnTouchingLetterChangedListener onTouchingLetterChangedListener)
{
this.onTouchingLetterChangedListener = onTouchingLetterChangedListener;
}
public interface OnTouchingLetterChangedListener
{
public void onTouchingLetterChanged(String s);
}
}
首先是我们进入这个界面看看logcat输出是什么:
首先是构造函数三,接下来就是执行onDraw函数
接下来来我们点击一个字母然后马上放开看看logcat输出什么:
首先是选中字母D,马上执行了dispatchTouchEvent()函数,接着打印出来,触摸类型是:MotionEvent.ACTION_DOWN
由于有这样一个函数存在:invalidate();,它马上就又执行了onDraw()函数,就是更新视图
接着又是执行dispatchTouchEvent()函数,触摸类型是:MotionEvent.ACTION_UP
接下来又是执行了onDraw()函数,就是更新视图,等待用户的继续操作
从这个输出logcat里面我们可以看到:activity可以去调用dispatchTouchEvent()函数,而且监听事件中是这个函数第一个执行,然后获取监听事件的类型,接下来就去调用onTouchEvent()函数,
我们再来看看拖动事件的执行过程:
好了 时间有限 ,今天就写到这里,具体的原理下次有机会再讲了,这个类写好之后,我们以后需要用这样的一个widget的时候,只需要导入这个类,然后在layout里面定义如下即可使用:
代码语言:javascript复制 <com.wust.citylist.activity.MyLetterListView
android:id="@ id/cityLetterListView"
android:layout_width="30dip"
android:layout_height="fill_parent"
android:layout_alignParentRight="true"
android:background="#40000000" />