Android 适配器模式应用及设计原理
作者:lqh 发布时间:2023-08-01 19:08:04
适配器模式是一种重要的设计模式,在 Android 中得到了广泛的应用。适配器类似于现实世界里面的插头,通过适配器,我们可以将分属于不同类的两种不同类型的数据整合起来,而不必去根据某一需要增加或者修改类里面的方法。
适配器又分为单向适配器和双向适配器,在 android 中前者使用的比较频繁。比较常见的实现方式是:首先定义一个适配类,内部定义一个私有的需要适配的对象,该类提供一个构造函数,将该对象的一个实例作为参数传入,并在构造函数里面进行初始化,再提供一个公有的方法,返回另外一个需要适配的类所需要的数据类型。这样通过创建一个额外的类,专门负责数据类型的转换,在不改动原有类的前提下实现了所需的功能。这种设计模式提供了更好的复用性和可扩展性,尤其在我们无法获修改其中一个类或者类与类之间有比较多的不同类型的数据需要进行适配的时候显得格外重要。
在 android 中常见的适配器类有: BaseAdapter 、 SimpleAdapter 等
初识Android时,我对ListView、GradView中的Adapter一直半懂非懂,每次写Adapter都觉得异常痛苦,故而有了此文,希望能帮到一些初学者。
先来看下适配器模式的官方解释,将一个类的接口转换成客户希望的另外一个接口,Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。乍看肯定不知所云,通俗说下我个人的理解:当我们创建一个View之后,肯定要接着规定该view的大小、位置、显示的内容信息、触摸事件的回调等等,我们发现将这些东西放一起导致代码太耦合甚至冗余了,这时候就可以使用Adapter模式,将这个流程分开,首先我们只管创建一个View,至于该View的内容样式我们完全不用关心,交给我们的小弟Adapter去做(前提是这个人是我们的小弟,和我们的View能联系起来),当小弟完成了我们分配给他的任务后(安排子View的样式,信息的解析显示,Event回调等),我们只需通过setAdapter()将他的工作内容窃取过来就行。
或者再直观点说,我们要买回来了一部Iphone7,我们只需要轻松愉快地用它来听歌,看电影,聊微信,至于当他没电了怎么办?如何使用110V、220V电压充电?怎么使用二孔、三孔插头?如何快充如何慢充?这些都不需要我们关心,交给我们的充电器(Adapter)即可,我们要做的只需连上usb线和找到插座(setAdapter)就行了。
当你不清楚适配的具体流程时,写Adapter是非常痛苦的,接下来我们就举栗子详细分析一个完整的适配器模式工作流程(现在除了我的奇葩公司,应该没人会用ListView了吧,所以这里直接以RecyclerView为栗,其实ListView的adapter也是一样的原理)。
1.创建一个RecyclerView并实例化它,然后等着我们的小弟(Adapter)完成剩下来的体力活。
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/mrecycler"/>
RecyclerView mrecycler= (RecyclerView) findViewById(R.id.mrecycler);
mrecycler.setLayoutManager(new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL));
2.Adapter使用,我们先从源码了解每个方法的回调周期。
public static abstract class Adapter<VH extends ViewHolder> {
private final AdapterDataObservable mObservable = new AdapterDataObservable();
public abstract VH onCreateViewHolder(ViewGroup parent, int viewType);
public abstract void onBindViewHolder(VH holder, int position);
public void onBindViewHolder(VH holder, int position, List<Object> payloads) {
onBindViewHolder(holder, position);
}
/**
* This method calls {@link #onCreateViewHolder(ViewGroup, int)} to create a new
* {@link ViewHolder} and initializes some private fields to be used by RecyclerView.
*
* @see #onCreateViewHolder(ViewGroup, int)
*/
public final VH createViewHolder(ViewGroup parent, int viewType) {
TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
final VH holder = onCreateViewHolder(parent, viewType);
holder.mItemViewType = viewType;
TraceCompat.endSection();
return holder;
}
/**
* This method internally calls {@link #onBindViewHolder(ViewHolder, int)} to update the
* {@link ViewHolder} contents with the item at the given position and also sets up some
* private fields to be used by RecyclerView.
*
* @see #onBindViewHolder(ViewHolder, int)
*/
public final void bindViewHolder(VH holder, int position) {
holder.mPosition = position;
if (hasStableIds()) {
holder.mItemId = getItemId(position);
}
holder.setFlags(ViewHolder.FLAG_BOUND,
ViewHolder.FLAG_BOUND | ViewHolder.FLAG_UPDATE | ViewHolder.FLAG_INVALID
| ViewHolder.FLAG_ADAPTER_POSITION_UNKNOWN);
TraceCompat.beginSection(TRACE_BIND_VIEW_TAG);
onBindViewHolder(holder, position, holder.getUnmodifiedPayloads());
holder.clearPayload();
TraceCompat.endSection();
}
/**
* Return the view type of the item at <code>position</code> for the purposes
* of view recycling.
*
* <p>The default implementation of this method returns 0, making the assumption of
* a single view type for the adapter. Unlike ListView adapters, types need not
* be contiguous. Consider using id resources to uniquely identify item view types.
*
* @param position position to query
* @return integer value identifying the type of the view needed to represent the item at
* <code>position</code>. Type codes need not be contiguous.
*/
public int getItemViewType(int position) {
return 0;
}
/**
* Returns the total number of items in the data set hold by the adapter.
*
* @return The total number of items in this adapter.
*/
public abstract int getItemCount();
/**
* Called when a view created by this adapter has been attached to a window.
*
* <p>This can be used as a reasonable signal that the view is about to be seen
* by the user. If the adapter previously freed any resources in
* {@link #onViewDetachedFromWindow(RecyclerView.ViewHolder) onViewDetachedFromWindow}
* those resources should be restored here.</p>
*
* @param holder Holder of the view being attached
*/
public void onViewAttachedToWindow(VH holder) {
}
/**
* Called when a view created by this adapter has been detached from its window.
*
* <p>Becoming detached from the window is not necessarily a permanent condition;
* the consumer of an Adapter's views may choose to cache views offscreen while they
* are not visible, attaching an detaching them as appropriate.</p>
*
* @param holder Holder of the view being detached
*/
public void onViewDetachedFromWindow(VH holder) {
}
/**
* Register a new observer to listen for data changes.
*
* <p>The adapter may publish a variety of events describing specific changes.
* Not all adapters may support all change types and some may fall back to a generic
* {@link android.support.v7.widget.RecyclerView.AdapterDataObserver#onChanged()
* "something changed"} event if more specific data is not available.</p>
*
* <p>Components registering observers with an adapter are responsible for
* {@link #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
* unregistering} those observers when finished.</p>
*
* @param observer Observer to register
*
* @see #unregisterAdapterDataObserver(RecyclerView.AdapterDataObserver)
*/
public void registerAdapterDataObserver(AdapterDataObserver observer) {
mObservable.registerObserver(observer);
}
/**
* Unregister an observer currently listening for data changes.
*
* <p>The unregistered observer will no longer receive events about changes
* to the adapter.</p>
*
* @param observer Observer to unregister
*
* @see #registerAdapterDataObserver(RecyclerView.AdapterDataObserver)
*/
public void unregisterAdapterDataObserver(AdapterDataObserver observer) {
mObservable.unregisterObserver(observer);
}
/**
* Called by RecyclerView when it starts observing this Adapter.
* <p>
* Keep in mind that same adapter may be observed by multiple RecyclerViews.
*
* @param recyclerView The RecyclerView instance which started observing this adapter.
* @see #onDetachedFromRecyclerView(RecyclerView)
*/
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
}
/**
* Called by RecyclerView when it stops observing this Adapter.
*
* @param recyclerView The RecyclerView instance which stopped observing this adapter.
* @see #onAttachedToRecyclerView(RecyclerView)
*/
public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
}
/**
* Notify any registered observers that the data set has changed.
*/
public final void notifyDataSetChanged() {
mObservable.notifyChanged();
}
/**
* Notify any registered observers that the item at <code>position</code> has changed.
* Equivalent to calling <code>notifyItemChanged(position, null);</code>.
*
* <p>This is an item change event, not a structural change event. It indicates that any
* reflection of the data at <code>position</code> is out of date and should be updated.
* The item at <code>position</code> retains the same identity.</p>
*
* @param position Position of the item that has changed
*
* @see #notifyItemRangeChanged(int, int)
*/
public final void notifyItemChanged(int position) {
mObservable.notifyItemRangeChanged(position, 1);
}
/**
* Notify any registered observers that the item reflected at <code>fromPosition</code>
* has been moved to <code>toPosition</code>.
*
* <p>This is a structural change event. Representations of other existing items in the
* data set are still considered up to date and will not be rebound, though their
* positions may be altered.</p>
*
* @param fromPosition Previous position of the item.
* @param toPosition New position of the item.
*/
public final void notifyItemMoved(int fromPosition, int toPosition) {
mObservable.notifyItemMoved(fromPosition, toPosition);
}
}
Adapter是RecyclerView的一个抽象内部类,我们只需要重写它暴露出来的各种回调方法(创建View,绑定View,获取数据内容,通知数据变化......),就可以达到创建及控制itemView内容的目的。这里挑我们创建Adapter时常用的重写方法讲解:
onCreateViewHolder:
根据需求,创建自定义样式的itemViw,最终return一个ViewHolder类型。由Adapter内部类中的createViewHolder调用。
//根据需求,创建自定义样式的itemViw
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item,viewGroup,false);
ViewHolder vh = new ViewHolder(view);
return vh;
}
onBindViewHolder:
将我们传递进来的数据bean与view绑定在一起,即决定我们的itemView的具体内容如何展示,由Adapter内部类中的bindViewHolder调用。同时也可以在这里处理触摸事件的回调,后面会讲到。
//将数据与界面进行绑定的操作
@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
viewHolder.mTextView.setText(datas[position]);
}
getItemCount:
返回数据bean的数量,即需要的itemView的个数。
//获取数据的数量
@Override
public int getItemCount() {
return datas.length;
}
一般情况我们重写上述三个方法即可。
onViewAttachedToWindow onViewDetachedFromWindow:
在View依附/脱离window的时候回调
registerAdapterDataObserver unregisterAdapterDataObserver:
主要用于注册与解绑适配器数据的观察者模式
notifyDataSetChanged notifyItemMoved:
通过Adapter来通知数据或item的变化,请求更新view.
那怎么让Adapter来为我们处理触摸事件?通过接口回调的方法就能很简单地完成。
首先在我们的Adapter中添加一个内部接口,其中的方法在第一步实例化View的时候实现。
public interface OnItemClickListener {
void ItemClickListener(View view,int postion);
void ItemLongClickListener(View view,int postion);
}
然后在onBindViewHolder中回调。
if(mListener!=null){//如果设置了监听那么它就不为空,然后回调相应的方法
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int pos = holder.getLayoutPosition();//得到当前点击item的位置pos
mListener.ItemClickListener(holder.itemView,pos);//把事件交给我们实现的接口那里处理
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
int pos = holder.getLayoutPosition();//得到当前点击item的位置pos
mListener.ItemLongClickListener(holder.itemView,pos);//把事件交给我们实现的接口那里处理
return true;
}
});
}
3.将RecyclerView与Adapter通过setAdapter联系起来,并实现Adapter内部的回调接口。
adapter=new MyRecyclerAdapter(this, list);
mrecycler.setAdapter(adapter);
adapter.setOnclickListener(new MyRecyclerAdapter.OnItemClickListener() {
@Override
public void ItemClickListener(View view, int postion) {
Toast.makeText(MainActivity.this,"点击了:"+postion, Toast.LENGTH_SHORT).show();
}
@Override
public void ItemLongClickListener(View view, int postion) {
list.remove(postion);
adapter.notifyItemRemoved(postion);
}
});
至此一个最简单的RecyclerViewAdapter就完成了,文末给出一个自己简单封装过后的CommonRecyclerAdapter,使用时我们只需要传入 context, layoutResId,List<T> data即可。
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!


猜你喜欢
- 最近在做保证金余额查询优化,在项目启动时候需要把余额全量加载到本地缓存,因为需要全量查询所有骑手的保证金余额,为了不影响主数据库的性能,考虑
- 解决方法有以下3种1、在Edittext中加入以下属性android:cursorVisible="true"andro
- 程序性能的主要表现点:执行速度:程序的反映是否迅速,响应时间是否足够短内存分配:内存分配是否合理,是否过多地消耗内存或者存在内存泄漏启动时间
- 一、需求Jenkins大多数情况下都是用来部署Java项目,Java项目有一个特点是>需要编译和打包的,一般情况下编译和打包都是用ma
- 本文实例为大家分享了Android实现按钮滚动选择效果的具体代码,供大家参考,具体内容如下效果图代码实现package com.demo.u
- 一、牵出缓存都有哪些缓存,作用是什么,为什么这么设计1.缓存还在屏幕内的ViewHolder——Sc
- 本文实例为大家分享了WPF ProgressBar实现实时进度的具体代码,供大家参考,具体内容如下简单测试,页面如图:利用上班的一点点空闲时
- 本文实例为大家分享了Android简单实现天气预报App的具体代码,供大家参考,具体内容如下一、UI设计首页UI<?xml versi
- 多播委托简介每一个委托都是继承自MulticastDelegate,也就是每个都是多播委托。带返回值的多播委托只返回最后一个方法的值多播委托
- kotlin基础教程之类和继承类声明使用class关键字声明类,查看其声明格式:: modifiers ("class"
- 使用类的全权名: System.Text.StringBuilder sb = new System.Text.StringBuilder(
- 首先倒入一个依赖: compile 'com.youth.banner:banner:1.4.9'添加的权限:<use
- 复合语句Java的复合语句是以整个区块为单位的语句,由{}以及{}内包含的内容组成对于复合语句来说,复合语句创建了一个局部变量的作用域,该作
- Device Administration对于这个应用,市场上很多,但是看一下评论就知道效果有多差了,因为99%一键锁屏应用没办法卸载。今天
- 策略模式的应用场景策略模式是否要使用,取决于业务场景是否符合,有没有必要。是否符合如果业务是处于不同的场景时,采取不同的处理方式的话,就满足
- 详解Java中HashSet和TreeSet的区别1. HashSetHashSet有以下特点:不能保证元素的排列顺序,顺序有可能发生变化不
- 下载maven 解压路径: 打开环境变量:右键此电脑-属性-高级系统设置-高级-环境变量添加以下系统变量:测试:win+
- 前言表之间的关系有几种:一对多、多对一、 一对一、多对多在多对一关系中,把多的部分拆成一个一个对象其实就是一对一关系,如账户和用户是多对一关
- 一:父级pom.xml文件 resources目录下新建指定文件夹,存放Spring配置文件<profiles> &
- 这篇文章主要介绍了通过实例了解spring使用构造器注入的原因,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,