Android群英传读书笔记——Android Scroll分析

5.1  滑动效果是如何产生的

        滑动的本质是改变View的坐标

5.1.1  Android坐标系

        屏幕左上角为原点,向右为x轴正方形,向下为y轴正方向

        系统提供了View.getLocationOnScreen(int location[])获取View左上角在整个屏幕中的坐标。View.getLocationInWindow(int location[])获取View左上角相对于父布局的位置。详见https://blog.csdn.net/ouyang_peng/article/details/46902957

        获取各种XY坐标图示

5.1.2  视图坐标系

        以父视图的左上角坐标为原点的相对坐标系,通过getX()getY()获取相对坐标

5.1.3  触控事件

        触控事件的不同类型

//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_CANCLE=3;
//触摸动作超出边界
public static final int ACTION_OUTSIDE=4;
//多点触摸按下动作
public static final int ACTION_POINTER_DOWN=5;
//多点离开动作
public static final int ACTION_POINTER_UP=6;

        通常通过在onTouchEvent(MotionEvent ev)中使用ev.getAction()来获取触控事件类型

@Override
public boolean onTouchEvent(MotionEvent ev){
    switch(ev.getAction()){
        case ACTION_DOWN:
            ...
            break;
        case ACTION_UP:
            ...
            break;
    }
    return true;
}

        Android获取坐标值,距离的方法。

        1、View提供的方法:getTop,getBottom,getLeft,getRight

        2、MotionEvent提供的方法:getX,getY(获取相对坐标),getRawX,getRawY(获取绝对坐标)

 

5.2  实现滑动的7种方法

        滑动的基本思想是触摸View时,记下坐标,移动后,记下坐标,算出位移,移动View。

        我们在LinearLayout(ViewGroup)中方一个View

5.2.1  layout方法

ViewGroup.setOnTouchListener((event)->{
    int x=(int)event.getX();
    int y=(int)event.getY();
    switch(event.getAction){
        case ACTION_DOWN:
            lastX=x;
            lastY=y;
        case ACTION_MOVE:
            int offsetX=x-lastX;
            int offsetY=y=lastY;
            //在当前left,right,top,bottom增加偏移量
            layout(getLeft()+offsetX,
                    getTop()+offsetY,
                    getRight()+offsetX
                    getBottom()+offsetY);
            break;
    }
    return true;
});

        同样,可以使用getRawX和getRawY计算偏移量。

5.2.2  offsetLeftAndRight()和offsetTopAndBottom()

        此方法为系统提供的上下左右偏移API,参数为XY偏移量

//同时对Left和Right偏移
offsetLeftAndRight(offsetX);
//同时对Top和Bottom偏移
offsetTopandBottom(offsetY);

5.2.3  LayoutParams

        LayoutParams保存了一个View的布局参数,可以通过修改LayoutParams动态的修改View的位置

LinearLayout.LayoutParams params=(LinearLayout.LayoutParams)getLayoutParams();
params.leftMargin=getLeft()+offsetX();
params.topMargin=getTop()+offsetY();
setLayoutParams(params);

        需要注意,获取LayoutParams,需指定父布局的类型(如LinearLayout,RelativeLayout),不然获取不到

        也可以通过MarginLayoutParams修改坐标,原理相同,需要指定ViewGroup。

ViewGroup.MarginLayoutParams params=(ViewGroup.MarginLayoutParams)getLayoutParams();
params.leftMargin=getLeft()+offsetX;
params.rightMargin=getTop()+offsetY;
setParams(params);

5.2.4  scrollTo()和scrollBy()

        scrollTo(x,y)移动到指定坐标,scrollBy(dx,dy)移动偏移量。在ViewGroup中使用,移动的是子View,在View中使用,移动的是内容,如TextView的文字,ImageView的图像。

        scrollby(dx,dy)的移动方式相当于固定canvas,移动屏幕玻璃。所以比如dx=-5,就是画布不动,屏幕玻璃左移5,其实就相当于玻璃不动,画布向右移5。所以scrollby用法如下:

int offsetX=x-lastX;
int offsetY=y-lastY;
((View)getParent()).scrollby(-offsetX,-offsetY);

5.2.5  Scroller

        Scroller与scrollTo与scrollBy相比,实现了平滑移动的效果,而不是瞬间移动。实现原理是在ACTION_MOVE中不断移动微笑的偏移量,获得平滑移动的效果。

        例:手指滑动时View跟着滑动,手指松开时View返回左上角

//1、初始化Scroller
mScroller=new Scroller();
//2、重写computeScroll()方法,系统绘制View的时候会在draw()方法中调用该方法
@Override
public void computeScroll(){
    super.computeScroll();
    if(mScroller.computeScrollOffset()){
        ((View)getParent).scrollerTo(mScroll.getCurrX,mScroller.getCurrY);
        //通过重绘不断调用computeScroll()
        invalidate();
    }
}
//3、startScroll
//public void startScroll(int x,inty,int dx,int dy,int duration);//duration类似于动画持续时长
//public void startScroll(int x,inty,int dx,int dy);
case MotionEvent.ACTION_UP:
    //手指离开时,返回左上角
    View viewGroup=((View)getParent());
    mScroller.startScroll(viewGroup.getScrollX(),
                            viewGroup.getScrollY(),
                            -viewGroup.getScrollX(),
                            -viewGroup.getScrollY()
);
invalidate();
break;

        computeScrollOffset()判断是否滑动完成。只能在computeScroll中获取XY,但computeScroll是不会自动调用的。只能通过

invalidate()->draw()->computeScroll()来调用。

5.2.6  属性动画

        (挖坑,相减动画章节)

5.2.7  ViewDragHelper

        Google在其support库中提供了DrawerLayout和SlidingPaneLayout两个布局来帮助开发者实现侧边栏。布局背后有一个强大的类——ViewDragHelper。(drug 拖拽)

        例:实现仿QQ侧边栏,拖拽超过一定距离,侧滑显示菜单。

        1、初始化ViewDrugHelper

        ViewDrugHelper通常定义在ViewGroup内部,使用静态工厂初始化。

//第一个参数是ViewGroup,用来监听View,第二个参数是callback回调,是逻辑核心
mViewDrugHelper=ViewDrugHelper.create(this,callback);

        2、拦截事件

        重写拦截事件,将事件传给ViewDrugHelper处理。

@Override
public boolean onInterceptTouchEvent(MotionEvent ev){
    return mViewDrugHelper.shouldInterceptTouchEvent(ev);
}

@Override
public boolean onTouchEvent(MotionEvent ev){
    //将触摸事件传给ViewDrugHelper,此操作必不可少
    mViewDrugHelper.processTouchEvent(ev);
    return true;
}

        3、处理computeScroll()

        ViewDrugHelper内部是通过Scroller实现滑动的

@Override
public coid computeScroll(){
    if(mViewDrugHelper.continueSetting(true)){
        ViewCompat.postInvalidateOnAnimation(this);
    }
}

        4、处理回调Callback

        

private ViewDragHelper.Callback callback=new ViewDragHelper.Callback(){
    //控制那个View可以被移动
    @Override
    public boolean tryCaptureView(View child,int pointId){
        //触摸的是mainView开始滑动,触摸的是menuView不滑动
        return mMainView==child;
    }

    //第二个参数为child移动的距离,第三个参数表示比较前一次的增量
    @Override
    public int clampViewPositionVertical(View child ,int top,int dy){
        return 0;
    }

    @Override
    public int clampViewPositionHorizontal(View child ,int left,int dx){
        return left;
    }

    //拖拽结束后调用
    @Override
    public void onViewReleased(View releaseChild,int xvel,int yvel){
        super.onViewReleased(releaseChild,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);
        }
    }
};

        重写onFinishInflate()和onSizeChanged()

@Override
protected void onFinishInflated(){
    super.onFinishInflated();
    mMainView=getChildAt(0);
    mMenuView=getChildAt(1);
}

@Override
protected void onSizeChanged(int w,int h,int oldw,int oldh){
    super.onSizeChanged(w,h,pldw,oldh);
    mWidth=mMenuView.getMeasureWidth();
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值