-
Android仿QQ好友列表分组实现增删改及持久化
这篇文章主要介绍了Android仿QQ好友列表分组实现增删改及持久化的相关资料,需要的朋友可以参考下Android自带的控件ExpandableListView实现了分组列表功能,本案例在此基础上进行优化,为此控件添加增删改分组及子项的功能,以及列表数据的持久化。
Demo实现效果:
GroupListDemo具体实现:
①demo中将列表页面设计为Fragment页面,方便后期调用;在主界面MainActivity中动态添加GroupListFragment页面;
MainActivity.java
123456789101112131415161718192021222324packagecom.eric.grouplistdemo;importandroid.app.Activity;importandroid.app.Fragment;importandroid.app.FragmentManager;importandroid.os.Bundle;importandroid.widget.RelativeLayout;publicclassMainActivityextendsActivity {publicstaticGroupListFragment fragment;@OverrideprotectedvoidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);fragment =newGroupListFragment();getFragmentManager().beginTransaction().replace(R.id.fragContainer, fragment).commit();}}动态添加GroupListFragment实例到界面的fragContainer布局中;将fragment声明为static用于在Adapter中组添加子项时进行调用。
activity_main.xml
123456789101112<?xmlversion="1.0"encoding="utf-8"?>android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><RelativeLayoutandroid:id="@+id/fragContainer"android:layout_width="match_parent"android:layout_height="match_parent"></RelativeLayout></LinearLayout>②实现自定义适配器类MyAdapter,继承自BaseExpandableListAdapter;组项布局及子项布局;
list_item_parent.xml组项布局文件,展开图标及名称,增删图标;
1234567891011121314151617181920212223242526272829303132333435<?xmlversion="1.0"encoding="utf-8"?>android:layout_width="match_parent"android:layout_height="50dp"android:background="#0099ff"android:orientation="horizontal"><ImageViewandroid:id="@+id/image_parent"android:layout_width="50dp"android:layout_height="50dp"android:src="@drawable/image_parent1"/><TextViewandroid:id="@+id/text_parent"android:layout_width="wrap_content"android:layout_height="50dp"android:textColor="#FFF"android:textSize="20sp"android:text="parent1"android:layout_toRightOf="@id/image_parent"android:gravity="center"/><ImageViewandroid:id="@+id/image_delete"android:layout_width="40dp"android:layout_height="40dp"android:layout_centerVertical="true"android:layout_alignParentRight="true"android:src="@drawable/delete"/><ImageViewandroid:id="@+id/image_add"android:layout_width="40dp"android:layout_height="40dp"android:layout_centerVertical="true"android:layout_toLeftOf="@id/image_delete"android:src="@drawable/add"/></RelativeLayout>list_item_child.xml子项布局文件,名称及删除图标;
12345678910111213141516171819202122<?xmlversion="1.0"encoding="utf-8"?>android:layout_width="match_parent"android:layout_height="40dp"><TextViewandroid:id="@+id/text_child"android:layout_width="wrap_content"android:layout_height="40dp"android:layout_margin="5dp"android:textColor="#0099ff"android:text="child"android:layout_centerInParent="true"android:gravity="center"/><ImageViewandroid:id="@+id/image_delete"android:layout_width="40dp"android:layout_height="40dp"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:src="@drawable/delete"/></RelativeLayout>MyAdapter.java自定义适配器
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154packagecom.eric.grouplistdemo;importjava.util.List;importjava.util.Map;importandroid.content.Context;importandroid.util.Log;importandroid.view.LayoutInflater;importandroid.view.View;importandroid.view.View.OnClickListener;importandroid.view.ViewGroup;importandroid.widget.BaseExpandableListAdapter;importandroid.widget.EditText;importandroid.widget.ImageView;importandroid.widget.TextView;publicclassMyAdapterextendsBaseExpandableListAdapter{privateList<String> parentList;privateMap<String,List<String>> map;privateContext context;privateEditText edit_modify;privateModifyDialog dialog;//构造函数publicMyAdapter(Context context, List<String> parentList, Map<String,List<String>> map) {this.context = context;this.parentList = parentList;this.map = map;}//获取分组数@OverridepublicintgetGroupCount() {returnparentList.size();}//获取当前组的子项数@OverridepublicintgetChildrenCount(intgroupPosition) {String groupName = parentList.get(groupPosition);intchildCount = map.get(groupName).size();returnchildCount;}//获取当前组对象@OverridepublicObject getGroup(intgroupPosition) {String groupName = parentList.get(groupPosition);returngroupName;}//获取当前子项对象@OverridepublicObject getChild(intgroupPosition,intchildPosition) {String groupName = parentList.get(groupPosition);String chidlName = map.get(groupName).get(childPosition);returnchidlName;}//获取组ID@OverridepubliclonggetGroupId(intgroupPosition) {returngroupPosition;}//获取子项ID@OverridepubliclonggetChildId(intgroupPosition,intchildPosition) {returnchildPosition;}@OverridepublicbooleanhasStableIds() {returntrue;}//组视图初始化@OverridepublicView getGroupView(intgroupPosition,booleanisExpanded,View convertView, ViewGroup parent) {finalintgroupPos = groupPosition;if(convertView ==null){convertView = LayoutInflater.from(context).inflate(R.layout.list_item_parent,null);}ImageView image = (ImageView) convertView.findViewById(R.id.image_parent);ImageView image_add = (ImageView) convertView.findViewById(R.id.image_add);ImageView image_delete = (ImageView) convertView.findViewById(R.id.image_delete);if(isExpanded){image.setImageResource(R.drawable.image_parent2);}else{image.setImageResource(R.drawable.image_parent1);}image_add.setOnClickListener(newOnClickListener() {@OverridepublicvoidonClick(View v) {alertAddDialog(MainActivity.fragment.getActivity(),"新增子项", groupPos);}});image_delete.setOnClickListener(newOnClickListener() {@OverridepublicvoidonClick(View v) {GroupListFragment.deleteGroup(groupPos);}});TextView parentText = (TextView) convertView.findViewById(R.id.text_parent);parentText.setText(parentList.get(groupPosition));returnconvertView;}//子项视图初始化@OverridepublicView getChildView(intgroupPosition,intchildPosition,booleanisLastChild, View convertView, ViewGroup parent) {finalintgroupPos = groupPosition;finalintchildPos = childPosition;if(convertView ==null){convertView = LayoutInflater.from(context).inflate(R.layout.list_item_child,null);}TextView childText = (TextView) convertView.findViewById(R.id.text_child);ImageView image_delete = (ImageView) convertView.findViewById(R.id.image_delete);String parentName = parentList.get(groupPosition);String childName = map.get(parentName).get(childPosition);childText.setText(childName);image_delete.setOnClickListener(newOnClickListener() {@OverridepublicvoidonClick(View v) {GroupListFragment.deleteChild(groupPos, childPos);}});returnconvertView;}@OverridepublicbooleanisChildSelectable(intgroupPosition,intchildPosition) {returntrue;}//弹新增子项对话框publicvoidalertAddDialog(Context context, String title,intcurrentGroup){finalintgroup = currentGroup;dialog =newModifyDialog(context, title,null);edit_modify = dialog.getEditText();dialog.setOnClickCommitListener(newOnClickListener() {@OverridepublicvoidonClick(View v) {GroupListFragment.addChild(group, edit_modify.getText().toString());dialog.dismiss();}});dialog.show();}}构造函数:将传入的parentList和map进行数据同步,parentList保存组数据,map保存对应组及其子项list数据;
获取分组数及子项数等很简单,就不介绍了,主要讲述一下,组视图初始化和子项视图初始化这两个函数;组视图初始化getGroupView():尽量复用convertView防止内存泄露,首先是进行判断,若convertView为空,则进行组视图初始化,加载list_item_parent子项布局;获取到相应的组布局控件:展开图标image、添加图标image_add、删除图标image_delete;通过传递过来的布尔类型参数isExpanded,进行判断给image赋值展开图标或合并图标;分别给添加图标和删除图标添加点击事件,分别调用GroupListFragment中的弹出添加窗口函数alertAddDialog()和删除组函数deleteGroup();
子项视图初始化getChildView():同样尽量复用convertView防止内存泄露,首先是进行判断,若convertView为空,则进行子项视图初始化,加载list_item_child子项布局;获取到相应的子布局控件:内容文本ChildText和删除图标Image_delete;从parentList和map中分别获取到,当前子项的组名parentName和子项名childName,赋值ChildText,删除图标添加点击事件,调用删除子项函数deleteChild();
③实现自定义对话框类ModifyDialog,继承自Dialog类,对输入修改内容,或新增项内容进行过渡;
no_title_dialog.xml,在values目录下自定义Dialog的style类型xml,除去Dialog的标题栏;
12345678<?xmlversion="1.0"encoding="utf-8"?><stylename="noTitleDialog"parent="android:style/Theme.Dialog"><itemname="android:width">300dp</item><itemname="android:height">40dp</item><itemname="android:windowNoTitle">true</item></style></resources>dialog_modify.xml 自定义对话框的布局文件,标题文本,输入框,确定按钮;
1234567891011121314151617181920212223242526272829<?xmlversion="1.0"encoding="utf-8"?>android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><TextViewandroid:id="@+id/text_title"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:gravity="center"android:background="#0099ff"android:text="修改名称"android:textColor="#FFF"android:textSize="20sp"/><EditTextandroid:id="@+id/edit_modify"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="组名称"/><Buttonandroid:id="@+id/btn_commit"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="确定"android:textColor="#FFF"android:background="#0099ff"/></LinearLayout>ModifyDialog.java
123456789101112131415161718192021222324252627282930313233343536packagecom.eric.grouplistdemo;importandroid.app.Dialog;importandroid.content.Context;importandroid.view.LayoutInflater;importandroid.view.View;importandroid.widget.Button;importandroid.widget.EditText;importandroid.widget.TextView;publicclassModifyDialogextendsDialog{privateTextView text_title;privateEditText edit_modify;privateButton btn_commit;publicModifyDialog(Context context, String title, String name) {super(context, R.style.noTitleDialog);View view = LayoutInflater.from(getContext()).inflate(R.layout.dialog_modify,null);text_title = (TextView) view.findViewById(R.id.text_title);edit_modify = (EditText)view.findViewById(R.id.edit_modify);btn_commit = (Button) view.findViewById(R.id.btn_commit);text_title.setText(title);edit_modify.setText(name);super.setContentView(view);}publicEditText getEditText(){returnedit_modify;}publicvoidsetOnClickCommitListener(View.OnClickListener listener){btn_commit.setOnClickListener(listener);}}ModifyDialog自定义构造函数中,通过super()加载刚刚自定义的no_title_dialog.xml,声明View加载layout布局dialog_modify.xml;并且获取布局中的相应控件,将构造函数中传来的字符串title和name,分别赋值到标题文本和输入框控件中;最后调用setContentView()初始化对话框视图;
添加一个返回输入框控件的函数getEditText(),用于获取输入框输入的内容;还需要一个自定义的点击事件监听器,绑定在确定按钮上;
④准备工作都完成了,下面就实现GroupListFragment,包括数据的初始化及持久化保存,组项和子项的增删改操作,列表子项点击事件,列表组项和子项的长按事件;
fragment_group_list.xml 页面的布局文件ExpandableListView列表以及一个添加组图标;
1234567891011121314151617181920<?xmlversion="1.0"encoding="utf-8"?>android:layout_width="match_parent"android:layout_height="match_parent"><ExpandableListViewandroid:id="@+id/expandablelistview"android:layout_width="match_parent"android:layout_height="match_parent"android:groupIndicator="@null"></ExpandableListView><ImageViewandroid:id="@+id/image_add"android:layout_width="40dp"android:layout_height="40dp"android:layout_centerHorizontal="true"android:layout_alignParentBottom="true"android:src="@drawable/add"/></RelativeLayout>这里需要将ExpandableListView的groupIndicator属性设置为@null,不使用其自带的展开图标;
GroupListFragment.java 加载列表的页面
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280packagecom.eric.grouplistdemo;importjava.util.ArrayList;importjava.util.HashMap;importjava.util.List;importjava.util.Map;importorg.json.JSONArray;importorg.json.JSONException;importorg.json.JSONObject;importandroid.R.integer;importandroid.app.Fragment;importandroid.content.Context;importandroid.content.SharedPreferences;importandroid.content.SharedPreferences.Editor;importandroid.os.Bundle;importandroid.util.Log;importandroid.view.LayoutInflater;importandroid.view.View;importandroid.view.View.OnClickListener;importandroid.view.ViewGroup;importandroid.widget.AdapterView;importandroid.widget.EditText;importandroid.widget.ExpandableListView;importandroid.widget.ImageView;importandroid.widget.Toast;publicclassGroupListFragmentextendsFragment{privateView view;privateExpandableListView expandableListView;publicstaticMyAdapter adapter;publicstaticList<String> parentList;publicstaticMap<String,List<String>> map;privateModifyDialog dialog;privateEditText edit_modify;privateImageView image_add;privateintcurrentGroup,currentChild;publicstaticSharedPreferences sp;publicstaticEditor editor;publicstaticString dataMap,dataParentList;@OverridepublicView onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {view = inflater.inflate(R.layout.fragment_group_list, container,false);expandableListView = (ExpandableListView) view.findViewById(R.id.expandablelistview);image_add = (ImageView) view.findViewById(R.id.image_add);image_add.setOnClickListener(newOnClickListener() {@OverridepublicvoidonClick(View v) {alertAddDialog(getActivity(),"新增组");}});initData();adapter =newMyAdapter(getActivity().getApplicationContext(), parentList, map);expandableListView.setAdapter(adapter);//设置子项点击事件MyOnClickListener myListener =newMyOnClickListener();expandableListView.setOnChildClickListener(myListener);//设置长按点击事件MyOnLongClickListener myLongListener =newMyOnLongClickListener();expandableListView.setOnItemLongClickListener(myLongListener);returnview;}publicvoidinitData(){map =newHashMap<String, List<String>>();parentList =newArrayList<String>();sp = getActivity().getApplicationContext().getSharedPreferences("spfile", getActivity().MODE_PRIVATE);dataMap = sp.getString("dataMap",null);dataParentList = sp.getString("dataParentList",null);if(dataMap ==null|| dataParentList ==null){parentList =newArrayList<String>();parentList.add("客厅");parentList.add("厨房");parentList.add("卧室");List<String> list1 =newArrayList<String>();list1.add("客厅空调");list1.add("客厅电视");list1.add("客厅电灯");map.put("客厅", list1);List<String> list2 =newArrayList<String>();list2.add("厨房油烟机");list2.add("厨房电灯");list2.add("厨房电器");map.put("厨房", list2);List<String> list3 =newArrayList<String>();list3.add("卧室空调");list3.add("卧室灯光");list3.add("卧室电视");map.put("卧室", list3);}else{try{//初始化parentListJSONArray jsonArray =newJSONArray(dataParentList);for(inti =0; i < jsonArray.length(); i++) {parentList.add(jsonArray.get(i).toString());}//初始化mapJSONObject jsonObject =newJSONObject(dataMap);for(inti =0; i < jsonObject.length(); i++) {String key = jsonObject.getString(parentList.get(i));JSONArray array =newJSONArray(key);List<String> list =newArrayList<String>();for(intj =0; j < array.length(); j++) {list.add(array.get(j).toString());}map.put(parentList.get(i), list);}Log.d("eric","①:"+map+"②:"+parentList);}catch(JSONException e) {e.printStackTrace();Log.e("eric","String转Map或List出错"+e);}}Log.e("eric", dataMap+"!&&!"+dataParentList);saveData();}//自定义点击监听事件publicclassMyOnClickListenerimplementsExpandableListView.OnChildClickListener{@OverridepublicbooleanonChildClick(ExpandableListView parent, View v,intgroupPosition,intchildPosition,longid) {String str ="choose"+groupPosition+"-"+childPosition;Toast.makeText(getActivity(), str, Toast.LENGTH_SHORT).show();returnfalse;}}//自定义长按监听事件publicclassMyOnLongClickListenerimplementsAdapterView.OnItemLongClickListener{@OverridepublicbooleanonItemLongClick(AdapterView<?> parent, View view,intposition,longid) {//长按子项if(ExpandableListView.getPackedPositionType(id) == ExpandableListView.PACKED_POSITION_TYPE_CHILD){longpackedPos = ((ExpandableListView) parent).getExpandableListPosition(position);intgroupPosition = ExpandableListView.getPackedPositionGroup(packedPos);intchildPosition = ExpandableListView.getPackedPositionChild(packedPos);currentGroup = groupPosition;currentChild = childPosition;String str = (String)adapter.getChild(groupPosition, childPosition);alertModifyDialog("修改此项名称",str);Toast.makeText(getActivity(),str,Toast.LENGTH_SHORT).show();returntrue;//长按组}elseif(ExpandableListView.getPackedPositionType(id) == ExpandableListView.PACKED_POSITION_TYPE_GROUP){longpackedPos = ((ExpandableListView) parent).getExpandableListPosition(position);intgroupPosition = ExpandableListView.getPackedPositionGroup(packedPos);intchildPosition = ExpandableListView.getPackedPositionChild(packedPos);currentGroup = groupPosition;currentChild = childPosition;String group = parentList.get(groupPosition);alertModifyDialog("修改组名称", group);String str = (String)adapter.getGroup(groupPosition);Toast.makeText(getActivity(),str,Toast.LENGTH_SHORT).show();}returnfalse;}}//新增组publicstaticvoidaddGroup(String newGroupName){parentList.add(newGroupName);List<String> list =newArrayList<String>();map.put(newGroupName, list);adapter.notifyDataSetChanged();saveData();}//新增子项到指定组publicstaticvoidaddChild(intgroupPosition, String newChildName){String groupName = parentList.get(groupPosition);List<String> list = map.get(groupName);list.add(newChildName);adapter.notifyDataSetChanged();saveData();}//删除指定组publicstaticvoiddeleteGroup(intgroupPos){String groupName = parentList.get(groupPos);map.remove(groupName);parentList.remove(groupPos);adapter.notifyDataSetChanged();saveData();}//删除指定子项publicstaticvoiddeleteChild(intgroupPos,intchildPos){String groupName = parentList.get(groupPos);List<String> list = map.get(groupName);list.remove(childPos);adapter.notifyDataSetChanged();saveData();}//修改该项名称publicvoidmodifyName(intgroupPosition,intchildPosition, String modifyName){Toast.makeText(getActivity(), String.valueOf(groupPosition)+'-'+String.valueOf(childPosition), Toast.LENGTH_SHORT).show();if(childPosition<0){//修改组名称String groupName = parentList.get(groupPosition);if(!groupName.equals(modifyName)){map.put(modifyName, map.get(groupName));map.remove(groupName);parentList.set(groupPosition, modifyName);}}else{//修改子项名称String group = parentList.get(groupPosition);List<String> list =map.get(group);list.set(childPosition, modifyName);map.put(group, list);}adapter.notifyDataSetChanged();saveData();}//弹修改对话框publicvoidalertModifyDialog(String title, String name){dialog =newModifyDialog(getActivity(), title, name);edit_modify = dialog.getEditText();dialog.setOnClickCommitListener(newOnClickListener() {@OverridepublicvoidonClick(View v) {modifyName(currentGroup, currentChild, edit_modify.getText().toString());dialog.dismiss();}});dialog.show();}//弹新增组对话框publicvoidalertAddDialog(Context context, String title){dialog =newModifyDialog(context, title,null);edit_modify = dialog.getEditText();dialog.setOnClickCommitListener(newOnClickListener() {@OverridepublicvoidonClick(View v) {addGroup(edit_modify.getText().toString());dialog.dismiss();}});dialog.show();}//保存数据publicstaticvoidsaveData(){JSONObject jsonObject =newJSONObject(map);dataMap = jsonObject.toString();dataParentList = parentList.toString();editor = sp.edit();editor.putString("dataMap", dataMap);editor.putString("dataParentList", dataParentList);editor.commit();}}内容有点多,一个个来:
初始化Fragment页面函数onCreateView():
首先,要进行layout布局fragment_group_list.xml加载,获取相应控件的实例,expandableListView列表控件以及添加组图标image_add,添加组图标添加点击事件;调用initData()方法进行组数据parentList和组对应子项map的初始化;然后,实例化adapter传入parentList和map数据到自定义适配器MyAdapter中,并将其绑定到expandableListView上;最后,给expandableListView添加自定义子项点击事件监听器,组项和子项长按事件监听器;
初始化数据函数initData():该函数通过ShareReference来保存数据;
首先,实例化parentList和map,从ShareReference中获取到保存的String类型的parentList和map实际数据,赋值到dataMap和dataParentList中,若当中数据不存在,默认返回null,第一次运行程序的时候肯定是没有数据的,故进行判断,若没获取到数据,那就给parentList和Map赋值“客厅”、“厨房”、“卧室”等一系列数据;如果有数据的话,那就得进行数据的转换,因为之前保存的String类型数据,parentList初始化:将dataParentList转换为JSONArray类型,通过循环将数据赋值到parentList中;同理,将dataMap转换为JSONObject类型,通过两层for循环将数据赋值到map中。
自定义点击子项监听其类MyOnClickListener:实现ExpandableListView.OnChildClickListener接口,这里就简单的进行Toast操作,读者可以修改为跳转等其他自定义功能;
自定义长按组项或子项监听器类MyOnLongClickListener:实现AdapterView.OnItemLongClickListener接口,通过调用ExpandableListView的getPackedPositionType()方法来判断此时长按的是组项还是子项;这里将长按组和子项分离出来,方便根据功能修改;无论是组项还是子项长按后,都调用alertModifyDialog()弹修改对话框,传入当前项的名称;
新增组addGroup函数:将传递过来的newGroupName,添加parentList中,且定义一个空的list,绑定newGroupName,将这对string和list,添加到map中;最后,调用adapter.notifyDataSetChanged()刷新列表,调用saveData()保存数据到ShareReference;同理,新增子项到指定组addChild(),删除指定组deleteGroup(),删除指定子项deleteChild(),都是对parentList和map进行操作,最后刷新列表和保存数据;
修改该项名称modifyName():通过传递过来childPosition进行判断,当修改项为组项时,childPosition的值为-1,以此区分组项和子项;这里遇到一个问题,当组项提交的名称与原名称相同会报错,故添加一个判断,仅提交的名称不同时才进行修改操作;修改的具体实现还是对parentList和map的操作,以修改组项为例,同新增组,添加一个modifyName的组,其对应的List为原来组名对应的List数据,然后再将原来的groupName组及其对应的list删除,实现修改;最后同样要刷新列表和保存数据;
弹修改对话框alertModifyDialog()函数:首先对自定义的dialog进行实例化,初始化标题和输入框中的数据;调用getEditText()函数获取输入框实例,添加提交按钮点击事件,调用modifyName方法传入当前组和子项数据,以及输入框中提交的文本;
弹新增组对话框alertAddDialog()函数:同样,实例化dialog,获取输入框控件,点击事件中调用addGroup()方法添加数据;保存数据saveData()函数:将parentList和map数据转化为String类型,分别赋值,保存至ShareReference的Editor中并提交;
以上就是本文的全部内容,希望对大家的学习有所帮助。
如对本文有疑问,请提交到交流社区,广大热心网友会为你解答!! 点击进入社区
相关文章: