Android基础之滑动

android android基础 滑动

坐标系相关

android_touch_coordinate_system.pngView 提供的获取坐标方法

getTop:获取到的是 View 自身的顶边到父布局顶边的距离

getLeft:获取到的是 View 自身的左边到父布局左边的距离

getRight:获取到的是 View 自身的右边到父布局右边的距离

getBottom:获取到的是 View 自身的底边到父布局底边的距离

3.0之后增加的:

getX:view的虚拟x坐标,所谓虚拟就是原本相对于parent的getLeft() + getTranslationX

getY:view的虚拟y坐标,所谓虚拟就是原本相对于parent的getLTop() + getTranslationY

getTranslationX:View在x轴上的偏移量

getTranslationY:View在y轴上的偏移量

还有两个数值,我们也需要了解

mScrollX:View 内容x轴上的偏移值

mScrollY:View 内容y轴上的偏移值

MotionEvent 提供的获取坐标方法

getX : 获取触摸点距离当前控件左边的距离

getY : 获取触摸点距离当前控件顶边的距离

getRawX : 获取触摸点距离屏幕左边的距离

getRawY : 获取触摸点距离屏幕顶边的距离

layout()

mBtn.setOnTouchListener(newView.OnTouchListener(){

floattLastX;

floattLastY;

@Override

publicbooleanonTouch(Viewv,MotionEventevent){

switch(event.getAction()){

caseMotionEvent.ACTION_DOWN:

Log.e(TAG,"按下事件");

tLastX=event.getX();

tLastY=event.getY();

break;

caseMotionEvent.ACTION_MOVE:

Log.e(TAG,"移动事件");

inttOffsetX=Math.round(event.getX()-tLastX);

inttOffsetY=Math.round(event.getY()-tLastY);

v.layout(v.getLeft()+tOffsetX,v.getTop()+tOffsetY,

v.getRight()+tOffsetX,v.getBottom()+tOffsetY);

break;

caseMotionEvent.ACTION_UP:

Log.e(TAG,"松开事件");

break;

}

returnfalse;

}

});

当 ViewGroup 的位置被确定后,会调用 onLayout() ,在 onLayout() 中会遍历所有的子元素并调用子元素的 layout(),layout()是确定 View 本身在屏幕上显示的具体位置,即在代码中设置其成员变量 mLeft,mTop,mRight,mBottom 的值,这几个值是在屏幕上构成矩形区域的四个坐标点,就是该 View 显示的位置,不过这里的具体位置都是相对与父视图的位置而言。

offsetLeftAndRight()和offsetTopAndBottom()

mBtn.setOnTouchListener(newView.OnTouchListener(){

floattLastX;

floattLastY;

@Override

publicbooleanonTouch(Viewv,MotionEventevent){

switch(event.getAction()){

caseMotionEvent.ACTION_DOWN:

Log.e(TAG,"按下事件");

tLastX=event.getX();

tLastY=event.getY();

break;

caseMotionEvent.ACTION_MOVE:

Log.e(TAG,"移动事件");

inttOffsetX=Math.round(event.getX()-tLastX);

inttOffsetY=Math.round(event.getY()-tLastY);

v.offsetLeftAndRight(tOffsetX);

v.offsetTopAndBottom(tOffsetY);

break;

caseMotionEvent.ACTION_UP:

Log.e(TAG,"松开事件");

break;

}

returnfalse;

}

});

修改原来的layout()为offsetLeftAndRight()和offsetTopAndBottom()即可了。这是系统提供的对View上下、左右同时进行移动的API,效果与上相同。

LayoutParams

mBtn.setOnTouchListener(newView.OnTouchListener(){

floattLastX;

floattLastY;

@Override

publicbooleanonTouch(Viewv,MotionEventevent){

switch(event.getAction()){

caseMotionEvent.ACTION_DOWN:

Log.e(TAG,"按下事件");

tLastX=event.getX();

tLastY=event.getY();

break;

caseMotionEvent.ACTION_MOVE:

Log.e(TAG,"移动事件");

inttOffsetX=Math.round(event.getX()-tLastX);

inttOffsetY=Math.round(event.getY()-tLastY);

ViewGroup.MarginLayoutParamstParams=(ViewGroup.MarginLayoutParams)v.getLayoutParams();

tParams.leftMargin=v.getLeft()+tOffsetX;

tParams.topMargin=v.getTop()+tOffsetY;

v.setLayoutParams(tParams);

break;

caseMotionEvent.ACTION_UP:

Log.e(TAG,"松开事件");

break;

}

returnfalse;

}

});

通过LayoutParams来移动View,主要通过修改Margin属性来实现的,所以View的getLeft()或getRight()就会改变。而这个Margin属性,只有ViewGroup.MarginLayoutParams及其子类才有这个属性,我们熟知的LinearLayout.LayoutParams或者RelativeLayout.LayoutParams都是继承了这个,所以可以使用,或者直接使用ViewGroup.MarginLayoutParams

setTranslationY和setTranslationX()

mBtn.setOnTouchListener(newView.OnTouchListener(){

floattLastX;

floattLastY;

@Override

publicbooleanonTouch(Viewv,MotionEventevent){

switch(event.getAction()){

caseMotionEvent.ACTION_DOWN:

tLastX=event.getX();

tLastY=event.getY();

Log.e(TAG,"按下事件: "+tLastX+", "+tLastY);

break;

caseMotionEvent.ACTION_MOVE:

inttOffsetX=Math.round(event.getX()-tLastX);

inttOffsetY=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;

caseMotionEvent.ACTION_UP:

Log.e(TAG,"松开事件");

Log.e(TAG,"left: "+mBtn.getLeft()+", right: "+mBtn.getRight()+", top: "+mBtn.getTop()

+", bottom: "+mBtn.getBottom());

break;

}

returnfalse;

}

});

View的setTranslationY(),会改变translationX和translationY的值,但是不会更改margin的值,所以getLeft()和getRight()不会改变。所以,我们从这里开得出一个结论:View的位置是会受translationX和translationY影响的,另外,setTranslationX(0)会恢复TranslationX的值。为了使View的移动显得更为平滑,因此可以使用View的属性动画来指定translationX和translationY。

scrollTo与scrollBy

mBtn.setOnTouchListener(newView.OnTouchListener(){

floattLastX;

floattLastY;

@Override

publicbooleanonTouch(Viewv,MotionEventevent){

switch(event.getAction()){

caseMotionEvent.ACTION_DOWN:

Log.e(TAG,"按下事件");

tLastX=event.getX();

tLastY=event.getY();

break;

caseMotionEvent.ACTION_MOVE:

Log.e(TAG,"移动事件");

inttOffsetX=Math.round(event.getX()-tLastX);

inttOffsetY=Math.round(event.getY()-tLastY);

((View)(v.getParent())).scrollBy(-tOffsetX,-tOffsetY);//移动子view

// v.scrollBy(-tOffsetX, -tOffsetY);//移动按钮文字

break;

caseMotionEvent.ACTION_UP:

Log.e(TAG,"松开事件");

break;

}

returnfalse;

}

});

在 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。

Scroller

前面我们使用的不管是 scrollBy 还是 scrollTo ,移动其实都是在一瞬间完成的,只是因为我们的触摸动作不断触发,View 不断改变位置,造成了一个过度动画的假象。

假如我们有一个按钮,比如点击按钮就会把 View 往右移动 100 像素,那么你会发现此时的 View 是瞬间变换位置,并不是慢慢移动到指定位置。

而Scroller可以根据需要移动的总距离,以及设置的移动时间,计算出每一次需要移动的距离,然后不断的进行移动,这样就实现了一个动画的效果。

VelocityTracker

属性动画

ViewDragHelper

结论:

1、View提供了分别提供了getLeft()、getRight、getRight()、getBottom()四个方法获取对于的信息。除此之外3.0之后View还提供了四个比较重要的位置参数信息,X、Y、translationX、translationY。View的宽高是有top、left、right、bottom参数决定的

而X,Y和translationX,和translationY则负责View位置的改变。

参考

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐