关闭

安卓自定义控件(五)触控基础MotionEvent

标签: android触控
215人阅读 评论(0) 收藏 举报
分类:

之前去面试,人家说,我这个事件拦截机制写得太少了,还有一个MotionEvent没写,这个确实也很重要,后来我考虑了一下,决定将这篇文章放到自己定义控件里。

先简单再提一下事件分发,事件分发和拦截主要涉及3个方法:

  • interceptTouchEvent()这个方法,顾名思义是我们的拦截方法,返回true事件就不会向下传递;
  • onTouchEvent()就是触摸事件,返回true就表明当前层消费了这次事件,它会阻止事件向上传递;
  • 而dispatchTouchEvent()是事件分发的方法,它的super方法是事件分发的关键,我们不要去动他,它分发它的事件,我们拦我们的,逻辑上不要乱。

而接下来讲的MotionEvent,MotionEvent其实就是onTouchEvent()和onTouch()这两个方法的参数,要想消费掉事件,就要在这两个方法块里面编辑我们的代码。

常量

先来认识一下这些常量,看一下就好,大概记住一下。

常见的动作常量:

public static final int ACTION_DOWN             = 0;单点触摸动作
public static final int ACTION_UP               = 1;单点触摸离开动作
public static final int ACTION_MOVE             = 2;触摸点移动动作
public static final int ACTION_CANCEL           = 3;触摸动作取消
public static final int ACTION_OUTSIDE          = 4;触摸动作超出边界
public static final int ACTION_POINTER_DOWN     = 5;多点触摸动作
public static final int ACTION_POINTER_UP       = 6;多点离开动作

以下是一些非touch事件

public static final int ACTION_HOVER_MOVE       = 7;
public static final int ACTION_SCROLL           = 8;
public static final int ACTION_HOVER_ENTER      = 9;
public static final int ACTION_HOVER_EXIT       = 10;

掩码常量

ACTION_MASK = 0X000000ff

动作掩码

ACTION_POINTER_INDEX_MASK = 0X0000ff00

触摸点索引掩码

ACTION_POINTER_INDEX_SHIFT = 8 获取触摸点索引需要移动的位数

加速球的实现

如何使用这个事件?我们就来做一下360加速球一样的东西吧,可以拖着那个加速球到处跑,加速球会自动旋转,此外还可以给他设置点击事件等等。

dragbutton_a

考虑一下我们的需求和设计方案:

  • 首先就是旋转动画,360加速球是可以旋转的,旋转可以用Canvas、Matrix来做,不过我算法不是特别好,做出来老是跑飞了,就用帧动画了;
  • 然后是拖拽这个控件,这个要在ACTION_MOVE里面处理,每次获取一下触摸的坐标,然后调用postInvalidate()重绘即可;
  • 其中onTouchEvent和onClick是冲突的,因此点击事件得重新设计,我准备如果坐标没发生变化,就在松开手指的时候触发点击事件。

源码

/**
 * 拖拽按钮,直接继承Button,可以省去很多代码,关于帧动画的代码我就不贴了,因为没什么技术含量
 * Created by ChenSS on 2017/1/2.
 */

public class DragButton extends Button {
    private Point point = new Point();
    //记录之前的坐标
    private Point pointBefore = new Point();
    //记录之后的坐标
    private Point pointAfter = new Point();
    //将点击事件获取下来,在松开手指的时候调用
    private OnClickListener listener;

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

    public DragButton(Context context, AttributeSet attrs) {
        this(context, attrs, android.R.attr.buttonStyle);
    }

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

    @Override
    public void setOnTouchListener(OnTouchListener l) {
        super.setOnTouchListener(l);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //图片旋转,我使用的是帧动画,其实直接通过onDraw直接重绘更好
        AnimationDrawable animationDrawable = (AnimationDrawable) getBackground();
        int eventAction = event.getAction();
        int x = (int) event.getRawX();
        int y = (int) event.getRawY();
        Log.e("point", x + "=========" + y);
        switch (eventAction) {
            case MotionEvent.ACTION_DOWN:
                //记录触摸时的坐标
                pointBefore.x = getLeft();
                pointBefore.y = getTop();
                point.x = (int) event.getX();
                point.y = (y - getTop());
                //启动动画
                animationDrawable.start();
                break;
            case MotionEvent.ACTION_MOVE:
                //这一步主要就是获取触摸的坐标,重新绘制View
                int left = x - point.x;
                int top = y - point.y;
                int right = left + getWidth();
                int bottom = top + getHeight();
                layout(left, top, right, bottom);
                //重新绘制
                postInvalidate();
                break;
            case MotionEvent.ACTION_UP:
                pointAfter.x = getLeft();
                pointAfter.y = getTop();
                //我们要判断一下,松开手指的时候,坐标是不是和原先一样
                //如果坐标变化了,说明用户在拖动按钮,这个时候就不要执行点击事件
                //如果没变化,就说明用户点击了一下这个按钮
                if (pointAfter.equals(pointBefore.x, pointBefore.y)) {
                    listener.onClick(this);
                } else {
                    pointBefore.set(pointAfter.x, pointAfter.y);
                }
                //结束动画
                animationDrawable.stop();
                break;
            default:
                break;
        }
        return true;
    }

    @Override
    public void setOnClickListener(OnClickListener listener) {
        //这里就不要调用super了,因为我们的点击事件比较特殊,不能交给父类处理
        this.listener = listener;
    }
}
/**
 * 我使用的是相对布局,然后就是上面的Button了
 */
public class TestActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        DragButton button = (DragButton) findViewById(R.id.test_btn);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 点击的时候,给一个弹窗
                new AlertDialog.Builder(TestActivity.this)
                        .setNeutralButton("OK", new DialogInterface.OnClickListener() {
                            public void onClick(DialogInterface arg0, int arg1) {
                            }
                        })
                        .setTitle("test button")
                        .setMessage("test test test!!!")
                        .show();
            }
        });
    }
}

多点触摸————图片放大

多点触摸最常见的使用方式,我觉得应该是在下拉刷新。无聊的小伙伴应该都玩过微信或者QQ的下拉刷新,把它一直拉到屏幕底部,拉到不能再拉了,然后再松开。这个过程,我们一个手指肯定无法一次性将ListView拉到最底部,这个过程应当是:用一个手指一直拉,然后换另一个手指拉,然后再换别的手指。这个换的过程,需要捕捉多个触摸点。不过下拉刷新都被写烂了,我也不想再写了,来简单地实现以下图片放大的功能吧。

/**
 * 代码我写得尽量精简,但是显得有些粗糙,用的还是帧布局,然后一个ImageView
 */
public class ZoomImgActivity extends AppCompatActivity implements View.OnTouchListener {
    private ImageView myImageView;
    //两个触点的间距
    private float currentDistance = 0;
    //旧的两个触点的间距
    private float lastDistance = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_zoom_img);
        myImageView = (ImageView) findViewById(R.id.activity_zoom_img);
        myImageView.setOnTouchListener(this);
    }


    @Override
    public boolean onTouch(View v, MotionEvent event) {
        ImageView myImageView = (ImageView) v;
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                //获取LayoutParams
                RelativeLayout.LayoutParams layoutParams =
                        (RelativeLayout.LayoutParams) myImageView.getLayoutParams();
                switch (event.getPointerCount()) {
                    case 1:
                        break;
                    case 2:
                        //两个的话就放大或者缩小
                        float offsetX = event.getX(0) - event.getX(1);
                        float offsetY = event.getY(0) - event.getY(1);
                        currentDistance = (float) Math.sqrt(offsetX * offsetX + offsetY * offsetY);
                        //5px有误差
                        if (currentDistance - lastDistance > 5) {
                            //放大
                            layoutParams.width = (int) (1.01f * myImageView.getWidth());
                            layoutParams.height = (int) (1.01f * myImageView.getHeight());
                            myImageView.setLayoutParams(layoutParams);
                        } else if (lastDistance - currentDistance > 5) {
                             //缩小
                            layoutParams.width = (int) (0.99f * myImageView.getWidth());
                            layoutParams.height = (int) (0.99f * myImageView.getHeight());
                            myImageView.setLayoutParams(layoutParams);
                        }
                        lastDistance = currentDistance;
                }
        }
        return true;
    }
}
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:25756次
    • 积分:669
    • 等级:
    • 排名:千里之外
    • 原创:42篇
    • 转载:3篇
    • 译文:0篇
    • 评论:3条
    最新评论