Android开发之多层树型目录的实现

转载 2015年11月20日 10:58:04

转自:http://user.qzone.qq.com/314154775/infocenter#!app=2&via=QZ.HashRefresh&pos=1335594212


树型目录是一种直观的列表显示目录方式,在用户界面得到普遍使用,如Windows的资源管理器窗口,EclipsePackage Explorer等。因此,我们也想在Android应用中使用这种方式实现对各类信息的分层与显示。然而,Android应用层只提供了ListView单层列表和ExpandableListView二级列表,无法直接显示像树型目录这样多层次的列表。那么如何在android应用上实现这树型目录呢。

我们知道ListView显示列表是通过添加适配器Adapter,适配器装配数据,在适配器的getView()方法中实现绘制列表的每一行。因此,我们可以得出,要实现树型目录的展开,收起,只是让适配器装配不同的数据。明白了这些,那么我们来实现树型目录就易如反掌。

首先,先来分析下树型目录的基本元素目录结点TreeNode,每一个节点都有父节点(根节点除外)和可能存在子节点列表,每一个节点可以是目录也可以是一个文件,每一个节点在树型目录中都有一个等级level,节点有展开,收起状态expanded

在弄清了目录节点具备的属性及动作后,我们需要为每一个TreeNode对象添加子节点列表childNodes,简单地说就是每一级目录的子目录。我们知道,在java程序中,使用递归遍历目录,因此,在这里我们也使用递归来为每一个节点增加子节点列表。

//parentNode是父节点,parentFile父节点所在的目录。

private void createTree(TreeNode parentNode, File parentFile) {
       if (parentFile.isDirectory()) { // 是目录
           parentNode.setIsDirectory(true);       //设置节点为目录
           if (parentFile.list().length > 0) {
              parentNode.setHasChild(true);      //该节点有子节点
           }
           for (File subFile : parentFile.listFiles()) {
              TreeNode subNode = new TreeNode(subFile.getName());
              subNode.setHasParent(true)      //子节点有父节点;
              subNode.setLevel(parentNode.getLevel() + 1);//节点等级
              parentNode.addChild(subNode);      //添加子节点
              createTree(subNode, subFile);      //递归
           }
       } else { // 是文件
           parentNode.setIsDirectory(false);
           parentNode.setHasChild(false);
       }
}
这样,我们为每一个节点都添加了子节点,在适配器中只需将子节点列表添加到适配器的数据列表就可以显示展开目录,数据列表移除子节点列表即可收起目录。代码如下:

 if (node.getIsDirectory()) {
                  if (node.isExpanded()) { // 收起
                     node.setExpanded(false);
                     ArrayList<TreeNode> temp = new ArrayList<TreeNode>();
                     for (int i = position + 1; i < treeNodes.size(); i++) {
                         if (node.getLevel() >= treeNodes.get(i).getLevel()) {
                            break;
                         }
                         treeNodes.get(i).setExpanded(false);
                         temp.add(treeNodes.get(i));
                     }
                     treeNodes.removeAll(temp);
                     TreeListAdapter.this.notifyDataSetChanged();
                  } else { // 展开
                     node.setExpanded(true);
                     for (TreeNode childNode : node.getChildNodes()) {
                         childNode.setExpanded(false);
                         treeNodes.add(position + 1, childNode);
                     }
                     TreeListAdapter.this.notifyDataSetChanged();
                  }
         }

最后,贴上运行的效果图:


完整代码:

main.xml

<?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="@drawable/background"
        android:orientation="vertical" >

    <ListView
            android:id="@+id/tree_list"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:cacheColorHint="#00000000"
            android:scrollbars="vertical"
            android:scrollingCache="false" />
   
</LinearLayout>

 

TreeNode.java

package gwn.develop.widget;

import java.util.ArrayList;

/**
     * @功能 节点属性与方法

 * **/

public class TreeNode {
         private int level;   //目录等级

     private boolean hasParent; // 是否有父节点
         private boolean hasChild; // 是否有子节点
         private boolean isDirectory; //是否是目录
         private boolean expanded; // 节点展开

     private String title; // 节点名称
         private ArrayList<TreeNode> childNodes = new ArrayList<TreeNode>(); // 子节点链表

     public TreeNode(String title) {
              this.title = title;
              hasParent = false;
              hasChild = false;
              isDirectory = false;
              this.expanded = false;
         }

     public String getTitle() {
              return title;
         }

     public boolean getHasParent(){
              return this.hasParent;
         }
 
         public void setHasParent(boolean hasParent) {
              this.hasParent = hasParent;
         }

     public boolean getHasChild(){
              return this.hasChild;
         }
 
         public void setHasChild(boolean hasChild) {
              this.hasChild = hasChild;
         }

     public int getLevel() {
              return this.level;
         }

     public void setLevel(int level) {
              this.level = level;
         }

     public ArrayList<TreeNode> getChildNodes() {
              return childNodes;
         }

     public boolean isExpanded() {
              return expanded;
         }

     public void setExpanded(boolean expanded) {
              this.expanded = expanded;
         }
 
         public void setIsDirectory(boolean isDirectory){
              this.isDirectory = isDirectory;
         }
 
         public boolean getIsDirectory(){
              return this.isDirectory;
         }
 
         public void addChild(TreeNode childNode) {
              this.hasChild = true;
              this.childNodes.add(childNode);
         }
    }

DataProvider.java

 

package gwn.develop.widget;

import java.io.File;
    import java.util.ArrayList;

/**
     * @功能 生成显示到树型目录的节点链表
     * **/
    public class DataProvider {

    //treeNodes适配器装配的节点链表。

     private ArrayList<TreeNode> treeNodes = new ArrayList<TreeNode>();
         private String rootPath;

     public DataProvider(String rootPath) {
              this.rootPath = rootPath;
         }

     // 递归列举目录文件
         private void createTree(TreeNode parentNode, File parentFile) {
              if (parentFile.isDirectory()) { // 是目录
                   parentNode.setIsDirectory(true);
                   if (parentFile.list().length > 0) {
                        parentNode.setHasChild(true);
                   }
                   for (File subFile : parentFile.listFiles()) {
                        TreeNode subNode = new TreeNode(subFile.getName());
                        subNode.setHasParent(true);

                     //当前节点的等级为父节点等级加1
                        subNode.setLevel(parentNode.getLevel() + 1);

                    //将当前的子节点添加到父节点的子节点链表
                        parentNode.addChild(subNode);

                    //递归遍历
                        createTree(subNode, subFile);
                   }
              } else { // 是文件
                   parentNode.setIsDirectory(false);
                   parentNode.setHasChild(false);
              }
         }

 

 public ArrayList<TreeNode> getTreeNodes() {
        if (treeNodes.size() == 0) {
           File rootDir = new File(rootPath);
           if (rootDir.isDirectory()) {
                TreeNode rootNode = new TreeNode(rootDir.getName());
                rootNode.setHasParent(false);
                rootNode.setLevel(0);
                treeNodes.add(rootNode); // 刚开始列出根目录
                createTree(rootNode, rootDir);
           }
        }
          return treeNodes;
     }
}

 

TreeListAdapter.java

 

package gwn.develop.widget;

import java.util.ArrayList;

import android.content.Context;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    import android.widget.TextView;

 

/**
     * @功能 构造适配器,并添加事件监听
     * **/
    public class TreeListAdapter extends BaseAdapter {
         private Context context;
         private ArrayList<TreeNode> treeNodes;

     public TreeListAdapter(Context context, ArrayList<TreeNode> treeNodes) {
              super();
              this.context = context;
              this.treeNodes = treeNodes;

     }

     @Override
         public int getCount() {
            return treeNodes.size();
         }

     @Override
         public Object getItem(int position) {
            return position;
         }

     @Override
         public long getItemId(int position) {
            return position;
         }

     @Override
         public View getView(final int position, View convertView, ViewGroup arg2) {
              ViewHolder holder = new ViewHolder();;

          LinearLayout ll = new LinearLayout(context);
              ll.setOrientation(LinearLayout.HORIZONTAL);
  
              holder.text = new TextView(context);
              holder.icon = new ImageView(context);
  
              final TreeNode node = treeNodes.get(position);
  
              ll.setPadding(50 * node.getLevel(), 5, 5, 5);
              holder.text.setPadding(20, 5, 5, 5);
              holder.text.setText(node.getTitle());
              ll.setOnClickListener(new View.OnClickListener() {

           @Override
               public void onClick(View arg0) {
                    if (node.getIsDirectory()) {
                         if (node.isExpanded()) { // 收起
                              node.setExpanded(false);
                              ArrayList<TreeNode> temp = new ArrayList<TreeNode>();
                              for (int i = position + 1; i < treeNodes.size(); i++) {
                                   if (node.getLevel() >= treeNodes.get(i).getLevel()) {
                                        //node的子节点已经完全添加到temp中

                                    break;
                                   }
                                   treeNodes.get(i).setExpanded(false);
                                   temp.add(treeNodes.get(i));
                              }
                          treeNodes.removeAll(temp);
                          TreeListAdapter.this.notifyDataSetChanged();
                     } else { // 展开
                          node.setExpanded(true);
                          for (TreeNode childNode : node.getChildNodes()) {
                               childNode.setExpanded(false);
                               treeNodes.add(position + 1, childNode);
                          }
                          TreeListAdapter.this.notifyDataSetChanged();
                     }
                }
               }
          });

      if (node.getIsDirectory() && (node.isExpanded() == false)) {
               holder.icon.setBackgroundResource(R.drawable.node_expand);
          } else if (node.getIsDirectory() && (node.isExpanded() == true)) {
               holder.icon.setBackgroundResource(R.drawable.node_unexpand);
          } else if (!node.getIsDirectory()) {
               holder.icon.setVisibility(View.INVISIBLE);
          }
          ll.addView(holder.icon);
          ll.addView(holder.text);
          return ll;
         }

     class ViewHolder {
              TextView text;
              ImageView icon;
         }
    }

 

TreeListViewActivity.java

 

package gwn.develop.widget;

import java.util.ArrayList;

import android.app.Activity;
        import android.os.Bundle;
        import android.os.Environment;
        import android.widget.ListView;

public class TreeListViewActivity extends Activity {
            private ListView treeListView;
            @Override
            public void onCreate(Bundle savedInstanceState) {
                    super.onCreate(savedInstanceState);
                    setContentView(R.layout.main);
                    String rootPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/主目录";
                    DataProvider dataProvider = new DataProvider(rootPath);
                    ArrayList<TreeNode> treeNodes = dataProvider.getTreeNodes();
                    TreeListAdapter adapter = new TreeListAdapter(this, treeNodes);
                    treeListView = (ListView) this.findViewById(R.id.tree_list);
                    treeListView.setAdapter(adapter);
            }
        }


BUG修复:

在适配收起时装配数据有误,node.getLevel() == treeNodes.get(i).getLevel())也将上一级的节点包括进去,造成循环未正常退出,改为node.getLevel() >= treeNodes.get(i).getLevel()),bug解决


android listView多层嵌套listView显示不全问题

最近在做项目,需要用到listVIew多层嵌套listVIew的需求,先发现已下两个处理办法比较好用 第一种: public class ListViewNesting extends ListV...

Android创建目录文件夹,多级目录逐一创建

Android创建目录文件夹,多级目录逐一创建 要点:多级目录要逐一创建   //文件夹目录"/sdcard/FirstFolder/SecondFolder",多级目录必须逐一创建 ...

android 多层目录文件创建

1.项目中遇到 android  path = sdcard/xx/x/..../x/xxx.bin 目录一开始不存在 RandomAccessFile raf = new RandomAcces...

android 选人 多级树的实现

源码是从网上找的,自己修改了一下,具体作者忘了是谁了  在此说一声抱歉 节点  Node.java package com.chinaunicom.yczft.tree; import java....

Android 自定义View,实现折线图

最近要完成一个折线图控件,用来显示一系列的状态,并可以进行滑动。虽然现在有很多大牛写好的控件可以直接使用,但我感觉那些控件是给高手的使用的,对于我这样的菜鸟,还是脚踏实地,自己慢慢码代码,才可以提高。...

Android自定义View之圆弧形进度条,支持背景图片设置

自定义的一款圆弧形进度条,支持中心图片设置,有兴趣的朋友可以尝试下。 Github地址点击打开链接 package com.julyapp.progressdemo.view; import a...

js 实现 树型目录菜单

  • 2010年03月30日 14:49
  • 3KB
  • 下载

Android开发指南目录翻译

  • 2012年01月11日 16:09
  • 18KB
  • 下载

RDIFramework.NET V2.7 Web版本升手风琴+树型目录(2级+)方法

手风琴风格在Web应用非常的普遍,越来越多的Web应用都是采用这种方式来体现各个功能模块,传统的手风琴风格只支持两级模块,当我们的功能模块多于两级时,我们一般采用树来构造功能菜单(我们的框架也提供了这...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android开发之多层树型目录的实现
举报原因:
原因补充:

(最多只允许输入30个字)