public class ScrollerView extends View {
private Scroller mScroller;
private Paint mPaint;
/**
* 屏幕拖动最小像素
*/
private int mTouchSlop;
/**
* View宽度
*/
private int width;
/**
* View高度
*/
private int height;
/**
* MotionEvent.getX()
*/
private int mEventX;
/**
* MotionEvent.getY()
*/
private int mEventY;
private Bitmap mBitmap;
/**
* View到屏幕左边距离
*/
private int mStartX;
/**
* View到屏幕顶部距离
*/
private int mStartY;
/**
* View默认大小
*/
private static int DEFAULT_SIZE = 200;
public ScrollerView(Context context) {
this(context, null);
}
public ScrollerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mScroller = new Scroller(context);
ViewConfiguration configuration = ViewConfiguration.get(context);
mTouchSlop = ViewConfigurationCompat.getScaledHoverSlop(configuration);
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
width = MeasureSpec.getSize(widthMeasureSpec);
} else {
if (heightMode == MeasureSpec.EXACTLY) {
width = MeasureSpec.getSize(heightMeasureSpec);
} else {
width = DEFAULT_SIZE;
}
}
if (heightMode == MeasureSpec.EXACTLY) {
height = MeasureSpec.getSize(heightMeasureSpec);
} else {
height = width;
}
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (null != mBitmap) {
Rect src = new Rect(0, 0, mBitmap.getWidth(), mBitmap.getHeight());
Rect dst = new Rect(0, 0, width, height);
canvas.drawBitmap(mBitmap, src, dst, mPaint);
} else {
Log.e("zzy", "Bitmap is null");
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
mEventX = (int) event.getX();
mEventY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
mStartX = (int) event.getRawX() - mEventX;
mStartY = (int) event.getRawY() - mEventY;
layout(mStartX,mStartY,mStartX+width,mStartY+height);
break;
case MotionEvent.ACTION_UP:
startScroller();
break;
}
return true;
}
@Override
public void computeScroll() {
if (mScroller.computeScrollOffset()){
int l = mScroller.getCurrX();
layout(l,mStartY,l+width,mStartY+height);
invalidate();
}
}
/**
* 开始Scroller动画
*/
private void startScroller(){
mScroller.forceFinished(true);
mScroller.startScroll(mStartX, mStartY,-mStartX,0);
int screenWidth = getScreenWidth();
// Scroller动画默认250ms,超过屏幕一半时设置为500ms
if (mStartX > screenWidth / 2){
mScroller.extendDuration(500);
}
invalidate();
}
private int getScreenWidth(){
return getResources().getDisplayMetrics().widthPixels;
}
}
Scroller其实是个辅助类,本身并不能完成动画的执行。而是帮我们计算随着时间的流逝,动画应该执行的位置值,我们需要获得当前时间的位置,然后调用View位置移动方法,将View移动到该位置,完成动画。
所以,在自定义View中。我们需要调用invalidate()
触发View的重绘,并覆写重绘会执行的方法computeScroll()
。
在computeScroll()
方法中调用Scroller
的computeScrollOffset()
计算当前时间动画应该移动的位置,返回值是动画是否在执行。
通过mScroller.getCurrX()
和mScroller.getCurrY()
获得当前时间的位置。手动调用View位置移动的方法将View的位置移动到当前时间的位置,实现View的滚动。
然后再次调用invalidate()
触发刷新。直到computeScrollOffset()
返回false,动画执行完成,滚动完成。
我们知道,Scroller会帮我们计算当前时间,插值器返回的值。
而如果直接使用Scroller实现平滑滚动的话,也需要借助带时间的监听器。
这里借助ValueAnimator
来实现Scroller平滑滚动
private Scroller mScroller;
private ImageView mImage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImage = findViewById(R.id.image);
mScroller =new Scroller(this);
}
public void btnStart(View view){
start();
}
private void start(){
mScroller.forceFinished(false);
mScroller.extendDuration(500);
mScroller.startScroll(0,0,400,400);
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0,1);
valueAnimator.setDuration(500);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (mScroller.computeScrollOffset()){
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mImage.getLayoutParams();
params.leftMargin = mScroller.getCurrX();
params.topMargin = mScroller.getCurrY();
mImage.setLayoutParams(params);
}
}
});
valueAnimator.start();
}