Android 使用 Scroller 实现平滑滚动功能的示例代码
作者:正义啊 发布时间:2022-01-20 22:49:35
标签:android,Scroller,平滑滚动
记录使用Scroller实现平滑滚动,效果图如下:
一、自定义View中实现View的平滑滚动
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实现View的平滑滚动
我们知道,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();
}
在ValueAnimator的addUpdateListener
中刷新Scroller当前值。并移动位置。效果如下:
来源:https://blog.csdn.net/sjdjdjdjahd/article/details/107257057


猜你喜欢
- 一、html代码 &n
- /// <summary>/// 获取数据缓存/// </summary>/// <param name=&q
- java编程中字节流转换成字符流的实现方法import java.io.*;/*readLine方法是字符流BufferReader类中的方
- 树形结构很多地方都有应用,比如我们在构造网站后台的授权限树的时候,再比如我们在设计多级留言的时候、还有分类等等。有些时候我们的树形结构并不需
- 前言我们之前介绍了不少有关动画的篇章。前面介绍的动画都是只有一个动画效果,那如果我们想对某个组件实现一组动效,比如下面的效果,该怎么办?st
- 一般我们在controller层调用service时,只需要使用@Autowired注解即可,例如如下代码我们经常看到:@RestContr
- 实例如下:private bool creatExcel(string xlsfile) { &nb
- 修改FeginCilent定义的服务名到指定服务通过覆盖类来修改对应的服务名,这里将所有的FeginClient对应的服务名都修改好。pac
- 本文实例为大家分享了Android实现轮播图片效果的具体代码,供大家参考,具体内容如下一、原理首先,将这些要轮播的图片和一些文本分别放置在不
- 最近这段时间一直在看Android,利用Listview去实现点赞功能,下面给大家介绍下基本思路。基本思路:进入界面–》获取数据–》 在Li
- 前言在计算机内存中,数据的存储方式都是以0和1的形式存储,也就是二进制的形式,数据是如何向内存写入的呢?整形数据以补码的形式存储,浮点型的存
- 最近在看进程间的通信,看到了fork()函数,虽然以前用过,这次经过思考加深了理解。现总结如下:1.函数本身(1)头文件#include&l
- 前言本文介绍的是Android如何实现数字跳动效果的TextView,主要运用了DancingNumberView,DancingNumbe
- 1. 前言本文主要是介绍一下RocketMQ消息生产者在发送消息的时候发送失败的问题处理?这里有两个点,一个是关于消息的处理,一个是关于br
- 【面试高频】- ThreadLocal的使用场景以及使用方式是怎么样的1 两大使用场景-ThreadLocal的用途典型场景1:每个线程需要
- 接口介绍:该请求用于检测和识别图片中的品牌LOGO信息。即对于输入的一张图片(可正常解码,且长宽比适宜),输出图片中LOGO的名称、位置和置
- .NET将关于多线程的功能定义在System.Threading名字空间中。因此,要使用多线程,必须先声明引用此名字空间(using Sys
- 本文实例为大家分享了Android高德地图marker自定义弹框窗口的具体代码,供大家参考,具体内容如下最终效果:1.gradle里添加高德
- Java中获取整点时间戳在实际的开发过程中,前端给后端传时间的时候,有时候传的是整点数值,比如:timeList=[00,01,02,03,
- 基于项目需求,想要实现Post消息推送,故采用HttpClient组件进行实现,相关代码如下(注:程序采用的httpclient和httpc