Кастомный список с меню для каждого элемента на основе ExpandableListView

от автора

Наверное многие знают приложение 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 TextViewImageButton]), а так же добавлять по мере необходимости. Согласен, у меня не оптимальное решение.

Теперь самое главное — создать свой класс, который наследуется от BaseExpandableListAdapterFriendsAdapted.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);			 	} }

На этом все. Надеюсь, моя статья поможет начинающим андроид-разработчикам.

ссылка на оригинал статьи http://habrahabr.ru/post/227549/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *