android listview头部大图 刷新,Android 列表(ListView、RecyclerView)不断刷新最佳实践

本文微信公众号「AndroidTraveler」首发。

背景

在 Android 列表开发过程中,有时候我们的 Item 会有一些组件,比如倒计时。这类组件要求不断刷新,这个时候由于列表复用的机制,因此会有一些坑。那么我们本篇文章就给大家讲两个主题。

第一个是列表复用是否一定有问题。

第二个是出现问题有哪些解决方案可供我们选择。

小 Demo

由于我们的主题重点是为了解决不断刷新问题,因此关于 RecyclerView 的基本使用就不再赘述,不清楚的小伙伴可以看下我之前的文章:

RecyclerView基本使用

首先我们看下效果图:

20da0464594fa1f224d09f395fdb72a6.gif

很简单,就是一个 RecyclerView 列表,列表项有两个组件。分别代表第几项和剩余秒数。

这里就是通过倒计时来演示刷新可能存在的问题。

重点代码是 Adapter 里面的显示逻辑,初始为:

@Override

public void onBindViewHolder(RecyclerViewViewHolder holder, int position) {

holder.mTvNum.setText(String.valueOf(position + 1));

updateTime(holder, itemList.get(position));

}

private void updateTime(final RecyclerViewViewHolder holder, final long time) {

String content;

long remainTime = time - System.currentTimeMillis();

remainTime /= 1000;

if (remainTime <= 0) {

content = "Time up";

holder.mTxtTitle.setText(content);

return;

}

content = "剩下"+remainTime+"秒";

holder.mTxtTitle.setText(content);

}

全部代码见:https://github.com/nesger/RecyclerView/tree/feature/refresh

接下来我们增加刷新方法,有很多种,我们一一说明。

1. 使用 handler 来实现倒计时刷新

修改显示代码,如下:

@Override

public void onBindViewHolder(RecyclerViewViewHolder holder, int position) {

holder.mTvNum.setText(String.valueOf(position + 1));

updateTime(holder, itemList.get(position));

}

private void updateTime(final RecyclerViewViewHolder holder, final long time) {

String content;

long remainTime = time - System.currentTimeMillis();

remainTime /= 1000;

if (remainTime <= 0) {

content = "Time up";

holder.mTxtTitle.setText(content);

return;

}

content = "剩下"+remainTime+"秒";

holder.mTxtTitle.setText(content);

holder.mTxtTitle.postDelayed(new Runnable() {

@Override

public void run() {

updateTime(holder, time);

}

}, 1000);

}

可以看到通过 handler 延时一秒,然后每次更新时间也是减少一秒。

我们看下效果图:

5d38adc8ba0e316c36f96189254fe271.gif

可以看到没滚动之前还好,滚动之后会发现,倒计时都乱了。

当然有时候可能不会暴露出来,比如滚动数目少,或者只有部分组件有倒计时,不像我们这个例子,所有项目都有倒计时,但是这也间接留下了可能的坑。

出现这个问题的原因在于组件的复用,如果你用 ListView 演示,并且不用复用,那么是不会错乱的。

当然列表不复用这个肯定是不推荐的。

因此,该方式不推荐。

全部代码见:https://github.com/nesger/RecyclerView/tree/feature/refresh_1

2. 使用 Timer 来实现倒计时刷新

@Override

public void onBindViewHolder(RecyclerViewViewHolder holder, int position) {

holder.mTvNum.setText(String.valueOf(position + 1));

updateTime(holder, itemList.get(position));

}

private void updateTime(final RecyclerViewViewHolder holder, final long time) {

String content;

long remainTime = time - System.currentTimeMillis();

remainTime /= 1000;

if (remainTime <= 0) {

content = "Time up";

holder.mTxtTitle.setText(content);

return;

}

content = "剩下"+remainTime+"秒";

holder.mTxtTitle.setText(content);

}

一样不行,不推荐。

全部代码见:https://github.com/nesger/RecyclerView/tree/feature/refresh_2

3. 使用 Timer + View 集合

其实我们简单分析一下就知道,出现上面错乱情况的原因大致是两个:一个是复用,一个是代码多次调用。

所以如果能够解决这两个问题,那么这个问题就解决了。

因为我们这里的业务是倒计时监听,所有 View 都是一样的,就是一秒更新一次。

所以我们的定时器不需要 N 个,只需要一个,在构造函数初始化即可。

另外为了避免复用和代码多次调用问题,我们将 View 通过一个集合保存起来。

最后修改的代码如下:

private Timer mTimer;

private Set mHolders;

public RecyclerViewAdapter(Activity activity, List itemList) {

if (activity == null || itemList == null) {

throw new IllegalArgumentException("params can't be null");

}

this.activity = activity;

this.itemList = itemList;

mHolders = new HashSet<>();

mTimer = new Timer();

mTimer.scheduleAtFixedRate(new TimerTask() {

@Override

public void run() {

for (RecyclerViewViewHolder holder : mHolders) {

updateTime(holder, holder.getTime());

}

}

}, 0, 1000);

}

@Override

public void onBindViewHolder(RecyclerViewViewHolder holder, int position) {

holder.setTime(itemList.get(position));

mHolders.add(holder);

holder.mTvNum.setText(String.valueOf(position + 1));

updateTime(holder, itemList.get(position));

}

效果图如下:

8ee0ea652ed081c8b23b0b8e79012cbd.gif

可以看到没问题了。

当然这里有些优化还没处理,因为本篇主要是思路分析,这里就不添加了。

待优化点:定时器的启动和关闭跟生命周期关联,无数据源不启用定时器等。

全部代码见:https://github.com/nesger/RecyclerView/tree/feature/refresh_3

该方法来自与一名朋友的分享。

4. 使用 ScheduledExecutorService + View 集合

这边 AndroidStudio 有安装阿里巴巴提供的一个代码检测插件,链接为:https://plugins.jetbrains.com/plugin/10046-alibaba-java-coding-guidelines

在 AndroidStudio 输入插件名字 Alibaba Java Coding Guidelines 查找安装即可。

在方法 3 使用 Timer 时提示下面信息:

Run multiple TimeTask by using ScheduledExecutorService rather than Timer because Timer will kill all running threads in case of failing to catch exceptions.

//org.apache.commons.lang3.concurrent.BasicThreadFactory

ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,

new BasicThreadFactory.Builder().namingPattern("example-schedule-pool-%d").daemon(true).build());

executorService.scheduleAtFixedRate(new Runnable() {

@Override

public void run() {

//do something

}

},initialDelay,period, TimeUnit.HOURS);

所以我们这里修改 Timer 为 ScheduledExecutorService:

private ScheduledExecutorService mExecutorService;

public RecyclerViewAdapter(Activity activity, List itemList) {

if (activity == null || itemList == null) {

throw new IllegalArgumentException("params can't be null");

}

this.activity = activity;

this.itemList = itemList;

mHolders = new HashSet<>();

mExecutorService = new ScheduledThreadPoolExecutor(1, new ThreadFactory() {

@Override

public Thread newThread(@NonNull Runnable r) {

Thread thread = new Thread(r);

thread.setName("countdown");

return thread;

}

});

mExecutorService.scheduleAtFixedRate(new Runnable() {

@Override

public void run() {

for (RecyclerViewViewHolder holder : mHolders) {

updateTime(holder, holder.getTime());

}

}

}, 0, 1000, TimeUnit.MILLISECONDS);

}

全部代码见:https://github.com/nesger/RecyclerView/tree/feature/refresh_4

有更多方法欢迎到上面的 GitHub 链接提 PR,可以基于 feature/refresh 分支新建分支。

有另外一位朋友提出了自定义 View 的处理方式,将倒计时的功能放到 View 里面去处理,这个感兴趣的小伙伴可以实现然后提 PR 哈,这里提供额外一种思路。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
您好,您的问题是关于使用Android Studio中的RecyclerView吗?如果是的话,我可以为您提供一些帮助。 首先,RecyclerView是一个用于显示大量数据的视图组件,它可以自动回收不可见的视图并重用它们以提高性能。在使用RecyclerView之前,您需要在build.gradle文件中添加RecyclerView依赖项: ``` implementation 'androidx.recyclerview:recyclerview:1.2.0' ``` 然后,在您的布局文件中添加RecyclerView组件: ``` <androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" /> ``` 接下来,在您的Activity或Fragment中,您需要创建一个Adapter类来管理RecyclerView中的数据和视图。这个Adapter类需要继承RecyclerView.Adapter,并实现三个方法:onCreateViewHolder、onBindViewHolder和getItemCount。例如: ``` class MyAdapter(private val data: List<String>) : RecyclerView.Adapter<MyAdapter.ViewHolder>() { class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val textView: TextView = itemView.findViewById(R.id.text_view) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.textView.text = data[position] } override fun getItemCount(): Int { return data.size } } ``` 最后,在您的Activity或Fragment中,您需要将Adapter设置给RecyclerView,并提供数据。例如: ``` val recyclerView = findViewById<RecyclerView>(R.id.recycler_view) val data = listOf("Item 1", "Item 2", "Item 3") val adapter = MyAdapter(data) recyclerView.adapter = adapter ``` 这样就完成了RecyclerView的基本使用。如果您需要更多的功能,例如添加分割线、添加动画等,可以参考RecyclerView的官方文档。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值