Android学习笔记二十四之ListView列表视图二
2016-07-11 14:47
691 查看
Android学习笔记二十四之ListView列表视图二
前面一篇我们介绍了常用的几种适配器的简单实现和ListView的简单使用,这一篇中,我们介绍一下ListView的优化和一些其它的问题。ListView优化方法一
在ListView中,我们最常用的就是自定义Adapter,在我们自定义Adapter中,需要实现两个比较重要的方法getCount()和getView(),前者是负责计算ListView的总Item数,后者是生成Item,有多少个Item就会调用getView()方法多少次。getView()方法每次调用的时候都会重新inflate一个View出来返回去,但是对于ListView,只需要保留能够显示的最大的View的数目即可,而新的View可以复用消失的View。ListView给我们提供了可复用的View对象,在getView()方法里面,有一个参数View,这个就是可以复用的View对象。当参数View为null的时候,我们需要inflate一个View,当它不为null的时候,我们可以直接将他返回。例如:@Override public View getView(int i, View view, ViewGroup viewGroup) { ViewHolder viewHolder; //view为空的时候,inflate一个新的view if (view == null) { view = LayoutInflater.from(context).inflate(R.layout.item_base_adapter, null); viewHolder = new ViewHolder(); viewHolder.tv_base_adapter = (TextView) view.findViewById(R.id.tv_base_adapter); view.setTag(viewHolder); } else { viewHolder = (ViewHolder) view.getTag(); } viewHolder.tv_base_adapter.setText(datas[i]); //不为空,复用view return view; }
ListView优化方法二
上面介绍的是对View复用的优化,这样我们可以不必每一item都inflate一个新的view,可以通过复用,减小内存开销。下面我们介绍一个每一个AndroidAPP中都必不可少的操作,获取控件句柄,简单的说就是拿到id。在ListView中,我们inflate一个View,里面也有需要获取到组件id的,我们可以用ViewHolder来实现优化:具体的思路就是,我们在ViewHolder中存放我们需要的控件,在View为null的时候,需要inflate一个新的view,同时我们还new一个ViewHolder类的对象,并将findviewById的结果赋值给ViewHolder中对应的成员变量,我们可以调用View中setTag()方法,将ViewHolder和View绑定起来。当view不为null的时候,通过getTag()方法取出ViewHolder对象,这样就可以获得ViewHolder中的成员变量,也不再需要调用findViewById方法了。例如:
@Override public View getView(int i, View view, ViewGroup viewGroup) { ViewHolder viewHolder; if (view == null) { view = LayoutInflater.from(context).inflate(R.layout.item_base_adapter, null); //view为空,new一个ViewHolder对象 viewHolder = new ViewHolder(); //获取到ViewHolder对象中成员变量的id viewHolder.tv_base_adapter = (TextView) view.findViewById(R.id.tv_base_adapter); //调用setTag方法,将ViewHolder绑定到中 view.setTag(viewHolder); } else { //view不为空,调用getTag方法,取出保存的ViewHolder对象 viewHolder = (ViewHolder) view.getTag(); } viewHolder.tv_base_adapter.setText(datas[i]); return view; } static class ViewHolder { TextView tv_base_adapter; }
ListView优化方法三
上面介绍了两种ListView的优化方法,第二种优化效率根据google官方文档的解析,可以优化5%左右的效率。下面介绍一下第三种优化方法。在我们实际开发中,ListView显示的数据都是在网络中加载,假如网络比较好,能一次将所有的数据加载出来,这样用户体验还好,如果网络不好,那么加载数据需要时间比较久,用户体验就不好。另外,我们知道,虚拟机为每一个进程分配的内存是有限的,如果一下加载太多的数据就会出现内存溢出的情况。为了解决这两个问题,我们可以用分批加载的方法,但是分批加载还是不能完全解决问题。假如我有10万条数据需要加载,分批加载任然可能会出现OOM问题,这时,我们需要将数据分页加载,先分页加载,然后在分批加载,这样用户体验会好一些。
ListView出现的一些问题和解决
ListView焦点问题
在一些情况中,我们需要在ListView的Item中添加Button、EditText、CheckBox等控件,这就涉及到了焦点获取的问题。我们在ListView的Item中添加了Button按钮,点击发现,我们不能触发onItemClick和onItemLongClick方法,这就是ListView的焦点被拦截了。解决办法也很简单:给拦截ListView焦点的控件设置android:focusable=”false”属性或者代码调用setFocusable(false) 防范即可。还有一种方法就是在Item布局的根节点设置android:descendantFocusability=”blocksDescendants” 属性,这个属性有三个可选值
beforeDescendants:viewgroup会优先其子类控件而获取到焦点
afterDescendants:viewgroup只有当其子类控件不需要获取焦点时才获取焦点
blocksDescendants:viewgroup会覆盖子类控件而直接获得焦点
这是ListView焦点问题的解决方法。
ListView数据更新问题
首先实现一个简单的ListView布局代码:
<?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:orientation="vertical"> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:layout_marginTop="10dp" android:orientation="horizontal"> <Button android:id="@+id/btn_add_one" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="插入一条数据" /> <Button android:id="@+id/btn_add_on_position" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="在特定位置插入一条数据" /> </LinearLayout> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:layout_marginLeft="10dp" android:orientation="horizontal"> <Button android:id="@+id/btn_delete_one" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="根据对象删除" /> <Button android:id="@+id/btn_delete_on_position" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="根据位置删除" /> <Button android:id="@+id/btn_delete_all" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="删除所有数据" /> </LinearLayout> <ListView android:id="@+id/lv_data" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
Item布局代码:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" android:gravity="center_vertical" android:orientation="horizontal"> <ImageView android:id="@+id/iv_icon" android:layout_width="40dp" android:layout_height="40dp" android:src="@drawable/icon" /> <TextView android:id="@+id/tv_data_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="5dp" android:layout_toRightOf="@id/iv_icon" android:text="加载出来的数据" android:textSize="18sp" /> </RelativeLayout>
这里的Item比较简单,就直接是一张图片和一个文字
Activity代码:
package com.example.listviewdemo.activity; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.ListView; import com.example.listviewdemo.MYData; import com.example.listviewdemo.R; import com.example.listviewdemo.adapter.DataAdapter; import java.util.ArrayList; import java.util.List; /** * Created by Devin on 2016/7/11. */ public class DataUpActivity extends AppCompatActivity { private Button btn_add_one; private Button btn_add_on_position; private Button btn_delete_one; private Button btn_delete_on_position; private Button btn_delete_all; private ListView lv_data; private DataAdapter adapter; private List<MYData> datas; private int flag = 1; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_data); btn_add_one = (Button) findViewById(R.id.btn_add_one); btn_add_on_position = (Button) findViewById(R.id.btn_add_on_position); btn_delete_one = (Button) findViewById(R.id.btn_delete_one); btn_delete_on_position = (Button) findViewById(R.id.btn_delete_on_position); btn_delete_all = (Button) findViewById(R.id.btn_delete_all); lv_data = (ListView) findViewById(R.id.lv_data); datas = new ArrayList<>(); adapter = new DataAdapter(this, datas); lv_data.setAdapter(adapter); } }
自定义适配器代码:
package com.example.listviewdemo.adapter; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import com.example.listviewdemo.MYData; import com.example.listviewdemo.R; import java.util.ArrayList; import java.util.List; /** * Created by Devin on 2016/7/11. */ public class DataAdapter extends BaseAdapter { private List<MYData> datas; private Context mContext; public DataAdapter(Context mContext, List<MYData> datas) { this.mContext = mContext; this.datas = datas; } @Override public int getCount() { return datas.size(); } @Override public Object getItem(int i) { return i; } @Override public long getItemId(int i) { return i; } @Override public View getView(int i, View view, ViewGroup viewGroup) { ViewHolder viewHolder; if (view == null) { view = LayoutInflater.from(mContext).inflate(R.layout.item_data, null); viewHolder = new ViewHolder(); viewHolder.tv_data_text = (TextView) view.findViewById(R.id.tv_data_text); viewHolder.iv_icon = (ImageView) view.findViewById(R.id.iv_icon); view.setTag(viewHolder); } else { viewHolder = (ViewHolder) view.getTag(); } viewHolder.iv_icon.setImageResource(datas.get(i).getIconId()); viewHolder.tv_data_text.setText(datas.get(i).getContent()); return view; } private static class ViewHolder { TextView tv_data_text; ImageView iv_icon; } }
还有就是一个普通的bean
package com.example.listviewdemo; /** * Created by Devin on 2016/7/11. */ public class MYData { private int iconId; private String content; public MYData() { } public MYData(int iconId, String content) { this.iconId = iconId; this.content = content; } public int getIconId() { return iconId; } public void setIconId(int iconId) { this.iconId = iconId; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } @Override public String toString() { return "Data{" + "iconId=" + iconId + ", content='" + content + '\'' + '}'; } }
这样,我们运行的效果是:
![](http://i.imgur.com/QXRDv87.png)
没有显示有Item,因为我们在适配器中添加的是一个空的list,接下来我们实现按钮的事件和具体更新ListView
插入一条数据
在适配器中添加这个方法:
/** * 添加数据 * * @param myData */ public void addData(MYData myData) { if (datas == null) { datas = new ArrayList<>(); } datas.add(myData); notifyDataSetChanged(); }
在activity中实现点击事件:
btn_add_one.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { MYData data = new MYData(R.drawable.icon, "这是添加的数据~~~~~~X" + flag); adapter.addData(data); flag++; } });
就可以实现在ListView中添加一条数据,具体的效果图会在后面统一附上
在指定位置插入一条数据
在适配器中添加如下方法:
/** * 在指定位置添加数据 * * @param position * @param myData */ public void addData(int position, MYData myData) { if (datas == null) { datas = new ArrayList<>(); } datas.add(position, myData); notifyDataSetChanged(); }
实现按钮的点击事件:
btn_add_on_position.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { MYData data = new MYData(R.drawable.icon, "这是添加的数据~~~~~~X" + flag); adapter.addData(3, data); } });
就可以完成在指定位置添加一条数据,这里只是简单的实现,如果list中没有那么多数据,会出现数组下标越界的错误
根据对象删除数据
在适配器中添加如下方法:
/** * 根据对象删除数据 * * @param myData */ public void removeData(MYData myData) { if (datas != null) { datas.remove(myData); } notifyDataSetChanged(); }
实现按钮的点击事件:
btn_delete_one.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { MYData data = datas.get(2); adapter.removeData(data); } });
这里只是简单的删除下标为3的数据,如果listview的长度小于3,会出现数组下标越界的错误
根据位置删除数据
在适配器中添加如下方法:
/** * 根据位置删除数据 * * @param position */ public void removeData(int position) { if (datas != null && position <= datas.size()) { datas.remove(position); } notifyDataSetChanged(); }
实现按钮的点击事件:
btn_delete_on_position.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { adapter.removeData(3); } });
删除指定位置的数据,需要判定是否存在不然会出现错误
删除所有的数据
在适配器中添加如下方法:
/** * 清除所有的数据 */ public void removeAll() { if (datas != null) { datas.clear(); } notifyDataSetChanged(); }
实现按钮的点击事件:
btn_delete_all.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { adapter.removeAll(); } });
最后附上效果图:
![](http://i.imgur.com/ctRA2hu.gif)
这里实现ListView的数据更新问题,但是任然存在一些小问题,比如,没有数据的时候显示一片空白,用户体验不是很好。可以重写,根据服务器返回的数据更新界面。这里就不做实现了。下一节我们介绍一下ListView多布局的实现,类似QQ等的聊天界面。
相关文章推荐
- android EditText 监听复制粘贴等操作
- Android6.0的phone应用源码分析(1)——智能手机的硬件结构介绍
- android 多线程断点续传下载
- Android使用Handler和Message更新UI
- 那些做Android开发必须知道的ADB命令
- Android xml资源文件中@、@android:type、@*、?、@+含义和区别
- 2048
- 我常用的BaseHandler
- Android下拉刷新以及GridView使用方法详解
- 使用viewPager实现引导页
- android studio Git版本管理工具的使用
- 带有一键清空功能的EditText
- Nexus 7 搞机教程
- android 按钮防止连续点击防止按钮重复点击
- android 多线程断点续传下载 三
- Android异常---ActivityNotFoundException
- AndroidStudio好用的插件
- android4.4电源管理——Input系统(Power键处理)
- android studio解决.9.png的报错问题
- Android休眠唤醒和wakeup_source机制的使用(1)