前言java
自定义View做为Android进阶的基础,是咱们开发者不得不学习的知识,而酷炫的自定义View效果,都离不开View的滑动,因此接下来咱们来一块儿探究View的滑动方式,看看View是如何滑动的,为Android进阶的道路打下基础。android
了解View的滑动方式,首先咱们得了解View在什么位置,咱们能够把手机屏幕区域当作是像数学中坐标系同样的区域,只不过是手机屏幕坐标系的Y轴和数学中的坐标系的Y轴正方向相反git
肯定View的位置主要是根据View的left、top、right、bottom四个属性来决定,须要注意的是View的这四个属性是相对于它的父容器来讲的,因此对应为left是View的左上角相对于父容器的横坐标,top为纵坐标,right为View右下角相对于父容器的横坐标,bottom为纵坐标。(具体能够看下方示意图A)github
//获取view位置的值
left = View.getLeft();
top = View.getTop();
right = View.getRight();
bottom = View.getBottom();
复制代码
除了上面肯定View位置的参数,还有x,y,translationX,translationY这四个参数,x和y表明View的左上角的坐标值,而translationX,translationY是左上角坐标相对于View的父容器的偏移量,默认为零,也就是view不移动,则x和y等于left和top,他们换算关系可看下面示意图A,在View的滑动过程当中,left和top表示的是View原始位置的值,这是不会改变的,因此改变的是滑动偏移量加上原始值获得新的左上角坐标。bash
layout方法改变View位置滑动Viewapp
首先咱们看看layout()方法源码ide
@SuppressWarnings({"unchecked"})
public void layout(int l, int t, int r, int b) {
.......
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
........
}
}
复制代码
了解过自定义View的各位应该都知道,onLayout()是View绘制过程当中的一个方法,能够经过它肯定View的位置,也就是说咱们经过layout()方法能够改变View的位置,下面咱们经过onLayout方法作一个能够随意滑动 view的例子布局
@Override
public boolean onTouchEvent(MotionEvent event) {
//获取触屏时候的坐标
Log.e("毛麒添","getLeft:"+getLeft()+"getTop:"+getTop()+"getRight:"+getRight()+"getBottom:"+getBottom());
x = event.getRawX();
y = event.getRawY();
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
//手指移动偏移量
int offsetX = (int) (x-lastX);
int offsetY = (int) (y-lastY);
layout(getLeft()+offsetX,getTop()+offsetY,getRight()+offsetX,getBottom()+offsetY);
break;
case MotionEvent.ACTION_UP:
Log.e("毛麒添","getLeft:"+getLeft()+"getTop:"+getTop()+"getRight:"+getRight()+"getBottom:"+getBottom());
break;
}
lastX=x;
lastY=y;
return super.onTouchEvent(event);
}
复制代码
offsetLeftAndRight()与offsetTopAndBottom() 方法改变View的位置让其滑动post
case MotionEvent.ACTION_MOVE:
//手指移动偏移量
int offsetX = (int) (x-lastX);
int offsetY = (int) (y-lastY);
offsetLeftAndRight(offsetX);
offsetTopAndBottom(offsetY);
break;
复制代码
使用scrollTo()和scrollBy()滑动View学习
/**
* Set the scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param x the x position to scroll to
* @param y the y position to scroll to
*/
public void scrollTo(int x, int y) {
if (mScrollX != x || mScrollY != y) {
int oldX = mScrollX;
int oldY = mScrollY;
mScrollX = x;
mScrollY = y;
invalidateParentCaches();
onScrollChanged(mScrollX, mScrollY, oldX, oldY);
if (!awakenScrollBars()) {
postInvalidateOnAnimation();
}
}
}
/**
* Move the scrolled position of your view. This will cause a call to
* {@link #onScrollChanged(int, int, int, int)} and the view will be
* invalidated.
* @param x the amount of pixels to scroll by horizontally
* @param y the amount of pixels to scroll by vertically
*/
public void scrollBy(int x, int y) {
scrollTo(mScrollX + x, mScrollY + y);
}
复制代码
经过源码咱们能够看到scrollBy()的实现实际上是调用了scrollTo()方法。这里有个mScrollX和mScrollY的规则咱们须要明白:scrollTo()中mScrollX的值等于view左边缘和view内容左边缘在水平方向的距离,而且当view的左边缘在view的内容左边缘右边时,mScrollX为正,反之为负;同理mScrollY等于view上边缘和view内容上边缘在竖直方向的距离,而且当view的上边缘在view的内容上边缘下边时,mScrollY为正,反之为负。当View没有使用scrollTo()和scrollBy()进行滑动的时候,mScrollX和mScrollY默认等于零,也就是view的左边缘与内容左边缘重合。
根据上面的规则,咱们假设将view内容右下滑动,获得下图
结合上面的知识,咱们将上面滑动的例子改写一下,若是使用scrollTo()则只是滑动到咱们手指滑动偏移量的距离的点,达不到要求,而scrollBy()是在scrollTo()的基础上偏移滑动的位置,正好符合咱们自由滑动的要求,而且根据上面的分析mScrollX和mScrollY为负值,则滑动偏移也应该为负值才能达到咱们想要的自由滑动效果(这个你们须要本身好好想明白可能才会更加清楚理解)
case MotionEvent.ACTION_MOVE:
//手指移动偏移量
int offsetX = (int) (x-lastX);
int offsetY = (int) (y-lastY);
//滑动方式1
((View)getParent()).scrollBy(-offsetX,-offsetY);
break;
复制代码
根据滑动打印的日志咱们能够看出,scrollBy()和scrollTo()在滑动的过程当中只是改变了View内容的位置,而没有改变初始的left,right,top,bottom的值
使用动画让View滑动
xml补间动画的方式让View滑动
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true"
android:duration="500"
>
<translate android:fromXDelta="0"
android:fromYDelta="0"
android:toXDelta="500"
android:toYDelta="500"
/>
</set>
复制代码
startAnimation(AnimationUtils.loadAnimation(mContext, R.anim.testscroll));
复制代码
属性动画让View滑动
ObjectAnimator.ofFloat(testScroll,"translationX",0,300).setDuration(2000).start();
复制代码
改变布局参数 LayoutParams 滑动View
......
case MotionEvent.ACTION_MOVE:
//手指移动偏移量
int offsetX = (int) (x-lastX);
int offsetY = (int) (y-lastY);
//滑动方式5
moveView(offsetX,offsetY);
break;
......
private void moveView(int offsetX, int offsetY) {
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
layoutParams.leftMargin = getLeft() + offsetX;
layoutParams.topMargin = getTop() + offsetY;
setLayoutParams(layoutParams);
}
复制代码
Scroller弹性滑动
首先咱们要明白什么是Scroller?
Scroller mScroller=new Scroller(context);
public void smoothScrollTo(int desx,int desy){
int scaleX = (int) getScaleX();
int scaleY = (int) getScaleY();
int deltaX = desx-scaleX;
int deltaY = desy-scaleY;
//3秒内弹性滑到desx desy 位置
mScroller.startScroll(scaleX,scaleY,deltaX,deltaY,3000);
//从新绘制界面 会调用computeScroll方法
invalidate();
}
@Override
public void computeScroll() {
super.computeScroll();
if(mScroller.computeScrollOffset()){//还没滑动到指定位置
((View)getParent()).scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
postInvalidate();
}
}
//拿到自定View的实例对象调用smoothScrollTo实现右下方向3秒
//内到指定位置的弹性滑动
//为何是-300 请看scrollTo()或scrollBy()滑动解析
testScroll.smoothScrollTo(-300,-300);
复制代码
下面咱们从源码角度来分析一下Scroller是如何实现弹性滑动的
/**
* Start scrolling by providing a starting point, the distance to travel,
* and the duration of the scroll.
*
* @param startX Starting horizontal scroll offset in pixels. Positive
* numbers will scroll the content to the left.
* @param startY Starting vertical scroll offset in pixels. Positive numbers
* will scroll the content up.
* @param dx Horizontal distance to travel. Positive numbers will scroll the
* content to the left.
* @param dy Vertical distance to travel. Positive numbers will scroll the
* content up.
* @param duration Duration of the scroll in milliseconds.
*/
public void startScroll(int startX, int startY, int dx, int dy, int duration) {
mMode = SCROLL_MODE;
mFinished = false;
mDuration = duration;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStartX = startX;
mStartY = startY;
mFinalX = startX + dx;
mFinalY = startY + dy;
mDeltaX = dx;
mDeltaY = dy;
mDurationReciprocal = 1.0f / (float) mDuration;
}
//View 中 computeScroll()方法没有实现内容,须要子View 自行实现
/**
* Called by a parent to request that a child update its values for mScrollX
* and mScrollY if necessary. This will typically be done if the child is
* animating a scroll using a {@link android.widget.Scroller Scroller}
* object.
*/
public void computeScroll() {
}
复制代码
/**
* Call this when you want to know the new location. If it returns true,
* the animation is not yet finished.
*/
public boolean computeScrollOffset() {
if (mFinished) {
return false;
}
int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
if (timePassed < mDuration) {
switch (mMode) {
case SCROLL_MODE:
final float x = mInterpolator.getInterpolation(timePassed * mDurationReciprocal);
mCurrX = mStartX + Math.round(x * mDeltaX);
mCurrY = mStartY + Math.round(x * mDeltaY);
break;
.......
}
}
else {
.......
}
return true;
}
复制代码
到此,View的滑动方式就已经了解完了。若是文章中有写得不对的地方,请给我留言指出,你们一块儿学习进步。若是以为个人文章给予你帮助,也请给我一个喜欢和关注。
参考连接
参考书籍