android进阶篇之View——基础篇(View移动)

移动View的位置的方法大总结和详细分析

1:scrollTo,scrollBy

scrollTo:绝对位置滑动
scrollBy:相对位置滑动

 //生硬的滑动
((View) getParent()).scrollTo(-100, -100);
((View) getParent()).scrollBy(-100, -100); 
linear.scrollBy((int) getResources().getDimension(R.dimen.px_500), (int) getResources().getDimension(R.dimen.px_500));

<resources>
    <dimen name="px_500">-500pxdimen>
resources>

注意:

1:站在子view的角度(也就是你想移动子view),直接用scrollTo,scrollBy是不行的,应该调用父控件的scrollTo,scrollBy;站在父view的角度(也就是你想移动父view中的子view),直接用scrollTo,scrollBy是可以的。总之,scrollTo,scrollBy移动的是View内的内容;

2:向右移动和向下移动,都为负值;

3:x、y、left、top、right、bottom的值是不会变的。

4:它只是内容区域的移动,本身view是不移动的,但是内容区域变了(如果超出自己的区域 就显示不出来)


与Scroller结合实现弹性滑动(缓慢滑动):

package com.example.viewmovedaemon.view;

import android.content.Context;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.widget.LinearLayout;
import android.widget.Scroller;

/**
 * Created by yuanpk on 2017/11/29.
 */

public class CustomLinearLayout extends LinearLayout {

    private Scroller scroller;

    public CustomLinearLayout(Context context) {
        super(context);

    }

    public CustomLinearLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        scroller = new Scroller(context);



    }

    //缓慢滚动到指定位置,调用该方法即可
    public void smoothScrollTo(int destX, int destY, int duration) {


        int scrollX = getScrollX();
        int deltaX = destX - scrollX;

        int scrollY = getScrollY();
        int deltaY = destY - scrollY;

        scroller.startScroll(scrollX, 0, deltaX, deltaY, duration * 1000);
        invalidate();

    }


    @Override
    public void computeScroll() {
        if (scroller.computeScrollOffset()) {
            scrollTo(scroller.getCurrX(), scroller.getCurrY());
            postInvalidate();
        }
    }
}

拿到该自定义控件后,直接调用该方法,该自定义控件中的子view就实现弹性滑动啦:

 linear.smoothScrollTo(-500, -500, 9);

2:动画

(1)属性动画

这样:

 //渐进匀速的滑动     
 /* AnimatorSet animatorSet = new AnimatorSet();
        animatorSet.playTogether(
                ObjectAnimator.ofFloat(button, "translationX", 0, 100).setDuration(6 * 1000),
                ObjectAnimator.ofFloat(button, "translationY", 0, 100).setDuration(6 * 1000)
        );
        animatorSet.start();*/

 //生硬的滑动       
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(
    ObjectAnimator.ofFloat(this, "translationX", 100),
    ObjectAnimator.ofFloat(this, "translationY", 100)
);
animatorSet.start();

或者这样:

//渐进匀速的滑动     
 ObjectAnimator.ofFloat(button, "translationX", 0, 500).setDuration(6 * 1000).start();
        ObjectAnimator.ofFloat(button, "translationY", 0, 500).setDuration(6 * 1000).start();

或者这样:

   //生硬的滑动
   button.setTranslationX(300);
   button.setTranslationY(300);

注意:
移动之后,x、y的值改变了,但是left、top、right、bottom值没有改变。
这边也许有疑问,x和left为什么不一样?
查看getX()方法的源码:

getX() {
    return mLeft + getTranslationX();
}

(2)位移动画

  //渐进匀速的滑动
  TranslateAnimation anim = new TranslateAnimation(0, 500, 0, 500);
        anim.setFillAfter(true);
        anim.setDuration(2 * 1000);
        button.startAnimation(anim);

注意:
位移动画过后,x、y、left、top、right、bottom都没有变。
这就是为什么位移动画过后,View的点击事件没有效果的原因,因为View的属性没有变。这也是属性动画应运而生的原因之一。

3:setLayoutParams

 //生硬的滑动
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) getLayoutParams();
lp.leftMargin = getLeft() + 100;
lp.topMargin = getTop() + 100;
setLayoutParams(lp);

注意:

1:此方法同样也是完完全全移动View的位置。
2:和layout中第2条注意一样。

4:layout

 //生硬的滑动
  button.layout(button.getLeft() + 300, button.getTop() + 100, button.getRight() + 300,  button.getBottom() + 100);
        //和下面的方法一个道理
        /*button.setLeft(button.getLeft() + 300);
        button.setRight(button.getRight() + 300);
        button.setTop(button.getTop() + 100);
        button.setBottom(button.getBottom() + 100);*/

注意:

1:此方法是完完全全移动View的位置,View的x、y、left、top、right、bottom都会相应的增加对应的px;
2:该方法若在Activity中,使用的话,应在 public void onWindowFocusChanged(boolean hasFocus)方法中或者延时几秒钟在使用该方法,否则不生效。具体原因参考,参考 android进阶篇之View——基础篇

5:offsetLeftAndRight,offsetTopAndBottom

 //生硬的滑动
offsetLeftAndRight(100);
offsetTopAndBottom(100);

注意:

1:此方法和layout一样,都是完完全全移动View的位置。
2:和layout中第2条注意一样。

各种滑动方式的对比

  • scrollTo,scrollBy:操作简单,适合对View内容的滑动;view内容的点击事件,也会跟随相应滑动。
  • 动画:操作简单,属性动画:也是很好的移动方案,移动之后,x、y的值改变了,但是left、top、right、bottom值没有改变。 (在不考虑兼容3.0以下的版本情况下);平移动画:主要适用于没有交互View和实现复杂的动画效果。
  • 改变布局参数:setLayoutParams,layout,offsetLeftAndRight,offsetTopAndBottom 适用于有交互的View。因为他们是完全移动地 View。View的x、y、left、top、right、bottom都会相应的增加对应的px。

源码git地址


相关博客:

View的三种移动方式

android view的多种移动方式(测试集合)

移动View的位置的几种方法

Android View移动的六种方法小结


在此多说一些题外话:

RequestLayout() , Invalidate() , layout()之间的区别

  • RequestLayout():

    • 控件会重新执行 onMesure() onLayout() 。当我们动态移动一个View的位置,或者特别是View的大小、形状发生了变化的时候,我们可以在view中调用这个方法。
    • 比如 ScrollView中有LinearLaout ,LinearLayout里面有纵向排列的ImageView和TextView,那么假如ImageView的长宽发生了变化,而要立即在手机上显示这个变化的话,就可调用 imageView.requestLayout();这样的话ScrollView 会重新执行onMesure()这个方法会确定控件的大小然后在确定出自己的宽高,最后在执行onLayout(),这个方法是对所有的子控件进行定位的
  • Invalidate():

    • 自定义View 的时候,会重新执行onDraw()方法。
  • layout():

    • 对控件进行重新定位执行onLayout()这个方法。

总结:比如说它的LayoutParams发生了改变,需要父布局对其进行重新测量、布局、绘制这三个流程,往往使用requestLayout。而invalidate则是刷新当前View,使当前View进行重绘,不会进行测量、布局流程,因此如果View只需要重绘而不需要测量,布局的时候,使用invalidate方法往往比requestLayout方法更高效。

参考文章:

Android View 深度分析requestLayout、invalidate与postInvalidate


滑动隐藏相关:

很不错
ByeBurger

也很不错
LBehavior

顶部标题栏随滑动时的渐变隐藏和渐变显示

Android手把手教你实现滑动隐藏(GeastureDetector使用)

你可能感兴趣的:(android进阶篇之View——基础篇(View移动))