ListView的缓存机制
listView在使用过程中需要注意item view的显示与缓存机制,即屏幕内显示,滑出屏幕就被会收到缓存,避免每次调用getView时都是通过inflate创建一个新的View对象,同时避免又在此view中通过findViewById找到对应的控件,利用ListView的缓存机制实现view的复用。
将数据放入ListView中显示,需要建立数据源与ListView之间的适配关系,也就是需要自定义适配器。
数据源:学生(姓名,班级,年龄)
public class Student {
private String name;
private String clas;
private int age;
public Student(String name, String clas, int age) {
this.name = name;
this.clas = clas;
this.age = age;
}
public Student(){
}
public String getName() {
return name;
}
public String getClas() {
return clas;
}
public int getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public void setClas(String clas) {
this.clas = clas;
}
public void setAge(int age) {
this.age = age;
}
}
ListView:样式layout.item_student
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tvName"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tvClass"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/tvAge"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
适配器:ListAdapter
这里以继承自BaseAdapter为例:
重写的方法有:
getCount():返回适配器对象关联的数据源中元素的个数;
getItem():根据指定的position返回索引位的数据对象;
getItemId():根据指定的索引位返回指定条目对应的行号;
getView(int position, View convertView, ViewGroup parent):当绘制ListView中的每一个item时自动调用的方法,绘制多少个item就会调用多少次;
public class ListAdapter extends BaseAdapter {
private Context context;
private ArrayList<Student> list;
public ListAdapter(Context context, ArrayList<Student> list) {
this.context = context;
this.list = list;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Student 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) {
ViewHolder holder;
if(convertView == null){
convertView = LayoutInflater.from(context).inflate(R.layout.item_student, null);
holder = new ViewHolder();
holder.tvName = (TextView) convertView.findViewById(R.id.tvName);
holder.tvClass = (TextView) convertView.findViewById(R.id.tvClass);
holder.tvAge = (TextView)convertView.findViewById(R.id.tvAge);
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
Student student = getItem(position);
holder.tvAge.setText(String.valueOf(student.getAge()));
holder.tvClass.setText(student.getClas());
holder.tvName.setText(student.getName());
return convertView;
}
class ViewHolder{
public TextView tvName;
public TextView tvClass;
public TextView tvAge;
}
}
listView使用
布局文件
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CursorActivity">
<ListView
android:id="@+id/lvCursor"
android:layout_width="match_parent"
android:layout_height="match_parent"></ListView>
</android.support.constraint.ConstraintLayout>
主程序调用,students为数据集,listView为列表。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cursor);
listView = findViewById(R.id.lvCursor);
initAdapter();
listView.setAdapter(adapter);
}
private void initAdapter() {
adapter = new ListAdapter(this, students);
}
ListView数据分页加载
上面数据集量很大时,需要分页进行加载,一次显示一定数量。
我们需要获取用户下拉到最后一个item的动作时,对数据进行加载,并对list进行更新。
设置每页数据数量 size
记录当前页数 page
用以确定当前加载到的数据节点。通过page*size确定
判断是否有数据需要加载 hasmore
可以通过当前获取到的数据的数量是不是比size小,如果不足size,说明数据全部加载完毕,没有更多数据需要加载。
判断加载的条件:
hasMore && view.getLastVisiblePosition() == view.getCount()-1 && scrollState == SCROLL_STATE_IDLE
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
Log.i("数据表信息","getLatVisiblePosition:"+ view.getLastVisiblePosition()+
" get count = " + view.getCount());
//到最后一个item ,滑动停止,有更多数据可以加载
if(hasMore && view.getLastVisiblePosition() == view.getCount()-1 && scrollState == SCROLL_STATE_IDLE){
mCurrPage++;
getData();//加载
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
});
获取新的数据
//分页加载数据代码,从mCurrNum = page*size开始,加载size个数据
//以下为伪代码,根据数据源决定
{
//get 数据
dataset = get data from mCurrNum to mCurrNum+size;
//添加进数据源lists中
lists.adds(dataset);
}
更新列表
adapter.notifyDataSetChanged();
整体代码:
public class CursorActivity extends AppCompatActivity {
private ListView listView;
private ListAdapter adapter;
private int mCurrPage = 0;//记录当前页
private int mSizePage = 10;//每页数据数量
private boolean hasMore = true;//判断是否有更多数据需要加载
ArrayList<Student> students = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cursor);
listView = findViewById(R.id.lvCursor);
initAdapter();//初始化适配器
configList();//配置下拉加载更多数据
listView.setAdapter(adapter);
getData();//加载数据
}
/**
* 滑动到底自动加载数据
*/
private void configList() {
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
Log.i("数据表信息","getLatVisiblePosition:"+ view.getLastVisiblePosition()+
" get count = " + view.getCount());
//非常重要的判断条件
if(hasMore && view.getLastVisiblePosition() == view.getCount()-1 && scrollState == SCROLL_STATE_IDLE){
mCurrPage++;
getData();
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
}
});
}
private void initAdapter() {
adapter = new ListAdapter(this, students);
}
/**
* 加载你自己的数据
* @return
*/
private ArrayList<Student> getData() {
int mCurrNum = mCurrPage * mSizePage;
int offset = mSizePage;
Log.i("数据表信息", "mCurr:"+mCurrNum+" moffset:"+offset);
//分页加载数据代码,从mCurrNum开始,加载offset个数据
//以下为伪代码,根据数据源决定
{
//get 数据
dataset = get data from mCurrNum to mCurrNum+offset;
//添加进数据源students中
students.adds(dataset);
}
//判断是否还有更多的数据需要加载
if(dataset.getCount() < offset){
hasMore = false;
}
//更新列表
adapter.notifyDataSetChanged();
return students;
}
}