Adroid 自定义目录节点控件
一、效果图如下
二、控件功能与背景
- 背景:该控件可用于层级目录过多的资源界面,最常见的是Android系统文件管理功能中的目录节点显示和点击功能。
- 功能:
1)、添加节点功能:
/**
* 添加节点
* @param node
*/
public void pushNode(Node node)
2)、移除栈顶节点,控件支持单节点抛,也支持多节点抛出。用户选择某一目录节点时,该目录节点之后的元素都将移除。
/**
* 抛出栈顶的节点
*/
public boolean popNode()
3)、每增加一个节点,视图都将移动到最后一个节点的视图。这里用到了父类的fullScroll方法。
fullScroll(HorizontalScrollView.FOCUS_RIGHT);
三、控件分析
1、简单分析
控件非常的简单,我们这里需要用到控件的横向滑动的功能,所以这里我们自然而然的就想到了继承HorizontalScrollView或者HorizontalScrollView。添加的子控件也非常的简单,TextView+ImageView组合。
2、需要用到Stack集合
节点的加入和取出属于后进先出类型的栈,所以我们可以选择Stack堆栈类集合,Stack 继承自 Vector,实现一个后进先出的堆栈。
关键方法:
/**
* 找到栈顶元素但不移除
*/
public synchronized E peek()
/**
* 找到栈顶元素并移除
*/
public synchronized E pop()
/**
* 将元素放置到栈顶
*/
public E push(E item)
3、添加回调方法,如果设置了回调方法,用户在抛出节点的时候会回调该方法。
public interface DirNodeListener {
void onPopNode(String nodeId);
}
四、控件源码
/**
* @author :chezi008 on 2018/4/20 15:02
* @description :目录节点控件
* @email :chezi008@163.com
*/
public class DirNodeView extends HorizontalScrollView {
private String TAG = getClass().getSimpleName();
private Stack<Node> mStack;
private LinearLayout llParent;
private View.OnClickListener onClickListener;
private DirNodeListener dirNodeListener;
private boolean isScrolledLast;
public DirNodeView(Context context) {
this(context, null);
}
public DirNodeView(Context context, AttributeSet attrs) {
super(context, attrs);
initVariable();
initView();
}
public void setDirNodeListener(DirNodeListener dirNodeListener) {
this.dirNodeListener = dirNodeListener;
}
private void initVariable() {
mStack = new Stack<>();
onClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "onClick: id:" + v.getTag());
if (dirNodeListener != null) {
dirNodeListener.onPopNode((String) v.getTag());
}
popNode((String) v.getTag());
}
};
}
private void initView() {
llParent = new LinearLayout(getContext());
llParent.setOrientation(LinearLayout.HORIZONTAL);
setHorizontalScrollBarEnabled(false);
addView(llParent);
}
/**
* 添加节点
* @param node
*/
public void pushNode(Node node) {
addNodeView(node);
}
public String getNodeId() {
return mStack.peek().nodeId;
}
public Stack<Node> getStack() {
return mStack;
}
private void popNode(String id) {
int length = mStack.size();
//for循环里面 mStack.size()是会变的
for (int i = 0; i < length; i++) {
Node node = mStack.peek();
if (node.nodeId.equals(id)) {
break;
}
popNode(node);
}
}
private void addNodeView(Node node) {
LinearLayout nodeParent = new LinearLayout(getContext());
nodeParent.setOrientation(LinearLayout.HORIZONTAL);
nodeParent.setGravity(Gravity.CENTER_VERTICAL);
int padding = DensityUtils.sp2px(getContext(), 10);
int paddingTop = DensityUtils.sp2px(getContext(), 5);
int paddingIvTop = DensityUtils.sp2px(getContext(), 7);
TextView textView = new TextView(getContext());
textView.setText(node.nodeName);
textView.setTextSize(16);
textView.setTag(node.nodeId);
textView.setPadding(padding, paddingTop, 0, paddingTop);
textView.setOnClickListener(onClickListener);
nodeParent.addView(textView);
ImageView ivRight = new ImageView(getContext());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT);
ivRight.setImageResource(R.drawable.ve_direction_right);
ivRight.setPadding(0, paddingIvTop, 0, paddingTop);
nodeParent.addView(ivRight, params);
llParent.addView(nodeParent);
node.setNodeView(nodeParent);
mStack.push(node);
postInvalidate();
isScrolledLast = true;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (isScrolledLast) {
fullScroll(HorizontalScrollView.FOCUS_RIGHT);
isScrolledLast = false;
}
}
private void popNode(Node node) {
llParent.removeView(node.nodeView);
mStack.pop();
}
/**
* 抛出栈顶的数据
*/
public boolean popNode() {
if (mStack.size() == 1) {
return false;
}
Node node = mStack.pop();
llParent.removeView(node.nodeView);
if (dirNodeListener != null) {
dirNodeListener.onPopNode(mStack.peek().nodeId);
}
return true;
}
public static class Node {
private String nodeId;
private String nodeName;
private LinearLayout nodeView;
public Node(String nodeId, String nodeName) {
this.nodeId = nodeId;
this.nodeName = nodeName;
}
public String getNodeId() {
return nodeId;
}
public String getNodeName() {
return nodeName;
}
public LinearLayout getNodeView() {
return nodeView;
}
public void setNodeView(LinearLayout nodeView) {
this.nodeView = nodeView;
}
}
public interface DirNodeListener {
void onPopNode(String nodeId);
}
}