Наверное многие знают приложение Lucky Patcher, в новых версиях которого список сделан интересно: для каждого элемента списка есть меню.
В моем приложении мне захотелось реализовать нечто подобное на примере списка друзей социальной сети.
Прочитав эту статью, где используется коллекция коллекций:
private ArrayList<ArrayList<String>> mGroups;
А так же покопавшись в исходниках, я решил объединить и доработать эти примеры и вот что у меня получилось:
Приступаем к реализации. Создаем новый проект с классом ListActivity, который наследуется от ActionBarActivity (можно и от Activity). Отредактируем код нашей активности (activity_list.xml):
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".ListActivity" >
<ExpandableListView
android:id="@+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indicatorLeft="200dp" />
</LinearLayout>
Ничего сложного — вертикальный LinearLayout, который содержит в себе двухуровневый список ExpandableListView.
Далее необходимо описать пункт списка - item_friend.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/friendLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<ImageView
android:id="@+id/photoFriend"
android:layout_width="60dp"
android:layout_height="60dp"
android:contentDescription="@string/todo"
android:src="@android:drawable/ic_menu_camera" />
<TextView
android:id="@+id/f_s_names"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_weight="1"
android:text="@string/Def_fname"
android:textAppearance="?android:attr/textAppearanceMedium" />
<ImageView
android:id="@+id/congratuated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginRight="7dp"
android:contentDescription="@string/todo"
android:src="@android:drawable/checkbox_on_background"
android:visibility="gone" />
</LinearLayout>
Элемент списка содержит фотографию в ImageView, имя и фамилию в TextView, а так же еще один ImageView. Набор элементов может быть абсолютно любым и ограничивается лишь фантазией разработчика.
Опишем меню пункта списка — item_friend_menu.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"
android:layout_marginTop="10dp"
android:orientation="vertical"
android:background="#E0E0E0" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="25dp"
android:orientation="horizontal" >
<TextView
android:id="@+id/textNick"
style="@style/Fm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/nick_" />
<TextView
android:id="@+id/m_Nick"
style="@style/Fm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="@string/empty" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="25dp"
android:orientation="horizontal" >
<TextView
android:id="@+id/textSex"
style="@style/Fm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/sex_" />
<TextView
android:id="@+id/m_Sex"
style="@style/Fm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:text="@string/man" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="25dp"
android:orientation="horizontal" >
<TextView
android:id="@+id/textBdate"
style="@style/Fm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/brithday_" />
<TextView
android:id="@+id/m_Bdate"
style="@style/Fm"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="5dp"
android:layout_weight="1"
android:text="@string/_17_2_1985" />
<ImageButton
android:id="@+id/imageEditdate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="7dp"
android:adjustViewBounds="true"
android:background="@android:color/transparent"
android:contentDescription="@string/todo"
android:scaleType="fitCenter"
android:src="@android:drawable/ic_menu_edit" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="25dp"
android:orientation="horizontal" >
<TextView
android:id="@+id/textTemplate"
style="@style/Fm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/template_" />
<TextView
android:id="@+id/m_Template"
style="@style/Fm"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="5dp"
android:layout_weight="1"
android:text="@string/temp_name" />
<ImageButton
android:id="@+id/imageSelectTemp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="7dp"
android:adjustViewBounds="true"
android:background="@android:color/transparent"
android:scaleType="fitCenter"
android:src="@android:drawable/ic_menu_edit"
android:contentDescription="@string/todo"/>
</LinearLayout>
</LinearLayout>
С меню элемента все аналогично — описываем то, что хотим увидеть на дисплее.
Все необходимые XML готовы, теперь приступим к кодингу на Java. Нам понадобятся два вспомогательных класса, описывающих содержимое элемента списка и меню этого элемента. Сначала опишем меню в классе FriendMenu.java:
public class FriendMenu {
public FriendMenu() {
}
private String nick, bdate, template_name;
private int sex;
public String getNick() {
return nick;
}
public void setNick(String nick) {
this.nick = nick;
}
public String getBdate() {
return bdate;
}
public void setBdate(String bdate) {
this.bdate = bdate;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public String getTemplate_name() {
return template_name;
}
public void setTemplate_name(String template_name) {
this.template_name = template_name;
}
public int getId() {
return user_id;
}
public void setId(int id) {
this.user_id = id;
}
}
Класс используется скорее не как меню, а как дополнительная информация об элементе списка, которую необходимо отображать по желанию пользователя.
Здесь 4 поля — ник, день рождения, пол и поле template_name, а так же методы get* и set* для работы с полями класса. В качестве поля sex можно использовать тип boolean, но мне пришлось использовать int, т.к. в списке друзей, получаемом с сервера, пол имеет тип целого числа(?).
Теперь опишем класс элемента списка Friend.java:
public class Friend {
public Friend() {
}
private ArrayList<FriendMenu> menu;
private Bitmap bmp;
private String text;
private boolean congratulated;
public ArrayList<FriendMenu> getMenu() {
return menu;
}
public void setMenu(ArrayList<FriendMenu> menus) {
this.menu = menus;
}
public boolean isCongratulated() {
return congratulated;
}
public void setCongratulated(boolean congratulated) {
this.congratulated = congratulated;
}
public Bitmap getBmp() {
return bmp;
}
public void setBmp(Bitmap bmp) {
this.bmp=bmp;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
Поле bmp хранит изображение для ImageView элемента списка, поле text — имя и фамилию, поле congratulated отвечает за показ галочки в ImageView.
В классе содиржится поле menu — коллекция менюшек для одного элемента списка. Помимо собственной информации внутри себя элемент 1-го уровня содержит информацию о своем меню. В моем случае menu будет состоять только из одного элемента.
Здесь у вас, уважаемые читатели, наверняка возникнет замечание, что можно было бы не цеплять сразу весь Layout в качестве элемента списка второго уровня, а разбить его на составляющие, т.к. внутреннее содержимое примерно одинаковое (горизонтальный LinearLayout, внутри которого 2 TextView [и ImageButton]), а так же добавлять по мере необходимости. Согласен, у меня не оптимальное решение.
Теперь самое главное — создать свой класс, который наследуется от BaseExpandableListAdapter — FriendsAdapted.java.
public class FriendsAdapted extends BaseExpandableListAdapter {
private ArrayList<Friend> friends;
private LayoutInflater inflater;
// каждый друг внутри себя содержит свое меню (доп. информацию).
public FriendsAdapted(Context cont, ArrayList<Friend> list) {
inflater = LayoutInflater.from(cont);
friends = list;
}
@Override
public int getGroupCount() {
return friends.size();
}
@Override
public int getChildrenCount(int groupPosition) {
return friends.get(groupPosition).getMenu().size();
}
@Override
public Object getGroup(int groupPosition) {
return friends.get(groupPosition);
}
@Override
public Object getChild(int groupPosition, int childPosition) {
return friends.get(groupPosition).getMenu().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) {
convertView = inflater.inflate(R.layout.item_friend, null);
}
// получили группу (друга)
Friend fr = friends.get(groupPosition);
((TextView) convertView.findViewById(R.id.f_s_names)).setText(fr
.getText());
// ((ImageView)
// convertView.findViewById(R.id.photoFriend)).setImageBitmap(fr.getBmp());
((ImageView) convertView.findViewById(R.id.congratuated))
.setVisibility((fr.isCongratulated() ? View.VISIBLE : View.GONE));
return convertView;
}
@Override
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
FriendMenu menu = (FriendMenu) getChild(groupPosition, childPosition);
convertView = inflater
.inflate(R.layout.item_friend_menu, parent, false);
((TextView) convertView.findViewById(R.id.m_Nick)).setText(menu
.getNick());
switch (menu.getSex()) {
case 1:
((TextView) convertView.findViewById(R.id.m_Sex))
.setText(R.string.sex_man);
break;
case 0:
((TextView) convertView.findViewById(R.id.m_Sex))
.setText(R.string.sex_fem);
break;
}
((TextView) convertView.findViewById(R.id.m_Bdate)).setText(menu
.getBdate());
((TextView) convertView.findViewById(R.id.m_Template)).setText(menu
.getTemplate_name());
return convertView;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return false;
}
}
Класс содержит коллекцию друзей, а в которой каждый друг содержит свое собственное меню. Для заполнения информацией элементов первого и второго уровней необходимо переопределить методы getGroupView и getChildView соответственно. С помощью LayoutInflater можно подключать кастомизированные Layout'ы.
Каждому View можно задает свои:
- Selector
- ClickListener
- LongClickListener
- и другие обработчики
Теперь осталось отобразись список. В моем приложении была предварительно заполненная база данных с таблицей друзей, поэтому список буду заполнять из нее:
Код ListActivity.java:
public class ListActivity extends ActionBarActivity {
ExpandableListView list_w;
static private SQLdb data;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list);
list_w = (ExpandableListView) findViewById(R.id.listView1);
data = new SQLdb(this, 2);
SQLiteDatabase db = data.getReadableDatabase();
Cursor c = db.rawQuery("select * from friends", null);
ArrayList<Friend> friends = new ArrayList<Friend>();
if (c.moveToFirst()) {
for (int i = 0; i < c.getCount(); ++i) {
Friend friend = new Friend();
ArrayList<FriendMenu> menus = new ArrayList<FriendMenu>();
FriendMenu menu = new FriendMenu();
menu.setBdate(c.getString(c.getColumnIndex("bdate")));
menu.setNick(c.getString(c.getColumnIndex("nickname")));
menu.setSex(c.getInt(c.getColumnIndex("sex")));
menus.add(menu);
friend.setMenu(menus);
friend.setText(c.getString(c.getColumnIndex("fname"))
+ " " + c.getString(c.getColumnIndex("sname")));
if (i % 2 == 0) {
friend.setCongratulated(true);
}
friends.add(friend);
if (!c.moveToNext()) {
break;
}
}
} else Toast.makeText(this, "You have not friends!", Toast.LENGTH_SHORT).show();
c.close();
db.close();
FriendsAdapted friendsadapter=new FriendsAdapted(this,friends);
list_w.setAdapter(friendsadapter);
list_w.setDivider(getResources().getDrawable(R.drawable.line));
list_w.setDividerHeight(2);
}
}
На этом все. Как видно из примера здесь нет ничего сложного. Можно реализовать практически любое меню для элемента списка первого уровня. Надеюсь, моя статья поможет начинающим андроид-разработчикам.
Автор: bagrusss