В данной статье хочу привести небольшой пример работы с кастомизацией ExpandableListView — двухуровневого списка.
То, что должно получиться в итоге
Приступим, создадим проект и добавим в активити данный код
Добавляем в main.xml ExpandableListView
Теперь создадим класс адаптера
Названия методов и параметров довольно информативны. Методы getGroupView и getChildView возвращают View для «родителей» и «детей» соответственно. Используя параметр isExpanded в методе getGroupView, можно, например, менять фон group при разных состояниях. С помощью LayoutInflater используем кастомные layout для нашего списка.
group_view.xml
child_view.xml
В child_view.xml добавлена кнопка, а в адаптере в методе getChildView обработали ее нажатие. Таким же образом можно добавлять кнопки и другие элементы в group_view.xml.
Так же списку можно «навесить» слушателей:
Теперь рассмотрим groupIndicator — индикатор состояния группы. Его положение задано в main.xml параметрами indicatorLeft и indicatorRight — соответственно левой и правой границей. По умолчанию индикатор располагается слева, что не очень привычно. Также можно подставить свои изображения, для этого нужно создать indicator.xml в папке drawable с таким кодом
Где imageOpen – будет отображаться при раскрытой группе, а imageClose – закрытой. Далее в main.xml нужно добавить строчку к параметрам нашего списка android:groupIndicator="@drawable/indicator". При подготовке изображений нужно учесть то, что они будут растянуты по всей высоте лэйаута group_view. Так что если нужен кастомный значок индикатора — лучше использовать пару изображений и контролировать их появления в методе getView. В этой статье хорошо описаны нюансы работы с адаптерами, а также хочу обратить ваше внимание на использование класса ViewHolder.
Ну вот и все, надеюсь, что пост поможет начинающим разработчикам.
То, что должно получиться в итоге
Приступим, создадим проект и добавим в активити данный код
public class ExpActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Находим наш list
ExpandableListView listView = (ExpandableListView)findViewById(R.id.exListView);
//Создаем набор данных для адаптера
ArrayList<ArrayList<String>> groups = new ArrayList<ArrayList<String>>();
ArrayList<String> children1 = new ArrayList<String>();
ArrayList<String> children2 = new ArrayList<String>();
children1.add("Child_1");
children1.add("Child_2");
groups.add(children1);
children2.add("Child_1");
children2.add("Child_2");
children2.add("Child_3");
groups.add(children2);
//Создаем адаптер и передаем context и список с данными
ExpListAdapter adapter = new ExpListAdapter(getApplicationContext(), groups);
listView.setAdapter(adapter);
}
}
Добавляем в main.xml ExpandableListView
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ExpandableListView
android:id="@+id/exListView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:indicatorLeft="250dp"
android:indicatorRight="300dp"
/>
</LinearLayout>
Теперь создадим класс адаптера
public class ExpListAdapter extends BaseExpandableListAdapter {
private ArrayList<ArrayList<String>> mGroups;
private Context mContext;
public ExpListAdapter (Context context,ArrayList<ArrayList<String>> groups){
mContext = context;
mGroups = groups;
}
@Override
public int getGroupCount() {
return mGroups.size();
}
@Override
public int getChildrenCount(int groupPosition) {
return mGroups.get(groupPosition).size();
}
@Override
public Object getGroup(int groupPosition) {
return mGroups.get(groupPosition);
}
@Override
public Object getChild(int groupPosition, int childPosition) {
return mGroups.get(groupPosition).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 true;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView,
ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.group_view, null);
}
if (isExpanded){
//Изменяем что-нибудь, если текущая Group раскрыта
}
else{
//Изменяем что-нибудь, если текущая Group скрыта
}
TextView textGroup = (TextView) convertView.findViewById(R.id.textGroup);
textGroup.setText("Group " + Integer.toString(groupPosition));
return convertView;
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild,
View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.child_view, null);
}
TextView textChild = (TextView) convertView.findViewById(R.id.textChild);
textChild.setText(mGroups.get(groupPosition).get(childPosition));
Button button = (Button)convertView.findViewById(R.id.buttonChild);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(mContext,"button is pressed",5000).show();
}
});
return convertView;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}
Названия методов и параметров довольно информативны. Методы getGroupView и getChildView возвращают View для «родителей» и «детей» соответственно. Используя параметр isExpanded в методе getGroupView, можно, например, менять фон group при разных состояниях. С помощью LayoutInflater используем кастомные layout для нашего списка.
group_view.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="match_parent">
<TextView
android:id="@+id/textGroup"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_marginLeft="5dp"
android:layout_marginTop="20dp"
android:textColor="@android:color/white"
android:textStyle="bold"
/>
</LinearLayout>
child_view.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="match_parent">
<TextView
android:id="@+id/textChild"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
android:textColor="@android:color/white"
/>
<Button
android:id="@+id/buttonChild"
android:layout_width="100dp"
android:layout_height="40dp"
android:layout_marginLeft="150dp"
android:layout_marginTop="10dp"
android:text="Button"
android:focusable="false"
/>
</LinearLayout>
В child_view.xml добавлена кнопка, а в адаптере в методе getChildView обработали ее нажатие. Таким же образом можно добавлять кнопки и другие элементы в group_view.xml.
Так же списку можно «навесить» слушателей:
- OnChildClickListener — нажатие на элемент
- OnGroupCollapseListener – сворачивание группы
- OnGroupExpandListener – разворачивание группы
- OnGroupClickListener – нажатие на группу
Теперь рассмотрим groupIndicator — индикатор состояния группы. Его положение задано в main.xml параметрами indicatorLeft и indicatorRight — соответственно левой и правой границей. По умолчанию индикатор располагается слева, что не очень привычно. Также можно подставить свои изображения, для этого нужно создать indicator.xml в папке drawable с таким кодом
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_expanded="true"
android:drawable="@drawable/imageOpen">
</item>
<item android:state_empty="true"
android:drawable="@drawable/imageClose">
</item>
</selector>
Где imageOpen – будет отображаться при раскрытой группе, а imageClose – закрытой. Далее в main.xml нужно добавить строчку к параметрам нашего списка android:groupIndicator="@drawable/indicator". При подготовке изображений нужно учесть то, что они будут растянуты по всей высоте лэйаута group_view. Так что если нужен кастомный значок индикатора — лучше использовать пару изображений и контролировать их появления в методе getView. В этой статье хорошо описаны нюансы работы с адаптерами, а также хочу обратить ваше внимание на использование класса ViewHolder.
Ну вот и все, надеюсь, что пост поможет начинающим разработчикам.