前言:因为业务的需要,需要实现IOS百度云里面的列表条目点击下部出现菜单的功能,这一次就来记录一下这个实现的具体过程吧,同时也将详细的讲一下ListView的使用方法,如果你正在需要这种Demo或者是初学者,那好好的看看这篇文章,你一定会有系统的认识的(不百分之百保证会—_—)。
1、前期准备
和之前一样,在开始之前我们需要先了解一下大概的思路,毕竟知己知彼百战不殆。
废话少说先看一下百度云里面的具体效果是什么,不过这个功能大部分的软件也都有,所以这也是为什么要自己动手做一下的原因。
图1
图2
在Android的百度云中菜单打开方式与IOS不同,此次实现的是仿照IOS版本的百度云。在开始之前先介绍一个图标网站,之前我也在其他文章中介绍过了,本次的操作图标全部来自于该网站-点击进入
2、开发思路
本次实现是在listview的基础上进行的,listview是有不同的条目也即是item,通过点击出现菜单,可见每一个item都应该有一个菜单,初始状态时,菜单处于隐藏状态,当点击按钮图标时显示点击按钮图标的item,实现对应item显示菜单的效果。
思路比较简单,主要还是看listview的使用是否熟练,同时还要自定义adapter来满足我们的需求,该模块的实现也算是对listview的一次加深认识,初学者也可以通过该部分实现认识listview的实现方法和实现的思路。
对于其他的跟listview类似的列如:PullToRefresh ListView(下拉刷新),实现方法类似。
3、ListView实现
第一步:每个人的习惯都不同,我们首先来建立xml布局文件,如下:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.listviewmenuontouch.MainActivity"> <TextView android:id="@+id/tv" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="仿IOS百度云条目点击显示菜单" android:gravity="center" /> <ListView android:id="@+id/listView" android:paddingTop="10dp" android:layout_width="match_parent" android:layout_height="wrap_content"/> </RelativeLayout>
该部分只写了布局显示效果如下(由于没有绑定adapter数据,listview没有显示):

第二步:编写MainActivity.java,给listview加入数据
public class MainActivity extends AppCompatActivity {
//布局文件中存在的textview和listview声明
private TextView tv;
private ListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//调用布局初始化
initUI();
}
//布局赋值初始化
public void initUI(){
//从布局文件中获得控件
tv = (TextView) findViewById(R.id.tv);
listView = (ListView) findViewById(R.id.listView);
//设置显示在listview上的字符集合
String[] array = {"第一个item","第二个item","第三个item","第四个item",
"第五个item","第六个item"};
//采用简单的adapter进行数据写入,android.R.layout.simple_list_item_1为Android提供的单一项布局文件
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1,array);
//为listview设置adapter
listView.setAdapter(arrayAdapter);
}
}如下图显示效果,此时为简单的条目显示效果:
4、自定义adapter的ListView实现
上一节主要使用ArrayAdapter来实现ListView的数据绑定,实际在百度云里的界面可以看出,并没有像我们展示的那样简单,要想做出像百度云这样的条目,简单的使用Android给我们提供的布局文件是达不到的,所以我们就需要自定义布局文件来实现该效果。
在使用自定义adapter之前先介绍一下Simpleadaper的使用,使用Simpleadapter和自定义的布局文件我们就可以做出百度云类似的条目了,下面先看一下我们的自定义ListView的布局文件list_item.xml:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" > <ImageView android:id="@+id/item_image" android:layout_width="46dp" android:layout_height="46dp" android:layout_marginLeft="10dp" android:src="@drawable/file" /> <TextView android:id="@+id/item_tv_main" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="项目文件" android:textSize="20sp" android:layout_marginLeft="16dp" android:layout_marginTop="2dp" android:layout_toRightOf="@id/item_image" /> <TextView android:id="@+id/item_tv_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="2016-12-07 15:00" android:textSize="15sp" android:layout_marginLeft="16dp" android:layout_marginTop="2dp" android:layout_toRightOf="@id/item_image" android:layout_below="@id/item_tv_main" /> <ImageView android:id="@+id/item_open" android:layout_width="25dp" android:layout_height="25dp" android:layout_marginRight="20dp" android:layout_centerVertical="true" android:src="@drawable/zhankai" android:layout_alignParentRight="true" /> </RelativeLayout>
item的具体xml布局,由文件可以看到有两个image和两个文本文件,我大致按照图片进行调整,在Androidstudio中的显示效果如下图:

item的布局文件写好后就需要进行MainActivity的编写了,如下采用了simpleadapter适配器,使用自定义布局:
public class MainActivity extends AppCompatActivity {
//布局文件中存在的textview和listview声明
private TextView tv;
private ListView listView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//调用布局初始化
initUI();
}
//布局赋值初始化
public void initUI(){
//从布局文件中获得控件
tv = (TextView) findViewById(R.id.tv);
listView = (ListView) findViewById(R.id.listView);
//注意该适配器的构造函数的参数,第一个为context,第二个为一个list<Map<String,Object>>,第三个为自定义的item布局文件,第四个为一个String数组 //声明每一个item的控件名称,与map中的对应,第五个为控件布局id,也即是一个int的数组,是对应的自定义布局中的控件的id,与第四个参数要一一对应
SimpleAdapter adapter = new SimpleAdapter(this,getData(),
R.layout.list_item,
new String[]{"item_image","item_tv_main","item_tv_time"},
new int[]{R.id.item_image,R.id.item_tv_main,R.id.item_tv_time}
);
listView.setAdapter(adapter);
}
private List<Map<String,Object>> getData(){
List<Map<String,Object>> list = new ArrayList<Map<String,Object>>();
Map<String,Object> map = new HashMap<String,Object>();
map.put("item_image",R.drawable.file);
map.put("item_tv_main","文件一");
map.put("item_tv_time","2016-12-07 15:00");
list.add(map);
map = new HashMap<String,Object>();
map.put("item_image",R.drawable.file);
map.put("item_tv_main","文件二");
map.put("item_tv_time","2016-12-07 15:10");
list.add(map);
map = new HashMap<String,Object>();
map.put("item_image",R.drawable.file);
map.put("item_tv_main","文件三");
map.put("item_tv_time","2016-12-07 15:20");
list.add(map);
map = new HashMap<String,Object>();
map.put("item_image",R.drawable.file);
map.put("item_tv_main","文件四");
map.put("item_tv_time","2016-12-07 15:30");
list.add(map);
map = new HashMap<String,Object>();
map.put("item_image",R.drawable.file);
map.put("item_tv_main","文件五");
map.put("item_tv_time","2016-12-07 15:40");
list.add(map);
map = new HashMap<String,Object>();
map.put("item_image",R.drawable.file);
map.put("item_tv_main","文件六");
map.put("item_tv_time","2016-12-07 15:50");
list.add(map);
return list;
}
}自定义布局文件做了微调,下面是实际的效果图:

接下来就要开始自定义的adapter进行实现,最终的实现效果是点击右侧的ImageView图标,然后在该列显示出一列菜单出来,之前思路也显示我们需要对布局文件做一下改动,将菜单文件添加进来,然后设置为不可见,之后再在自定义的adapter编写点击的响应事件,完成最终功能的实现。
首先看一下改过的布局和实际显示效果(为了直观显示,此时没有把菜单设置为不可见):
list_item.xml:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingBottom="5dp" android:paddingTop="5dp" android:gravity="center" > <RelativeLayout android:id="@+id/item_show" android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/item_image" android:layout_width="46dp" android:layout_height="46dp" android:layout_marginLeft="10dp" android:src="@drawable/file" /> <TextView android:id="@+id/item_tv_main" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="项目文件" android:textSize="20sp" android:textColor="#000" android:layout_marginLeft="16dp" android:layout_marginTop="2dp" android:layout_toRightOf="@id/item_image" /> <TextView android:id="@+id/item_tv_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="2016-12-07 15:00" android:textSize="15sp" android:layout_marginLeft="16dp" android:layout_marginTop="2dp" android:layout_toRightOf="@id/item_image" android:layout_below="@id/item_tv_main" /> <ImageView android:id="@+id/item_open" android:layout_width="25dp" android:layout_height="25dp" android:layout_marginRight="20dp" android:layout_centerVertical="true" android:src="@drawable/zhankai" android:layout_alignParentRight="true" /> </RelativeLayout> <LinearLayout android:id="@+id/item_hide" android:layout_below="@id/item_show" android:layout_width="match_parent" android:background="#D6D6D6" android:layout_height="wrap_content" android:orientation="horizontal" > <TextView android:id="@+id/item_hide_1" android:layout_width="26dp" android:layout_height="wrap_content" android:text="下载" android:gravity="center" android:layout_weight="1" android:drawableTop="@drawable/xz" /> <TextView android:id="@+id/item_hide_2" android:layout_width="26dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="分享" android:gravity="center" android:drawableTop="@drawable/fx" /> <TextView android:id="@+id/item_hide_3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="删除" android:gravity="center" android:drawableTop="@drawable/sc" /> </LinearLayout> </RelativeLayout>
这个时候看确实有点丑,,,,,,
接下来编写自定义的adapter:
public class MyAdapter extends SimpleAdapter implements View.OnClickListener{
private Context context;
private Map<String, Object> map;
private int resource;
private String[] from;
private int[] to;
private ViewHolder viewHolder;
private int currentItem = -1; //用于记录点击的 Item 的 position,是控制 item 展开的核心
public MyAdapter(Context context, List<? extends Map<String, ?>> data,
int resource, String[] from, int[] to) {
super(context, data, resource, from, to);
this.resource = resource;
this.from = from;
this.to = to;
this.context = context;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//获得单个item的map
map = (Map<String, Object>)getItem(position);
View view = convertView;
//获取到每个控件对应的布局中的控件
if(view == null){
view = LayoutInflater.from(context).inflate(resource, null);
viewHolder = new ViewHolder();
viewHolder.item_image = (ImageView)view.findViewById(R.id.item_image);
viewHolder.item_tv_main = (TextView)view.findViewById(R.id.item_tv_main);
viewHolder.item_tv_time = (TextView)view.findViewById(R.id.item_tv_time);
viewHolder.item_open = (ImageView)view.findViewById(R.id.item_open);
viewHolder.item_hide = (LinearLayout)view.findViewById(R.id.item_hide);
//设置点击监听
viewHolder.item_open.setOnClickListener(this);
//将隐藏的部分隐藏起来
viewHolder.item_hide.setVisibility(View.GONE);
view.setTag(viewHolder);
}else
viewHolder = (ViewHolder) view.getTag();
//根据设置的数据设置数据
viewHolder.item_image.setImageResource((int)map.get(from[0]));
viewHolder.item_tv_main.setText((String)map.get(from[1]));
viewHolder.item_tv_time.setText((String)map.get(from[2]));
//设置点击展开菜单控件为未选择状态,选择状态图标会改变,编写背景xml文件即可实现
viewHolder.item_open.setSelected(false);
//根据 currentItem 记录的点击位置来设置"对应Item"的可见性(在list依次加载列表数据时,每加载一个时都看一下是不是需改变可见性的那一条)
if (currentItem == position) {
viewHolder.item_open.setSelected(true);
viewHolder.item_hide.setVisibility(View.VISIBLE);
} else {
viewHolder.item_open.setSelected(false);
viewHolder.item_hide.setVisibility(View.GONE);
}
viewHolder.item_open.setTag(position);
viewHolder.item_open.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//用 currentItem 记录点击位置
int tag = (Integer) view.getTag();
if (tag == currentItem) { //再次点击
currentItem = -1; //给 currentItem 一个无效值
} else {
currentItem = tag;
}
//通知adapter数据改变需要重新加载
notifyDataSetChanged(); //必须有的一步
}
});
return view;
}
@Override
public void onClick(View view) {
//此处可将隐藏部分的控件设置监听,来响应不同的操作,通过setTag设置的position可以得到哪一个item点击
}
/**
* item控件列表
*/
static class ViewHolder{
ImageView item_image;
TextView item_tv_main;
TextView item_tv_time;
ImageView item_open;
LinearLayout item_hide;
}
}整个调用过程与simpleadapter一样,因为自定义的adapter是继承了它的,所以MainActivity调用adapter如下:MyAdapter adapter = new MyAdapter(this,getData(),
R.layout.list_item,
new String[]{"item_image","item_tv_main","item_tv_time"},
new int[]{R.id.item_image,R.id.item_tv_main,R.id.item_tv_time}
);
listView.setAdapter(adapter);只需将该部分修改即可,这也就完成整个demo,看图:

5、一些注意事项
按钮的点击通过setselected的值得改变来改变图片的显示,将点击图片的src改为xml文件,文件如下:
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/bihe" android:state_selected="true" /> <!--暂时背景图片没有变化--> <item android:drawable="@drawable/zhankai"/> </selector>
6、下载
作者:iceyung



