在Android4.x之后,滑动操作大量的出现在Android系统中,滑动的操作方式具有更好的用户体验。所以本章会对Android中

实现滑动效果的方式做一个小归纳。

    在介绍滑动方法前,先讲一下Android的坐标系,Android的坐标系分为两种。

    1,Android坐标,

    以屏幕的左上角为坐标0点,0点向右为X轴正方向,0点向下为Y轴的正方向。在触摸事件中event.getRawX()与event.getRawY()可一个获得

触点的X坐标值与Y轴坐标值。

    2,视图坐标

    以父控件的左上角为坐标0点,0点向右为X轴的正方向,0点向下为Y轴的正方向。在触摸事件中event.getX(),与event.getY()可以获得在视图坐标中

X轴坐标与Y轴坐标。

    在Android中系统提供了非常多的方法来获取坐标值和相对位置,我刚学的时候常常弄混。我就画了张图,来理清这些问题。

android 上下左右居中 android左右滑动是怎么做的_Android

    View提供的获取坐标的方法。

getLeft():view本身左边到父控件左边的距离。

getRight():view本身右边到父控件的左边的距离。

getTop():view本身顶部到父控件顶部的距离。

getBottom():view本身底部到父控件顶部的距离。

    MotionEvent提供的触点获取坐标的的方法。

getX(),视图坐标X的坐标

getY(),视图坐标Y轴坐标。

getRawX(),Android坐标系X轴坐标。

getRawY(),Android坐标系Y轴坐标。

    

      讲完坐标系后,我们正式归纳scroll滑动的方法,这里我们就以实现View跟随手指触摸的滑动为例。

在计算手指偏移的时候我们有两种方法。源于两种坐标系的选择(Android坐标系,视图坐标系);

视图坐标

@Override
    public boolean onTouchEvent(MotionEvent event) {
        //视图坐标
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch(event.getAction()){
            case MotionEvent.ACTION_DOWN:
                lastX = x;
                lastY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                int offsetX = x - lastX;//X偏移
                int offsetY = y - lastY;//Y偏移
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return true;
    }

Android坐标


@Override
    public boolean onTouchEvent(MotionEvent event) {
        //android坐标
        int rawX = (int) event.getRawX();
        int rawY = (int) event.getRawY();
        switch(event.getAction()){
            case MotionEvent.ACTION_DOWN:
                lastX = rawX;
                lastY = rawY;
                break;
            case MotionEvent.ACTION_MOVE:
                int offsetX = rawX - lastX;
                int offsetY = rawY - lastY;
                layout(getLeft() + offsetX, getTop() + offsetY,getRight() + offsetX, getBottom() + offsetY);
                lastX = rawX;
                lastY = rawY;
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return true;
    }



一,Layout方法。

调用Layout方法对自己重新布局。达到移动的效果。

layout(getLeft() + offsetX, getTop() + offsetY,getRight() + offsetX, getBottom() + offsetY);



二,setOffsetLeftAndRight与setOffsetTopAndRight方法。

offsetLeftAndRight(offsetX);
     offsetTopAndBottom(offsetY);



三,LayoutParams方法。

LayoutParams这个类相信大家都不陌生,这个类是View(子视图)向ViewGroup(父视图)传达自己意愿的方式,是ViewGroup的内部类。

不同ViewGroup有不同的LayoutParams的实现类。所以这个使用比较基础的MarginLayoutParams。

ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) getLayoutParams();
     lp.leftMargin  = getLeft() + offsetX;
     lp.topMargin = getTop() + offsetY;
     setLayoutParams(lp);



四,ScrollTo()与 scrollBy().

scrollTo(x,y)标示移动到(x,y)这个坐标。

scrollBy (dx,dy),dx,dy表示X轴与Y轴方向上的偏移量。

同时scroll&移动的并不是View本身,移动的是View的内容,ViewGroup使用移动的是所有的子View。

所以个要想View有滑动效果。我应该调用的其父控件(getParent())的scrol$方法。然后再想想相对运动。所以代码如下。

((View)getParent()).scrollBy(-offsetX, -offsetY);
     ((View)getParent()).scrollTo(((View)getParent()).getScrollX() -offsetX,((View)getParent()).getScrollY() -offsetY);



五,Scroller。

大家看到这个类的名称就知道,它和scrollTo与scrollBy方法羁绊很深。其实功能是一样的。不多它可以定义滑动的时长,而ScrollTo与scrollBy

方法几乎都是一瞬间完成的。

Scroller的使用步骤。

1,初始化:

mScroller = new Scroller(context);



2,重写computeScroll()方法。

@Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            ((View) getParent()).scrollTo(
                    mScroller.getCurrX(),
                    mScroller.getCurrY()
            );
            //通过重绘制来不停的调用computeScroll
            invalidate();
        }
    }



3,启动滑动,这里我们实现当手指抬起,即ACTION_UP响应时。回到初始位置。

case MotionEvent.ACTION_UP:
         View viewGroup = (View) getParent();
         mScroller.startScroll(viewGroup.getScrollX(), viewGroup.getScrollY(), -viewGroup.getScrollX(), -viewGroup.getScrollY(), 3000);
         invalidate();
         break;



6,属性动画。

      属性动画


7,ViewDragHelper方法。

     ViewDragHelper是所有滑动方法中最复杂的,但是也是功能最强大的。下面是ViewDragHelper的使用步骤:

1,ViewDragHelper的初始化:

//第一步
    private void initView() {
        mViewDragHelper = ViewDragHelper.create(this, mCallback);
    }



 2,处理事件拦截:

//第二步
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mViewDragHelper.shouldInterceptTouchEvent(ev);
    }

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



 3,重写computeScroll()方法。

//第三步
    @Override
    public void computeScroll() {
        //super.computeScroll();
        if (mViewDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }



4,处理ViewDragHelper.Callback()回调,重写一些重要的方法:

@Override
        public boolean tryCaptureView(View child, int pointerId) {
            //开始滑动检测的条件,返回true开始检测,即指定跟随手指滑动的View
            return child == mMainView;
        }
@Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            return left;//通常只要这样写MainView在水平方向就能滑动了
        }

        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            return 0;//默认返回0,不能滑动。
        }



以上是全部步骤。我直接上代码,是我自定义的DrawerLayout侧滑菜单。

package com.example.songbinwang.littledemo.view;

import android.content.Context;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;

/**
 * Created by songbinwang on 2016/6/2.
 */
public class MyDrawerLayout extends FrameLayout {

    View mMenuView, mMainView;
    ViewDragHelper mViewDragHelper;
    private int mMenuWidth;

    //第四步
    ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {

        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            //开始滑动检测的条件,返回true开始检测
            return child == mMainView;
        }

        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            //通常只要return left就可以实现滑动。
            //return left;
            //这里我们复杂一点,控制其滑动的范围为(leftPadding, leftPadding+mMenuView.Width)
            int newLeft = Math.min(Math.max(getPaddingLeft(), left), getPaddingLeft() + mMenuWidth);
            return newLeft;
        }

        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            return 0;
        }

        /**
         * 用户手指离开时回调。
         * @param releasedChild
         * @param xvel
         * @param yvel
         */
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            if (mMainView.getLeft() > mMenuWidth / 2) {
                //滑到一半时展开菜单
                mViewDragHelper.smoothSlideViewTo(mMainView, mMenuWidth, 0);
                ViewCompat.postInvalidateOnAnimation(MyDrawerLayout.this);
            } else {
                //未滑到一半时,关闭菜单
                mViewDragHelper.smoothSlideViewTo(mMainView, 0, 0);
                ViewCompat.postInvalidateOnAnimation(MyDrawerLayout.this);
            }
        }

        /**
         * 用户触摸到MainView时回调
         * @param capturedChild
         * @param activePointerId
         */
        @Override
        public void onViewCaptured(View capturedChild, int activePointerId) {
            super.onViewCaptured(capturedChild, activePointerId);
        }

        /**
         * 滑动状态改变时调用
         * @param state
         */
        @Override
        public void onViewDragStateChanged(int state) {
            super.onViewDragStateChanged(state);
        }

        /**
         * 在位置改变时回调,通常用于滑动时进行缩放效果
         * @param changedView
         * @param left
         * @param top
         * @param dx
         * @param dy
         */
        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            super.onViewPositionChanged(changedView, left, top, dx, dy);
        }
    };


    public MyDrawerLayout(Context context) {
        super(context);
        initView();
    }

    public MyDrawerLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

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

    public MyDrawerLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initView();
    }

    //第一步
    private void initView() {
        mViewDragHelper = ViewDragHelper.create(this, mCallback);
    }

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

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

    //第二步
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mViewDragHelper.shouldInterceptTouchEvent(ev);
    }

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

    //第三步
    @Override
    public void computeScroll() {
        //super.computeScroll();
        if (mViewDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }
}



代码的引用:

<?xml version="1.0" encoding="utf-8"?>
<com.example.songbinwang.littledemo.view.MyDrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:id="@+id/menu"
        android:layout_width="200dp"
        android:layout_height="match_parent"
        android:background="@android:color/holo_red_light"></FrameLayout>

    <FrameLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/holo_blue_light"></FrameLayout>
</com.example.songbinwang.littledemo.view.MyDrawerLayout>