Android开发进阶 -- 通用适配器 CommonAdapter
在Android开发中,我们经常会用到ListView 这个组件,为了将ListView 的内容展示出来,我们会去实现一个Adapter来适配,将Layout中的布局以列表的形式展现到组件中。
比如,像 GGTalk 安卓版的查找用户功能,会把符合条件的用户都列在下面:
为了达到这个效果,我们需要实现一个自定义的Adapter,而其它地方的ListView也要实现一个Adapter,这些Adapter会有很多重复的代码,非常繁琐,现在我就将重复代码封装到了一个通用的适配器CommonAdapter中,这样,在使用时只要继承CommonAdapter就可以了,如此就避免了大段代码的重复,让代码更简洁易懂。我们先来看看CommonAdapter的定义。
一.CommonAdapter 实现
public abstract class CommonAdapter<T> extends BaseAdapter { private List<T> dataList; protected Context context; protected int item_layoutId=0; protected HashMap<Integer,Integer> layoutIdMap; //多种itemView时用到。 第一个int对应type类型,第二个int对应 itemlayoutId /** * 设置单个子模版VIew, * @param context * @param datas 绑定的对象集合 * @param item_layoutId 被绑定的视图layoutID * */ public CommonAdapter(Context context, List<T> datas, int item_layoutId) { this.context=context; this.dataList=datas; this.item_layoutId=item_layoutId; } /** *设置多个子模版VIew, 并配合重写 getItemViewType(int position)来确定是设置哪个模版 * @param context * @param datas 绑定的对象集合 * @param layoutIdMap 多种itemViewID Map 第一个int对应type类型,第二个int对应 itemlayoutId */ public CommonAdapter(Context context, List<T> datas, HashMap<Integer,Integer> layoutIdMap) { this.context=context; this.dataList=datas; this.layoutIdMap=layoutIdMap; } @Override public int getViewTypeCount() { if(this.layoutIdMap==null) { return 1; } return this.layoutIdMap.size(); } @Override public int getCount() { return this.dataList.size(); } @Override public Object getItem(int position) { return this.dataList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { int type = getItemViewType(position); return getConvertView(position,convertView,type); } private View getConvertView(int position,View convertView,int itemViewType) { if(convertView==null) { int layoutId =0; if(this.item_layoutId!=0) { layoutId=this.item_layoutId; } if (this.layoutIdMap != null) { layoutId = this.layoutIdMap.get(itemViewType); } convertView = LayoutInflater.from(context).inflate(layoutId, null); } T t = this.dataList.get(position); this.setConvertView(convertView,t,itemViewType); return convertView; } /** * 局部更新数据,调用一次getView()方法;Google推荐的做法 * * @param parent 要更新的listview * @param position 要更新的位置 */ public void onOneItemChanged(ListView parent, int position) { /**第一个可见的位置**/ int firstVisiblePosition = parent.getFirstVisiblePosition(); /**最后一个可见的位置**/ int lastVisiblePosition = parent.getLastVisiblePosition(); /**在看见范围内才更新,不可见的滑动后自动会调用getView方法更新**/ if ((position >= firstVisiblePosition && position <= lastVisiblePosition)) { /**获取指定位置view对象**/ View view = parent.getChildAt(position - firstVisiblePosition); getView(position, view, parent); } } /** * 需要去实现的对item中的view的设置操作 * * @param convertView 转换后的试图 * @param t Model对象 * @param itemViewType 试图类型。对应layoutIdMap中的key */ protected abstract void setConvertView(View convertView, T t,int itemViewType); }
说明如下:
(1)泛型参数<T>就是我们要绑定数据Model对象的class类型。
(2)getViewTypeCount()方法会根据HashMap是否为空来识别是传入的单一模版还是多模版.
(3)CommonAdapter还重写了getCount(),getItem(int position),getView,getConvertView等BaseAdapter 的方法,这些方法的代码都基本每个Adapter都是一样的,我们只需关心 setConvertView 这个抽象方法,在子类中重写它,将具体的逻辑和数据的绑定在此即可。
二.使用CommonAdapter
- 先建一个类继承CommonAdapter,在构造函数中调用父类的构造方法以初始化数据。实现 setConvertView 方法,将对象的数据绑定到模版上 即可
public class SearchFriendAdapter extends CommonAdapter<OrayUser> { /** * 设置单个子模版VIew, * @param context * @param datas 绑定的对象集合 * @param item_layoutId 被绑定的视图layoutID * */ public SearchFriendAdapter(Context context,List<OrayUser> datas,int item_layoutId) { super(context,datas,item_layoutId); } @Override protected void setConvertView(View view, OrayUser orayUser,int itemViewType) { ...具体将对象的数据绑定到模版上 } }
- Activity中使用时new 一个Adapter,然后将adapter绑定到 ListView
adapter = new SearchFriendAdapter(this,this.userList,R.layout.friend_child); listView.setAdapter(adapter);
- 更新数据源中的数据同步到View
//有2种方式将数据源同步到View adapter.notifyDataSetChanged();//全部刷新 adapter.onOneItemChanged(ListView parent, int position);//刷新指定位置的数据
三. 让CommonAdapter支持多模板
当然CommonAdapter还支持多模版的应用,我们只需将第一步中的Adapter实现稍稍做改动即可
public class ListViewAdapter extends CommonAdapter<ListViewItemModel> { /** *设置多个子模版VIew, 并配合重写 getItemViewType(int position)来确定是设置哪个模版 * @param context * @param datas 绑定的对象集合 * @param layoutIdMap 多种itemViewID Map 第一个int对应type类型,第二个int对应 itemlayoutId */ public ListViewAdapter(Context context, List<ListViewItemModel> datas, HashMap<Integer,Integer> layoutIdMap) { super(context,datas,layoutIdMap); } @Override public int getItemViewType(int position) { ListViewItemModel model= (ListViewItemModel)super.getItem(position); if(model.floatRight) { return 1; } return 0; } @Override protected void setConvertView(View view, ListViewItemModel listViewItemModel,int itemViewType) { //根据itemViewType 的值来识别是哪个模版,以对应给模版绑定数据 } }
如上图我们将构造函数的最后一个参数从单一的layoutID 换成了 模版集合的HashMap,再就是多加了 getItemViewType(int position) 方法来返回 具体Model对应的是哪一个模版类型(即hashMap 中的key值),使用时更新数据源中的数据同步到View和上面的单一模版一样使用。(同上2种更新方法)
HashMap<Integer,Integer> layoutMap=new HashMap<>(); layoutMap.put(0,R.layout.listview_item);//key值最好从0开始向上递增,否则可能会找不到key的BUG layoutMap.put(1,R.layout.listview_right_item); adapter=new ListViewAdapter(this,models,layoutMap); listView.setAdapter(adapter);
四.综合实例
最后我们还是以本文开头的查找用户的例子,来更全面地说明CommonAdapter的使用。页面截图如下:
1.模版布局
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | <?xml version= "1.0" encoding= "utf-8" ?> <RelativeLayout xmlns:android= "http://schemas.android.com/apk/res/android" xmlns:app= "http://schemas.android.com/apk/res-auto" android:id= "@+id/layout" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:background= "@drawable/selector_item" android:descendantFocusability= "blocksDescendants" android:gravity= "center_vertical" android:orientation= "horizontal" android:padding= "4dp" > <ImageView android:id= "@+id/ct_photo" android:layout_width= "@dimen/headImageSize" android:layout_height= "@dimen/headImageSize" android:layout_margin= "5dp" android:src= "@drawable/plus" /> <ImageView android:id= "@+id/ct_status" android:layout_width= "11dip" android:layout_height= "11dip" android:layout_marginLeft= "@dimen/headImageSize" android:layout_marginTop= "@dimen/headImageSize" android:src= '@drawable/status_2' /> <LinearLayout android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:gravity= "center_vertical" android:layout_alignTop= "@id/ct_photo" android:layout_toRightOf= "@id/ct_photo" > <TextView android:id= "@+id/ct_name" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:paddingLeft= "5dp" android:paddingRight= "5dp" android:paddingBottom= "5dp" android:paddingTop= "0dp" android:text= "name" android:textColor= "@color/dimgrey" android:textSize= "16sp" android:textStyle= "bold" /> <TextView android:id= "@+id/ct_describe" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_marginLeft= "10dp" android:layout_toRightOf= "@id/ct_name" android:paddingLeft= "5dp" android:paddingRight= "5dp" android:paddingBottom= "5dp" android:paddingTop= "0dp" android:text= "describe" android:textColor= "@color/dimgrey" android:textSize= "14sp" android:visibility= "gone" /> </LinearLayout> <TextView android:id= "@+id/ct_sign" android:layout_width= "wrap_content" android:layout_height= "wrap_content" android:layout_alignBottom= "@id/ct_photo" android:layout_toRightOf= "@id/ct_photo" android:paddingLeft= "5dp" android:text= "sign" android:singleLine= "true" android:ellipsize= "start" android:textColor= "@color/dimgrey" android:textSize= "12sp" /> </RelativeLayout> |
2.具体继承CommonAdapter实现子类
public class SearchFriendAdapter extends CommonAdapter<OrayUser> { public SearchFriendAdapter(Context context,List<OrayUser> datas,int item_layoutId) { super(context,datas,item_layoutId); } @Override protected void setConvertView(View view, OrayUser orayUser,int itemViewType) { try{ SearchFriendAdapter.ViewHolder holder; if(view.getTag()==null) { holder = new SearchFriendAdapter.ViewHolder(view); view.setTag(holder); } else { holder = (SearchFriendAdapter.ViewHolder) view.getTag(); } HeadImgHelper.setUserHeadImg(holder.headImg,orayUser); if(orayUser.getName().equals(orayUser.getCommentName())) { holder.userName.setText(orayUser.getName()); } else { holder.userName.setText(orayUser.getCommentName()+"("+ orayUser.getName()+")"); } String catalogName= ClientGlobalCache.getInstance().getCurrentUser().getCatalogNameByFriend(orayUser.getUserID()); if(!StringHelper.isNullOrEmpty(catalogName)) { holder.describe.setText("[ "+ catalogName+" ]"); holder.describe.setVisibility(View.VISIBLE); } else { holder.describe.setText(""); holder.describe.setVisibility(View.GONE); } holder.orgName.setText(orayUser.getUserID()); }catch (Exception ex){ex.printStackTrace();} } private class ViewHolder { public ImageView headImg; public TextView userName; public TextView describe; public TextView orgName; public ViewHolder(View view) { this.headImg = (ImageView) view.findViewById(R.id.ct_photo); ImageView statusImg=(ImageView) view.findViewById(R.id.ct_status); statusImg.setVisibility(View.GONE); this.userName=(TextView) view.findViewById(R.id.ct_name); this.orgName=(TextView) view.findViewById(R.id.ct_sign); this.describe=(TextView) view.findViewById(R.id.ct_describe); } } }
3. Activity类 核心代码
public class SearchFriendActivity extends Activity implements TextView.OnEditorActionListener{ private DrawableEditText input; private ListView listView; private TextView search_resultStr; private List<OrayUser> userList=new ArrayList<OrayUser>() ; private SearchFriendAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_search_friend); this.initView(); } private void initView() { TextView pageTitle=(TextView)findViewById(R.id.headerText); pageTitle.setText("查找用户"); search_resultStr =(TextView) findViewById(R.id.search_resultStr); listView=(ListView)findViewById(R.id.listview_user); listView.setOnItemClickListener(this); adapter = new SearchFriendAdapter(this,this.userList,R.layout.friend_child); listView.setAdapter(adapter); } /** * 执行点击搜索指令 * */ private void action_search(String idOrName) { List<OrayUser> temp_users=Manager.getInstance().doSearchUser(idOrName); search_resultStr.setText("没有找到符合条件的用户或群组"); this.setSearchResult(temp_users); } private void setSearchResult(List<OrayUser> temp_users) { this.userList.clear(); if(temp_users==null||temp_users.size()==0) { search_resultStr.setVisibility(View.VISIBLE); } else { for (OrayUser orayUser :temp_users) { if(orayUser.getUserID().equals(ClientGlobalCache.getInstance().getCurrentUser().getUserID())) { temp_users.remove(orayUser); break; } } this.userList.addAll(temp_users) ; search_resultStr.setVisibility(View.GONE); } this.adapter.notifyDataSetChanged(); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探