使用listView实现多级可展开的菜单 – Android开发问答 – Android开发论坛 – 安卓开发论坛 – Android开发 – 安卓论坛 – 移动互联网门户

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