您的位置:首页 > 移动开发 > Android开发

安卓开发学习之020 自定义视图的用户交互事件

2015-12-22 10:12 501 查看
要使自定义的视图是可交互的,就需要使它能够对用户事件作出反应,例如,按下按键、触摸屏幕或者单击按钮等。Android提供了多个虚拟事件处理程序,可以对用户输入作出反应,如下所示:

onKeyDown 当任何设备按键被按下时,就会调用它;包括D-pad、键盘、挂断、通话、返回和摄像头按键

onKeyUp 当用户释放一个按键时调用

onTouchEvent 当触摸屏被按下或者释放时调用,或者当检测到运动时调用

本文将通过如下列子简单的介绍上述几个方法

在上一篇自定义视图的基础上,画一个圆,然后实现屏幕触摸事件和点击事件

判断是点击的是圆内还是圆外(系统默认是可以点击View所在的矩形区域)

自定义点击事件回调接口

先给出本文所使用的自定义文件

package com.antex.myview_events;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;

/**
* @author xiaosanyu on 15/12/20.
*/
public class MyView extends View {
private static final String TAG = MyView.class.getSimpleName();
private Paint mPaint;
//自定义回调接口
private MyClickListener mListener;
//坐标点,记录屏幕按下的位置,后面判断此位置到圆心的距离
private PointF mPoint;

public MyView(Context context) {
this(context,null);
}

public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
//消除锯齿
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
mPaint.setTextSize(30);
//描边宽度
mPaint.setStrokeWidth(3);

mPoint = new PointF();
//设置可获取焦点
setFocusable(true);
//添加点击事件监听器
setOnClickListener(mOnClickListener);

}

public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//获取真正的尺寸
int measuredWidth = measureWidth(widthMeasureSpec);
int measuredHeight = measureHeight(heightMeasureSpec);
//必须调用setMeasuredDimension,否则在布局控件的时候会造成运行时异常
setMeasuredDimension(measuredWidth, measuredHeight);
}

private int measureHeight(int heightMeasureSpec) {
//设置默认高度
int result = 200;
//获取控件上下间距
int padding = getPaddingTop() + getPaddingBottom();
//获取模式
int specMode = MeasureSpec.getMode(heightMeasureSpec);
//获取值大小
int specSize = MeasureSpec.getSize(heightMeasureSpec);
//打印模式和值大小
//Log.i(TAG,"测量高度 " + MeasureSpec.toString(heightMeasureSpec));

switch (specMode) {
case MeasureSpec.UNSPECIFIED:
break;
case MeasureSpec.AT_MOST:
//result = specSize;
//因为在ondraw里面只是简单的画一个文本。所以只需要文本的高度即可
result = Math.min(specSize, measureTextHeight(mPaint) + padding);
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}

private int measureWidth(int widthMeasureSpec) {
//设置默认宽度
int result = 2000;
//获取控件左右间距
int padding = getPaddingLeft() + getPaddingRight();
//获取模式
int specMode = MeasureSpec.getMode(widthMeasureSpec);
//获取值大小
int specSize = MeasureSpec.getSize(widthMeasureSpec);
//打印模式和值大小
//Log.i(TAG, "测量宽度 " + MeasureSpec.toString(widthMeasureSpec));
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
break;
case MeasureSpec.AT_MOST:
result = Math.min(specSize, specSize + padding);
break;
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setColor(Color.GRAY);

canvas.drawCircle(getWidth() / 2, getHeight() / 2, Math.min(getWidth(), getHeight()) / 2,
mPaint);
mPaint.setColor(Color.RED);
String string = "MyView";
canvas.drawText("MyView", (getWidth() - measureTextWidth(string, mPaint)) / 2, (getHeight
() + measureTextHeight(mPaint)) / 2, mPaint);
}

//获取文本宽度
private int measureTextWidth(String string, Paint paint) {
Rect result = new Rect();
// Measure the text rectangle to get the width
paint.getTextBounds(string, 0, string.length(), result);
return result.width();
}

//获取文本高度
private int measureTextHeight(Paint paint) {
Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();
//        System.out.println("fontMetrics.toString() = " + fontMetrics.toString());
return fontMetrics.descent - fontMetrics.ascent + fontMetrics.leading;
}

@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
Log.d(TAG, "MyView.onKey");
switch (event.getAction()) {
case KeyEvent.ACTION_DOWN:
Log.d(TAG, "KeyEvent ACTION_DOWN");
break;
case KeyEvent.ACTION_UP:
Log.d(TAG, "KeyEvent ACTION_UP");
break;
case KeyEvent.ACTION_MULTIPLE:
Log.d(TAG, "KeyEvent ACTION_MULTIPLE");
break;
default:
break;

}
return super.onKeyDown(keyCode, event);
}

//点击事件注册
private OnClickListener mOnClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "MyView.onClick");
Toast.makeText(getContext(), "MyView.onClick", Toast.LENGTH_SHORT).show();
//自定义接口回调,判断点击的是圆内还是圆外
if (mListener != null) {
//计算当前点击的坐标点与圆心坐标点的距离
int distance = (int) Math.sqrt(Math.pow(mPoint.x - getWidth() / 2, 2) + Math.pow
(mPoint.y - getHeight() / 2, 2));
if (distance <= Math.min(getWidth(), getHeight()) / 2)
mListener.myClick("inside circle");
else mListener.myClick("outside circle");
}
}
};

@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG, "MyView.onTouch");
mPoint.set(event.getX(), event.getY());
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "MotionEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "MotionEvent ACTION_UP");
break;
case MotionEvent.ACTION_MOVE:
Log.d(TAG, "MotionEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_OUTSIDE:
Log.d(TAG, "MotionEvent ACTION_OUTSIDE");
break;
case MotionEvent.ACTION_CANCEL:
Log.d(TAG, "MotionEvent ACTION_CANCEL");
break;
}
return super.onTouchEvent(event);
}

public void setListener(MyClickListener listener) {
mListener = listener;
}

public interface MyClickListener {
void myClick(String s);
}
}


代码介绍:

1. 重写onDraw方法,在方法中画一个圆,在圆心位置写一字符串

2. 在自定义视图中重写了onKeyDown方法

当按下设备按键时候会调用此方法

注意:必须设置视图可获取焦点

setFocusable(true);方能监听到按键按下事件

3. 重写onTouchEvent方法,监听屏幕触摸事件

4. 定义一个接口 ,里面包含一个点击事件,参数为String类型

public interface MyClickListener {

void myClick(String s);

}


5. 给View添加点击监听事件

//添加点击事件监听器

setOnClickListener(mOnClickListener);


//点击事件注册
private OnClickListener mOnClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "MyView.onClick");
Toast.makeText(getContext(), "MyView.onClick", Toast.LENGTH_SHORT).show();
//自定义接口回调,判断点击的是圆内还是圆外
if (mListener != null) {
//计算当前点击的坐标点与圆心坐标点的距离
int distance = (int) Math.sqrt(Math.pow(mPoint.x - getWidth() / 2, 2) + Math.pow
(mPoint.y - getHeight() / 2, 2));
if (distance <= Math.min(getWidth(), getHeight()) / 2)
mListener.myClick("inside circle");
else mListener.myClick("outside circle");
}
}
};


当点击View的时候判断点击点与圆形的距离,判断点击的是圆内还是圆外,然后调用回调接口中的方法

在布局中写定义了一个自定义视图MyView,在Activity中设置回调事件

view = (MyView) findViewById(R.id.myView);
//调用自己定义的点击事件
view.setListener(new MyView.MyClickListener() {
@Override
public void myClick(String s) {
Toast.makeText(MainActivity.this, "MyView.onClick CallBack " + s, Toast
.LENGTH_SHORT).show();
}
});


当然在Activity中调用自定义视图的默认点击事件也是可以的

//亦可调用系统原有的点击监听方法
/*    view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this,"View setOnClickListener",Toast.LENGTH_SHORT)
.show();
}
});

view.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
Toast.makeText(MainActivity.this,"View setOnLongClickListener",Toast.LENGTH_LONG)
.show();
return true;
}
});*/


效果演示图如下:



开发工具:Android Studio1.5

SDK: Android 6.0

API 23

代码下载:020_MyView_Events
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息