实现上图功能有两种思路。

一:普通做法,更新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列表带进度条的更多相关文章

  1. [Delphi]带进度条的ListView

    带进度条的ListView unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, C ...

  2. Qt带进度条的启动界面(继承QSplashScreen,然后使用定时器)

    通过继承QSplashScreen类,得到CMySplashScreen类,然后在CMySplashScreen中定义QProgressBar变量,该变量以CMySplashScreen为父类,这样就 ...

  3. wxpython StatuBar 带进度条的状态栏

    # -*- coding: utf- -*- import wx class customStatusBar(wx.StatusBar): def __init__(self, parent): wx ...

  4. [整理] C#调用SQLDMO.DLL时间数据库备份 / 还原。 (香神无涯) // C#实现SQLSERVER2000数据库备份还原的两种方法 (带进度条)

    /// <summary>/// 通过调用MSSQL的SQLDMO.DLL文件来实现备份数据库/// 1.首先在在项目中引用SQLDMO.DLL文件./// 2.在引用中的SQLDMO.D ...

  5. Android更新带进度条的通知栏

    在网上查询了下.Android版本号更新通知栏带进度条,醉了,基本都是复制过来.有的代码不全,连源代码下载都没有.有下载也须要积分,还不能用,真黑心啊!!之前自己也写过自己定义通知栏Notificat ...

  6. linux 复 带进度条

    rsync命令 #rsync -av --progress /mnt/yidong2/full20100526.tar.gz /mnt/yidong1/ 可以实现本机带进度条提示拷贝,可以实现不同机器 ...

  7. java进行文件上传,带进度条

    网上看到别人发过的一个java上传的代码,自己写了个完整的,附带源码 项目环境:jkd7.tomcat7. jar包:commons-fileupload-1.2.1.jar.commons-io-1 ...

  8. 赞!带进度条的 jQuery 文件拖放上传插件

    jQuery File Uploader 是一个 jQuery 文件拖放上传插件,包括 Ajax 上传和进度条效果.作者编写这个插件的想法是要保持它非常简单,不像其他的插件,很多的标记,并提供一些 H ...

  9. Extjs 使用fileupload插件上传文件 带进度条显示

    一.首先我们看看官方给出的插件的解释: 一个文件上传表单项具有自定义的样式,并且可以控制按钮的文本和 像文本表单的空文本类似的其他特性. 它使用一个隐藏的文件输入元素,并在用户选择文件后 在form提 ...

随机推荐

  1. hashlib模块--摘要算法

    算法介绍: Python的hashlib提供了常见的摘要算法:MD5,SHA()等. 摘要算法,又称哈希算法,散列算法.通过一个函数,吧任意长度的字符串转换为固定长度的字符串(16进制) 摘要算法就是 ...

  2. nexus3 添加第三方本地文件jar到仓库

    因为nexus3和nexus2手动上传第三方jar有点区别 故记录一下. 如上传京东 open-api-sdk-2.0.jar 首先创建一个目录 方便执行上传的时候url参数 也可以不创建 mkdir ...

  3. 80806汇编(5)——[BX]和Loop指令

    80806汇编(5)--[BX]和Loop指令 已经好久没写点东西了,国庆节就一直想弄个个人网站,这段时间一直在弄那个,虽然有现成的框架(Hexo),但是总想弄出自己的效果来,但是最后还是有些差强人意 ...

  4. js面向对象知识点之对象属性 创建对象 总结中

    昨天面试出了一道面试题 本人我做错了 于是痛定思痛 再过一遍面向对象 var name="一体机"; var value="infolist"; //构造函数 ...

  5. cmd 编译java WebService

    格式:wsimport -s "src目录" -p "生成类所在包名" -keep "wsdl发布地址" 示例: wsimport -s G ...

  6. MySQL冗余和重复索引

    MySQL允许在相同列上创建多个索引,无论是有意还是无意,mysql需要单独维护重复的索引,并且优化器在优化查询的时候也需要逐个地进行考虑,这会影响性能. 重复索引是指的在相同的列上按照相同的顺序创建 ...

  7. [转]Java - 集合框架完全解析

    数据结构是以某种形式将数据组织在一起的集合,它不仅存储数据,还支持访问和处理数据的操作.Java提供了几个能有效地组织和操作数据的数据结构,这些数据结构通常称为Java集合框架.在平常的学习开发中,灵 ...

  8. Windows环境下多线程编程原理与应用读书笔记(1)————基本概念

    自从学了操作系统知识后,我就对多线程比较感兴趣,总想让自己写一些有关多线程的程序代码,但一直以来,发现自己都没怎么好好的去全面学习这方面的知识,仅仅是完成了操作系统课程上的小程序,对多线程的理解也不是 ...

  9. Problem V

    Problem Description The aspiring Roy the Robber has seen a lot of American movies, and knows that th ...

  10. Constructing Roads(最小生成树)

    Constructing Roads Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) ...