前言:因为业务的需要,需要实现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:
整个调用过程与simpleadapter一样,因为自定义的adapter是继承了它的,所以MainActivity调用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; } }
只需将该部分修改即可,这也就完成整个demo,看图: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);
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、下载