5大方法完美解决View的移动:layout(),offsetLeftAndRight(),MarginLayoutParams,动画,scrollTo

借鉴自Android进阶之光,刘望舒的好书!

此外再介绍各个阶段应该看的书:

入门 第一行代码 

中阶 Android群英传(偏UI) Android进阶之光(偏业务)

高阶 Android开发艺术探索 (还有一本源代码解析好像挺不错的,不过我没看过)


(这里采用自定义控件的方式,接下来我会写更具体的应用场景)

自定义控件继承自View,里面就一个简单的方法,用来监听手势的,然后在布局文件中引用这个自定义控件就可以(后面我会给出完整代码)

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;

                //1.layout
                //getTop()就是顶部距离父容器顶部,
                //getTop()就是底部距离父容器顶部
//                layout(getLeft() + offsetX, getTop() + offsetY,
//                        getRight() + offsetX, getBottom() + offsetY);

                //2.offsetLeftAndRight()
                //类似方法1
//                offsetLeftAndRight(offsetX);
//                offsetTopAndBottom(offsetY);

                //3.改变布局参数(1)LayoutParams(根据不同父布局选择),这里是ConstraintLayout
                //(2)ViewGroup的MarginLayoutParams
                //测试发现用不了
//                ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams)
//                        getLayoutParams();
//                ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams)
//                        getLayoutParams();
//                layoutParams.leftMargin = getLeft() + offsetX;
//                layoutParams.topMargin = getTop() + offsetY;
//                setLayoutParams(layoutParams);

                //4.属性动画(相比普通动画,是真是移动了位置,而不是表象)
                //不用在这里定义

                //5.scrollTo,scrollBy
//                ((View)getParent()).scrollBy(-offsetX, -offsetY);


                break;
        }
        return true;
    }
其中1235方法这里用就可以了,4动画是这样用的。

1.res下新建anim文件夹,新建translate.xml,以下是内容

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true">
    <!--fillAfter让动画执行好后不回去-->

    <!--duration 时间;fromXDelta 起始X位置;toXDelta 终点X位置-->
    <translate
        android:duration="1000"
        android:fromXDelta="0"
        android:toXDelta="300" />

</set>
2.普通动画是需要这样的xml文件的

@BindView(R.id.slidingView)
    SlidingView mSlidingView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_translate);
        ButterKnife.bind(this);

        //4(1).普通动画
//        mSlidingView.setAnimation(AnimationUtils.loadAnimation(this, R.anim.translate));
        //4(2).属性动画
//        ObjectAnimator.ofFloat(mSlidingView, "translationX", 0, 300).setDuration(1000).start();
3.而属性动画不用这样,一句话就可以了。


还有一个问题:为什么

               //5.scrollTo,scrollBy
//                ((View)getParent()).scrollBy(-offsetX, -offsetY);
scroller的参数要反着来,可以把所有的内容看成是放在画布上的,可能这个画布很大,有些内容你手机看不到

5大方法完美解决View的移动:layout(),offsetLeftAndRight(),MarginLayoutParams,动画,scrollTo

这个时候你移动的,是画布,而不是那个控件,所以自然要反着来。


以下是自定义控件所有代码

public class SlidingView extends View {
    public SlidingView(Context context) {
        super(context);
    }

    public SlidingView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public SlidingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private int lastX;
    private int lastY;

    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;

                //1.layout
                //getTop()就是顶部距离父容器顶部,
                //getTop()就是底部距离父容器顶部
//                layout(getLeft() + offsetX, getTop() + offsetY,
//                        getRight() + offsetX, getBottom() + offsetY);

                //2.offsetLeftAndRight()
                //类似方法1
//                offsetLeftAndRight(offsetX);
//                offsetTopAndBottom(offsetY);

                //3.改变布局参数(1)LayoutParams(根据不同父布局选择),这里是ConstraintLayout
                //(2)ViewGroup的MarginLayoutParams
                //测试发现用不了
//                ConstraintLayout.LayoutParams layoutParams = (ConstraintLayout.LayoutParams)
//                        getLayoutParams();
//                ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams)
//                        getLayoutParams();
//                layoutParams.leftMargin = getLeft() + offsetX;
//                layoutParams.topMargin = getTop() + offsetY;
//                setLayoutParams(layoutParams);

                //4.属性动画(相比普通动画,是真是移动了位置,而不是表象)
                //不用在这里定义

                //5.scrollTo,scrollBy
//                ((View)getParent()).scrollBy(-offsetX, -offsetY);

                //6.Scroller

                break;
        }
        return true;
    }
}
自定义控件的引用

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.test.ViewTranslateActivity">

    <com.example.test.widget.SlidingView
        android:id="@+id/slidingView"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:background="@color/colorPrimaryDark"/>

</android.support.constraint.ConstraintLayout>