Android笔记 View 的滑动方式(三)

1、layout方法:

视图坐标方式

public class DragView1 extends View {

    private int lastX;
    private int lastY;

    public DragView1(Context context) {
        super(context);
        ininView();
    }

    public DragView1(Context context, AttributeSet attrs) {
        super(context, attrs);
        ininView();
    }

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

    private void ininView() {
        // 给View设置背景颜色,便于观察
        setBackgroundColor(Color.BLUE);
    }

    // 视图坐标方式
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //最后视图的坐标
        int x = (int) event.getX();
        int y = (int) event.getY();
        Log.e("view","x:"+x+"y:"+y);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 记录触摸点坐标
                lastX = x;
                lastY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                // 计算偏移量
                int offsetX = x - lastX;
                int offsetY = y - lastY;
                // 在当前left、top、right、bottom的基础上加上偏移量
                layout(getLeft() + offsetX,
                        getTop() + offsetY,
                        getRight() + offsetX,
                        getBottom() + offsetY);
//                        offsetLeftAndRight(offsetX);
//                        offsetTopAndBottom(offsetY);
                break;
        }
        return true;
    }
}

绝对坐标方式:使用绝对坐标系,在每次执行完 ACTION_MOVE 的逻辑后,一定要重新设置初始坐标,这样才能准确地获取偏移量(一定要注意),原因如下图所示:




在这里是通过调用 layout() 方法来重新设置 View 显示的位置,而 layout() 方法是设置相对于父容器的位置,采用的是视图坐标系,如采用Android坐标系获取坐标位置(getRawX()/getRawY()),获取到的坐标位置是相对于屏幕左上角的点(就是说即使控件的宽高都是填充父布局,纵坐标的计算会算上状态栏的高度),故需要一个变量来记录上次移动的位置。

public class DragView2 extends View {

    private int lastX;
    private int lastY;

    public DragView2(Context context) {
        super(context);
        ininView();
    }

    public DragView2(Context context, AttributeSet attrs) {
        super(context, attrs);
        ininView();
    }

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

    private void ininView() {
        setBackgroundColor(Color.BLUE);
    }

    // 绝对坐标方式
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int rawX = (int) (event.getRawX());
        int rawY = (int) (event.getRawY());
        Log.e("view","rawX:"+rawX+"rawY:"+rawY);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 记录触摸点坐标
                lastX = rawX;
                lastY = rawY;
                break;
            case MotionEvent.ACTION_MOVE:
                // 计算偏移量
                int offsetX = rawX - lastX;
                int offsetY = rawY - lastY;
                // 在当前left、top、right、bottom的基础上加上偏移量
                layout(getLeft() + offsetX,
                        getTop() + offsetY,
                        getRight() + offsetX,
                        getBottom() + offsetY);
                // 重新设置初始坐标
                lastX = rawX;
                lastY = rawY;
                break;
        }
        return true;
    }
}
每次移动后,View 都会调用Layout方法来对自己重新布局,从而达到移动View的效果

2、LayoutParams
        可通过 getLayoutParams()来获取一个View 的LayoutParams,采用 getLayoutParams()方法获取LayoutParams时,需要根据View所在父布局的类型来设置不同的类型,如这里将View放在LinearLayout中,就可用LinearLayout.LayoutParams,如在RelativeLayout中,则采用RelativeLayout.LayoutParams,这一切的前提是View必须有一个父布局,不然系统无法获取LayoutParams


public class DragView3 extends View {

    private int lastX;
    private int lastY;

    public DragView3(Context context) {
        super(context);
        ininView();
    }

    public DragView3(Context context, AttributeSet attrs) {
        super(context, attrs);
        ininView();
    }

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

    private void ininView() {
        setBackgroundColor(Color.BLUE);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 记录触摸点坐标
                lastX = (int) event.getX();
                lastY = (int) event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                // 计算偏移量
                int offsetX = x - lastX;
                int offsetY = y - lastY;
                //通过getLayoutParams()获取View的布局参数
                ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
//                LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();
                layoutParams.leftMargin = getLeft() + offsetX;
                layoutParams.topMargin = getTop() + offsetY;
                //动态的修改布局的位置参数
                setLayoutParams(layoutParams);
                //requestLayout();也可以
                break;
        }
        return true;
    }
}
 通过改变LayoutParams来改变一个View的位置时,通常改变的是这个View的Margin属性,所以除了使用布局的LayoutParams之外,还可以使用ViewGroup.MarginLayoutParams来实现,而无需考虑父布局的类型,他们的本质是一样的

3、scrollTo和scrollBy(不能在View中使用,在View所在的ViewGroup中使用,子View的平移都是瞬时发生的),移动的是屏幕,改变的是可视区域在整个视图中的显示位置,并不会改变View 在父布局中的位置
    
 scrollTo和scrollBy移动的是View的content,即让View的内容移动如果在ViewGroup中使用 scrollTo、scrollBy方法,移动的将是所有子View但如果在View中使用,则移动的是View的内容,如TextView,content就是他的文本,ImageView,content就是他的drawable对象,所以不能在View 中使用这两个方法来拖动这个View ,而应该在View 所在的 ViewGroup 中使用 scrollBy 方法,移动它的子View
((View)getParent()).scrollBy(offsetX,offsetY);
区别:
    scrollTo(x,y):表示移动到一个具体的坐标点
    scrollBy(dx,dy):表示移动的增量为dx、dy


public class DragView4 extends View {

    private int lastX;
    private int lastY;

    public DragView4(Context context) {
        super(context);
        ininView();
    }

    public DragView4(Context context, AttributeSet attrs) {
        super(context, attrs);
        ininView();
    }

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

    private void ininView() {
        setBackgroundColor(Color.BLUE);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //最终View 的左上角坐标
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                //记录按下的点的坐标
                lastX = (int) event.getX();
                lastY = (int) event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                //计算偏移量
                int offsetX = x - lastX;
                int offsetY = y - lastY;
                //通过parentView 调用 scrollBy 来移动内容子 View
                ((View) getParent()).scrollBy(-offsetX, -offsetY);
                break;
        }
        return true;
    }
}
5、Scroller(通过Scroller类可以实现平滑移动的效果)
a、新建 Scroller 对象
b、重写 computeScroll 方法,不断的调用 scrollTo 方法 重绘 View
c、调用 startScroll 开启滑动

注:因为通过 Scroll 类来实现弹性滑动,其核心方法 computeScroll 本质上是通过不断的调用 scrollTo方法来实现的,故其滑动的效果和 scrollTo\scrollBy 类似,均是移动的 View 的内容或 ViewGroup 的所有子 View

computeScroll  无法手动调用,必须经过 invalidate → draw → computeScroll 来间接调用,实现滑动逻辑后,必须手动调用一次 invalidate 



public class DragView5 extends View {

    private int lastX;
    private int lastY;
    private Scroller mScroller;

    public DragView5(Context context) {
        super(context);
        ininView(context);
    }

    public DragView5(Context context, AttributeSet attrs) {
        super(context, attrs);
        ininView(context);
    }

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

    private void ininView(Context context) {
        setBackgroundColor(Color.BLUE);
        // 初始化Scroller
        mScroller = new Scroller(context);
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        // 判断Scroller是否执行完毕
        if (mScroller.computeScrollOffset()) {
            ((View) getParent()).scrollTo(
                    mScroller.getCurrX(),
                    mScroller.getCurrY());
            // 通过重绘来不断调用computeScroll
            invalidate();
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                lastX = (int) event.getX();
                lastY = (int) event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                int offsetX = x - lastX;
                int offsetY = y - lastY;
                ((View) getParent()).scrollBy(-offsetX, -offsetY);
                break;
            case MotionEvent.ACTION_UP:
                // 手指离开时,执行滑动过程
                View viewGroup = ((View) getParent());
                mScroller.startScroll(
                        viewGroup.getScrollX(),
                        viewGroup.getScrollY(),
                        -viewGroup.getScrollX(),
                        -viewGroup.getScrollY());
                invalidate();// 手动调用 draw 方法
                break;
        }
        return true;
    }
}

猜你喜欢

转载自blog.csdn.net/daxiong25/article/details/80253765
今日推荐