@TryLoveCatch
2022-05-17T10:30:37.000000Z
字数 5650
阅读 1789
Android知识体系
View 提供的获取坐标方法
MotionEvent 提供的获取坐标方法
mBtn.setOnTouchListener(new View.OnTouchListener() {
float tLastX;
float tLastY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "按下事件");
tLastX = event.getX();
tLastY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "移动事件");
int tOffsetX = Math.round(event.getX() - tLastX);
int tOffsetY = Math.round(event.getY() - tLastY);
v.layout(v.getLeft() + tOffsetX, v.getTop() + tOffsetY,
v.getRight() + tOffsetX, v.getBottom() + tOffsetY);
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "松开事件");
break;
}
return false;
}
});
当 ViewGroup
的位置被确定后,会调用 onLayout()
,在 onLayout()
中会遍历所有的子元素并调用子元素的 layout()
,layout()
是确定 View 本身在屏幕上显示的具体位置,即在代码中设置其成员变量 mLeft
,mTop
,mRight
,mBottom
的值,这几个值是在屏幕上构成矩形区域的四个坐标点,就是该 View 显示的位置,不过这里的具体位置都是相对与父视图的位置而言。
mBtn.setOnTouchListener(new View.OnTouchListener() {
float tLastX;
float tLastY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "按下事件");
tLastX = event.getX();
tLastY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "移动事件");
int tOffsetX = Math.round(event.getX() - tLastX);
int tOffsetY = Math.round(event.getY() - tLastY);
v.offsetLeftAndRight(tOffsetX);
v.offsetTopAndBottom(tOffsetY);
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "松开事件");
break;
}
return false;
}
});
修改原来的layout()
为offsetLeftAndRight()
和offsetTopAndBottom()
即可了。这是系统提供的对View上下、左右同时进行移动的API,效果与上相同。
mBtn.setOnTouchListener(new View.OnTouchListener() {
float tLastX;
float tLastY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "按下事件");
tLastX = event.getX();
tLastY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "移动事件");
int tOffsetX = Math.round(event.getX() - tLastX);
int tOffsetY = Math.round(event.getY() - tLastY);
ViewGroup.MarginLayoutParams tParams = (ViewGroup.MarginLayoutParams)v.getLayoutParams();
tParams.leftMargin = v.getLeft() + tOffsetX;
tParams.topMargin = v.getTop() + tOffsetY;
v.setLayoutParams(tParams);
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "松开事件");
break;
}
return false;
}
});
通过LayoutParams
来移动View,主要通过修改Margin
属性来实现的,所以View的getLeft()
或getRight()
就会改变。而这个Margin
属性,只有ViewGroup.MarginLayoutParams
及其子类才有这个属性,我们熟知的LinearLayout.LayoutParams
或者RelativeLayout.LayoutParams
都是继承了这个,所以可以使用,或者直接使用ViewGroup.MarginLayoutParams
mBtn.setOnTouchListener(new View.OnTouchListener() {
float tLastX;
float tLastY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
tLastX = event.getX();
tLastY = event.getY();
Log.e(TAG, "按下事件: " + tLastX + ", " + tLastY);
break;
case MotionEvent.ACTION_MOVE:
int tOffsetX = Math.round(event.getX() - tLastX);
int tOffsetY = Math.round(event.getY() - tLastY);
Log.e(TAG, "移动事件: " + tOffsetX + ", " + tOffsetY + ", " + (v.getTranslationX() + tOffsetX)
+ ", " + (v.getTranslationY() + tOffsetY));
v.setTranslationX(v.getTranslationX() + tOffsetX);
v.setTranslationY(v.getTranslationY() + tOffsetY);
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "松开事件");
Log.e(TAG, "left: " + mBtn.getLeft() + ", right: " + mBtn.getRight() + ", top: " + mBtn.getTop()
+ ", bottom: " + mBtn.getBottom());
break;
}
return false;
}
});
View的setTranslationY()
,会改变translationX和translationY的值,但是不会更改margin
的值,所以getLeft()
和getRight()
不会改变。所以,我们从这里开得出一个结论:View的位置是会受translationX
和translationY
影响的,另外,setTranslationX(0)
会恢复TranslationX的值。为了使View的移动显得更为平滑,因此可以使用View的属性动画来指定translationX和translationY。
mBtn.setOnTouchListener(new View.OnTouchListener() {
float tLastX;
float tLastY;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "按下事件");
tLastX = event.getX();
tLastY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "移动事件");
int tOffsetX = Math.round(event.getX() - tLastX);
int tOffsetY = Math.round(event.getY() - tLastY);
((View)(v.getParent())).scrollBy(-tOffsetX, -tOffsetY);//移动子view
// v.scrollBy(-tOffsetX, -tOffsetY);//移动按钮文字
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "松开事件");
break;
}
return false;
}
});
在 View
类中,有两个变量 mScrollX
和 mScrollY
,它们记录的是 View 的内容的偏移值。mScrollX
和 mScrollY
的默认值都是 0,即默认不偏移。假设我们令 mScrollX = 10
,那么该 View
的内容会相对于原来向左偏移 10px
。
我们一般不直接设置mScrollX
和 mScrollY
,可以通过一下两个方法来设置:
scrollTo()
:移动到具体的坐标点位置
scrollBy()
:在原有的位置基础上再移动一个偏移量
1、这两个方法会修改mScrollX
和 mScrollY
的值
2、这两个方法会触发onScrollChanged
回调
前面几个方法都是移动View自己本身,而这两个方法移动的都是View里面的内容,比如 textView 中移动的是文字,imagView 中移动的是图片;如果放在ViewGroup中使用,则移动的是ViewGroup里面所有的子View。
所以说,我们为了移动View,那我们就来移动View所在的ViewGroup,但是要注意的是,移动的偏移量要取反,为什么呢?这是因为本来是该View移动dx、dy,现在View保持不动,让ViewGroup移动,则根据相对运动原理,就相当于ViewGroup移动了-dx、-dy。
前面我们使用的不管是 scrollBy
还是 scrollTo
,移动其实都是在一瞬间完成的,只是因为我们的触摸动作不断触发,View 不断改变位置,造成了一个过度动画的假象。
假如我们有一个按钮,比如点击按钮就会把 View 往右移动 100 像素,那么你会发现此时的 View 是瞬间变换位置,并不是慢慢移动到指定位置。
而Scroller
可以根据需要移动的总距离,以及设置的移动时间,计算出每一次需要移动的距离,然后不断的进行移动,这样就实现了一个动画的效果。
1、View提供了分别提供了getLeft()、getRight、getRight()、getBottom()四个方法获取对于的信息。除此之外3.0之后View还提供了四个比较重要的位置参数信息,X、Y、translationX、translationY。View的宽高是有top、left、right、bottom参数决定的
而X,Y和translationX,和translationY则负责View位置的改变。
Android实现滑动的几种方法
Android Scroller完全解析,关于Scroller你所需知道的一切
Android Scroller大揭秘
Android中实现滑动的七种方式
[Android] 滑动 View 的原理及处理
Android View 滑动和弹性滑动读书笔记
实现View滑动的七种方法
深入理解Android View(一)
Android View 的滚动原理和 Scroller、VelocityTracker 类的使用