View的移动方式
View的移动方式
常见的View的移动方式有setX()/setY()、setTranslationX()/setTranslationY()、动画、scrollTo/scrollBy等。因为View的属性比较多。view使用不同的移动方式,受影响的是那些变量属性已经方法。以及配合使用时会产生何种效果。比如view的getX/Y、getSrollX/Y、getTranslationX/Y、getLeft/top/right/bottom、点击事件触发区域等等,是否会受到影响改变,由哪些所影响?
各个移动方式对属性的验证:
1、getX()、getY()
2、getScrollX() 、getScrollY()
3、getTranslationX() 、getTranslationY()
4、getLeft()、 getTop()、 getRight()、 getBottom()(座标位置是否改变)
5、点击事件触发区域是否改变
6、是否会影响同层级的其他view的位置
7、超过父View是否绘制
View座标系
view的位置大小由以下参数决定:
1.mLeft、mRight、mTop、mBottom 这四个参数相对于父view
2.mScrollX、mScrollY
3.translationX、translationY
4.getX()、getY()
- 获取高宽:
public final int getWidth() {
return mRight - mLeft;
}
public final int getHeight() {
return mBottom - mTop;
}
在Activity中布局完成后,我们可以通过View一些方法获取这些参数信息:
//left,top,right,bottom值的获取
int left = getLeft();
int top = getTop();
int right = getRight();
int bottom = getBottom();
public final int getLeft() {
return mLeft;
}
scrollTo()/scrollBy()
scrollTo()和scrollBy()都是View中的方法。其移动的本质都是View/ViewGroup中的内容。如果是一个TextView,则移动的是TextView显示的文本内容。如果是一个ViewGroup则移动的是其包含的childview。
-
scrollTo() : 指是的移动的绝对位置,如果位置没有变化,多次调用则不会起作用。
-
scrollBy() : 其本质依然是调用的scrollTo(),指的的移动当前位置的相对距离(每次都是先将当前的位置和设置的距离相加之和调用scrollTo(),这样如果你多次调用,你就会发现其每次都会移动一段距离,这是和scrollTo()的本质区别)
-
scrollTo()和scrollBy()移动改变的是mScrollX、mScrollY的值。
所以getX()/getY()不变。getScrollXY 会变。视觉上view的位置会改变但实质上是view的内容改变。所以view的点击事件还是在原位置。 -
获取mScrollX、mScrollY
public final int getScrollX() {
return mScrollX;
}
public final int getScrollY() {
return mScrollY;
}
setTranslationX/Y、setX/setY
- 在Android3.0以后加入了X、Y、TranslationX、TranslationY;(x,y)表示为View在ViewGroup左上角的x,y的值。translationX,translationY用于平移一个View。默认是都为0,在调用了View的setTranslationX()/setTranslationY()之后发生改变。通过setX()/setY()/setTranslationX()/setTranslationY()都可改变view的位置。(通过属性动画ObjectAnimator可使View的移动使得更为平滑)
//x,y,translationX,translationY参数的获取
int x = getX();
int y = getY();
int translationX = getTranslationX();
int translationY = getTranslationY();
public float getX() {
return mLeft + getTranslationX();
}
public void setX(float x) {
setTranslationX(x - mLeft);
}
public void setTranslationX(float translationX) {
if (translationX != getTranslationX()) {
invalidateViewProperty(true, false);
mRenderNode.setTranslationX(translationX);
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
public float getTranslationX() {
return mRenderNode.getTranslationX();
}
-
View根本就没有x、y的成员变量,不过是根据translationY和mTop的值计算出来的而已。所以:
getX getY 会变
getTranslationXY会变
点击事件的位置也变了但是不会超过父布局 -
系统对于mTop、mLeft等的修改,仅仅限于在layout方法内部,而layout方法我们都知道,是View在onMeasure测量之后,对自身以及子控件进行位置布局的。translationY开启的属性动画,并不会影响到mTop的值。mTop的值的意义是什么?是告诉父容器,我的控件的位置在哪儿。意义就是,父容器在onMeasure和onLayout的时候,是以mTop的值作为基准的。所以会超过边界到同级View的区域去(被覆盖或者覆盖别人)https://blog.csdn.net/cc_lova_wxf/article/details/72676830
-
执行setTranslationY,mTop等于0,translationY和y都等于300,并且能够在300的地方响应点击事件,这就说明了系统点击事件的范围判断是根据translationY来判断的,而不是通过mTop。
-
如果想要改变View对于父容器的影响,那么影响的是mLeft、mTop、width、height等属于父容器的属性(mLeft、mTop、width、height等都是父容器的属性,而不属于本View)。
-
如果想要改变View自身的影响,比如添加监听器,添加动画等等,那么影响的是translationX、translationY等属于自身的属性。
使用平移动画或者属性动画
android动画分三类:一是View 动画,又叫Tween动画,二是frame 动画(帧动画),又叫drawable 动画,三是属性动画,即property animation.
-
View动画,包名android.view.animation,基类为Animation,核心子类为TranslateAnimation,ScaleAnimation,AlphaAnimation,RotateAnimation及AnimationSet。
-
Property动画,包名android.animation,基类为Animator,核心子类为AnimatorSet,ValueAnimator,ObjectAnimator,TimeAnimator。
-
使用平移动画点击事件还是在原位置。(属性动画没有此问题)
-
使用平移动画如果setFillAfter位置保留 但是其他任何座标位置没有改变 再次点击从原位置重新开始移动
设置View的LayoutParams来移动View
LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) textView.getLayoutParams();
layoutParams.leftMargin = 50;
textView.requestLayout();
使用setLayoutParams和requestLayout如果多次执行性能很差。
layout()
如果你将滑动后的目标位置的座标传递给layout(),这样子就会把view的位置给重新布置了一下,在视觉上就是view的一个滑动的效果。
public class DragView extends View{
private int lastX;
private int lastY;
public DragView(Context context, AttributeSet attrs) {
super(context, attrs);
}
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 offX = x - lastX;
int offY = y - lastY;
//调用layout方法来重新放置它的位置
layout(getLeft()+offX, getTop()+offY,
getRight()+offX , getBottom()+offY);
break;
}
return true;
}
}