1. TreeView란?
RecyclerView와 다르게 TreeView는 특별한 장점을 갖고 있다.
- 계층 구조 표현
- 데이터 조작
- 데이터 시각화
상위노드와 하위노드 그리고 하위노드 depths가 크게 작용될 때, TreeView의 사용이 적합하다. RecyclerView는 depths가 높아질수록 성능 저하를 일으키는 문제가 발생된다. 일반적으로 TreeView는 각 노드가 데이터를 갖고 있어 데이터 처리 및 수정 조작에 적합하다. 이러한 특성 덕분에 좀 더 세밀한 하위구조에 적합하며, 조직도 파일 탐색기, 카테고리 분류 등과 같이 하위 노드를 필요로 하는 시각화 부분에 있어 사용되고 있다.
2. TreeView구조
일반적인 TreeView구조에 대해 예시 코드로 같이 알아보자
CODE
(1) TreeNode.java
// TreeNode.java
public class TreeNode {
private String nodeName;
private List<TreeNode> children;
public TreeNode(String nodeName) {
this.nodeName = nodeName;
this.children = new ArrayList<>();
}
public String getNodeName() {
return nodeName;
}
public List<TreeNode> getChildren() {
return children;
}
public void addChild(TreeNode child) {
children.add(child);
}
}
(2) TreeViewAdapter.java
// TreeViewAdapter.java
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.TextView;
import java.util.List;
public class TreeViewAdapter extends BaseExpandableListAdapter {
private Context context;
private List<TreeNode> nodeList;
public TreeViewAdapter(Context context, List<TreeNode> nodeList) {
this.context = context;
this.nodeList = nodeList;
}
@Override
public int getGroupCount() {
return nodeList.size();
}
@Override
public int getChildrenCount(int groupPosition) {
return nodeList.get(groupPosition).getChildren().size();
}
@Override
public Object getGroup(int groupPosition) {
return nodeList.get(groupPosition);
}
@Override
public Object getChild(int groupPosition, int childPosition) {
return nodeList.get(groupPosition).getChildren().get(childPosition);
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.group_item, null);
}
TextView groupNameTextView = convertView.findViewById(R.id.groupNameTextView);
groupNameTextView.setText(((TreeNode) getGroup(groupPosition)).getNodeName());
return convertView;
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.child_item, null);
}
TextView childNameTextView = convertView.findViewById(R.id.childNameTextView);
childNameTextView.setText(((TreeNode) getChild(groupPosition, childPosition)).getNodeName());
return convertView;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}
(3) group_item.xml
<!-- group_item.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="8dp">
<TextView
android:id="@+id/groupNameTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp" />
</LinearLayout>
(4) child_item.xml
<!-- child_item.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="16dp">
<TextView
android:id="@+id/childNameTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="16sp" />
</LinearLayout>
(5) MainActivity.java
// MainActivity.java
import android.os.Bundle;
import android.widget.ExpandableListView;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ExpandableListView expandableListView = findViewById(R.id.expandableListView);
// 데이터 생성
List<TreeNode> nodeList = new ArrayList<>();
TreeNode root = new TreeNode("Root");
root.addChild(new TreeNode("Child 1"));
TreeNode child2 = new TreeNode("Child 2");
child2.addChild(new TreeNode("Grandchild 2.1"));
child2.addChild(new TreeNode("Grandchild 2.2"));
root.addChild(child2);
nodeList.add(root);
// 어댑터 설정
TreeViewAdapter adapter = new TreeViewAdapter(this, nodeList);
expandableListView.setAdapter(adapter);
}
}
(6) 구조
Root
|
-----------------
| |
Child 1 Child 2
|
--------------
| |
Grandchild 2.1 Grandchild 2.2
상위 코드의 구조는 기본적으로 다음과 같다. Root안에 child1, child2가 있고 child2의 하위 노드로 Grandchild 2.1과 Grandchild 2.2가 들어있다. 통상적으로 조직도와 같이 하위 노드를 표한하는 방법이나 각 노드의 데이터를 갖고 다니는 구조의 경우 트리뷰가 널리 사용되어 오고 있다.
간단하게 요약하면 카테고리나 조직도와 같이 하위 구조가 깊거나 텍스트 나열로 이루어졌을 때 트리뷰를 사용하고 나머지는 리사이클러뷰를 사용한다. 생각보다 트리뷰는 많이 사용되고 있고 트리구조 또한 많은 부분에서 사용되고 있다. 하지만 안드로이드 혹은 Kotlin에서는 이 부분에 대하여 많은 개선이 되고 있는 것 같다. xml 구조만 하더라도 트리구조로 되어 있었으나 Compose가 등장하지 않았는가? 트리뷰도 업데이트가 되지 않을까 하는 기대를 해본다.
'Android > Android Java' 카테고리의 다른 글
[Android Java] Synchronize와 AtomicBoolean (0) | 2024.08.01 |
---|---|
[Android Java] China Push, Doze Mode (0) | 2023.11.23 |
[Android Java] 사진, 동영상 촬영(ActivityResultLauncher, Scoped Storage) (0) | 2023.11.22 |
[Android Java] Scoped Storage 대응 (1) | 2023.11.21 |
[Android Java] Notification Action, RemoteInput (1) | 2023.11.13 |