Android/Android Java

[Android Java] TreeView

Bell91 2023. 12. 20. 16:05
반응형

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가 등장하지 않았는가? 트리뷰도 업데이트가 되지 않을까 하는 기대를 해본다.

반응형