ListView ,recycleView列表带进度条

时间:2021-08-03 08:36:37

ListView ,recycleView列表带进度条

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

一:普通做法,更新item的数据,不停调用notifydatachange ;

二:各管自家刷新。

一个下载对应一个下载线程。线程持有对应item在Listview中的位置。当该线程所对应的item可见时,获得该Item的progressbar更新。

第二种方式相对省资源效率更高。

一步步来解决关键问题:

1.进度条实现

ListView ,recycleView列表带进度条

不熟悉进度条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>

效果如下:

ListView ,recycleView列表带进度条

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

ListView ,recycleView列表带进度条

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 + "%");
}
}
}

ListView ,recycleView列表带进度条


帖上所有代码

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>