View的事件体系(一)view基础和view的几种滑动方式

View的事件体系

View的事件体系(一)view基础和view的几种滑动方式_第1张图片

一 、view 基础知识

View的事件体系(一)view基础和view的几种滑动方式_第2张图片

1、view的几个方法
  • getTop:获取到的,是view自身的顶边到其父布局顶边的距离

  • getLeft:获取到的,是view自身的左边到其父布局左边的距离

  • getRight:获取到的,是view自身的右边到其父布局左边的距离

  • getBottom:获取到的,是view自身的底边到其父布局顶边的距离

2、motionEvent的方法:
  • getX():获取点击事件相对控件左边的x轴坐标,即点击事件距离控件左边的距离

  • getY():获取点击事件相对控件顶边的y轴坐标,即点击事件距离控件顶边的距离

  • getRawX():获取点击事件相对整个屏幕左边的x轴坐标,即点击事件距离整个屏幕左边的距离

  • getRawY():获取点击事件相对整个屏幕顶边的y轴坐标,即点击事件距离整个屏幕顶边的距离

ps:在触摸事件中这些值是随着触摸滑动事件一直变化

3、TouchSlop

系统所能识别滑动的最小距离,手指在屏幕上滑动小于这个距离时,系统认为没有滑动。这是一个常量和设备有关,不同的设备这个值有所不同。(栗子如下:夜神模拟器打印出的常量)

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i(TAG, "onCreate: "+ViewConfiguration.get(this).getScaledTouchSlop());

    }
    // log:04-08 09:35:48.274 2726-2726/? I/aaa: onCreate: 12
4、VelocityTracker

速度追踪,用于追踪手指在滑动过程中的速度。如下栗子:在view的ontouchEvent中追踪滑动速度。

//1、添加事件
 velocityTracker = VelocityTracker.obtain();
 velocityTracker.addMovement(event);
 //2、设置时间段
 velocityTracker.computeCurrentVelocity(1000);
 //3、获得x,y上,某时间段移动的像素
 int x = (int) velocityTracker.getXVelocity();
 int y = (int) velocityTracker.getYVelocity();
//4、不用时回收
 velocityTracker.clear();
 velocityTracker.recycle();
 
ps:速度有正负

速度=(终点位置-起点位置)/时间段
5、GestureDetector

手势检测用于辅助检测用户的单击、长摁、滑动、双击等行为。

基本使用:

// 1创建对象
参数为: GestureDetector.OnGestureListener类型,OnGestureListener是一个接口我们可以实现他
 GestureDetector gestureDetector = new GestureDetector(参数)
栗子:
   final GestureDetector gestureDetector = new GestureDetector(new GestureDetector.OnGestureListener() {
            @Override
            public boolean onDown(MotionEvent e) {
                return false;
            }

            @Override
            public void onShowPress(MotionEvent e) {

            }

            @Override
            public boolean onSingleTapUp(MotionEvent e) {
                return false;
            }

            @Override
            public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
                return false;
            }

            @Override
            public void onLongPress(MotionEvent e) {

            }

            @Override
            public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
                return false;
            }
        });
//2 接管目标view的onTouchEvent方法(如下举例子)

 findViewById(R.id.text).setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                // 接管
                return gestureDetector.onTouchEvent(event);
            }
        });

接口方法详解:

  • onDown
    手指轻触屏幕一瞬间,由一个ACTION_DOWN触发

  • onShowPress
    手指轻触屏幕,尚未松开或拖动由一个ACTION_DOWN触发。注意和onDown区别,这里强调的是没有松开,或拖动的状态。

  • onSingleTapUp
    单击行为

  • onScroll
    滑动行为 一个ACTION_DOWN多个ACTION_MOVE

  • onLongPress
    长按行为

  • onFling
    用户按下触摸屏快速滑动松开,一个ACTION_DOWN多个ACTION_MOVE,一个ACTION_UP。属于快速滑动行为。

ps:
实际开发中可以不使用GestureDetector,完全可以在自己的view中的onToucEvent中实现所有的监听。
参考建议:如果只是滑动相关监听就使用onToucEvent,如果是监听双击行为再用GestureDetector(如下)
//设置双击监听器
mGestureDetector.setOnDoubleTapListener(new doubleTapListener());
private class doubleTapListener implements GestureDetector.OnDoubleTapListener

参考:https://blog.csdn.net/harvic880925/article/details/39520901

6、Scroller

弹性滑动对象,用于实现view的弹性滑动。我们知道使用使用view的ScrollTo、ScrollBy方法来进行滑动时,滑动的动作是瞬间完成,用户体验不好。而使用Scroller可以让滑动的动作在时间段完成,增加用户体验。
ps:Scroller的使用代码固定,详细参考下面的view的滑动方式中详解。

二 、View的几种滑动方式

  • 使用ScrollTo/ScrollBy
    view本身提供的方法,可以使view滑动
  • 使用动画
    给view设置动画出现平移等效果,来实现滑动。
  • 使用布局参数
    通过不断的改变view的布局参数来实现view的滑动
1、使用ScrollTo/ScrollBy

栗子:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity"
    android:orientation="vertical"
    android:id="@+id/layout">

    <Button
        android:id="@+id/btn_scrollto"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="ScrollTo"/>

    <Button
        android:id="@+id/btn_scrollby"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="ScrollBy"/>


</LinearLayout>
package com.example.administrator.androidview;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "aaa";
    private LinearLayout mLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        mLayout = (LinearLayout) findViewById(R.id.layout);
        findViewById(R.id.btn_scrollto).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e("test", "点击前getScrollX值:" + mLayout.getScrollX());
                Log.e("test", "点击前getScrollY值:" + mLayout.getScrollY());
                mLayout.scrollTo(-50, -50);
                Log.e("test", "点击后getScrollX值:" + mLayout.getScrollX());
                Log.e("test", "点击后getScrollY值:" + mLayout.getScrollY());
            }
        });

        findViewById(R.id.btn_scrollby).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mLayout.scrollBy(-50, -50);
            }
        });
    }




}

二者的源码:
ScrollTo:


 public void scrollTo(int x, int y) {
        if (mScrollX != x || mScrollY != y) {
            int oldX = mScrollX;
            int oldY = mScrollY;
            mScrollX = x;
            mScrollY = y;
            invalidateParentCaches();
            onScrollChanged(mScrollX, mScrollY, oldX, oldY);
            if (!awakenScrollBars()) {
                postInvalidateOnAnimation();
            }
        }
    }

ScrollBy:

 public void scrollBy(int x, int y) {
        scrollTo(mScrollX + x, mScrollY + y);
    }

从源码得知scrollBy其实就是调用了ScrollTo
区别:
1、ScrollTo实现了基于所传参数的绝对滑动
2、ScrollBy实现了基于当前位置的相对滑动
大白话就是ScrollTo滑动到指定位置,ScrollBy滑动到相对当前位置的指定距离。

源码中mScrollX、mScrollY 的理解:

  • 这两个属性都可以通过getScrollX,getScrollY得到
  • mScrollX的值总是等于view左边缘和view内容左边缘的水平方向距离(参考栗子运行图分析)
  • mScrollY 的值总是等于view上边缘和view内容上边缘竖直方向上距离(参考栗子运行图分析)

ScrollTo/ScrollBy的理解:
ScrollTo/ScrollBy只能改变view内容的位置,不能改变view本身的位置(参考栗子运行图分析)

栗子运行图:View的事件体系(一)view基础和view的几种滑动方式_第3张图片

如上就是我们前面代码中运行的草图,首先是一个LinearLayout,里面有两个按钮我们一看xml布局就知道。
1、我们结合图很快就理解了mScrollX,mScrollY。
2、可以看到我们让LinearLayout直行了ScrollTo/ScrollBy这时两个button滑动,而LinearLayout本身没滑动。而正好验证了我们上文对“ScrollTo/ScrollBy的理解”

再次深入解释 mScrollX、mScrollY:

  • 二者的单位都为像素
  • 二者有正负
    当view的左边缘在view内容的左边缘时mScrollX为正,反之为负。(参考栗子运行图分析)
    当view的上边缘在view内容的上边缘时mScrollY为正,反之为负。(参考栗子运行图分析)
    也即是说从左往右滑动mScrollX为负,从上往下滑动mScrollY为负值。
2 、使用动画

动画我们都熟悉啊
view动画移动view栗子:

TranslateAnimation anim = new TranslateAnimation(0, 100, 0, 0);
anim.setDuration(300);
anim.start();
btn.setAnimation(anim);

属性动画移动view的栗子:

ObjectAnimator anim = ObjectAnimator.ofFloat(btn, "translationX", 0, 100);
anim.setDuration(300);
anim.start();

view动画:
1、view动画是对view的影响做操作,他并不能真正改变view的布局参数。
2、如果希望view动画后的状态得以保留,必须设置fillAfter为true,反之动画结束时,view回复原来的状态。
3、使用属性动画可以解决上面view动画的缺点
ps:自己写个demo搞个点击事件,设置好view动画后,看看点击原来位置生效,还是动画后的位置生效。

3、使用布局参数
通过这种方式实现的View的移动,View移动后的位置就是其真实的位置。

自定义个view:

package com.example.administrator.androidview;

import android.content.Context;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewGroup;


/**
 * Create by SunnyDay on 2019/04/08
 *
 * 随手指滑动的AppCompatImageView
 */
public class MoveView extends AppCompatImageView {
    public MoveView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    int lastX = 0;
    int lastY = 0;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //1、用户手指的坐标 用户滑动时坐标不停变化(事件封装在MotionEvent)
        int x = (int) event.getX();
        int y = (int) event.getY();
        int action = event.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                lastX = x; //2、 触发down事件时记录下触摸位置
                lastY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                 // 计算滑动距离
                int offsetX = x - lastX;// 滑动一段距离后:新的x坐标 - 上次记录的位置
                int offsetY = y - lastY;
                // 不断修改位置  这种方法也行
//                layout(getLeft() + offsetX, getTop() + offsetY,
//                        getRight() + offsetX , getBottom() + offsetY);

                // 使用布局参数
                ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) getLayoutParams();
                lp.leftMargin = getLeft() + offsetX;
                lp.topMargin = getTop() + offsetY;
                setLayoutParams(lp);
            
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return true;
    }

}

我们手指滑动时,view跟着滑动。

4 、各种滑动对比
  • scrollTo/scrollBy
    他是view提供的原生方法,方便快使view的内容滑动,不影响内部元素的点击事件。只能滑动view内容,不能滑动view本身。
  • 动画
    android3.0以上使用属性动画,没有明显优缺点,但是使用view动画时均不能改变view本身的属性,所以不适合做用户交互,但是一些复杂的效果还需要使用动画才能实现。
  • 布局参数
    除了使用起来麻烦些,没有明显优缺点。适用于用户交互的情况。

三、小结

这里总结了view的基础知识和几种滑动方式,下节总结view的弹性滑动。
View的事件体系(二)view的弹性滑动

The end

本文来自<安卓开发艺术探索>笔记总结

你可能感兴趣的:(安卓开发艺术笔记)