欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-05-05 10:25:43
...

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开发艺术探索