移动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));
<?xml version="1.0" encoding="utf-8"?>
<resources>
<dimen name="px_500">-500px</dimen>
</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地址
相关博客:
在此多说一些题外话:
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