用户界面包括两方面内容:屏幕布局和事件处理。
事件处理主要包括两个方式来进行,一个是事件处理器,一个是事件监听器。对于事件处理器,一般是事件触发,调用相应的回调函数来进行事件的处理,这些都是系统自发完成的。而事件监听器,则是你需要注册相应的事件,并定义相应 的事件处理函数。
先看事件处理器,一般系统的key事件主要有:
- KEYCODE_POWER
- KEYCODE_BACK
- KEYCODE_MENU
- KEYCODE_HOME
- KEYCODE_SERACH
- KEYCODE_CAMERA
- KEYCODE_VOLUME_UP
- KEYCODE_VOLUME_DOWN
…….
系统会将KeyEvent事件发送给获得焦点的Activity的回调函数。相应的回调函数为 - onKeyUp()
- onKeyDown()
- onKeyLongPress()
- onTrackballEvent()
- onTouchEvent()
- onFocusChanged()
此处还有一些由于设备版本不同,一些处理需要注意的地方,相关知识可以参考《Android开发揭秘》。
现在考虑这样一个问题:
我在照相机里选择一张图片,然后进行显示。并完成以下功能:
- 放大
- 缩小
双击恢复原始 大小
先把部分代码贴出来(还没实现第三个功能):
public class Photo extends AppCompatActivity implements View.OnTouchListener{
private static final String TAG = "AppCompatActivity";
Matrix mMatrix = new Matrix();
Matrix rawMatrix = new Matrix();
ImageView photo;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_photo);
Intent i = getIntent();
String tmp = i.getExtras().getString("photo");
Bitmap bitmap = BitmapFactory.decodeFile(tmp);
Bitmap bitmap1 = Bitmap.createScaledBitmap(bitmap, 300, 400, false);
photo = (ImageView)findViewById(R.id.photo);
photo.setImageBitmap(bitmap1);
photo.setOnTouchListener(this);
rawMatrix.set(photo.getImageMatrix());
}
private static final float DOUBLE_CLICKED_DIS = 10;
private static final int NONE_MODE = 0;
private static final int DRAG_MODE = 1;
private static final int ZOOM_MODE = 2;
private static int eventMode = NONE_MODE;
float two_dis = 0;
private float beginX = 0;
private float beginY = 0;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG,"ACTION_DOWN Pressed...");
beginX = event.getX();
beginY = event.getY();
Log.d(TAG,"beginX: "+beginX);
Log.d(TAG, "beginY: " + beginY);
eventMode = DRAG_MODE;
Log.d(TAG, rawMatrix.toString());
break;
case MotionEvent.ACTION_POINTER_DOWN:
Log.d(TAG,"ACTION_POINTER_DOWN pressed...");
two_dis = (float)calDis(event);
eventMode = ZOOM_MODE;
break;
case MotionEvent.ACTION_MOVE:
if(eventMode==DRAG_MODE) {
float dx = event.getX()-beginX;
float dy = event.getY()-beginY;
float len = (float)Math.sqrt(dx*dx+dy*dy);
if(len<20)
break;
Log.d(TAG,"ACTION_MOVE Pressed...");
mMatrix.set(rawMatrix);
mMatrix.postTranslate(dx, dy);
rawMatrix.set(mMatrix);
beginX = event.getX();
beginY = event.getY();
}else if(eventMode==ZOOM_MODE) {
mMatrix.set(rawMatrix);
float dis = (float)calDis(event);
float scale = dis / two_dis;
float[] values = new float[9];
mMatrix.getValues(values);
if(scale*values[Matrix.MSCALE_X]>6)
scale = scale/values[Matrix.MSCALE_X];
two_dis = dis;
mMatrix.postScale(scale,scale,v.getWidth()/2,v.getHeight()/2);
rawMatrix.set(mMatrix);
}
photo.setImageMatrix(mMatrix);
break;
case MotionEvent.ACTION_POINTER_UP:
Log.d(TAG,"ACTION_POINTER_UP...");
eventMode = NONE_MODE;
break;
case MotionEvent.ACTION_UP:
Log.d(TAG,"ACTION_IP...");
eventMode = NONE_MODE;
break;
default:
break;
}
return true;
}
double calDis(MotionEvent event) {
if(event.getPointerCount()<2)
return 0;
double xDis = event.getX(1) - event.getX(0);
double yDis = event.getY(1) - event.getY(0);
return Math.sqrt(xDis*xDis+yDis*yDis);
}
函数calDis(MotionEvent event)是用来计算两个接触点之间的距离,此时必须保证具有两个接触点。
值得注意的是:第一,在XML布局文件中要设置 android:scaleType=”matrix”(或者在onCreate方法中设置),这样你的图才可以进行按照Matrix方式放大或者缩小 。
第二,要明白几个Matrix方法的区别,开始我就将public boolean postTranslate(float dx, float dy)用成了setTranslate(float dx, float dy)方法,结果得到的图像老是从左上角重置。
第三,要明白整个案件触发流程,可以把真个lifecycle看做是一个状态机。
首先,一个手指接触屏幕,会触发MotionEvent.ACTION_DOWN事件,两个手指会触发MotionEvent.ACTION_POINTER_DOWN事件 。一般MotionEvent.ACTION_POINTER_DOWN事件之前一定会触发MotionEvent.ACTION_DOWN事件的。当以上两个事件有一个触发时,接着就进入了MotionEvent.ACTION_MOVE事件。然后依次执行MotionEvent.ACTION_POINTER_UP,MotionEvent.ACTION_UP事件。
当没有MotionEvent.ACTION_POINTER_DOWN事件触发时,是不会触发MotionEvent.ACTION_POINTER_UP事件的。
举个列子,在触发MotionEvent.ACTION_DOWN后,并没有按下 第二个手指,此时会直接进入MotionEvent.ACTION_MOVE事件,然后一直重复触发此事件,直到你手指离开,因此你可以将此过程以状态机的形式进行编程。