这几天一直在为了这个Listview嵌套Progressbar的情况伤脑筋,可是项目中又不能少了这些功能,上周老大说要在项目中增加一个图片上传的功能,我一听,这好啊,可以实现客户与服务器的交互,又能增加客户体验,不是两全其美么?哈哈,不过问题来了,在我使用Progressbar在Listview中的时候发现并不是自己想象的那么顺利,因为牵扯到很多UI错乱的情况,这可急坏我了,怎么办?只能自己好想办法解决了
我相信你大家遇到这样的情况也同样很头疼,接下来就让我们一起探讨,怎样在ListView中嵌套,效果类似于QQ空间的图片上传效果:
先看item的布局文件吧:
<?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="match_parent"
android:layout_margin="@dimen/margin_10"
android:orientation="horizontal" >
<ImageView
android:id="@id/iv_photo"
android:layout_width="@dimen/layout_80"
android:layout_height="@dimen/layout_80"
android:layout_gravity="center_vertical"
android:contentDescription="@string/hello_world"
android:scaleType="centerCrop" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="vertical" >
<TextView
android:id="@+id/tv_photo_name"
style="@style/text_style"
android:layout_marginLeft="@dimen/margin_10"
android:layout_marginRight="@dimen/margin_10"
android:textColor="@color/black" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_5" >
<TextView
android:id="@+id/tv_progress"
style="@style/text_style"
android:layout_alignParentRight="true"
android:layout_marginLeft="@dimen/margin_10"
android:layout_marginRight="@dimen/margin_10"
android:textColor="@color/black"
android:visibility="invisible" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_10"
android:layout_marginRight="@dimen/margin_10" >
<ProgressBar
android:id="@+id/progressbar"
style="@style/StyleProgressBarMini"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/shape_progressbar_bg"
android:visibility="gone" />
<TextView
android:id="@+id/tv_loading"
style="@style/text_style"
android:text="@string/loading"
android:textColor="@color/black" />
</RelativeLayout>
</LinearLayout>
</LinearLayout>
布局文件很简单,就是一个进度条,外加一个图片控件,还有几个textview,主要是用来模仿QQ空间上传效果,每次只对第一个图片进行上传,其余的图片进行等待,第一张上传完毕后从list中移除,并且刷新Adapter
在实现这部分功能的时候还需要增加一个实体类,来保存下载状态,和进度条的进度,实体类如下:
public class UploadPhoto extends Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String path; //图片路径
private boolean isDownload; //下载状态
private int progress; //进度
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public boolean isDownload() {
return isDownload;
}
public void setDownload(boolean isDownload) {
this.isDownload = isDownload;
}
public int getProgress() {
return progress;
}
public void setProgress(int progress) {
this.progress = progress;
}
}
我们再看一下这个时候的adapter是怎么样写:
public class UploadPhotoAdapter extends BaseAdapter{
ViewHolder holder = null;
private List<UploadPhoto> list = new ArrayList<UploadPhoto>();
private Context mContext;
public UploadPhotoAdapter(Context mContext, List<UploadPhoto> list) {
this.list = list;
this.mContext = mContext;
int maxMemory = (int)Runtime.getRuntime().maxMemory();
int chcheSize = maxMemory / 8;
FileUtils fileUtil = new FileUtils(mContext, Common.CACHE_DIR);
utils = new BitmapUtils(mContext, fileUtil.getCacheDir(), chcheSize);
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null) {
LayoutInflater inflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.upload_photo_list_item, parent, false);
holder = new ViewHolder();
holder.tv_name = (TextView) view.findViewById(R.id.tv_photo_name);
holder.tv_loading = (TextView) view.findViewById(R.id.tv_loading);
holder.iv_photo = (ImageView) view.findViewById(R.id.iv_photo);
holder.tv_progress = (TextView) view.findViewById(R.id.tv_progress);
holder.progressbar = (ProgressBar) view.findViewById(R.id.progressbar);
LayoutParams mLayoutParams = holder.iv_photo.getLayoutParams();
mLayoutParams.width = (int) (Common.Width / 5);
mLayoutParams.height = (int) (Common.Width / 5);
holder.iv_photo.setLayoutParams(mLayoutParams);
view.setTag(holder);
} else {
holder = (ViewHolder) view.getTag();
}
UploadPhoto item = (UploadPhoto) getItem(position);
if (item != null) {
utils.display(holder.iv_photo, item.getPath());
String name = item.getPath().substring(item.getPath().lastIndexOf("/") + 1, item.getPath().lastIndexOf("."));
holder.tv_name.setText(name);
//这里对下载状态进行判断,是否显示进度条
if (item.isDownload()) {
holder.progressbar.setVisibility(View.VISIBLE);
holder.tv_loading.setVisibility(View.GONE);
holder.tv_progress.setVisibility(View.VISIBLE);
}else {
holder.progressbar.setVisibility(View.GONE);
holder.tv_loading.setVisibility(View.VISIBLE);
holder.tv_progress.setVisibility(View.GONE);
}
}
return view;
}
public class ViewHolder {
public TextView tv_name;
public ImageView iv_photo;
public ProgressBar progressbar;
public TextView tv_loading;
public TextView tv_progress;
}
/**
*
* 移除上传成功的item
* @param i
*
* 2016年7月28日
*
* ZhaoDongShao
*
*/
public void remove(int i) {
// TODO Auto-generated method stub
list.remove(i);
notifyDataSetChanged();
}
}
这个adapter很简单,就是个最普通不过的适配器,在适配器中小编用了基于Afinal的Xutils框架,不过目前该框架在6.0的系统上无法发挥效果,所以作者已经放弃维护,这里小编建议如果想继续使用Xutils的可以选择使用作者的新框架Xutil3,具体地址可以自行百度,如果想换个框架的话,小编推荐大家使用okhttp,这是一个非常好的框架,大家可以试试
上面的代码都是辅助代码,都是在实现功能的前提下所必需的,都不是很难,接下来我们一起看下核心的图片上传部分:
/**
* 上传的核心方法,可以直接调用
* @param i 当前要上传的索引
*
* 2016年7月28日
*
* ZhaoDongShao
*
*/
protected void UploadPhoto(final int i) {
// TODO Auto-generated method stub
//设置item的下载状态为true
lists.get(i).setDownload(true);
//判断当前上传的item的索引值是否在当前显示的屏幕里,getFirstVisiblePosition()获取可见的第一条item的索引,getLastVisiblePosition()获取可见的最后一个索引
if (i >= lv_upload.getFirstVisiblePosition() && i <= lv_upload.getLastVisiblePosition()) {
int position = i - lv_upload.getFirstVisiblePosition();
ProgressBar progressBar = (ProgressBar)lv_upload.getChildAt(position).findViewById(R.id.progressbar);
TextView tv_loading = (TextView)lv_upload.getChildAt(position).findViewById(R.id.tv_loading);
TextView tv_progress = (TextView)lv_upload.getChildAt(position).findViewById(R.id.tv_progress);
tv_loading.setVisibility(View.GONE);
tv_progress.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.VISIBLE);
}
if (httpUtils == null) {
httpUtils = new HttpUtils();
}
//小编这里使用的Xutil的网络框架,大部分框架基本都是大同小异
RequestParams rp = new RequestParams();
rp.addBodyParameter("householderid", poorFamily.getHouseholderid());
rp.addBodyParameter("file", new File(lists.get(i).getPath()));
//设置网络访问超时时间
httpUtils.configCurrentHttpCacheExpiry(1000 * 10);
httpUtils.send(HttpRequest.HttpMethod.POST, URLs.IMAGE_UP_LOAD,
rp, new RequestCallBack<String>() {
//上传成功后会调用此方法
@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
FileRequestList list = FileRequestList.parseToT(responseInfo.result, FileRequestList.class);
if (list != null) {
if (list.getSuccess()) {
if (adapter.getCount()>0) {
adapter.remove(i);
if (adapter.getCount() > 0) {
count_photo ++;
//上传完一张后,接着上传下一张
Message message = new Message();
message.what = 1;
Handler.sendMessage(message);
}else {
MyAdapter.mSelectedImage.clear();
showToast("成功上传" + count_photo + "张照片,重复" + count_photo_file +"张");
}
}
}else {
adapter.remove(i);
count_photo_file++;
Message message = new Message();
message.what = 1;
Handler.sendMessage(message);
showToast(list.getMsg());
}
}
System.out.println("上传成功");
}
@Override
public void onFailure(HttpException error, String msg) {
System.out.println("上传失败");
}
@Override
public void onLoading(long total, long current, boolean isUploading) {
int count = (int) ((current * 1.0 / total) * 100);
lists.get(i).setDownload(true);
lists.get(i).setProgress(count);
//这里和上面的代码差不多,原理也相似,这里的i表示数据及显示位置
if(i >= lv_upload.getFirstVisiblePosition() && i <= lv_upload.getLastVisiblePosition()) {
int positionInListView = i - lv_upload.getFirstVisiblePosition();
ProgressBar item = (ProgressBar) lv_upload.getChildAt(positionInListView)
.findViewById(R.id.progressbar);
TextView tv_progress = (TextView)lv_upload.getChildAt(positionInListView)
.findViewById(R.id.tv_progress);
tv_progress.setText(count + "%");
//设置进度条的进度
item.setProgress(count);
}
Log.i("上传 Progress>>>>>", "count=" + count + "--" + current + " / " + total);
}
});
首先来看看这个方法的参数:i表示在数据集中的位置,count表示进度,这个不难理解。接下来,我们通过更新list任务列表中对应条目的progress来保存一下现在的进度,但是,我们没有notify。接下来还是同样的逻辑,只不过,我们把控制ProgressBar显示的部分替换成了更新ProgressBar进度了。这样,我们就实现了,在不调用notifyDatasetChanged()的情况下来更新ListView的Item的目的。这样做有一个很明显的好处就是,每次,我们只更新一条item,其他的item我们并没有去更新,而notifyDatasetChanged的实现方式是,保存当前的位置,并更新所有的item,然后恢复位置。这样一比较,我们这种方式的优势就体现出来了。
有人可能还会有疑问,我们在滑动的时候怎么处理呢? 别忘了,我们在更新的前面都在list列表中更新了数据,所以在滑动的时候,我们交给Adapter.getView()去处理了。这样就很好理解了,所以用一个实体来保存上传的进度和状态,让我们在下一次滑动的时候能得到更好的更新ui。
因为我们这里要实现的是模仿QQ空间的图片上传效果,呢么就是每次都只是上传第一张图片,只有当第一张图片上传完毕后,再去调用上传,上传第二张图片,这里还用到了handler:
Handler Handler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 1:
//当第一张图片上传完毕之后,接着上传第二张
UploadPhoto(0);
break;
default:
break;
}
};
};
到这里ListView嵌套Progressbar就讲完了,其实也不是很复杂,主要就是要新建一个实体类来保存当前的进度。
效果图如下:
如果大家还有什么不懂的可以留言一起讨论,由于这是在公司项目中实现的,所就不附上demo了,以后有时间了。会及时附上demo