当前位置: 代码迷 >> 综合 >> Iwfu-ListView(二) -BaseAdapter分析以及实现同一个ListView加载不同的布局
  详细解决方案

Iwfu-ListView(二) -BaseAdapter分析以及实现同一个ListView加载不同的布局

热度:7   发布时间:2023-12-19 02:12:18.0

承接上文:
ListView(一) - ListView使用ViewHolder优化以及ListView的其他

上文使用viewHolder对ListView进行优化,防止每次加载视图调用findViewById()。为什么viewHolder要写成static的啊??参考stackOverflow的一个回答:

这里写图片描述

大致是说使用static的好处:

  • 防止非静态内部类的实例包含外围类实例的引用导致的内存泄漏
  • 使用内部类可以让其他的adapter复用,不过不推荐,一般一个adapter写一个viewholder
  • 使用静态类在类加载时就会被调用,比不使用static占用更少的内存

    关于这个问题有更好的答案的请大声举手O(≧口≦)O!!

ListView第二波:BaseAdapter分析

(以下分析纯属个人意见,有错误请指正)
使用ListView最常用的是使用自定义适配器继承BaseAdapter,而BaseAdapter中有很多方法要重写,这里试下创建ListView,使用BaseAdapter加载数据的时候有哪些方法会被调用:
(代码就是最简单的使用viewHolder的自定义适配器,让重写的四个方法每个都打印方法名,这里就不贴了)

直接上结果:

这里写图片描述

这个结果是加载完listview的第一屏(listview只显示了最开始状态的列表项,没有滑动)的输出结果,可以看到重写的四个方法只有getCount()和getView()被调用了,而且在listview多次绘制过程中当getCount调用后才会执行getView(),并每次getView()是根据第一屏能显示的条目数来决定执行多少次,另外BaseAdapter还有两个方法值得留意:getItemViewType()以及getViewTypeCount(),我们重写这两个方法并打印输出:

这里写图片描述

同样看到加载视图时,先执行getCount(),然后执行了getViewTypeCount(),与上面不同的是,这里调用了getItemId(),并且每次调用getView()之后会调用getItemViewType(),不过反复试验后,发现getItemViewType()和getView没有绝对的调用先后顺序,而getCount总是先执行(大家可以自己试验一下),getViewTypeCount总是先于getItemViewType执行。通过查阅Api,可以解释出现这个现象的原因:

  • getCount用于获取listView要加载的数据条目的数量,如果返回0则不执行getView.
  • getViewType 返回的是getView()方法会创建的视图的类型数,也就是当前这个adapter会在listview创建多少种视图,如果整个listView都创建同一种样式的item,则这个方法返回1。
  • getItemViewType返回的是某个特定posititon的视图类型(用int标识的),大小一定在0~ViewType数量-1之间

根据以上的知识,我们就可以做出同一个ListView中显示不同的布局的效果。

同一个ListView加载不同布局:

现在我们来实现这个需求:ListView包含四种布局,上半部分提示信息一行数据+上半部分数据+下半部分提示信息数据+下半部分数据。一共四种类型的布局

关键在重写getViewType()和getItemViewType(),并在getView()中根据类型值的不同对应加载不同的布局。

布局文件就加了一个ListView

主要代码:

“`
package com.chan.betterlistview;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
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.TextView;

import java.util.ArrayList;
import java.util.List;

public class ActivityListView3 extends AppCompatActivity {

private ListView mlistView;
// 要显示的两组数据
private List<String> datasList1;
private List<String> datasList2;
private static final String TAG = "tag";// 4种类型的标示
private static final int TYPE1 = 0;
private static final int TYPE2 = 1;
private static final int TYPE3 = 2;
private static final int TYPE4 = 3;private LayoutInflater mLayoutInflater;@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_activity_list_view3);mLayoutInflater = LayoutInflater.from(this);initDataList();initListView();
}private void initDataList() {datasList1 = new ArrayList<>();for (int i = 0; i < 5; i++) {datasList1.add(i + "");}datasList2 = new ArrayList<>();for (int i = 0; i < 5; i++) {datasList2.add(i + 100 + "");}
}private void initListView() {mlistView = (ListView) findViewById(R.id.lvlvlvl);MyAdapter adapter = new MyAdapter();mlistView.setAdapter(adapter);
}private class MyAdapter extends BaseAdapter {@Overridepublic int getItemViewType(int position) {if (position == 0) {// 第一项显示上半部分提示布局return TYPE1;} else if (position <= datasList1.size()) {// 接下来显示上半部分数据布局return TYPE2;} else if (position == datasList1.size()+1) {// 注意position变化,这里显示下半部分提示布局return TYPE3;} else {// 最后显示下半部分数据布局return TYPE4;}}@Overridepublic int getViewTypeCount() {// 总共四种布局return 4;}@Overridepublic int getCount() {// list大小变为两种数据集合大小+两个信息提示return datasList1.size() + datasList2.size() + 1 + 1;}@Overridepublic Object getItem(int position) {return null;}@Overridepublic long getItemId(int position) {return 0;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder1 viewHolder1 = null;ViewHolder2 viewHolder2 = null;ViewHolder3 viewHolder3 = null;ViewHolder4 viewHolder4 = null;// 获取当前要加载的是哪一种布局int type = getItemViewType(position);if (convertView == null) {switch (type) {case TYPE1 :viewHolder1 = new ViewHolder1();convertView = mLayoutInflater.inflate(R.layout.holder1_layout, parent, false);viewHolder1.iv1 = (ImageView) convertView.findViewById(R.id.holder1_iv);viewHolder1.tv1 = (TextView) convertView.findViewById(R.id.holder1_tv);convertView.setTag(viewHolder1);break;case TYPE2 :viewHolder2 = new ViewHolder2();convertView = mLayoutInflater.inflate(R.layout.holder2_layout, parent, false);viewHolder2.tv2 = (TextView) convertView.findViewById(R.id.holder2_tv);convertView.setTag(viewHolder2);break;case TYPE3 :viewHolder3 = new ViewHolder3();convertView = mLayoutInflater.inflate(R.layout.holder3_layout, parent, false);viewHolder3.iv3 = (ImageView) convertView.findViewById(R.id.holder3_iv);viewHolder3.tv3 = (TextView) convertView.findViewById(R.id.holder3_tv);convertView.setTag(viewHolder3);break;case TYPE4 :viewHolder4 = new ViewHolder4();convertView = mLayoutInflater.inflate(R.layout.holder4_layout, parent, false);viewHolder4.tv4 = (TextView) convertView.findViewById(R.id.holder4_tv);convertView.setTag(viewHolder4);break;default :break;}} else {switch (type) {case TYPE1 :viewHolder1 = (ViewHolder1) convertView.getTag();break;case TYPE2 :viewHolder2 = (ViewHolder2) convertView.getTag();break;case TYPE3 :viewHolder3 = (ViewHolder3) convertView.getTag();break;case TYPE4 :viewHolder4 = (ViewHolder4) convertView.getTag();break;default :break;}}// 设置具体的数据,注意取值的时候position的变化switch (type) {case TYPE1 :viewHolder1.iv1.setImageResource(R.mipmap.ic_launcher);viewHolder1.tv1.setText("上半部分的数据有:" + datasList1.size() + "个");break;case TYPE2 :viewHolder2.tv2.setText(datasList1.get(position-1));break;case TYPE3 :viewHolder3.iv3.setImageResource(R.mipmap.ic_launcher);viewHolder3.tv3.setText("下半部分的数据有:" + datasList2.size() + "个");break;case TYPE4 :viewHolder4.tv4.setText(datasList2.get(position-2-datasList1.size ()));break;default :break;}return convertView;}
}// 定义出不同的viewHolder对应缓存不同的布局
static class ViewHolder1 {ImageView iv1;TextView tv1;
}
static class ViewHolder2 {TextView tv2;
}
static class ViewHolder3 {ImageView iv3;TextView tv3;
}
static class ViewHolder4 {TextView tv4;
}

}

最后实现的效果:

“`这里写图片描述

对这个例子的几点说明:

  • 这个例子可以在同一个listView加载不同布局,但方法并不唯一。

  • 代码未经封装显得杂乱,但内部逻辑很简单,就是通过布局类型来判断getView()应该加载哪一种布局,填充数据时也根据类型判断应该填入哪些数据,实际使用中封装后会更好。

  • position的位置要进行多次判断,同时因为涉及到许多布局,他们对应的数据集合有不同的大小,所以要格外仔细计算position的位置,否则会导致程序崩溃。

            精力有限,就此搁笔,不足之处,敬请指教。
    

下一篇:实现ListView万能适配器(周日完成)

  相关解决方案