Posted at

マルチヒエラルキーなExpandableListView

More than 5 years have passed since last update.

AndroidのExpandableListView(以下ExLV)はルート階層のアイテムはすべて親になりますが、展開できるアイテムと、通常のListView的なアイテムを共存させるようにしてみました。


準備

ExLVに渡す子のモデルクラスを作ります。下のコードでは名前しか無いですが、クリックされた時の処理のためにRunnableやら他のインスタンスやらを渡しておいて、機能を付け足してください。


Element.java

public class Element

{

private String name;
private ArrayList<Element> children = new ArrayList<Element>();

public Element(String name)
{
this.name = name;
}

public String getName()
{
return name;
}

public boolean isParent()
{
return children.size() > 0;
}

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

public void addChild(Element element)
{
children.add(element);
}
}



Viewの生成

今回はインラインで生成します。XMLでも同じ事できると思います

ArrayList<Element> elements = new ArrayList<Element>();

// elementsを仕込む
ExpandableListView listview = new ExpandableListView(activity);
listview.setGroupIndicator(getResource().getDrawable(android.R.color.transparent));
MyExpandListAdapter adapter = new MyExpandListAdapter(activity, elements);

listview.setGroupIndicator(getResource().getDrawable(android.R.color.transparent));で、


とか V みたいなインジケータを消しています。これ重要。展開できるできないにかかわらずExLVはすべてのトップアイテムにインジケータを最後に上書きするので、Inflaterだけで普通のアイテムぽくしようとしてもダメです。



Adapterを作る

肝心要のAdapterです。BaseExpandableListAdapterを継承して作ります。


MyExpandListAdapter.java

public class MyExpandListAdapter extends BaseExpandableListAdapter

{

private List<Element> elements;
private Activity activity;
private LayoutInflater inflater;

public MyExpandListAdapter(Activity activity, List<Element> elements)
{
this.activity = activity;
this.elements = new ArrayList<Element>(elements);
this.inflater = activity.getLayoutInflater();
}

@Override
public Object getChild(int arg0, int arg1)
{
return elements.get(arg0).getChildren().get(arg1);
}

@Override
public long getChildId(int arg0, int arg1)
{
return arg1;
}

@Override
public View getChildView(int arg0, int arg1, boolean arg2, View view, ViewGroup arg4)
{
/**
* 子アイテムの描画
*/

return view;
}

@Override
public int getChildrenCount(int arg0)
{
return elements.get(arg0).getChildren().size();
}

@Override
public Object getGroup(int arg0)
{
return elements.get(arg0);
}

@Override
public int getGroupCount()
{
return elements.size();
}

@Override
public long getGroupId(int arg0)
{
return arg0;
}

@Override
public View getGroupView(int arg0, boolean isExpanded, View view, ViewGroup parent)
{
Element element = elements.get(arg0);
if(element.isParent())
{
view = inflater.inflate(R.layout.parent, null);
TextView textView = (TextView) view.findViewById(R.id.parent_name);
textView.setText(element.getName());
ImageView indicator = (ImageView) view.findViewById(R.id.parent_indicator);
indicator.setImageResource(isExpanded ? R.drawable.expand_open : R.drawable.expand_close);
}
else
{
/**
* 子アイテムの描画
*/

}

return view;
}

@Override
public boolean hasStableIds()
{
return true;
}

@Override
public boolean isChildSelectable(int arg0, int arg1)
{
return true;
}
}


getGroupViewの中で、ElementがisParentかどうかで描画処理を変えています。

子の場合はgetChildViewと全く同じ処理で構いません。親の場合は、予め親用のレイアウト(今回はR.layout.parent)を作っておいて、その中にインジケータの代わりになるImageViewを置いておきます。そんで引数のisExpandedで用いる画像(Drawable)を切り替えれば完成です。

親にインジケータ表示が要らないという方はこの辺りはばっさりカットで、単に親用に描画すればいいだけです。

stackoverflowとかいろいろめぐった結果この実装が一番簡単かなという感じです。

最初からカスタムViewでゴリゴリっと作ってしまえる方はぜひそうして、そんでもってライブラリ配布してくださいお願いします(他力本願)