你还在用notifyDataSetChanged? 你已经out了
帖子中讲述的项目使用了listView这个控件,而且自定义了adapter。在更新item的进度条时发现每次使用notifyDataSetChanged(),都会去调用自定义adapter中的getView方法。这时问题就出现了,用notifyDataSetChanged方法去更新listView中的item,是更新需要更新的Item呢?还是更新所有的item呢?如果是更新所有的item那么效率不就会很低吗?有什么办法可以解决这个问题呢?
怀着心中的疑惑,我开始了这次的实验。。。
我的想法很简单现实模拟远程下载文件,创建一个Activity做主界面,主界面采用listView。然后自定义一个adapter实现BaseAdapter,再创建一个线程类,线程类当中采用循环的方式不断的往adapter发送消息.然后使用notifyDataSetChanged方法更新界面,在调用getView方法时在控制台输出语句,这样我就可以知道notifyDatatSetChanged方法执行时是更新一个item还是更新所有的item了。
有了思路就好办了,我们先建立一个类,叫FileState。
package edu.notify.viking.entity;public class FileState
{
String fileName;//文件名字
int completeSize;//完成的长度
boolean state;//文件状态,true为已经完成,false为未完成
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public int getCompleteSize() {
return completeSize;
}
public void setCompleteSize(int completeSize) {
this.completeSize = completeSize;
}
public boolean isState() {
return state;
}
public void setState(boolean state) {
this.state = state;
}}
这个类中有3个属性,分别是文件名字,文件已经下载的长度,还有文件当前的状态。然后就是get与set方法。这个类的作用我想大家应该一眼就明白了,没错,既然使用了listView,我在主界面就想着要定义一个List,这个list当中的内容当然就是我们FileState啦。
接着我们去实现主界面。创建主界面MainActivity
package edu.notify.viking.activity;import java.util.ArrayList;
import java.util.List;import edu.notify.viking.adapter.MyAdapter;
import edu.notify.viking.entity.FileState;import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;public class MainActivity extends Activity
{
private List<FileState> list=new ArrayList<FileState>();
private ListView listView;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
initFileState();//先对FileState进行初始化
initUI();//对界面进行初始化
}/**
* 把数据放进list中,因为是测试所以我手工添加数据
* **/
private void initFileState()
{
//给FileState赋值
for(int i =1;i<8;i++)
{
FileState fileState=new FileState();
fileState.setFileName(i+".mp3");//名字
fileState.setCompleteSize(100);//初始化下载程度
fileState.setState(true);
list.add(fileState);
}
FileState f=new FileState();
f.setFileName("8.mp3");
f.setCompleteSize(0);
f.setState(false);
list.add(f);
}private void initUI()
{
listView = (ListView)this.findViewById(R.id.listview);
MyAdapter adapter = new MyAdapter(list,this);
listView.setAdapter(adapter);
adapter.setListView(listView);
}
}
接着我创建了自定义的adapter,并将他与listView绑定在一起。然后将listView传进了adapter中。
那我们来看看adapter中是如何实现的吧。新建一个adapter,取名叫MyAdatper继承BaseAdapter.
<font face="宋体">package edu.notify.viking.adapter;import java.util.List;import edu.notify.viking.activity.R;
import edu.notify.viking.down.Downloader;
import edu.notify.viking.entity.FileState;import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;public class MyAdapter extends BaseAdapter
{
private List<FileState> list;
private Context context;
private LayoutInflater inflater=null;
private ListView listView;
private Handler mHandler = new Handler()
{@Override
public void handleMessage(Message msg)
{
if(msg.what==1)
{
String name=(String)msg.obj;
int length=msg.arg1;
for(int i=0;i<list.size();i++)
{
FileState fileState=list.get(i);
if(fileState.getFileName().equals(name))
{
fileState.setCompleteSize(length);
list.set(i, fileState);break;
}
}
notifyDataSetChanged();
}
}};
public MyAdapter(List<FileState> list,Context context)
{
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.list=list;
}class ViewHolder
{
public TextView fileName;//文件名称
public ProgressBar progressBar;//进度条
public TextView percent;//百分比
public ImageView down;//下载
}public int getCount()
{
// TODO Auto-generated method stub
return list.size();
}public Object getItem(int position)
{
// TODO Auto-generated method stub
return list.get(position);
}public long getItemId(int position)
{
// TODO Auto-generated method stub
return position;
}public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder holder;
if(convertView==null)
{
convertView=inflater.inflate(R.layout.main_item, null);
holder=new ViewHolder();
holder.fileName=(TextView)convertView.findViewById(R.id.fileName);
holder.progressBar=(ProgressBar)convertView.findViewById(R.id.down_progressBar);
holder.percent = (TextView) convertView.findViewById(R.id.percent_text);
holder.down = (ImageView) convertView.findViewById(R.id.down_view);
convertView.setTag(holder);
}
else
{
holder=(ViewHolder)convertView.getTag();
}
FileState fileState=list.get(position);
final String name = fileState.getFileName();
System.out.println(name+"---run getView");
//如果文件状态为已经下载
if(fileState.isState()==true)
{
holder.fileName.setText(fileState.getFileName());
//下载完成的文件,进度条被隐藏
holder.progressBar.setVisibility(ProgressBar.INVISIBLE);
//设置为已下载
holder.percent.setText("已下载");
//下载完成的文件,下载按钮被隐藏,防止重复下载
holder.down.setVisibility(ImageView.INVISIBLE);
}
else
{
holder.fileName.setText(fileState.getFileName());
holder.progressBar.setVisibility(ProgressBar.VISIBLE);
holder.progressBar.setProgress(fileState.getCompleteSize());
holder.percent.setText(fileState.getCompleteSize()+"%");
holder.down.setOnClickListener(new View.OnClickListener()
{public void onClick(View v)
{
Downloader down= new Downloader(name,mHandler);
down.download();
}});
if(fileState.getCompleteSize()==100)
{
holder.progressBar.setVisibility(ProgressBar.INVISIBLE);
holder.percent.setText("已下载");
holder.down.setVisibility(ProgressBar.INVISIBLE);
fileState.setState(true);
list.set(position, fileState);
}}
return convertView;
}public void setListView(ListView listView) {
this.listView = listView;
}}
</font>
为了运行的效率,我在adapter中定义了一个内部类,ViewHolder,其中的属性都是我们要绘制出来的控件。主要的绘制工作在于getView这个方法,在getView中我们对变量初始化以后,就将list当中对应的FileState拿了出来,根据文件的状态进行了分别的处理,下载完成的怎么怎么显示。。。下载为完成的怎么怎么显示。。。这些都不难,很容易就看明白了。在这里面我们实现了下载按钮这个点击事件,这个事件中的代码也很简单,就是创建一个Downloader对象,然后调用其中的download方法进行下载。在这个类中我们看到,其中创建了一个Handler对象,并在里面实现它的消息处理方法handleMessage。当我们点击下载图标时会把这个Handler对象传进Downloader这个类中。当文件开始下载时,下载完成的数据长度将会传到handlerMessage这个方法中被处理,我们在这个方法中对FileState与list做了更新,然后调用了notifyDatatSetChanged方法.
接下来我们来看最后一个类。创建Downloader类。
package edu.notify.viking.down; import java.util.Map;import android.os.Handler; import android.os.Message;public class Downloader { private String fileName; private Handler mHandler; public Downloader(String fileName, Handler handler) { super(); this.fileName = fileName; mHandler = handler; }public void download() { new MyThread().start(); }class MyThread extends Thread {@Override public void run() { for(int i=0;i<=100;i++) { System.out.println("i:"+i); try { this.currentThread().sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Message msg=Message.obtain(); msg.what=1; msg.obj=fileName; msg.arg1=i; mHandler.sendMessage(msg); } }} }
先是main.xml
<font face="宋体"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ListView
android:id="@+id/listview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fastScrollEnabled="true"
/>
</LinearLayout>
</font>
复制代码
<font face="宋体"><?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:id="@+id/fileName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
<ProgressBar
android:id="@+id/down_progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
style="@android:style/Widget.ProgressBar.Horizontal"
/>
<TextView
android:id="@+id/percent_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<ImageView
android:id="@+id/down_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/down"
/>
</LinearLayout></font>
复制代码
下载附件(362.59 KB)
从图中我们可以看见,尽管我们只是想更新8.mp3这个item的进度条,但是所有的进度条都被更新了,每次使用notifyDataSetChanged方法,对会调用8次getView方法。天哪。这个效率。。。
这不是我们想要的,我们想要的是什么?我们想要的就是如果只需要更新8.mp3这个item,那么其他的item将保持不变,不需要更新他们。那么我们该怎么解决这个问题呢?
其实这个问题的解决也不麻烦,所谓难者不会,会者不难啊。我们只需要修改adapter这个类,在其中添加一个方法即可。现在我们来看更新后的adapter类。
<font face="宋体">package edu.notify.viking.adapter;import java.util.List;import edu.notify.viking.activity.R;
import edu.notify.viking.down.Downloader;
import edu.notify.viking.entity.FileState;import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;public class MyAdapter extends BaseAdapter
{
private List<FileState> list;
private Context context;
private LayoutInflater inflater=null;
private ListView listView;
private Handler mHandler = new Handler()
{@Override
public void handleMessage(Message msg)
{
if(msg.what==1)
{
String name=(String)msg.obj;
int length=msg.arg1;
for(int i=0;i<list.size();i++)
{
FileState fileState=list.get(i);
if(fileState.getFileName().equals(name))
{
fileState.setCompleteSize(length);
list.set(i, fileState);
updateView(i);//用我们自己写的方法
break;
}
}
//notifyDataSetChanged();不用了
}
}};
public MyAdapter(List<FileState> list,Context context)
{
inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.list=list;
}class ViewHolder
{
public TextView fileName;//文件名称
public ProgressBar progressBar;//进度条
public TextView percent;//百分比
public ImageView down;//下载
}/**
* 用于更新我们想要更新的item
* @param itemIndex 想更新item的下标
* **/
private void updateView(int itemIndex)
{
//得到第1个可显示控件的位置,记住是第1个可显示控件噢。而不是第1个控件
int visiblePosition = listView.getFirstVisiblePosition();
//得到你需要更新item的View
View view = listView.getChildAt(itemIndex - visiblePosition);
FileState fileState=list.get(itemIndex);
final String name=fileState.getFileName();
System.out.println(name+"---run updateView");
if(fileState.isState()==false)
{
ViewHolder holderOne=new ViewHolder();
//start:初始化
holderOne.fileName=(TextView)view.findViewById(R.id.fileName);
holderOne.progressBar=(ProgressBar)view.findViewById(R.id.down_progressBar);
holderOne.percent = (TextView) view.findViewById(R.id.percent_text);
holderOne.down = (ImageView) view.findViewById(R.id.down_view);
//end
holderOne.fileName.setText(fileState.getFileName());
holderOne.progressBar.setVisibility(ProgressBar.VISIBLE);
holderOne.progressBar.setProgress(fileState.getCompleteSize());
holderOne.percent.setText(fileState.getCompleteSize()+"%");
holderOne.down.setOnClickListener(new View.OnClickListener()
{public void onClick(View v)
{
Downloader down= new Downloader(name,mHandler);
down.download();
}});
if(fileState.getCompleteSize()==100)
{
holderOne.progressBar.setVisibility(ProgressBar.INVISIBLE);
holderOne.percent.setText("已下载");
holderOne.down.setVisibility(ProgressBar.INVISIBLE);
fileState.setState(true);
list.set(itemIndex, fileState);
}
}
} public int getCount()
{
// TODO Auto-generated method stub
return list.size();
}public Object getItem(int position)
{
// TODO Auto-generated method stub
return list.get(position);
}public long getItemId(int position)
{
// TODO Auto-generated method stub
return position;
}public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder holder;
if(convertView==null)
{
convertView=inflater.inflate(R.layout.main_item, null);
holder=new ViewHolder();
holder.fileName=(TextView)convertView.findViewById(R.id.fileName);
holder.progressBar=(ProgressBar)convertView.findViewById(R.id.down_progressBar);
holder.percent = (TextView) convertView.findViewById(R.id.percent_text);
holder.down = (ImageView) convertView.findViewById(R.id.down_view);
convertView.setTag(holder);
}
else
{
holder=(ViewHolder)convertView.getTag();
}
FileState fileState=list.get(position);
final String name = fileState.getFileName();
System.out.println(name+"---run getView");
//如果文件状态为已经下载
if(fileState.isState()==true)
{
holder.fileName.setText(fileState.getFileName());
//下载完成的文件,进度条被隐藏
holder.progressBar.setVisibility(ProgressBar.INVISIBLE);
//设置为已下载
holder.percent.setText("已下载");
//下载完成的文件,下载按钮被隐藏,防止重复下载
holder.down.setVisibility(ImageView.INVISIBLE);
}
else
{
holder.fileName.setText(fileState.getFileName());
holder.progressBar.setVisibility(ProgressBar.VISIBLE);
holder.progressBar.setProgress(fileState.getCompleteSize());
holder.percent.setText(fileState.getCompleteSize()+"%");
holder.down.setOnClickListener(new View.OnClickListener()
{public void onClick(View v)
{
Downloader down= new Downloader(name,mHandler);
down.download();
}});
if(fileState.getCompleteSize()==100)
{
holder.progressBar.setVisibility(ProgressBar.INVISIBLE);
holder.percent.setText("已下载");
holder.down.setVisibility(ProgressBar.INVISIBLE);
fileState.setState(true);
list.set(position, fileState);
}}
return convertView;
}public void setListView(ListView listView) {
this.listView = listView;
}}
</font>
下载附件(85.08 KB)
看到红色箭头的地方就是修改过的。然后多添加了一个updateView方法。这个方法需要传入你想更新的item下标。
最核心的地方是这2句代码。
下载附件(21.68 KB)
主要的意思就是根据你想要更新的item下标去得到这个item的View对象,这样你就可以为所欲为啦。哈哈哈哈哈。。。
咱们来看看运行效果。
下载附件(280.63 KB)
这里我改为同时更新2个item,看看这样会不会出错,可以看到,运行的很正常。控制台的打印结果也只是更新了7-8.mp3这2个item。
好了,文章到此已经结束了,肯定有人要骂我了,这么简单的东西,你啰嗦了半天。整的我都没怎么听懂。
你还在用notifyDataSetChanged? 你已经out了相关推荐
- 你还在用notifyDataSetChanged?
2019独角兽企业重金招聘Python工程师标准>>> 想到发这篇帖子是源于我的上一篇帖子 #Testin杯#多线程断点续传后台下载 . 帖子中讲述的项目使用了listView这个 ...
- android view刷新界面,优雅地刷新RecyclerView
前言 还是那句话,RecyclerView的viewType增多,逻辑变复杂,几个月后,你确定还能理清思路吗?假设我们服务端是多个接口返回数据,你确定能正确刷新相应type吗?想一个RecyclerV ...
- 从源代码的角度分析--在BaseAdapter调用notifyDataSetChanged()之后发生了什么
导师安排我做一个小项目,其中涉及到利用Adapter作为ListView的适配器,为ListView提供数据.选中某一项后,要让这一项变成选中状态,也就是背景图片要换一下.下面我就用一个小例子来模拟. ...
- android 解决listview.notifyDataSetChanged刷新时Imageloader加载图片闪烁问题
今天,简单讲讲android里再listview调用notifyDataSetChanged刷新界面时,Imageloader加载图片会闪烁的问题. 最近,发现app里的listview图片会出现闪烁 ...
- 安卓笔记-视频版(还没学完)
/Users/yangyangzi/Desktop/YangZi2/android/Android12/2019/视频学习.rtfd (公司电脑) 视频 一.控件:TextView:标签,用于显示内容 ...
- notifyDataSetChanged使用总结
在做客户端项目时,想使用notifidatasetchanged来刷新ListView,但是一直报错, The content of the adapter has changed but ListV ...
- Android中notifyDataSetInvalidated()和notifyDataSetChanged()
首先看一下notifyDataSetChanged与notifyDataSetInvalidated的区别: 我们打开官方的源码: /*** Notifies the attached observe ...
- Android记录15--关于ListView中adapter调用notifyDataSetChanged无效的原因
Android记录15--关于ListView中adapter调用notifyDataSetChanged无效的原因 2014年1月16日 开发记录 话说这个问题已经困扰我很久了,一直找不到原因,我以 ...
- DataBinding快速入门(还在用findViewById?)
一.DataBinding介绍 了解一门新技术,一般从2W1H入手: 是什么?(What) 有什么用?(What) 怎么用?(How) DataBinding翻译过来就是数据绑定,把数据绑定在控件上. ...
最新文章
- python与c语言数据交互,python与c语言交互应用实例
- 基于redis的悲观锁实现
- Django从理论到实战(part7)--关于视图函数与URL映射
- jpush推送格式 swift_Swift中配置极光推送
- python循环for不从零开始_Python-多处理-巨大的for循环
- 提升用户体验,你不得不知道的事儿——三种提醒框的微技巧
- 机器学习近年来之怪现状
- 串口接收到的字符型数据如何转化成数字
- 【Shiro】六、Apache Shiro Session管理
- iOS App图标和启动画面尺寸http://www.jianshu.com/p/adpKye
- 智能家居系统c语言源代码,智能家居软件设计(附源程序).doc
- SpringCloud微服务架构前后端分离项目实践
- java流程图怎么画_计算机流程图怎么画
- MockingBrid(AI拟声)教程
- Golang底层原理剖析之panic与recover
- 有5个人坐在一起,问第5个人多少岁?他说比第4个人大2岁
- yolox的正负样本分配策略mmdet代码详解
- whistle安装,成功率高
- 敏感信息识别方法探究
- Java人员该如何站稳脚跟 需要具备哪些技能