第三章 view的事件体系 ----- view滑动/拖动

Android view的滑动

先看下view完整代码如下:

public class DemoView extends View {

    private int lastX;
    private int lastY;
    private Scroller mScroller;
    public DemoView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public DemoView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mScroller = new Scroller(context);
    }

    public DemoView(Context context) {
        super(context);
        mScroller = new Scroller(context);
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()){
            ((View) getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
            postInvalidate();
        }
        super.computeScroll();
    }

    @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;
                int offsetY = y - lastY;
                //调用layout方法来重新放置它的位置
//                layout(getLeft() + offsetX, getTop() + offsetY,
//                        getRight() + offsetX, getBottom() + offsetY);
                //通过translation来实现
//                int translationX = (int) getTranslationX() + offsetX;
//                int translationY = (int) getTranslationY() + offsetY;
//                setTranslationX(translationX);
//                setTranslationY(translationY);
                //通过layout param 实现 需要注意:如果view在布局文件中设置了layout_above、center等类似属性,则会出现问题
//                RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
//                layoutParams.leftMargin = getLeft() + offsetX;
//                layoutParams.topMargin = getTop() + offsetY;
//                setLayoutParams(layoutParams);
                //通过MarginLayoutParams
//                ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
//               layoutParams.leftMargin = getLeft() + offsetX;    //这样也可以
//               layoutParams.topMargin = getTop() + offsetY;
//                layoutParams.leftMargin += offsetX;
//                layoutParams.topMargin += offsetY;
//                setLayoutParams(layoutParams);
                /*通过scrollBy操作,getParent是因为scrollBy是对view的内容进行移动,而不是对view本身移动
                所以,当前view的移动通过获取父布局,对父布局进行scrollBy操作,当前view就是父布局的内容
                note:scrollBy会对view的所有内容进行移动,如果父布局中还有其他的view(button啊 TextView啊)
                则在拖动该view时其他view也会一起移动*/
                ((View) getParent()).scrollBy(-offsetX,- offsetY);
                break;
        }
        return true;
    }
    public void smoothScrollTo(int destX,int destY){
        int scrollX = getScrollX();
        int scrollY = getScrollY();
        int deltaX = destX - scrollX;
        int deltaY = destY - scrollY;
        //1000秒内滑向destX destY
        Log.d("xj", "smoothScrollTo: ");
        mScroller.startScroll(scrollX,scrollY,deltaX,deltaY,1000);
        postInvalidate();// invalidate();
    }
}

一、通过layout方法实现

  • case MotionEvent.ACTION_MOVE: 下的代码如下:
    //调用layout方法来重新放置它的位置
    layout(getLeft() + offsetX, getTop() + offsetY,
          getRight() + offsetX, getBottom() + offsetY);

二、offsetLeftAndRight()offsetTopAndBottom()

  • case MotionEvent.ACTION_MOVE: 下的代码如下:
    //调用offset方法来重新放置它的位置,
    offsetLeftAndRight(offX);
    offsetTopAndBottom(offY);

三、通过translation(动画的方式)来实现

  • case MotionEvent.ACTION_MOVE: 下的代码如下:
    //通过translation来实现
    int translationX = (int) getTranslationX() + offsetX;
    int translationY = (int) getTranslationY() + offsetY;
    setTranslationX(translationX);
    setTranslationY(translationY);
  • 不随手指移动,仅实现view的滑动,使用属性动画(若要兼容3.0以下版本,需要使用开源动画库nineoldandroids)动画开源库
    ObjectAnimator.ofFloat(demoview,"translationX",0,300).setDuration(1000).start();
    -使用view动画滑动,R.anim.translate为动画资源文件:
 demoview.setAnimation(AnimationUtils.loadAnimation(this, R.anim.translate));

四、通过LayoutParams来实现

如果view在布局文件中设置了layout_above、center(因文中使用了相对布局)等类似属性,则会出现问题,不会正常拖动

  • case MotionEvent.ACTION_MOVE: 下的代码如下:
/*通过LayoutParams来实现,这里父布局使用的是RelativeLayout,因此使用RelativeLayout.LayoutParams,若父布局是LinearLayout,则使用RelativeLayout.LayoutParams*/
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) getLayoutParams();
layoutParams.leftMargin = getLeft() + offsetX;
layoutParams.topMargin = getTop() + offsetY;
setLayoutParams(layoutParams); 

还可以使用通过MarginLayoutParams实现,代码替换为:

ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
//下面两种写法:使用一种即可
//方法一(注释中)
//layoutParams.leftMargin = getLeft() + offsetX;
//layoutParams.topMargin = getTop() + offsetY; 
//方法二
layoutParams.leftMargin += offsetX;
layoutParams.topMargin += offsetY;
setLayoutParams(layoutParams);

五、通过scollTo与scollBy(动画的方式)来实现

sceollTo(x,y) 绝对滑动,传入的是移动的终点坐标
scrollBy(dx,dy) 相对滑动,传入的是移动的增量,
通过scrollBy传入的值应该是你需要的那个增量的相反数!
通过scrollBy操作,getParent是因为scrollBy是对view的内容进行移动,而不是对view本身移动,所以,当前view的移动通过获取父布局,对父布局进行scrollBy操作,当前view就是父布局的内容


note:scrollBy会对view的所有内容进行移动,如果父布局中还有其他的view(button啊 TextView啊,则在拖动该view时其他view也会一起移动

  • case MotionEvent.ACTION_MOVE: 下的代码如下:
    //通过scrollBy来实现
    ((View) getParent()).scrollBy(-offsetX,- offsetY); 

六、通过Scroller(弹性滑动)来实现

这个是滑动效果,不是随着手指拖动效果
- 初始化Scroller

public DemoView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mScroller = new Scroller(context);
    }
  • 重写computeScroll()方法,
@Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()){
            ((View) getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
           // postInvalidate();//这个也可以,效果上没啥区别
            invalidate();
        }
    }
  • 调用Scroller.startScroll()方法。写一个smoothScrollTo()方法来调用Scroller.startScroll()方法,实现滑动
public void smoothScrollTo(int destX,int destY){
        int scrollX = getScrollX();
        int scrollY = getScrollY();
        int deltaX = destX - scrollX;
        int deltaY = destY - scrollY;
        //2000秒内滑动到指定点
        mScroller.startScroll(scrollX,scrollY,deltaX,deltaY,2000
       //postInvalidate();//这个也可以,效果上没啥区别
        invalidate();
    }
  • 在activity中调用该方法,注意:该方法移动的是demoView所在父布局中的所用内容
demoView.smoothScrollTo(-500,-300);
  • computeScrollOffset方法判断是否完成了整个滑动,返回true,则没有完成,否则完成滑动。
  • 必须要用invalidate方法刷新,因为computeScroll方法不会自动调用,它是在view的draw方法中被调用的,所以必须使用invalidate刷新。
  • 在startScroll中,偏移量跟使用scrollBy方法中的偏移量用法是一样的,即也必须填写你实际想要移动距离的相反数。也就是你实际想让它偏移一个正值,这里就填写它相应的负值,如果想偏移一个负值,这里就填写相应的正值!
  • 通过Scroller实现弹性滑动的代码基本是固定的,实现原理:
    startScroll方法下面的invalidate方法会让view进行重绘,view进行重绘时draw方法又会去调用computeScroll方法,该方法在view中是一个空实现,需要我们自己实现。computeScroll方法会向Scroller获取当前的scrollX和scrollY,然后通过scrollTo方法滑动;接着又调用了invalidate方法重绘,重复之前的动作,直到整个滑动结束。

总结

  • 其中一二三四五都是view随手指可以移动
  • scrollTo 和scrollBy 操作简单,适合对view内容滑动
  • 动画:适用于没有交互的view和实现复杂的动画效果
  • 改变布局参数:操作稍微复杂,适用于有交互的view
  • 参考文章,书籍,Android开发艺术探索

猜你喜欢

转载自blog.csdn.net/xj531/article/details/80264987