您的位置:首页 > 移动开发 > Android开发

android多层树形结构列表学习笔记

2015-06-10 19:26 519 查看
android中既有组件只有 expandlistview来实现二级的分级目录,但是如果是任意级的分级目录呢?今天看到了一个DEMO,学习一下~

一、filebean类

simpleadapter的数据源的数据单元(需转化为nodes),包含以下三个属性和构造函数

private int _id;
private int parentId;
private String name;
private long length;
public FileBean(int _id, int parentId, String name)
{
super();
this._id = _id;
this.parentId = parentId;
this.name = name;
}


  二、nodes类

simpleadapter的数据源的数据单元(已转正),一大堆属性:ID、parentId、名称、级别、是否展开、下一级的孩子list、父节点、图标,可判断是否展开,是否为叶子节点,是否为根节点。

public class Node
{

private int id;
/**
* 根节点pId为0
*/
private int pId = 0;

private String name;

/**
* 当前的级别
*/
private int level;

/**
* 是否展开
*/
private boolean isExpand = false;

private int icon;

/**
* 下一级的子Node
*/
private List<Node> children = new ArrayList<Node>();

/**
* 父Node
*/
private Node parent;

public Node()
{
}

public Node(int id, int pId, String name)
{
super();
this.id = id;
this.pId = pId;
this.name = name;
}

public int getIcon()
{
return icon;
}

public void setIcon(int icon)
{
this.icon = icon;
}

public int getId()
{
return id;
}

public void setId(int id)
{
this.id = id;
}

public int getpId()
{
return pId;
}

public void setpId(int pId)
{
this.pId = pId;
}

public String getName()
{
return name;
}

public void setName(String name)
{
this.name = name;
}

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

public boolean isExpand()
{
return isExpand;
}

public List<Node> getChildren()
{
return children;
}

public void setChildren(List<Node> children)
{
this.children = children;
}

public Node getParent()
{
return parent;
}

public void setParent(Node parent)
{
this.parent = parent;
}

/**
* 是否为跟节点
*
* @return
*/
public boolean isRoot()
{
return parent == null;
}

/**
* 判断父节点是否展开
*
* @return
*/
public boolean isParentExpand()
{
if (parent == null)
return false;
return parent.isExpand();
}

/**
* 是否是叶子界点
*
* @return
*/
public boolean isLeaf()
{
return children.size() == 0;
}

/**
* 获取level
*/
public int getLevel()
{
return parent == null ? 0 : parent.getLevel() + 1;
}

/**
* 设置展开
*
* @param isExpand
*/
public void setExpand(boolean isExpand)
{
this.isExpand = isExpand;
if (!isExpand)
{

for (Node node : children)
{
node.setExpand(isExpand);
}
}
}

}


  三、TreeListViewAdapter

继承BaseAdapter的抽象类,主要控制listView的点击事件,并提供了获取convertview的抽象方法。

public abstract class TreeListViewAdapter<T> extends BaseAdapter
{

protected Context mContext;
/**
* 存储所有可见的Node
*/
protected List<Node> mNodes;
protected LayoutInflater mInflater;
/**
* 存储所有的Node
*/
protected List<Node> mAllNodes;

/**
* 点击的回调接口
*/
private OnTreeNodeClickListener onTreeNodeClickListener;

public interface OnTreeNodeClickListener
{
void onClick(Node node, int position);
}

public void setOnTreeNodeClickListener(
OnTreeNodeClickListener onTreeNodeClickListener)
{
this.onTreeNodeClickListener = onTreeNodeClickListener;
}

/**
*
* @param mTree
* @param context
* @param datas
* @param defaultExpandLevel
*            默认展开几级树
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
public TreeListViewAdapter(ListView mTree, Context context, List<T> datas,
int defaultExpandLevel) throws IllegalArgumentException,
IllegalAccessException
{
mContext = context;
/**
* 对所有的Node进行排序
*/
mAllNodes = TreeHelper.getSortedNodes(datas, defaultExpandLevel);
/**
* 过滤出可见的Node
*/
mNodes = TreeHelper.filterVisibleNode(mAllNodes);
mInflater = LayoutInflater.from(context);

/**
* 设置节点点击时,可以展开以及关闭;并且将ItemClick事件继续往外公布
*/
mTree.setOnItemClickListener(new OnItemClickListener()
{
@Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id)
{
expandOrCollapse(position);

if (onTreeNodeClickListener != null)
{
onTreeNodeClickListener.onClick(mNodes.get(position),
position);
}
}

});

}

/**
* 相应ListView的点击事件 展开或关闭某节点
*
* @param position
*/
public void expandOrCollapse(int position)
{
Node n = mNodes.get(position);

if (n != null)// 排除传入参数错误异常
{
if (!n.isLeaf())
{
n.setExpand(!n.isExpand());
mNodes = TreeHelper.filterVisibleNode(mAllNodes);
notifyDataSetChanged();// 刷新视图
}
}
}

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

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

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

@Override
public View getView(int position, View convertView, ViewGroup parent)
{
Node node = mNodes.get(position);
convertView = getConvertView(node, position, convertView, parent);
// 设置内边距
convertView.setPadding(node.getLevel() * 30, 3, 3, 3);
return convertView;
}

public abstract View getConvertView(Node node, int position,
View convertView, ViewGroup parent);

}


  四、treehelper类

提供了许多帮助我们的静态方法:

convertData2Node:将传入的bean转化为node,并通过两两比较,得出他们父子间的关系。

getRootNodes:遍历列表,筛选出root节点,返回父亲列表

addNode:采用递归的方式,将属于一个父节点的子节点挂上去

getSortedNodes:利用以上三个方法,将输入的无序nodes排列为层次分明的list

filterVisibleNode:遍历列表,筛选出正在出现的notes,以此作为listview的填充数据(筛选的标准就是看expand)

setNodeIcon:设置所有节点的icon,并根据是否展开设置不同的icon

public class TreeHelper
{
/**
* 传入我们的普通bean,转化为我们排序后的Node
*
* @param datas
* @param defaultExpandLevel
* @return
* @throws IllegalArgumentException
* @throws IllegalAccessException
*/
public static <T> List<Node> getSortedNodes(List<T> datas,
int defaultExpandLevel) throws IllegalArgumentException,
IllegalAccessException

{
List<Node> result = new ArrayList<Node>();
// 将用户数据转化为List<Node>
List<Node> nodes = convetData2Node(datas);
// 拿到根节点
List<Node> rootNodes = getRootNodes(nodes);
// 排序以及设置Node间关系
for (Node node : rootNodes)
{
addNode(result, node, defaultExpandLevel, 1);
}
return result;
}

/**
* 过滤出所有可见的Node
*
* @param nodes
* @return
*/
public static List<Node> filterVisibleNode(List<Node> nodes)
{
List<Node> result = new ArrayList<Node>();

for (Node node : nodes)
{
// 如果为跟节点,或者上层目录为展开状态
if (node.isRoot() || node.isParentExpand())
{
setNodeIcon(node);
result.add(node);
}
}
return result;
}

/**
* 将我们的数据转化为树的节点
*
* @param datas
* @return
* @throws NoSuchFieldException
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
private static <T> List<Node> convetData2Node(List<T> datas)
throws IllegalArgumentException, IllegalAccessException

{
List<Node> nodes = new ArrayList<Node>();
Node node = null;

for (T t : datas)
{
int id = -1;
int pId = -1;
String label = null;
Class<? extends Object> clazz = t.getClass();
Field[] declaredFields = clazz.getDeclaredFields();
for (Field f : declaredFields)
{
if (f.getAnnotation(TreeNodeId.class) != null)
{
f.setAccessible(true);
id = f.getInt(t);
}
if (f.getAnnotation(TreeNodePid.class) != null)
{
f.setAccessible(true);
pId = f.getInt(t);
}
if (f.getAnnotation(TreeNodeLabel.class) != null)
{
f.setAccessible(true);
label = (String) f.get(t);
}
if (id != -1 && pId != -1 && label != null)
{
break;
}
}
node = new Node(id, pId, label);
nodes.add(node);
}

/**
* 设置Node间,父子关系;让每两个节点都比较一次,即可设置其中的关系
*/
for (int i = 0; i < nodes.size(); i++)
{
Node n = nodes.get(i);
for (int j = i + 1; j < nodes.size(); j++)
{
Node m = nodes.get(j);
if (m.getpId() == n.getId())
{
n.getChildren().add(m);
m.setParent(n);
} else if (m.getId() == n.getpId())
{
m.getChildren().add(n);
n.setParent(m);
}
}
}

// 设置图片
for (Node n : nodes)
{
setNodeIcon(n);
}
return nodes;
}

private static List<Node> getRootNodes(List<Node> nodes)
{
List<Node> root = new ArrayList<Node>();
for (Node node : nodes)
{
if (node.isRoot())
root.add(node);
}
return root;
}

/**
* 把一个节点上的所有的内容都挂上去
*/
private static void addNode(List<Node> nodes, Node node,
int defaultExpandLeval, int currentLevel)
{

nodes.add(node);
if (defaultExpandLeval >= currentLevel)
{
node.setExpand(true);
}

if (node.isLeaf())
return;
for (int i = 0; i < node.getChildren().size(); i++)
{
addNode(nodes, node.getChildren().get(i), defaultExpandLeval,
currentLevel + 1);
}
}

/**
* 设置节点的图标
*
* @param node
*/
private static void setNodeIcon(Node node)
{
if (node.getChildren().size() > 0 && node.isExpand())
{
node.setIcon(R.drawable.tree_ex);
} else if (node.getChildren().size() > 0 && !node.isExpand())
{
node.setIcon(R.drawable.tree_ec);
} else
node.setIcon(-1);

}

}


  五、SimpleTreeAdapter类

listview的适配器,使用viewholder作为view的载体

public class SimpleTreeAdapter<T> extends TreeListViewAdapter<T>
{
int[] resids = new int[]{R.drawable.delete,R.drawable.rename};
List<mButton> btList = new ArrayList<mButton>();
PopupWindow popupWindow= new PopupWindow(mContext);;
public SimpleTreeAdapter(ListView mTree, Context context, List<T> datas,
int defaultExpandLevel) throws IllegalArgumentException,
IllegalAccessException
{
super(mTree, context, datas, defaultExpandLevel);
}

@Override
public View getConvertView(Node node , int position,  View convertView, ViewGroup parent)
{
ViewHolder viewHolder = null;
if (convertView == null)
{
convertView = mInflater.inflate(R.layout.list_item, parent, false);
viewHolder = new ViewHolder();
viewHolder.icon = (ImageView) convertView
.findViewById(R.id.id_treenode_icon);
viewHolder.label = (TextView) convertView
.findViewById(R.id.id_treenode_label);
viewHolder.operate = (Button)convertView.findViewById(R.id.id_treenode_operate);
convertView.setTag(viewHolder);

} else
{
viewHolder = (ViewHolder) convertView.getTag();
}

if (node.getIcon() == -1)
{
viewHolder.icon.setVisibility(View.INVISIBLE);
} else
{
viewHolder.icon.setVisibility(View.VISIBLE);
viewHolder.icon.setImageResource(node.getIcon());
}
viewHolder.label.setText(node.getName());
viewHolder.operate.setOnClickListener(new OnClickListener() {
//以下设置管理按钮的点击事件添加解除popupwindow
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (popupWindow.isShowing()) {
Log.d("test", "coming");
popupWindow.dismiss();
//popupWindow = null;
}
else {
LinearLayout pop_layout = (LinearLayout)mInflater.inflate(R.layout.pop_layout, null);
fillButton(resids);
for (mButton button: btList) {
pop_layout.addView(button);
}

popupWindow.setContentView(pop_layout);
popupWindow.setWidth(LayoutParams.WRAP_CONTENT);
popupWindow.setHeight(LayoutParams.WRAP_CONTENT);

popupWindow.showAtLocation((((MainActivity)mContext).findViewById(R.id.container)), Gravity.CENTER, 0, 0);
Log.d("test2",String.valueOf(popupWindow.isShowing()));
}
}
});

return convertView;
}
/**
* 利用给定资源,定制button,并添加到btlist中
* */
private void fillButton(int[] resids){
if (!btList.isEmpty()) {
btList.clear();
}
for (int i = 0; i < resids.length; i++) {
mButton button = ButtonUtil.getButton(mContext, i, resids[i]);
btList.add(button);
}
}
private final class ViewHolder
{
ImageView icon;
TextView label;
Button operate;
}

}


  六、mainactivity

一个listview适配即可

public class MainActivity extends Activity
{
private List<Bean> mDatas = new ArrayList<Bean>();
private List<FileBean> mDatas2 = new ArrayList<FileBean>();
private ListView mTree;
private SimpleTreeAdapter<FileBean> mAdapter;

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

initDatas();
mTree = (ListView) findViewById(R.id.id_tree);
try
{
mAdapter = new SimpleTreeAdapter<FileBean>(mTree, this, mDatas2, 10);
mAdapter.setOnTreeNodeClickListener(new OnTreeNodeClickListener()
{
@Override
public void onClick(Node node, int position)
{
if (node.isLeaf())
{
Toast.makeText(getApplicationContext(), node.getName(),
Toast.LENGTH_SHORT).show();
}
}

});

} catch (Exception e)
{
e.printStackTrace();
}
mTree.setAdapter(mAdapter);
}

private void initDatas()
{
mDatas.add(new Bean(1, 0, "根目录1"));
mDatas.add(new Bean(2, 0, "根目录2"));
mDatas.add(new Bean(3, 0, "根目录3"));
mDatas.add(new Bean(4, 0, "根目录4"));
mDatas.add(new Bean(5, 1, "子目录1-1"));
mDatas.add(new Bean(6, 1, "子目录1-2"));

mDatas.add(new Bean(7, 5, "子目录1-1-1"));
mDatas.add(new Bean(8, 2, "子目录2-1"));

mDatas.add(new Bean(9, 4, "子目录4-1"));
mDatas.add(new Bean(10, 4, "子目录4-2"));

mDatas.add(new Bean(11, 10, "子目录4-2-1"));
mDatas.add(new Bean(12, 10, "子目录4-2-3"));
mDatas.add(new Bean(13, 10, "子目录4-2-2"));
mDatas.add(new Bean(14, 9, "子目录4-1-1"));
mDatas.add(new Bean(15, 9, "子目录4-1-2"));
mDatas.add(new Bean(16, 9, "子目录4-1-3"));

mDatas2.add(new FileBean(1, 0, "音乐"));
mDatas2.add(new FileBean(2, 1, "流行音乐"));
mDatas2.add(new FileBean(3, 1, "爵士音乐"));
mDatas2.add(new FileBean(4, 1, "古典音乐"));
mDatas2.add(new FileBean(5, 2, "约定"));
mDatas2.add(new FileBean(6, 2, "大男人"));

mDatas2.add(new FileBean(7, 4, "bottoms up"));
mDatas2.add(new FileBean(8, 4, "goodbye allis"));

mDatas2.add(new FileBean(9, 7, "歌词一"));
mDatas2.add(new FileBean(10, 7, "歌词二"));
mDatas2.add(new FileBean(11, 7, "歌词三"));
mDatas2.add(new FileBean(12, 8, "歌词一"));

}

}


  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: