第五章:Android Scroll 分析

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_38515203/article/details/82885009

《Android1群英传》——个人读书笔记

5.1 滑动效果是如何产生的

  • 要实现View的滑动,就必须监听的触摸事件,并根据事件传入的坐标,动态且不断的改变坐标,从而实现View跟随用户触摸的滑动而滑动

5.1.1 Android 坐标系

  • 坐标系原点:屏幕最左上角的顶点
  • 获取坐标系中点坐标: getLocationOnScreen(intlocation[])
  • 获取X轴坐标: getRowX()
  • 获取Y轴坐标: getRowY()

5.1.2 视图坐标系

  • 原点:父视图左上角为坐标原点
  • 它描述了子视图在父视图中的位置
  • 两种坐标系相辅相成,并不冲突
  • 视图坐标系的获取:getX()getY()

5.1.3 触控事件——MotionEvent

  • 1)MotionEvent中封装的一些常用的事件常量:
// 单点触摸按下动作
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;
  • 2)View提供的获取坐标的方法:
  • getTop(): 获取到的是View 自身的顶边 到其 父布局顶边 的距离
  • getLeft(): <font color=#ED1A3D>左边————>左边</font>
  • getRight(): 右边————>左边
  • getBottom(): <font color=#ED1A3D>底边————>顶边</font>

补充:
这边需要注意:这四个方法是相互对相应的,一组是到父控件左边的距离(getLeft()、getRight()),一组是到父控件顶部的距离(getTop()、getBottom()),每一组的对应点是相同的。

7208373-6180dc39f6b8722a.png

  • 根据上图我们可获取View的大小:
  • View的Height值 = view.getBottom() - view.getTop();
    View的Width值 = view.getRight() - view.getLeft();
  • 3)MotionEvent提供的方法:
  • getX(): 获取点击事件距离控件左边的距离,即视图坐标
  • getY(): 顶边
  • getRawX(): 获取点击事件距离整个屏幕左边的距离,即绝对坐标
  • getRawY(): 顶边

    7208373-53687f243aa6ec01.png

5.2 实现滑动的其中方法

5.2.1 Layout方法

  •  

5.2.2 offsetLeftAndRight() 与 offsetTopAndBottom()

  • 这两个方法相当于系统提供的一个对左右、上下移动的API的封装。当计算出偏移量之后,只需要使用如下代码就可以完成View的重新布局,效果与使用Layout方法一样。
//同时对left和right进行偏移
offsetLeftAndRight(offsetX);
//同时对top和bottom进行偏移
offsetTopAndBottom(offsetY)

5.2.3 LayoutParams

  •  

5.2.4 scrollTo 与 scrollBy

  • 改变View的位置
  • scrollTo(x,y) 表示移动到一个具体的坐标点(x,y)
  • scrollBy(dx,dy) 表示移动的增量为dx,dy
  • 获取偏移量后,使用 scrollBy 来移动View
int offsetX = x - lastX;
int offsetY = y = lastY;
scrollBy(offsetX,offsetY);
  • 注意:scrollTo 与 scrollBy 移动的是View的content,即移动的是view的内容。当然如果是在ViewGroup中使用这两个方法,那么移动的将是所有的子View
int offsetX = x - lastX;
int offsetY = y = lastY;
((View)getParent()).scrollBy(offsetX, offsetY)
  • 按照上述方法进行设置,视图不会跟随手指的移动而移动,而是反方向的移动。要想跟随手指移动需要将偏移量设置为负数
((View)getParent()).scrollBy(-offsetX, -offsetY)

5.2.5 Scroller类

  • Scroller类可以实现平滑移动的效果,而非瞬间完成的移动
  • 使用Scroll类的步骤:
  • 1)初始化Scroller
//初始化Scroller
mScroller = new Scroller(context);
  • 2)重写computeScroll()方法,实现模拟滑动
            它是使用Scroller类的核心,系统会在绘制View的时候在draw()方法中调用这个方法。
@Override
public void commputeScroll()
{
    super.commputeScroll();
    //判断 Scroll 是否执行完毕
    if(mScroller.commputeScrollOffset())
    {
        ((View)getParent()).scrollTo(
            mScroller.getCurrX(),
            mScroller.getCurrY());
        //通过重绘来不断调用 commputeScroll
        invalidate();
    }
}
  • Scroller类提供了 commputeScrollOffset() 方法来判断是否完成了整个滑动,同时也提供了 getCurrXgetCurrY 来获得当前的滑动坐标。
  • 注意: invalidate()方法只能在 computeScroll() 方法中获取模拟过程中的 scrollX 和 scrollY 坐标。但是 computeScroll() 是不会自动调用的,只能通过 invalidate()——>draw()——>computeScroll() 来间接调用 computeScroll() 方法,所以需要在模板代码中调用 invalidate() 方法,实现循环获取 scrollX 和 scrollY 的目的。而当模拟过程结束后,scroller.commputeScrollOffset() 方法会返回 false ,从而中断循环,完成整个平滑移动过程。
  • 3)startScroll 开启模拟过程
  • 调用 Scroller 类中的starScroll()方法来开启平滑移动过程
  • startScroll() 方法具有两个重载方法:
            public void startScroll(int startX, int startY, int dx, int dy, int duration)
            public void startScroll(int startX, int startY, int dx, int dy)

5.2.6 属性动画

  • 与第七章重复,这里不做介绍

5.2.7 ViewDragHelper

  • Google在support库中提供了 DrawLayoutSlidingPaneLayout 用于实现侧边栏滑动效果
  • 实现方式:
  • 1)初始化ViewDragHelper
private void initView()
{
   mViewDragHelper = ViewDragHelper.create(this, callback);
}
  • 传入的两个参数:第一个是要监听的View,通常是ViewGroup,即ParentView;第二个是CallBack回调, 这个回调就是整个ViewDragHelper 的逻辑核心。
  • 2)拦截事件
  • 重写事件拦截方法,将事件传递给ViewDragHelper进行处理
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mViewDragHelper.shouldInterceptTouchEvent(ev);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
        //将触摸事件传递给ViewDragHelper,此操作必不可少
        mViewDragHelper.processTouchEvent(event);
        return true;
}
  • 3)处理computeScroll()
  • ViewDragHelper 的内部也是通过Scroller来实现滑动的。
@Override
public void computeScroll() {
        if (mViewDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
}
  • IDE 自动重写的 tryCaptureView() ,可以指定在创建ViewDragHelper时,参数ParentView中的哪个子View可以被移动。
// 何时开始检测触摸事件
@Override
public boolean tryCaptureView(View child, int pointerId) {
//如果当前触摸的child是mMainView时开始检测
    return mMainView == child;
}
  • 以上代码就是指定了 MainView 是可以被移动的
  • 具体实现滑动的方法 :
// 处理垂直滑动
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
     return top;
}

// 处理水平滑动
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
    return left;
}
  • 重写 onViewReleased() 方法,可以非常简单的实现当是指离开屏幕后实现的操作,其内部也是通过 Scroller 类实现的
                // 拖动结束后调用
                @Override
                public void onViewReleased(View releasedChild, float xvel, float yvel) {
                    super.onViewReleased(releasedChild, xvel, yvel);
                    //手指抬起后缓慢移动到指定位置
                    if (mMainView.getLeft() < 500) {
                        //关闭菜单
                        //相当于Scroller的startScroll方法
                        mViewDragHelper.smoothSlideViewTo(mMainView, 0, 0);
                        ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
                    } else {
                        //打开菜单
                        mViewDragHelper.smoothSlideViewTo(mMainView, 300, 0);
                        ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
                    }
                }
  • 系统定义了大量监听事件来帮助我们处理各种事情:
            1)onViewCaptured(): 这个事件在用户触摸到View之后回调;
            2)onViewDragStateChanged(): 这个事件在拖拽状态改变时回调
            3)onViewPositionChanged(): 这个事件在位置改变时回调,常用于滑动时更改scale进行缩放等效果。
    r

猜你喜欢

转载自blog.csdn.net/weixin_38515203/article/details/82885009