ListView ,recycleView列表带进度条

实现上图功能有两种思路。
一:普通做法,更新item的数据,不停调用notifydatachange ;
二:各管自家刷新。
一个下载对应一个下载线程。线程持有对应item在Listview中的位置。当该线程所对应的item可见时,获得该Item的progressbar更新。
第二种方式相对省资源效率更高。
一步步来解决关键问题:
1.进度条实现

不熟悉进度条progressbar的样式定义,可以翻系统的源码。
水平样式:
<pre name="code" class="java">
<style name="Widget.ProgressBar.Horizontal">
<item name="android:indeterminateOnly">false</item>
<item name="android:progressDrawable">@android:drawable/progress_horizontal</item>
<item name="android:indeterminateDrawable">@android:drawable/progress_indeterminate_horizontal</item>
<item name="android:minHeight">20dip</item>
<item name="android:maxHeight">20dip</item>
<item name="android:mirrorForRtl">true</item>
</style>
关键是:
<style name="Widget.ProgressBar.Horizontal">和android:indeterminateDrawable
制一个Item布局
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="86dp"
android:paddingTop="15dp"
tools:context="com.loopbanner.DiscoveryFragment"> <ImageView
android:id="@+id/iv_soft_icon"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginStart="13dp"
android:scaleType="fitXY"
android:src="@mipmap/ic_launcher_round" /> <TextView
android:layout_toLeftOf="@+id/rl_pro"
android:id="@+id/tv_soft_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="10dp"
android:layout_toRightOf="@+id/iv_soft_icon"
android:maxLines="1"
android:text="TextView"
android:textColor="#333333" /> <TextView
android:layout_toLeftOf="@+id/rl_pro"
android:layout_toRightOf="@+id/iv_soft_icon"
android:id="@+id/tv_introduce"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_soft_name"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="10dp"
android:maxLines="1"
android:text="TextView"
android:textColor="#999999" /> <RelativeLayout
android:id="@+id/rl_pro"
android:layout_width="60dp"
android:layout_height="25dp"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="12dp"
android:gravity="center"> <ProgressBar
android:id="@+id/progressbar"
style="@android:style/Widget.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminateOnly="false"
android:max="100"
android:minHeight="25dp"
android:progress="0"
android:progressDrawable="@drawable/progress_bg_list" /> <TextView
android:id="@+id/tv_donwload"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="下载"
android:textColor="#F88C08" />
</RelativeLayout> <View
android:id="@+id/view"
android:layout_width="match_parent"
android:layout_height="1px"
android:layout_alignParentBottom="true"
android:layout_marginLeft="83dp"
android:background="#999999"> </View>
</RelativeLayout>
自定义
android:indeterminateDrawable
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<corners android:radius="12.5dip" />
<solid android:color="#fff" />
<stroke
android:width="1dp"
android:color="#F88C08" />
</shape>
</item>
<item android:id="@android:id/secondaryProgress">
<clip>
<shape>
<corners android:radius="12.5dip" />
<gradient
android:angle="270"
android:centerColor="#EE5C42"
android:centerY="0.75"
android:endColor="#EE5C42"
android:startColor="#EE5C42" />
</shape>
</clip>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<corners android:radius="12.5dip" />
<solid android:color="#FEE2CD" />
<stroke
android:width="1dp"
android:color="#F88C08" />
</shape>
</clip>
</item>
</layer-list>
效果如下:

按一种思路,下载只针对data数据操作。adapter要不断刷新。这个原理比较简单,不再写了。
第二种思路的关键是,下载线程去刷新progressbar,关键点是找是当前是否可见的item并刷新。
看一下关键代码:
找到当前可见item是listView现有api,这里面出于设计模式考虑。activity不要跟下载线程有交互,减少耦合。那么数据作都与adapter去交互。所以使用以下方法获取ListView:

int firstItem = mAdapter.getListView().getFirstVisiblePosition();
int lastItem = mAdapter.getListView().getLastVisiblePosition(); 下一步如何生成持有位置信息的下载类。通常方法都是要adapter 的getview方法里去设置tag.
vh.tvDonwload.setOnClickListener(onClickListener);
vh.tvDonwload.setTag(R.id.progressbar, vh.progressbar);
vh.tvDonwload.setTag(R.id.tag_progress_bar, vh.progressbar);
vh.tvDonwload.setTag(R.id.tag_positon, position);
vh.progressbar.setTag(R.id.tag_url); 如何设置多个tag请自行baidu,给一个唯一id和一下object ; 可以如下去做:
<resources>
<string name="hello_blank_fragment">Hello blank fragment</string>
<item name="tag_positon" type="id">1</item>
<item name="tag_progress_bar" type="id">2</item>
<item name="tag_url" type="id">3</item>
</resources>
在点击时开启下载工作。
public View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (v instanceof TextView) {
int pos = (int) v.getTag(R.id.tag_positon);
DiscoveryModel item = (DiscoveryModel) getItem(pos);
if (item.getStatus() == DiscoveryModel.NORMAL) {
((TextView) v).setText("0%");
new FileDownLoaderTask(DiscoveryAdapter.this, pos);
item.setStatus(DiscoveryModel.DOWNLOADING);
} else if (item.getStatus() == DiscoveryModel.DOWNLOADING) {
item.setStatus(DiscoveryModel.PAUSE);
}
}
}
};
最后是如何更新
看代码:
//如果本下载线程所带的item的位置在当前listview中可见
if (((DiscoveryModel) mAdapter.getItem(mPosition)).getStatus() == DiscoveryModel.DOWNLOADING && firstItem <= mPosition && mPosition <= lastItem) {
//在listview中找到该item
for (int i = 0; i < mAdapter.getListView().getChildCount(); i++) {
//通过比对tag的位置
if ((int) (mAdapter.getListView().getChildAt(i).findViewById(R.id.tv_donwload).getTag(R.id.tag_positon)) == mPosition) {
//找到ProgressBar 去更新;
ProgressBar progressBar = (ProgressBar) mAdapter.getListView().getChildAt(i).findViewById(R.id.progressbar);
progressBar.setProgress(process);
TextView tv_donwload = (TextView) mAdapter.getListView().getChildAt(i)
.findViewById(R.id.tv_donwload);
progressBar.setProgress(process);
tv_donwload.setText(process + "%");
}
}
}

帖上所有代码
MainActivity
package com.loopbanner; import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction; public class MainActivity extends FragmentActivity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.add(R.id.root, new DiscoveryFragment());
ft.commit();
}
}
fragment
package com.loopbanner; import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
import android.widget.Toast; import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject; import java.util.ArrayList;
import java.util.List; /**
* A simple {@link Fragment} subclass.
*/
public class DiscoveryFragment extends Fragment { @Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
} @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_discovery, container, false);
init(rootView);
return rootView;
} ListView mListView;
DiscoveryAdapter mAdapter;
List<Object> modelList; private void init(View rootView) {
mListView = (ListView) rootView.findViewById(R.id.lv_discovery);
modelList = new ArrayList<>();
mAdapter = new DiscoveryAdapter(getActivity(), modelList);
mListView.setAdapter(mAdapter);
getData();
} private void getData() {
GetDiscoveryDataTask gddt = new GetDiscoveryDataTask() {
@Override
public void onResult(String resMsg, int code) {
if (resMsg != null && resMsg.length() > 0) {
parseData(resMsg);
} else {
Toast.makeText(getActivity(), "未获取到数据!", Toast.LENGTH_SHORT).show();
}
}
};
gddt.request();
} public void parseData(String string) {
try {
JSONObject jsonObject = new JSONObject(string);
if (jsonObject != null) {
JSONArray jsonArray = jsonObject.optJSONArray("apps");
if (jsonArray != null && jsonArray.length() > 0) {
for (int i = 0; i < jsonArray.length(); i++) {
DiscoveryModel dm = new DiscoveryModel();
JSONObject app = (JSONObject) jsonArray.get(i);
dm.setSoftId(app.optString("softId"));
dm.setSoftBrief(app.optString("softBrief"));
dm.setSoftDown(app.optString("softDown"));
dm.setSoftLogo(app.optString("softLogo"));
dm.setSoftName(app.optString("softName"));
modelList.add(dm);
}
mAdapter.notifyDataSetChanged();
}
} } catch (JSONException e) {
e.printStackTrace();
}
} }
adpater
package com.loopbanner; import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView; import com.bumptech.glide.Glide; import java.util.List; public class DiscoveryAdapter extends ArrayAdapter<Object> {
private static class ViewHolder {
public final RelativeLayout rootView;
public final ImageView ivSoftIcon;
public final TextView tvSoftName;
public final TextView tvIntroduce;
public final ProgressBar progressbar;
public final TextView tvDonwload;
public final View view; private ViewHolder(RelativeLayout rootView, ImageView ivSoftIcon, TextView tvSoftName, TextView tvIntroduce, ProgressBar progressbar, TextView tvDonwload, View view) {
this.rootView = rootView;
this.ivSoftIcon = ivSoftIcon;
this.tvSoftName = tvSoftName;
this.tvIntroduce = tvIntroduce;
this.progressbar = progressbar;
this.tvDonwload = tvDonwload;
this.view = view;
} public static ViewHolder create(RelativeLayout rootView) {
ImageView ivSoftIcon = (ImageView) rootView.findViewById(R.id.iv_soft_icon);
TextView tvSoftName = (TextView) rootView.findViewById(R.id.tv_soft_name);
TextView tvIntroduce = (TextView) rootView.findViewById(R.id.tv_introduce);
ProgressBar progressbar = (ProgressBar) rootView.findViewById(R.id.progressbar);
TextView tvDonwload = (TextView) rootView.findViewById(R.id.tv_donwload);
View view = (View) rootView.findViewById(R.id.view);
return new ViewHolder(rootView, ivSoftIcon, tvSoftName, tvIntroduce, progressbar, tvDonwload, view);
}
} GlideRoundTransform glideRoundTransform;
ListView mListView; public ListView getListView() {
return mListView;
} @Override
public View getView(int position, View convertView, ViewGroup parent) {
mListView = (ListView) parent;
final ViewHolder vh;
if (convertView == null) {
View view = mInflater.inflate(R.layout.fragment_discovery_item, parent, false);
vh = ViewHolder.create((RelativeLayout) view);
view.setTag(vh);
} else {
vh = (ViewHolder) convertView.getTag();
} DiscoveryModel item = (DiscoveryModel) getItem(position);
vh.tvSoftName.setText(item.getSoftName());
vh.tvIntroduce.setText(item.getSoftBrief());
Glide.with(getContext()).load(item.getSoftLogo()).transform(this.glideRoundTransform).into(vh.ivSoftIcon);
vh.progressbar.setProgress(item.getCompletePercent());
switch (item.getStatus()) {
case DiscoveryModel.DOWNLOADING:
vh.tvDonwload.setText(item.getCompletePercent() + "%");
break;
case DiscoveryModel.NORMAL:
vh.tvDonwload.setText("下载");
break;
case DiscoveryModel.PAUSE:
vh.tvDonwload.setText("暂停");
break;
} vh.tvDonwload.setOnClickListener(onClickListener);
vh.tvDonwload.setTag(R.id.progressbar, vh.progressbar);
vh.tvDonwload.setTag(R.id.tag_progress_bar, vh.progressbar);
vh.tvDonwload.setTag(R.id.tag_positon, position);
vh.progressbar.setTag(R.id.tag_url); return vh.rootView;
} private LayoutInflater mInflater; // Constructors
public DiscoveryAdapter(Context context, List<Object> objects) {
super(context, 0, objects);
this.mInflater = LayoutInflater.from(context);
glideRoundTransform = new GlideRoundTransform(getContext());
} public DiscoveryAdapter(Context context, Object[] objects) {
super(context, 0, objects);
this.mInflater = LayoutInflater.from(context);
glideRoundTransform = new GlideRoundTransform(getContext());
} public View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (v instanceof TextView) {
int pos = (int) v.getTag(R.id.tag_positon);
DiscoveryModel item = (DiscoveryModel) getItem(pos);
if (item.getStatus() == DiscoveryModel.NORMAL) {
((TextView) v).setText("0%");
new FileDownLoaderTask(DiscoveryAdapter.this, pos);
item.setStatus(DiscoveryModel.DOWNLOADING);
} else if (item.getStatus() == DiscoveryModel.DOWNLOADING) {
item.setStatus(DiscoveryModel.PAUSE);
}
}
}
};
}
laoder模拟
package com.loopbanner; import android.os.Handler;
import android.os.Message;
import android.widget.ProgressBar;
import android.widget.TextView; /**
* 本类负责,下载数据,并更新UI
* 更新逻辑为:当前类保存apapter引用。获取数据后,如果本类所属的item是可见的,
* 则更新progressbar
*/ public class FileDownLoaderTask {
private String mUrl;
private int process = 0;
private DiscoveryAdapter mAdapter;
private int mPosition; public FileDownLoaderTask(DiscoveryAdapter adapter, int position) {
mAdapter = adapter;
mUrl = ((DiscoveryModel) adapter.getItem(position)).getSoftDown();
mHandler.sendEmptyMessageDelayed(1, 500);
mPosition = position;
} private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
process += 1;
((DiscoveryModel) mAdapter.getItem(mPosition)).setCompletePercent(process);
int firstItem = mAdapter.getListView().getFirstVisiblePosition();
int lastItem = mAdapter.getListView().getLastVisiblePosition();
if (((DiscoveryModel) mAdapter.getItem(mPosition)).getStatus() == DiscoveryModel.DOWNLOADING && firstItem <= mPosition && mPosition <= lastItem) {
for (int i = 0; i < mAdapter.getListView().getChildCount(); i++) {
if ((int) (mAdapter.getListView().getChildAt(i).findViewById(R.id.tv_donwload).getTag(R.id.tag_positon)) == mPosition) {
ProgressBar progressBar = (ProgressBar) mAdapter.getListView().getChildAt(i)
.findViewById(R.id.progressbar);
progressBar.setProgress(process);
TextView tv_donwload = (TextView) mAdapter.getListView().getChildAt(i)
.findViewById(R.id.tv_donwload);
progressBar.setProgress(process);
tv_donwload.setText(process + "%");
}
}
}
if (process != 100) {
mHandler.sendEmptyMessageDelayed(1, 500);
}
}
};
}
资源
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<corners android:radius="12.5dip" />
<solid android:color="#fff" />
<stroke
android:width="1dp"
android:color="#F88C08" />
</shape>
</item>
<item android:id="@android:id/secondaryProgress">
<clip>
<shape>
<corners android:radius="12.5dip" />
<gradient
android:angle="270"
android:centerColor="#EE5C42"
android:centerY="0.75"
android:endColor="#EE5C42"
android:startColor="#EE5C42" />
</shape>
</clip>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<corners android:radius="12.5dip" />
<solid android:color="#FEE2CD" />
<stroke
android:width="1dp"
android:color="#F88C08" />
</shape>
</clip>
</item>
</layer-list>
ListView ,recycleView列表带进度条的更多相关文章
- [Delphi]带进度条的ListView
带进度条的ListView unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, C ...
- Qt带进度条的启动界面(继承QSplashScreen,然后使用定时器)
通过继承QSplashScreen类,得到CMySplashScreen类,然后在CMySplashScreen中定义QProgressBar变量,该变量以CMySplashScreen为父类,这样就 ...
- wxpython StatuBar 带进度条的状态栏
# -*- coding: utf- -*- import wx class customStatusBar(wx.StatusBar): def __init__(self, parent): wx ...
- [整理] C#调用SQLDMO.DLL时间数据库备份 / 还原。 (香神无涯) // C#实现SQLSERVER2000数据库备份还原的两种方法 (带进度条)
/// <summary>/// 通过调用MSSQL的SQLDMO.DLL文件来实现备份数据库/// 1.首先在在项目中引用SQLDMO.DLL文件./// 2.在引用中的SQLDMO.D ...
- Android更新带进度条的通知栏
在网上查询了下.Android版本号更新通知栏带进度条,醉了,基本都是复制过来.有的代码不全,连源代码下载都没有.有下载也须要积分,还不能用,真黑心啊!!之前自己也写过自己定义通知栏Notificat ...
- linux 复 带进度条
rsync命令 #rsync -av --progress /mnt/yidong2/full20100526.tar.gz /mnt/yidong1/ 可以实现本机带进度条提示拷贝,可以实现不同机器 ...
- java进行文件上传,带进度条
网上看到别人发过的一个java上传的代码,自己写了个完整的,附带源码 项目环境:jkd7.tomcat7. jar包:commons-fileupload-1.2.1.jar.commons-io-1 ...
- 赞!带进度条的 jQuery 文件拖放上传插件
jQuery File Uploader 是一个 jQuery 文件拖放上传插件,包括 Ajax 上传和进度条效果.作者编写这个插件的想法是要保持它非常简单,不像其他的插件,很多的标记,并提供一些 H ...
- Extjs 使用fileupload插件上传文件 带进度条显示
一.首先我们看看官方给出的插件的解释: 一个文件上传表单项具有自定义的样式,并且可以控制按钮的文本和 像文本表单的空文本类似的其他特性. 它使用一个隐藏的文件输入元素,并在用户选择文件后 在form提 ...
随机推荐
- win10 UWP 蜘蛛网效果
我看见了知乎首页登录背景和普通的地球人写的博客,发现了个好看的效果. 那么我来告诉大家如何做这个效果. 第一步是在 Canvas 画点,第二步是让点移动,第三步是画线 在 Canvas 画一个点 我们 ...
- ST40 自制 JTAG 适配器
// 文章首发于 https://zhuanlan.zhihu.com/p/28762429 // 但是不知道为什么搜索引擎还没有收录,便在博客再次发布. 0. 引言 意法半导体生产的 SH4 架构的 ...
- jsp中的盲区-面试可能会问到的东西
1.今天看到一个有趣的JSP题目. <body> This is my JSP page. <br> <% int a = 10; %&g ...
- (转)log4j使用介绍
原文出自: log4j使用介绍 日志是应用软件中不可缺少的部分,Apache的开源项目Log4j是一个功能强大的日志组件,提供方便的日志记录.以下是个人经验,具体请参考Log4j文档指南. Log4j ...
- [Java第一课]环境变量的配置以及eclipse一些常用快捷键
1.环境变量的配置(这里对xp系统电脑来说:) 首先安装jdk软件. 然后在我的电脑(右键)-->属性-->高级-->环境变量-->系统变量(注意)-->新建(新建两个p ...
- JAVA基础知识总结:六
一.不定长参数 1.语法:数据类型... 变量名称 使用注意事项:a.不定长参数就相当于是一个数组 b.不定长参数只能出现在参数列表的最后面 c.一个函数的参数列表中只能出现一次不定长参数 d.对于不 ...
- PHP操作Memcached
一.PHP连接Memcached: 一个简单的使用示例: $memcache = new Memcache; $memcache->connect("127.0.0.1",1 ...
- 温故而知新 Volley源码解读与思考
相比新的网络请求框架Volley真的很落后,一无是处吗,要知道Volley是由google官方推出的,虽然推出的时间很久了,但是其中依然有值得学习的地方. 从命名我们就能看出一些端倪,volley中 ...
- Vim - 常用配置
基本配置 不用任何插件的情况下,先按如下配置: set nu syntax on set hlsearch set tabstop=4 set shiftwidth=4 set expandtab s ...
- VS2008中C#开发webservice简单实例
1.创建工程 文件-> 新建->网站 如下图. 工程建好后,会自动添加如下代码: using System; using System.Linq; using System.Web; us ...