触摸[5] Scroller
来源:互联网 发布:手机动漫制作软件 编辑:程序博客网 时间:2024/05/14 04:42
【参考链接】
前面只实现了内容随手指滑动而滑动,如何在手指离开屏幕以后还能继续平滑滑动一段距离呢?
要实现平滑滑动,需要借助于Scroller类,它其实是通过起始位置、终止位置或初始速度,加上持续时间,计算出中间时刻的滑动位置,然后不断触发重绘来实现的。即通过不断的移动一个小的距离来实现整体的平滑滑动效果。
提供了两个方法
startScroll(),给它传入滑动的起始位置、终止位置和持续时间。
fling(),给他传入手初始速度和起始位置、终止位置的范围。(不过在我这里的实验结果,在靠近边界的时候,fling()效果不是很好)
具体使用方式如下
1. 在ACTION_UP中根据速度分别调用startScroll()或者fling(),并调用invalidate()触发重绘。
2. 重写computeScroll()方法(),在其中通过Scroller获取到中间时刻的滑动位置,并调用scrollTo()/scrollBy()方法(会设置新的滑动位置并触发重绘),并再次调用invalidate()。
3. 可以在ACTION_DOWN中进行处理,如果正在惯性滑动,则在手指按下时取消当前的惯性滑动。
public classMyFrameLayoutextendsFrameLayout {
//内容的最大值、最小值
private intminX=0;
private int maxX=0;
//滑动坐标的最大值、最小值
private intminScrollX;
private int maxScrollX;
private int lastX;
VelocityTrackermVelocityTracker;
int scaledMaximumFlingVelocity;
int scaledMinimumFlingVelocity;
private MyScrollermScroller;
public MyFrameLayout(Context context) {
super(context);
init();
}
publicMyFrameLayout(Context context,AttributeSet attrs) {
super(context,attrs);
init();
}
publicMyFrameLayout(Context context,AttributeSet attrs, intdefStyleAttr) {
super(context,attrs,defStyleAttr);
init();
}
public voidinit(){
ViewConfiguration configuration = ViewConfiguration.get(getContext());
scaledMinimumFlingVelocity=configuration.getScaledMinimumFlingVelocity();
scaledMaximumFlingVelocity= configuration.getScaledMaximumFlingVelocity();
Log.e("shadowfaxghh","scaledMinimumFlingVelocity="+scaledMinimumFlingVelocity+" scaledMaximumFlingVelocity="+scaledMaximumFlingVelocity);
mScroller=newMyScroller(getContext());
}
@Override
protected voidonMeasure(intwidthMeasureSpec, intheightMeasureSpec) {
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
}
@Override
protected voidonLayout(booleanchanged, intleft, inttop, intright, intbottom) {
super.onLayout(changed,left,top,right,bottom);
//计算出内容的最大值最小值
for(inti=0;i<getChildCount();i++){
View childView = getChildAt(i);
if(minX>childView.getLeft())
minX=childView.getLeft();
if(maxX<childView.getRight())
maxX=childView.getRight();
}
//计算出滑动坐标的最大值、最小值//只有内容大小超出自身大小时,才能进行滑动
if(minX<0)
minScrollX=minX;
else
minScrollX=0;
if(maxX>getWidth())
maxScrollX=maxX-getWidth();
else
maxScrollX=0;
}
@Override
public booleanonTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
switch (event.getAction()){
caseMotionEvent.ACTION_DOWN:
// Log.e("shadowfaxghh", "ACTION_DOWN");
lastX=(int) event.getRawX();
obtaionVelocityTracker();
mVelocityTracker.addMovement(event);
if(mScroller!=null& !mScroller.isFinished()){
mScroller.abortAnimation();
}
break;
case MotionEvent.ACTION_MOVE:
// Log.e("shadowfaxghh", "ACTION_MOVE");
intnewX= (int) event.getRawX();
int deltaX=newX-lastX;
// if (Math.abs(deltaX) >ViewConfiguration.get(getContext()).getScaledTouchSlop()) {
intnewScrollX = getScrollX() + (-deltaX);
if (newScrollX >maxScrollX)
newScrollX = maxScrollX;
if (newScrollX <minScrollX)
newScrollX = minScrollX;
scrollTo(newScrollX,getScrollY());
//更新坐标
lastX= newX;
// }
obtaionVelocityTracker();
mVelocityTracker.addMovement(event);
break;
case MotionEvent.ACTION_UP:
Log.e("shadowfaxghh","ACTION_UP");
lastX=(int) event.getRawX();
if(mVelocityTracker!=null) {
//计算速度
mVelocityTracker.computeCurrentVelocity(1000,scaledMaximumFlingVelocity);
//获取x方向的速度
intxVelocity = (int)mVelocityTracker.getXVelocity();
Log.e("shadowfaxghh","xVelocity="+ xVelocity);
recycleVelocityTracker();
//使用startScroll()进行滑动
Log.e("shadowfaxghh","startScroll()");
//假定手指离开以后以当前速度/4继续滑动1000ms
intscrollDuration=1000;
int scrollDistance=Math.abs(scrollDuration/1000* xVelocity/4);//computeCurrentVelocity(1000//速度是以1000ms为单位的
intstartScrollX = getScrollX();
int endScrollX =startScrollX;
if(xVelocity>0){//手指向右滑动
endScrollX=startScrollX-scrollDistance;
}else if(xVelocity<0){//手指向左滑动
endScrollX=startScrollX+scrollDistance;
}
if(endScrollX<minScrollX)
endScrollX=minScrollX;
if(endScrollX>maxScrollX)
endScrollX=maxScrollX;
//实际滑动时间ms//当然这是以匀速滑动来计算的,实际上Scroller内部并不是匀速滑动
scrollDuration= (int) (Math.abs((float) (startScrollX-endScrollX)/(float)scrollDistance)*scrollDuration);
mScroller.startScroll(startScrollX,getScrollY(),endScrollX-startScrollX,0,scrollDuration);
invalidate();
// //使用Fling()进行滑动
// if(Math.abs(xVelocity)>scaledMinimumFlingVelocity) {//快速滑动
// Log.e("shadowfaxghh", "fling()");
//
// //注意xVelocity要取反,因为velocity正负值跟屏幕坐标系一致,但是scroll参数跟屏幕坐标系相反
// mScroller.fling(getScrollX(), getScrollY(), -xVelocity, 0, minScrollX,maxScrollX, 0, 0);
// invalidate();
// }
}
break;
case MotionEvent.ACTION_CANCEL:
Log.e("shadowfaxghh","ACTION_CANCEL");
lastX=(int) event.getRawX();
recycleVelocityTracker();
break;
default:
break;
}
return true;
}
@Override
public voidcomputeScroll() {
// super.computeScroll();
if(mScroller!=null&&mScroller.computeScrollOffset()){
intcurrX =mScroller.getCurrX();
int currY =mScroller.getCurrY();
scrollTo(currX,currY);
invalidate();//某些情况下scrollTo()无法触发重绘,需要自己invalidate()触发
}
}
private voidobtaionVelocityTracker(){
if(mVelocityTracker==null)
mVelocityTracker=VelocityTracker.obtain();
}
private voidrecycleVelocityTracker(){
if(mVelocityTracker!=null){
mVelocityTracker.clear();
mVelocityTracker.recycle();
mVelocityTracker=null;
}
}
@Override
protected voidonScrollChanged(intl, intt, intoldl, intoldt) {
super.onScrollChanged(l,t,oldl,oldt);
}
}
可以看一下Scroller中相关方法的源码,如下代码基于android-2.3.3_r1
startScroll()
public voidstartScroll(intstartX, intstartY, intdx, intdy, intduration) {
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;
Log.e("shadowfaxghh","Scroller startScroll()mStartX="+mStartX+" mFinalX="+mFinalX+" mDuration="+mDuration);
}
fling()
public voidfling(intstartX, intstartY, intvelocityX, intvelocityY,
int minX, intmaxX, intminY, intmaxY) {
mMode=FLING_MODE;
mFinished=false;
float velocity = (float)Math.hypot(velocityX,velocityY);
mVelocity= velocity;
//计算出滑动时间
mDuration= (int) (1000* velocity /mDeceleration);// Duration is in
// milliseconds
mStartTime= AnimationUtils.currentAnimationTimeMillis();
mStartX= startX;
mStartY= startY;
mCoeffX= velocity ==0?1.0f: velocityX / velocity;
mCoeffY= velocity ==0?1.0f: velocityY / velocity;
//计算出滑动距离
inttotalDistance = (int) ((velocity * velocity) / (2*mDeceleration));
mMinX= minX;
mMaxX= maxX;
mMinY= minY;
mMaxY= maxY;
//跟传入的参数进行比较
mFinalX= startX + Math.round(totalDistance*mCoeffX);
// Pin to mMinX <= mFinalX <=mMaxX
mFinalX= Math.min(mFinalX,mMaxX);
mFinalX= Math.max(mFinalX,mMinX);
mFinalY= startY + Math.round(totalDistance*mCoeffY);
// Pin to mMinY <= mFinalY <=mMaxY
mFinalY= Math.min(mFinalY,mMaxY);
mFinalY= Math.max(mFinalY,mMinY);
Log.e("shadowfaxghh","Scroller fling() mStartX="+mStartX+" mFinalX="+mFinalX+" mDuration="+mDuration);
}
computeScrollOffset()
public booleancomputeScrollOffset() {//返回滑动是否已经结束
if(mFinished) {
return false;
}
inttimePassed = (int)(AnimationUtils.currentAnimationTimeMillis()-mStartTime);
//从开始滑动到现在还没有结束
if(timePassed <mDuration) {
switch(mMode) {//分两种模式计算中间时刻的速度
caseSCROLL_MODE:
floatx = (float)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:
floattimePassedSeconds = timePassed /1000.0f;
float distance = (mVelocity* timePassedSeconds)
- (mDeceleration* timePassedSeconds * timePassedSeconds/2.0f);
mCurrX=mStartX+ Math.round(distance *mCoeffX);
// Pin to mMinX<= mCurrX <= mMaxX
mCurrX= Math.min(mCurrX,mMaxX);
mCurrX= Math.max(mCurrX,mMinX);
mCurrY=mStartY+ Math.round(distance *mCoeffY);
// 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;
}
startScroll() vs fling()
参考ScrollView的源码,startScroll()和fling()并不是同时使用的
当手指离开屏幕的时候,如果速度大于minFlingVelocity,则进行fling()。否则停止(或回弹)。
- 触摸[5] Scroller
- 如何实现触摸滑屏以及Scroller类详解
- Android中滑屏实现----触摸滑屏以及Scroller类详解
- Scroller
- Scroller
- Scroller
- Scroller
- scroller
- scroller
- scroller
- Scroller
- scroller
- Scroller
- Scroller
- Scroller
- Scroller
- scroller
- Scroller
- 使用git上传本地文件到Gayhub(面向初学者)
- vue的进阶之路(一) 环境的配置
- Spring属性注入
- 文章标题
- C语言:自定义字符串函数
- 触摸[5] Scroller
- PHP比较好的学习网站
- 在 Spring MVC + Spring 项目中使用 Spring Security 4.2.3
- ROSLaunch学习笔记
- React入门教程
- 质数因子
- firewall-cmd 基础使用
- linux下的jmeter如何修改测试计划?
- docker 中dockerfile volumn 与-v 的区别