android没有实现多级菜单的控件,但是,我们总可以通过一些手段来实现我们所需要的效果,下面我来介绍一下我是怎么用ListView + SimpleAdapter来实现多级菜单效果的。
其实很简单,首先我们需要写一个适配器,继承自SimpleAdapter,源代码如下:
/** * @author ouyang *此类实现多级菜单的主要方法是:将菜单项的树形结构按照前序排列(什么是前序排列不懂 *的话去看数据结构)成连续的存储结构。 *本人是将多级菜单项存储在一个List类中进行操作,List中的每一个元素是一个HashMap,存储 *了对应菜单项的一些信息。 *其中每个元素中必须设置两个键值,那就是 KEY_LEVEL 和 KEY_EXPANDED 这两个键,一个表示 *菜单的级别,一个表示菜单展开状况(展开和非展开) */ public class MultiExpandableAdapter extends SimpleAdapter { //在mTreeList的每个Map元素中,必须设置 这两个键值,这是在一个list中显示多级菜单的关键 public static final String KEY_LEVEL = “KEY_LEVEL”; //菜单的级别 public static final String KEY_EXPANDED = “KEY_EXPANDED”;//菜单展开状况 public static final int CHILD_BASE_OFFSET = 30; private int mChildOffset; private List<String,Map<String,Object>> mTreeList; public MultiExpandableAdapter(Context context,List> list, int resource,String[] from, int[] to) { super(context, list, resource, from, to); mTreeList = list; //设置默认单位偏移量 mChildOffset = CHILD_BASE_OFFSET; } @Override public int getCount() { if(mTreeList == null){ return 0; } return mTreeList.size(); } @Override public Object getItem(int position) { if(mTreeList != null && mTreeList.size() > 0 && position < mTreeList.size() && position > -1 ){ return mTreeList.get(position); } return null; } @Override public long getItemId(int position) {
return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { //如果是继承这个类重写了此方法的话 ,这句代码可以注释掉 convertView = super.getView(position, convertView, parent); Log.i(“adapter”, “execute……”); //如果继承了这个类, 必须在重写此方法的最后 调用 这个方法,这个方法是为了在视觉上 //分清多级菜单的级别,级别越深的偏移量越大 return getOffsetChangeView(position, convertView, parent); } /** * 此方法是用来计算和设置不同级别菜单项的偏移量的,级别越大,偏移量越大. * 如果继承此类,在重写getView方法中必须调用一次该方法 * @param position * @param convertView * @param parent * @return */ protected View getOffsetChangeView(int position, View convertView,ViewGroup parent) { if(convertView != null){ Map item = mTreeList.get(position); Integer level = (Integer)item.get(KEY_LEVEL); if(level instanceof Integer){ //设置每个item项的内补丁,级别越大,内补丁越大,级别相同,内补丁相同 convertView.setPadding( level.intValue() * mChildOffset, 0, 0, 0); } } return convertView; } /** * 当打开一个菜单项后,将其子菜单项添加在该项的后面,如果此时该菜单项处于非展开状态 * (这是实现多级菜单显示的关键方法) * @param childList 子菜单项数据列表 * @param parent 要展开的父菜单项的索引 */ public void addChildListData(List> childList,int parent){ if(mTreeList ==null || mTreeList.size() == 0){ return; } if(parent < 0 || parent >= mTreeList.size()){ return; } Map item = mTreeList.get(parent); boolean expanded = (Boolean)item.get(KEY_EXPANDED); if(expanded) return; mTreeList.addAll(parent + 1, childList); item.put(KEY_EXPANDED, true); notifyDataSetChanged(); } /** * 当此父菜单项处于展开状态时,关闭其下的所有子菜单项 * @param parent 要关闭的父菜单项的索引(对应mTreeList中数据项的索引) */ public void removeChildListData(int parent){ if(mTreeList ==null || mTreeList.size() == 0){ return; } if(parent < 0 || parent >= mTreeList.size()){ return; } Map item = mTreeList.get(parent); boolean expanded = (Boolean)item.get(KEY_EXPANDED); if(!expanded) return;
int pLevel = (Integer)item.get(KEY_LEVEL); for(int i = parent + 1 ; i < mTreeList.size();){ Map temp = mTreeList.get(i); int cLevel = (Integer)temp.get(KEY_LEVEL); if(cLevel > pLevel){ temp.put(KEY_EXPANDED, false); mTreeList.remove(i); }else{ break; } } item.put(KEY_EXPANDED, false); notifyDataSetChanged(); } /** * 判断对应索引的菜单是否展开 * @param position 菜单项索引 * @return 展开返回 true,否则返回false */ public boolean isExpanded(int position){ if(mTreeList ==null || mTreeList.size() == 0){ return false; } if(position < 0 || position >= mTreeList.size()){ return false; } Map item = mTreeList.get(position); return (Boolean)item.get(KEY_EXPANDED); } /** * 设置每级菜单之间的偏移量 * @param offset 偏移量大小 */ public void setChildOffset(int offset){ mChildOffset = offset; notifyDataSetChanged(); } } 适配器做好了,接下来就是怎么应用了,直接使用listview即可,无需重写,代码如下: public class MainActivity extends Activity implements OnItemClickListener{ private ListView mListView; private MultiExpandableAdapter mAdapter; private List<String,Map<String,Object>> dataList; private List<String,Map<String,Object>> child1; private List<String,Map<String,Object>> child2; private List<String,Map<String,Object>> child11; private List<String,Map<String,Object>> child111; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { mListView = (ListView)findViewById(R.id.list_view); //设置监听器,点击菜单项时对其做相应的操作 mListView.setOnItemClickListener(this); //设置适配器 mListView.setAdapter(mAdapter = getAdapter()); } //获取适配器 private MultiExpandableAdapter getAdapter() { String[] from = {“icon”,”title”}; int[] to = {R.id.item_image,R.id.item_text}; //(建议继承此MultiExpandedAdapter类重写getView方法去设置自己想要实现的效果) return new MultiExpandableAdapter(this, getListData(), R.layout.list_item, from, to); } //获取数据。。。此数据只做测试用,请根据自己的情况去获取自己想得到的数据 //此次测试就写到展开4层把,写多了蛋疼。。 private List<String,Map<String,Object>> getListData() { dataList = new ArrayList>(); Map item1 = new HashMap();
item1.put(MultiExpandableAdapter.KEY_LEVEL, 1); //填写的这个级别决定了该项的深度 item1.put(MultiExpandableAdapter.KEY_EXPANDED, false);
item1.put(“icon”, R.drawable.list_item_folder_type); item1.put(“title”, “Level 1 first (1)”); dataList.add(item1);
Map item2 = new HashMap(); item2.put(MultiExpandableAdapter.KEY_LEVEL, 1); item2.put(MultiExpandableAdapter.KEY_EXPANDED, false); item2.put(“icon”, R.drawable.list_item_folder_type); item2.put(“title”, “Level 1 second (2)”); dataList.add(item2);
for(int i = 0 ;i <30 ; i++){ Map itemi = new HashMap(); itemi.put(MultiExpandableAdapter.KEY_LEVEL, 1); itemi.put(MultiExpandableAdapter.KEY_EXPANDED, false); itemi.put(“icon”, R.drawable.list_item_folder_type); itemi.put(“title”, “level 1 (” + i + “)”); dataList.add(itemi); }
createChild();
return dataList; } //获取父菜单项对应的子项列表,只做测试用,请根据自己的情况去获取对应的子菜单列表 //刚开始时,所有的子菜单都是关闭的,所以不会显示在界面上,只有当点击相应的父菜单 //项时才会在父菜单项的下边显示子菜单,并且会相对父菜单项向右偏移。 private void createChild() { child1 = new ArrayList<String,Map<String,Object>> (); Map item11 = new HashMap();
item11.put(MultiExpandableAdapter.KEY_LEVEL, 2); //这是第二级的菜单项 将会显示在第一级第一个父菜单项的下面第一个位置 item11.put(MultiExpandableAdapter.KEY_EXPANDED, false);
item11.put(“icon”, R.drawable.list_item_folder_type); item11.put(“title”, “Level 2 first (11)”); child1.add(item11);
Map item12 = new HashMap(); item12.put(MultiExpandableAdapter.KEY_LEVEL, 2); item12.put(MultiExpandableAdapter.KEY_EXPANDED, false); item12.put(“icon”, R.drawable.list_item_folder_type); item12.put(“title”, “Level 2 second (12)”); child1.add(item12);
child2 = new ArrayList<String,Map<String,Object>> (); Map item21 = new HashMap();
item21.put(MultiExpandableAdapter.KEY_LEVEL, 2); item21.put(MultiExpandableAdapter.KEY_EXPANDED, false);
item21.put(“icon”, R.drawable.list_item_folder_type); item21.put(“title”, “Level 2 first (21)”); child2.add(item21);
Map item22 = new HashMap(); item22.put(MultiExpandableAdapter.KEY_LEVEL, 2); item22.put(MultiExpandableAdapter.KEY_EXPANDED, false); item22.put(“icon”, R.drawable.list_item_folder_type); item22.put(“title”, “Level 2 second (22)”); child2.add(item22);
child11 = new ArrayList<String,Map<String,Object>> (); Map item111 = new HashMap();
item111.put(MultiExpandableAdapter.KEY_LEVEL, 3); item111.put(MultiExpandableAdapter.KEY_EXPANDED, false);
item111.put(“icon”, R.drawable.list_item_folder_type); item111.put(“title”, “Level 3 first (111)”); child11.add(item111);
Map item112 = new HashMap(); item112.put(MultiExpandableAdapter.KEY_LEVEL, 3); item112.put(MultiExpandableAdapter.KEY_EXPANDED, false); item112.put(“icon”, R.drawable.list_item_video); item112.put(“title”, “Level 3 second (112)”); child11.add(item112);
child111 = new ArrayList<String,Map<String,Object>> (); Map item1111 = new HashMap(); item1111.put(MultiExpandableAdapter.KEY_LEVEL, 4); item1111.put(MultiExpandableAdapter.KEY_EXPANDED, false);
item1111.put(“icon”, R.drawable.list_item_video); item1111.put(“title”, “Level 4 first (1111)”); child111.add(item1111);
Map item1112 = new HashMap(); item1112.put(MultiExpandableAdapter.KEY_LEVEL, 4); item1112.put(MultiExpandableAdapter.KEY_EXPANDED, false); item1112.put(“icon”, R.drawable.list_item_video); item1112.put(“title”, “Level 4 second (1112)”); child111.add(item1112); } @Override public void onItemClick(AdapterView parent, View view, int position, long id) { Map item = dataList.get(position); //获取点击项的名称 String title = (String)item.get(“title”); //获取展开状态 boolean expanded = (Boolean)item.get(MultiExpandableAdapter.KEY_EXPANDED); if(!expanded){ //菜单项处于非展开状态,则展开菜单项 //展开对应的菜单 if(“Level 1 first (1)”.equals(title)){ //将对应的子菜单列表添加到父菜单的末尾 mAdapter.addChildListData(child1, position); }else if(“Level 2 first (11)”.equals(title)){ //将对应的子菜单列表添加到父菜单的末尾 mAdapter.addChildListData(child11, position); }else if(“Level 1 second (2)”.equals(title)){ //将对应的子菜单列表添加到父菜单的末尾 mAdapter.addChildListData(child2, position); }else if(“Level 3 first (111)”.equals(title)){ //将对应的子菜单列表添加到父菜单的末尾 mAdapter.addChildListData(child111, position); } }else{ //点击的菜单项处于展开状态,则关闭菜单项 mAdapter.removeChildListData(position); }
} } 大家看代码多理解一下,其实很简单,没有去重写什么控件的,主要是对adapter进行了一些改进,所以不会很麻烦。 demo已经上传,大家可以下载看下效果。有什么要改进的地方希望大家能提供建议。 |
来源URL:http://www.apkbus.com/forum.php?mod=viewthread&tid=142765