您的位置:首页 > 移动开发 > Android开发

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 + '\'' +
'}';
}
}


这样,我们运行的效果是:



没有显示有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();
}
});


最后附上效果图:



这里实现ListView的数据更新问题,但是任然存在一些小问题,比如,没有数据的时候显示一片空白,用户体验不是很好。可以重写,根据服务器返回的数据更新界面。这里就不做实现了。下一节我们介绍一下ListView多布局的实现,类似QQ等的聊天界面。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: