-
说明
在我们开发项目的过程中,有时候会遇到这样的需求,就是首先显示一个列表,可以用ListView或者RecycleView实现,每个item都有一个向下箭头,然后点击列表的每个item条目,下边就又会出现一些内容,此时箭头方向朝上,再次点击item条目,下边新出现的内容隐藏,箭头又会回到初始时向下的方向,效果如下:
ok,效果就是这样的,这里有3种应用场景:
第一种是listview的单个item展开;
第二种是listview的所有item展开;
第三种是表格布局item展开;
那么接下来我们就逐个分析这几个效果实现的思路,然后再去带大家去实现这个效果,我们首先来看我们第一种情况 —— 单个item展开 。
- 思路分析
2.1 单个item展开思路分析及代码实现
2.1.1 首先可以使用ListView先来实现下这个列表;
2.1.2 然后在自己定义的Adapter的最外层定义一个private int currentItem = -1,用于记录点击的item的position,这个是控制item展开的核心;
2.1.3 然后在getView()方法中给你设置点击事件的这个控件设置tag标记值;
2.1.4 然后根据currentItem 记录点击位置来设置对应item的可见性;
2.1.5 然后可以给整个显示的区域一个点击事件或者只是给箭头一个点击事件,比如我这边就给item的显示的部分整体设置点击事件,然后取出tag,判断如果tag ==currentItem,就设置一个设置一个无效值,否则就让tag=currentItem ;
2.1.6 最后通知adapter数据改变了需要刷新数据;
自定义的Adapter中具体实现如下:
/**
* 点击item展开隐藏部分,再次点击收起
* 只可展开一条记录
*
* @author JackChen
* @date 2016.01.31
*/
public class OneExpandAdapter extends BaseAdapter {
private Context context;
private ArrayList<HashMap<String, String>> list;
private int currentItem = -1; //用于记录点击的 Item 的 position,是控制 item 展开的核心
public OneExpandAdapter(Context context,
ArrayList<HashMap<String, String>> list) {
super();
this.context = context;
this.list = list;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(
R.layout.item_2, parent, false);
holder = new ViewHolder();
holder.showArea = (LinearLayout) convertView.findViewById(R.id.layout_showArea);
holder.tvPhoneType = (TextView) convertView
.findViewById(R.id.tv_phoneType);
holder.tvDiscount = (TextView) convertView
.findViewById(R.id.tv_discount);
holder.tvPrice = (TextView) convertView
.findViewById(R.id.tv_price);
holder.tvTime = (TextView) convertView
.findViewById(R.id.tv_time);
holder.tvNum = (TextView) convertView
.findViewById(R.id.tv_num);
holder.btnBuy = (Button) convertView
.findViewById(R.id.btn_buy);
holder.hideArea = (RelativeLayout) convertView.findViewById(R.id.layout_hideArea);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
HashMap<String, String> item = list.get(position);
// 注意:我们在此给响应点击事件的区域(我的例子里是 showArea 的线性布局)添加Tag,为了记录点击的 position,我们正好用 position 设置 Tag
holder.showArea.setTag(position);
holder.tvPhoneType.setText(item.get("phoneType"));
holder.tvDiscount.setText(item.get("discount"));
holder.tvPrice.setText(item.get("price"));
holder.tvTime.setText(item.get("time"));
holder.tvNum.setText(item.get("num"));
//根据 currentItem 记录的点击位置来设置"对应Item"的可见性(在list依次加载列表数据时,每加载一个时都看一下是不是需改变可见性的那一条)
if (currentItem == position) {
holder.hideArea.setVisibility(View.VISIBLE);
} else {
holder.hideArea.setVisibility(View.GONE);
}
holder.showArea.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
//用 currentItem 记录点击位置
int tag = (Integer) view.getTag();
if (tag == currentItem) { //再次点击
currentItem = -1; //给 currentItem 一个无效值
} else {
currentItem = tag;
}
//通知adapter数据改变需要重新加载
notifyDataSetChanged(); //必须有的一步
}
});
holder.tvPhoneType.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(context, "hehe", Toast.LENGTH_SHORT).show();
}
});
return convertView;
}
private static class ViewHolder {
private LinearLayout showArea;
private TextView tvPhoneType;
private TextView tvDiscount;
private TextView tvPrice;
private TextView tvTime;
private TextView tvNum;
private Button btnBuy;
private RelativeLayout hideArea;
}
}
2.2 所有item展开思路分析及代码实现
2.2.1 首先定义一个boolean 类型的数组,用于记录后台返回list数据集合中每个item是否需要展开,然后在Adapter中的构造方法中对这个boolean 的数据进行初始化;
2.2.2 同样的,我还是在getView()方法中给我需要显示的布局区域设置一个tag值 ;
2.2.3 然后给这个区域设置点击事件,并且从中取出tag值,然后判断这个boolean数组,如果为true,就把这个boolean数组置为false;否则就把这个boolean数组置为true,最后通知adapter,数据已经发生改变需要重新加载;
2.2.4 然后再次去判断如果这个boolean数组为true,让隐藏的部分显示;否则就隐藏,如果你项目中有向上和向下箭头的话,这里也是显示向上箭头和向下箭头的位置;
自定义的Adapter中具体实现如下:
/**
* 点击item展开隐藏部分,再次点击收起 可展开多条 Item
*
* @author WangJ
* @date 2016.02.01
*/
public class MultiExpandAdapter extends BaseAdapter {
private Context context;
private ArrayList<HashMap<String, String>> list;
private boolean[] showControl; // 用一个布尔数组记录list中每个item是否要展开
public MultiExpandAdapter(Context context,
ArrayList<HashMap<String, String>> list) {
super();
this.context = context;
this.list = list;
showControl = new boolean[list.size()]; // 构造器中初始化布尔数组
}
/**
* 省略别的 @Override 方法
*/
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
convertView = LayoutInflater.from(context).inflate(R.layout.item_2,
parent, false);
/**
* 省略 findView 方法
*/
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final HashMap<String, String> item = list.get(position);
// 注意:我们在此给响应点击事件的区域(我的例子里是 showArea 的线性布局)添加Tag,
// 为了记录点击的 position,我们正好用position 设置 Tag
holder.showArea.setTag(position);
holder.tvPhoneType.setText(item.get("phoneType"));
holder.tvDiscount.setText(item.get("discount"));
holder.tvPrice.setText(item.get("price"));
holder.tvTime.setText(item.get("time"));
holder.tvNum.setText(item.get("num"));
// list依次加载每个item,加载的同时查看showControl控制数组中对应位置的true/false
// true显示隐藏部分
// false不显示
if (showControl[position]) {
holder.hideArea.setVisibility(View.VISIBLE);
} else {
holder.hideArea.setVisibility(View.GONE);
}
holder.showArea.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
// 根据点击位置改变控制数组中对应位置的布尔值
int tag = (Integer) view.getTag();
// 如果已经是true则改为false,反过来同理(即点击展开,再次点击收起)
if (showControl[tag]) {
showControl[tag] = false;
} else {
showControl[tag] = true;
}
//通知adapter数据改变需要重新加载
notifyDataSetChanged(); //必须要有一步
}
});
// 对于 Item 中子控件的监听(区别于整个Item)都是在适配器类中添加,
// 不要和在Activity中给ListView添加setOnItemClickListener搞混了
holder.btnBuy.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
Toast.makeText(context, "快快下单!剩余" + item.get("num") + "台",
Toast.LENGTH_SHORT).show();
}
});
return convertView;
}
/**
* 省略ViewHolder
*/
}
2.3 表格布局的item展开思路分析及代码实现
这个内容其实在项目中我们可能不太常用,所以这边就不做进一步讲解了,但是这个代码我已经上传到github,有兴趣的可以直接下载下来看
- 需要注意的地方
这里需要注意下listview的点击事件 和 listview中的item的各个子view的点击事件的区别:
3.1 listview的点击事件:这个是在Activity或者Fragment中进行的,响应区域是整个item,直接在Activity或者Fragment中setOnItemClickListener;
3.2 item的子view的点击事件:这个是指item中的一些字控件,比如Button、TextView、ImageView,一般是给这些控件单独设置点击事件,是在adapter中的getView()方法中进行的,可以直接给对应控件设置setOnClickListener,比如我们代码中的
holder.showArea.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
//用 currentItem 记录点击位置
int tag = (Integer) view.getTag();
if (tag == currentItem) { //再次点击
currentItem = -1; //给 currentItem 一个无效值
} else {
currentItem = tag;
}
//通知adapter数据改变需要重新加载
notifyDataSetChanged(); //必须有的一步
}
});
holder.tvPhoneType.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(context, "hehe", Toast.LENGTH_SHORT).show();
}
});