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

要使自定义的视图是可交互的,就需要使它能够对用户事件作出反应,例如,按下按键、触摸屏幕或者单击按钮等。Android提供了多个虚拟事件处理程序,可以对用户输入作出反应,如下所示:
onKeyDown 当任何设备按键被按下时,就会调用它;包括D-pad、键盘、挂断、通话、返回和摄像头按键
onKeyUp 当用户释放一个按键时调用
onTouchEvent 当触摸屏被按下或者释放时调用,或者当检测到运动时调用

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

  1. 在上一篇自定义视图的基础上,画一个圆,然后实现屏幕触摸事件和点击事件
  2. 判断是点击的是圆内还是圆外(系统默认是可以点击View所在的矩形区域)
  3. 自定义点击事件回调接口

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

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;
            }
        });*/

效果演示图如下:
安卓开发学习之020 自定义视图的用户交互事件_第1张图片

开发工具:Android Studio1.5
SDK: Android 6.0
API 23

代码下载:020_MyView_Events

你可能感兴趣的:(android,触摸,自定义视图,交互事件,自定义点击)