Android 如何在 ListView 中更新 ProgressBar 进度

时间:2021-10-18 04:22:36

=======================ListView原理==============================

Android 如何在 ListView 中更新 ProgressBar 进度

Android 的 ListView 的原理打个简单的比喻就是:

演员演小品(假设演员都长一样,每个角色任何演员都可以演)

Android 如何在 ListView 中更新 ProgressBar 进度

小品剧不需要为每个角色都招募一个演员。ListView 也没必要为每一个 Item 创建 View 对象。

小品剧的演员在一个角色表演完成后,会在后台换下一个角色的服装,等待需要表演的时候再出场。

ListView 会让未显示的 View 填充数据后缓存在后台,等待滑动时再将它显示出来。

小品演员换个服装就成了另一个角色,所以不能以角色来判断是哪个演员。

ListView 中的 Item 的样式是随填充的数据动态变化的,所以不能以某个样式作为Item的标识。

如果你是导演,你要警察这个角色在白领抬手时双手举起,你会怎们做?如果你找上次演过警察的那个演员,告诉他你在白领抬手时将双手举起。

那么有三个结果:一、他仍然演警察、他出色的完成了表演。二、他演厨师,结果白领抬手时厨师举起了手。三、他没上台,结果警察没举手。

ListView 中如果你根据某个 Item 的状态来获取它的 View 对象,通过线程改变它的状态,就会发生这三种情况。

那么要如何做呢?当然是告诉所有演员,谁扮演警察谁就在白领抬手时双手举起。那表演时如何判断谁演的是警察呢?警察帽子就是标志,谁戴着谁是警察。

ListView 中你要获取所有的 View 对象的集合,并为每一个 View 设置标识,传递需要更新状态的视图标识。更新前,在集合中找到标识匹配的 View 对象,让它做出相应的更新操作。

=====================ListView 中更新 ProgressBar=========================

知道这个 ListView 这个特性之后,就可以动手开始写了:

布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"> <ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"> </ListView> </RelativeLayout>

activity_main

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal"> <ProgressBar
android:id="@+id/progress_bar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:max="100" /> <Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="download" /> </LinearLayout>

list_item

Java文件

public class MainActivity extends AppCompatActivity {
private ListView listView; //列表控件
private List<MyObject> data; //数据源(模拟)
private MyAdapter adapter; //自定义适配器 @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/* 初始化控件 */
listView = (ListView) findViewById(R.id.list_view);
/* 初始化数据 */
data = new ArrayList<>();
for (int i = 0; i < 30; i++) {
//组装数据
MyObject myObject = new MyObject();
myObject.text = "按钮" + i;
myObject.progress = -1;
//添加到数据源
data.add(myObject);
}
/* 填充适配器 */
adapter = new MyAdapter(this, data);
listView.setAdapter(adapter);
} /**
* 实体对象,用于保存数据
*/
class MyObject {
Integer progress; //下载进度
String text; //按钮文字
}
}

MainActivity

public class MyAdapter extends BaseAdapter {
private Context context; //上下文对象用于视图填充
private List<MainActivity.MyObject> data; //需要适配的数据源
private List<View> viewList; //View对象集合 public MyAdapter(Context context, List<MainActivity.MyObject> data) {
this.viewList = new ArrayList<>();
this.context = context;
this.data = data;
} @Override
public int getCount() {
return data.size();
} @Override
public Object getItem(int position) {
return data.get(position);
} @Override
public long getItemId(int position) {
return position;
} @Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder viewHolder;
/* 初始化控件 */
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
viewHolder = new ViewHolder();
viewHolder.progressBar = (ProgressBar) convertView.findViewById(R.id.progress_bar);
viewHolder.button = (Button) convertView.findViewById(R.id.btn);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
/* 添加控件样式 */
final MainActivity.MyObject myObject = data.get(position);
viewHolder.button.setText(myObject.text);
viewHolder.progressBar.setProgress(myObject.progress);
/* 设置按钮点击事件 */
viewHolder.button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (myObject.progress == -1) {
myObject.progress = 0;
//如果未开始下载,启动异步下载任务
MyAsyncTask asyncTask = new MyAsyncTask(viewList, position);
//添加THREAD_POOL_EXECUTOR可启动多个异步任务
asyncTask.executeOnExecutor(MyAsyncTask.THREAD_POOL_EXECUTOR, myObject);
}
}
}); /* 标识View对象 */
//将list_view的ID作为Tag的Key值
convertView.setTag(R.id.list_view, position);//此处将位置信息作为标识传递
viewList.add(convertView); return convertView;
} /**
* 用于缓存控件ID
*/
class ViewHolder {
ProgressBar progressBar;
Button button;
}
}

MyAdapter

public class MyAsyncTask extends AsyncTask<MainActivity.MyObject, Integer, Void> {
private MainActivity.MyObject myObject; //单个数据,用于完成后的处理
private List<View> viewList; //视图对象集合,用于设置样式
private Integer viewId; //视图标识,用于匹配视图对象 public MyAsyncTask(List<View> viewList, Integer viewId) {
this.viewList = viewList;
this.viewId = viewId;
} @Override
protected Void doInBackground(MainActivity.MyObject... params) {
myObject = params[0];
/* 模拟下载任务 */
for (int i = 0; i < 100; i++) {
//发布进度
publishProgress(i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
} @Override
protected void onProgressUpdate(Integer... values) {
View view = null;
/* 匹配视图对象 */
for (int i = 0; i < viewList.size(); i++) {
if (viewList.get(i).getTag(R.id.list_view) == viewId) {
//检查所有视图ID,如果ID匹配则取出该对象
view = viewList.get(i);
break;
}
}
if (view != null) {
//将视图对象中缓存的ViewHolder对象取出,并使用该对象对控件进行更新
MyAdapter.ViewHolder viewHolder = (MyAdapter.ViewHolder) view.getTag();
viewHolder.progressBar.setProgress(values[0]);
}
myObject.progress = values[0];
} @Override
protected void onPostExecute(Void aVoid) {
//更新数据源信息
myObject.progress = 100;
}
}

MyAsyncTask

实现效果

Android 如何在 ListView 中更新 ProgressBar 进度

这里主要强调一下如何为View设置标识,以及从View集合中匹配标识:

首先是为View设置标识:

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder viewHolder;
/* 初始化控件 */
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
viewHolder = new ViewHolder();
viewHolder.progressBar = (ProgressBar) convertView.findViewById(R.id.progress_bar);
viewHolder.button = (Button) convertView.findViewById(R.id.btn);
convertView.setTag(viewHolder);//记录ViewHolder对象,缓存控件实例
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
/* 添加控件样式 */
//略去…… /* 设置按钮点击事件 */
//略去…… /* 标识View对象 */
convertView.setTag(R.id.list_view, position); //此处将位置信息作为标识传递
viewList.add(convertView); //将每个View添加到视图集合中 /**
* View.setTag(int Key,Object object)中的Key值必须唯一
* 传入任何常量都是无效的,必须传入R.id中生成的值
*
* 标识并非用于识别View对象,而是识别View的状态
* 就像警帽并非用于识别演员,而是识别演员当前扮演的角色
*
* View集合就像演员名单一样重要,如果没有它表演无从开展
*
* notifyDataSetChanged()虽然能更新列表,但是它是更新所有控件数据
* 相比于选择某个控件进行更新,这种方法性能开销大,体验差
*/
return convertView;
}

然后是在更新时匹配标识:

    @Override
protected void onProgressUpdate(Integer... values) {
View view = null;
/* 匹配视图对象 */
for (int i = 0; i < viewList.size(); i++) { //上场名单清点
if (viewList.get(i).getTag(R.id.list_view) == viewId) { //服装确认匹配
//检查所有视图ID,如果ID匹配则取出该对象
view = viewList.get(i);
break;
}
}
if (view != null) { //上场进行表演
//将视图对象中缓存的ViewHolder对象取出,并使用该对象对控件进行更新
MyAdapter.ViewHolder viewHolder = (MyAdapter.ViewHolder) view.getTag();
viewHolder.progressBar.setProgress(values[0]);
} /**
* 在更新时ViewList的重要性就体现出来了
* 遍历整个ViewList直到找到标识相同的视图
*
* 因为每次填充View时,View都会添加一个标识,而标识记录了当前的位置
* 所以标识代表某个视图在特定的位置,如果标识固定那么位置也就固定了
*
* 就像演员每次表演前,虽然角色谁都可以演,但是只要服装确定
* 那么只有穿着这服装且在上场名单内的演员才可以进行表演
*
*/
}

其实更新 ListView 中的某个控件的状态真是是很麻烦的事,因为适配器会为视图填充新的数据,这就要求使用对象记录状态,比如在实体对象中添加完成与否的判断,还有完成进度的记录,并且在更新视图中也同步更新这些数据。

Android 如何在 ListView 中更新 ProgressBar 进度的更多相关文章

  1. Android如何在ListView中嵌套ListView

    前几天因为项目的需要,要在一个ListView中放入另一个ListView,也即在一个ListView的每个ListItem中放入另外一个ListView.但刚开始的时候,会发现放入的小ListVie ...

  2. DataBinding注意事项Error parsing XML&colon; duplicate attribute以及如何在listview中使用DataBinding

    <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=&quo ...

  3. Android入门 在ListView中如何进行精确的定位

      在android的开发中,经常会遇到需要主动去设定某条ListItem的位置的需求.设置位置的函数有 ListView.setSelection(int position) ListView.se ...

  4. &period;NET winform 在listview中添加progressbar

    找了好长时间没找到,后来索性自己写了一个: 首先,在往listview加载数据的事件里添加progressbar: foreach (string d in arr) { ; item = new L ...

  5. Android在子线程中更新UI&lpar;二&rpar;

    MainActivity如下: package cc.testui2; import android.os.Bundle; import android.view.View; import andro ...

  6. Android在子线程中更新UI&lpar;一&rpar;

    MainActivity如下: package cc.testui1; import android.os.Bundle; import android.os.Handler; import andr ...

  7. Android 在子线程中更新UI的几种方法

    第一种: new Handler(context.getMainLooper()).post(new Runnable() { @Override public void run() { // 在这里 ...

  8. Android 如何在ScrollView中嵌套ListView

    前几天因为项目的需要,要在一个ListView中放入另一个ListView,也即在一个ListView的每个ListItem中放入另外一个ListView.但刚开始的时候,会发现放入的小ListVie ...

  9. &lbrack;转&rsqb;&lbrack;Android&rsqb; ListView中getView的原理+如何在ListView中放置多个item

      ListView 和 Adapter 的基础 工作原理: ListView 针对List中每个item,要求 adapter “给我一个视图” (getView). 一个新的视图被返回并显示 如果 ...

随机推荐

  1. Visulalization Voronoi in OpenSceneGraph

    Visulalization Voronoi in OpenSceneGraph eryar@163.com Abstract. In mathematics a Voronoi diagram is ...

  2. 安全退出,清空Session或Cookie

    概览: 网站中点击退出,如果仅仅是重定向到登录/出页面,此时在浏览器地址栏中输入登录后的某个页面地址如主页,你会发现不用登录就能访问.这种所谓的退出并不是安全的. 那么怎样做到安全退出呢? 那就是点击 ...

  3. MVC权限控制

    基本方法是重写AuthorizeAttribute类的AuthorizeCore方法 protected override bool AuthorizeCore(HttpContextBase htt ...

  4. Swift闭包概念与常见使用场景总结

    ·Swift 闭包 闭包(Closures)是自包含的功能代码块,可以在代码中使用或者用来作为参数传值. Swift 中的闭包与 C 和 Objective-C 中的代码块(blocks)以及其他一些 ...

  5. openfire muc 移除成员

    muc添加成员到数据库可参考 将Openfire中的MUC改造成类似QQ群一样的永久群 插件 插件是一位大神参考第一篇文章改进后编写的插件,进测试可以直接使用. ------------------- ...

  6. Java解析word&comma;获取文档中图片位置

    前言(背景介绍): Apache POI是Apache基金会下一个开源的项目,用来处理office系列的文档,能够创建和解析word.excel.ppt格式的文档. 其中对word文档的处理有两个技术 ...

  7. LOJ 2547 「JSOI2018」防御网络——思路&plus;环DP

    题目:https://loj.ac/problem/2547 一条树边 cr->v 会被计算 ( n-siz[v] ) * siz[v] 次.一条环边会被计算几次呢?于是去写了斯坦纳树. #in ...

  8. Golang的单引号、双引号与反引号

    Go语言的字符串类型string在本质上就与其他语言的字符串类型不同: Java的String.C++的std::string以及Python3的str类型都只是定宽字符序列 Go语言的字符串是一个用 ...

  9. 第一个微信小程序踩的几个小坑

    1.小程序测试调试阶段可以打开项目设置中的“开发环境不校验请求域名.TLS版本及HTTPS证书”配置,即可以和自己的服务器联调了. (需要在工具栏的设置 -> 项目设置 中配置,mac下直接co ...

  10. 统计百分比的一个SQL脚本

    统计一个表中一个百分比的SQL脚本,不过这个是个万分比,这个数据类型要调一调 ),) declare @num3 decimal,@num4 decimal declare @percent deci ...