View中scroll相关方法
public void computeScroll() { //空实现 ,子 view重写时用于计算滚动并实现滚动动作 }
public void scrollTo(int x,int y) {//view的(left,top)滚动到一个点(x,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();
}
}
}
public void scrollBy(int x,int y) {//view的(left,top)需要滚动的距离为(x,y)累计
scrollTo(mScrollX + x, mScrollY + y);
}
public finalint getScrollX() {
return mScrollX;//最后一次滚动到的left坐标值
}
public finalint getScrollY() {
return mScrollY;//最后一次滚动到的top坐标值
}
scroll 滚动结果的说明:
不论是scrollTo还是scrollBy, 最后都是滚动到某一点(x,y)
表现在界面上的变化就是,将内容区(x,y)位置,移动到屏幕的(0,0)坐标
在SlidingMenu中,一般会先scrollTo(leftMenuWidth,0),表示右向滚动到x=leftMenuWidth,y=0的坐标点
这时就是将此坐标点及之后的内容 移动到屏幕的(0,0)位置进行显示
> 如果View的内容要向左滚动,那么scrollX要传入正数;反之传负,向右滚
> 如果View的内容要向上滚动,那么scrollY要传入正数;反之传负,向下滚
形象的说:
初始时,滚动器(Scroll)和View的内容的左顶点是重合的。
当滚动器的x、y变化时,比如 x = 100,那此时 滚动器所在的位置(100, 0) 即成了View的(0, 0) 点了,
view的x方向上即有 -100 的内容不可见了; 也就是说 内容向左滚动了
总的来说:就是view的滚动方向与屏幕坐标方向 相反
关于computeScroll:
调用view.invalidate(); 会触发该方法。
重写该方法时,内部一般使用一个android.widget.Scroller来处理滚动
android.widget.Scroller
View 的内容滚动器,不会改变 View 本身的位置,只是将内容进行滚动
构造方法:
public Scroller(Context context) {
this(context, null);
}
public Scroller(Context context, Interpolator interpolator) {//使用了插值器Interpolator
this(context, interpolator,
context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.HONEYCOMB);
}
public Scroller(Context context, Interpolator interpolator,boolean flywheel) {
mFinished = true;
mInterpolator = interpolator;
mPpi = context.getResources().getDisplayMetrics().density * 160.0f;
mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction());
mFlywheel = flywheel;
mPhysicalCoeff = computeDeceleration(0.84f);// look and feel tuning
}
一般使用第二个构造方法就可以,不想滚动效果太突兀,Interpolator传入一个LinearInterpolator(匀速运动)即可。
/*
* 开始滚动
* startX,startY: 开始滚动的x,y方向的偏移量;正数将从左开始
* 距离dx,dy: 正数将向左移动
*/
public void startScroll(int startX,int startY,int dx,int dy) {
startScroll(startX, startY, dx, dy, DEFAULT_DURATION);
}
//开始滚动 (,,,,滚动持续时间)
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; //需要滚动到的最终点x
mFinalY = startY + dy; //需要滚动到的最终点y
mDeltaX = dx;
mDeltaY = dy;
mDurationReciprocal = 1.0f / (float)mDuration;
}
//基于手势的滑动
public void fling(int startX,int startY,int velocityX,int velocityY,
int minX, int maxX, int minY, int maxY) {
...
}public boolean computeScrollOffset() {
if (mFinished) {
return false;
}
int timePassed = (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
if (timePassed < mDuration) {
switch (mMode) {
case SCROLL_MODE:
float x = timePassed * mDurationReciprocal;
if (mInterpolator == null)
x = viscousFluid(x);
else
x = mInterpolator.getInterpolation(x);
mCurrX = mStartX + Math.round(x * mDeltaX);
mCurrY = mStartY + Math.round(x * mDeltaY);
break;
case FLING_MODE:
final float t = (float) timePassed / mDuration;
final int index = (int) (NB_SAMPLES * t);
float distanceCoef = 1.f;
float velocityCoef = 0.f;
if (index < NB_SAMPLES) {
final float t_inf = (float) index / NB_SAMPLES;
final float t_sup = (float) (index + 1) / NB_SAMPLES;
final float d_inf = SPLINE_POSITION[index];
final float d_sup = SPLINE_POSITION[index + 1];
velocityCoef = (d_sup - d_inf) / (t_sup - t_inf);
distanceCoef = d_inf + (t - t_inf) * velocityCoef;
}
mCurrVelocity = velocityCoef * mDistance / mDuration * 1000.0f;
mCurrX = mStartX + Math.round(distanceCoef * (mFinalX - mStartX));
// Pin to mMinX <= mCurrX <= mMaxX
mCurrX = Math.min(mCurrX, mMaxX);
mCurrX = Math.max(mCurrX, mMinX);
mCurrY = mStartY + Math.round(distanceCoef * (mFinalY - mStartY));
// Pin to mMinY <= mCurrY <= mMaxY
mCurrY = Math.min(mCurrY, mMaxY);
mCurrY = Math.max(mCurrY, mMinY);
if (mCurrX == mFinalX && mCurrY == mFinalY) {
mFinished = true;
}
break;
}
}
else {
mCurrX = mFinalX;
mCurrY = mFinalY;
mFinished = true;
}
return true;
}
computeScrollOffset 计算滚动的偏移量:
两种滚动的模式:
1.scroll-mode
当知道滚动的目标距离时,调用startScroll方法后,即为这种模式。
根据插入器Interpolator(如果有),算出一定时间内需要滚动的距离,
最后将滚动到的目标点x,y存储在mCurrx和mCurrY中。
2.fling-mode
当不知道滚动的目标距离时,如ListView、GridView、ScrollView,
根据手指滑动后弹起的速率来计算目标的滚动距离。
最后将滚动到的目标点x,y存储在mCurrx和mCurrY中。
Scroller 自身不能将view滚动,只是用于计算view的滚动值的。
1.scroll-mode 一般使用流程
可以在onTouchevent中,算出将要滚动的距离dx、dy后,调用scroller.startScroll(...);view.invalidate();
在view的computeScroll中,调用if (scroller.computeScrollOffset()){ //滚动未完成
//1.取出计算出的一定时间内需要滚动到的目标点
int x = scroller.getCurrX();
int y = scroller.getCurrY();
//执行滚动
view.scrollTo(x, y);
invalidate();
}
invalidate后,会再次执行view.computeScroll(); 直至滚动到最终需要的目标点
2.fling-mode 一般使用流程
可以在onTouchEvent中,算出scroller.fling(...)需要的一些参数。
速率值可以用VelocityTracker类的computeCurrentVelocity()方法来计算。
也可以用GestureDetector来监听touchevent,GestureDetector初始化时,重写一个onFling();
即手势探测器,探测到fling的动作后,就会触发它。
调用scroller.fling()后,算出滚动到的目标点。再在view.computeScroll中使用它,用法同上。
属性translationX/Y
//坐标关系
private void coordinatorRelation() {
getLeft(); //view 的相对于父容器的左坐标
getTop();
getRight();
getBottom();
// setLeft(); //都有对应的 set 方法
getX(); //x = left + translationX; 即view 的内容区的左上角 x 坐标
getY(); //y = top + translationY; 即view 的内容区的左上角 y 坐标
// setX(); //都有对应的 set 方法
getTranslationX(); //左上角相对于父容器 x 方向的偏移量
getTranslationY(); //左上角相对于父容器 y 方向的偏移量
// setTranslationX(); //都有对应的 set 方法
}
使用改变该偏移量属性来进行滚动,需要配合动画;
而使用Animation,不会改变 View 的位置
http://nineoldandroids.com/