本文主要介绍Android设计模式的适配器模式,并通过源代码分析对适配器模式进行分析,有一定的参考价值,感兴趣的朋友可以参考一下。
本文分享了Android适配器模式的源代码,供大家参考。具体情况如下
1. 模式介绍
1.1模式的定义:
适配器模式将一个类的接口转换成客户端期望的另一个接口,这样两个因为接口不匹配而不能协同工作的类就可以协同工作了。
1.2模式的使用场景:
以电源接口为例。笔记本电脑的电源一般接受5V电压,但我们生活中的电线电压一般是220V输出。这时候就出现了不匹配的情况,在软件开发中叫做接口不兼容。这时就需要一个适配器来进行接口转换。软件开发中有一句话正好反映了这一点:任何问题都可以通过增加一个中间层来解决。这一层在这里可以理解为适配器层,通过它进行一次接口转换就可以达到兼容的目的。
2.模式的简单实现
2.1简单实现的介绍:
在上面的电源接口例子中,5V电压是目标接口,220V电压是Adaptee类,将220v电压转换为5V是适配器。
2.2类适配器模式:
/**
*目标角色
*/
公共接口五伏{
public int get volt 5();
}
/**
* Adaptee角色,要转换的对象。
*/
公共类Volt220 {
public int getVolt220() {
返回220;
}
}
//适配器角色
公共类ClassAdapter扩展Volt220实现{
@覆盖
public int getVolt5() {
返回5;
}
}
目标角色给出所需的目标接口,而Adaptee类是需要转换的对象。适配器是将Volt220转换为目标的接口。相应的,Target的目标是获得5V的输出电压,而Adaptee的正常输出电压是220V。这时候我们就需要电源适配器类把220V的电压转换成5V,解决接口不兼容的问题。
公共类测试{
公共静态void main(String[] args) {
class adapter adapter=new class adapter();
System.out.println('输出电压:' adapter . get volt 5());
}
}
2.3.Android源码中的模式实现
像类的适配器模式一样,对象的适配器模式将适配类的API转换成目标类的API。与类的适配器模式不同,对象的适配器模式通过代理而不是继承连接到被适配器类。
从图2中可以看出,Adaptee类(Volt220)没有getVolt5()方法,但是客户端需要这个方法。为了使客户端能够使用Adaptee类,有必要提供一个包装类适配器。这个包装器类包装了Adaptee的一个实例,因此这个包装器类可以将Adaptee的API与目标类的API连接起来。与适配器Adaptee存在委托关系,这决定了适配器模式是object。
/**
*目标角色
*/
公共接口五伏{
public int get volt 5();
}
/**
* Adaptee角色,要转换的对象。
*/
公共类Volt220 {
public int getVolt220() {
返回220;
}
}
//对象适配器模式
公共类ObjectAdapter实现{
Volt220 mVolt220
公共对象适配器(Volt220适配器){
mVolt220=adaptee
}
public int getVolt220() {
返回mvolt 220 . get volt 220();
}
@覆盖
public int getVolt5() {
返回5;
}
}
2.4.类适配器和对象适配器的权衡
*类适配器使用对象继承的方式,这是一种静态定义方式;对象适配器采用对象组合的方式,也就是动态组合的方式。
*对于类适配器,因为适配器直接继承Adaptee,所以不能和Adaptee的子类一起工作,因为继承是静态关系。当适配器继承Adaptee时,不可能处理Adaptee的子类。对于对象适配器,一个适配器可以使许多不同的源适应同一个目标。换句话说,同一个适配器可以使源类及其子类适应目标接口。因为对象适配器采用的是对象组合的关系,只要对象类型正确,是不是子类并不重要。
*对于类适配器,适配器可以重定义Adaptee的一些行为,相当于子类覆盖了父类的一些实现方法。对于对象适配器,很难重新定义Adaptee的行为。在这种情况下,需要定义Adaptee的子类来实现重定义,然后让适配器组合子类。虽然很难重新定义Adaptee的行为,但添加一些新的行为很方便,而且新添加的行为可以同时应用于所有的源。
*对于类适配器,只引入一个对象,不需要额外的引用来间接获取Adaptee。对于对象适配器,需要额外的引用来间接获取Adaptee。
建议尽量使用对象适配器的实现,多组合/聚合,少继承。当然具体问题具体分析,根据需要选择实施,最适合的才是最好的。
3.Android ListView中的Adapter模式
在开发过程中,ListView的适配器是我们最常见的类型之一。的一般用法大致如下:
//适配器
公共类MyAdapter扩展BaseAdapter{
私人LayoutInflater mInflater
ListString mDatas
公共MyAdapter(上下文上下文,列表字符串数据){
this . min flater=layoutinflater . from(上下文);
mDatas=datas
}
@覆盖
public int getCount() {
返回mdatas . size();
}
@覆盖
公共字符串getItem(int pos) {
返回mdatas . get(pos);
}
@覆盖
public long getItemId(int pos) {
退货位置;
}
//解析、设置、缓存convertView及相关内容
@覆盖
公共视图getView(int position,View convertView,ViewGroup parent) {
ViewHolder holder=null
//重用itemview
if (convertView==null) {
holder=new view holder();
convert view=min flater . inflate(r . layout . my _ listview _ item,null);
//获取标题
holder . title=(TextView)convert view . findviewbyid(r . id . title);
convert view . settag(holder);
}否则{
holder=(view holder)convert view . gettag();
}
holder . title . settext(mdatas . get(position));
返回convertView
}
}
这个好像挺麻烦的。看到这里,我们不禁要问,ListView为什么要用适配器模式?
我们知道,作为最重要的视图,ListView需要能够显示各种视图。每个人需要不同的显示效果,显示数据的种类和数量也是千变万化的。那么如何隔离这种变化就显得尤为重要。
Android的做法是增加一个适配器层来应对变化,将ListView需要的接口抽象到Adapter对象中,这样只要用户实现了适配器的接口,ListView就可以根据用户设置的显示效果、数量、数据来显示特定的项目视图。
代理数据集用于通知ListView数据的数量(getCount函数)和每个数据的类型(getItem函数)。最重要的是解决项目视图的输出。项目千变万化,但毕竟都是视图类型。适配器将项目视图统一输出为视图(getView函数),因此可以应对项目视图的可变性。
那么ListView是如何通过适配器模式(不仅仅是适配器模式)工作的呢?我们一起来看看吧。
ListView继承自AbsListView,适配器是在AbsListView中定义的。让我们来看看这个类。
公共抽象类AbsListView扩展AdapterViewListAdapter实现TextWatcher,
ViewTreeObserver。OnGlobalLayoutListener,过滤器。FilterListener,
ViewTreeObserver。OnTouchModeChangeListener,
RemoteViewsAdapter。RemoteAdapterConnectionCallback {
ListAdapter mAdapter
//与窗口关联时调用的函数
@覆盖
受保护的void onAttachedToWindow() {
super . onattachedtowindow();
//代码省略
//为适配器注册一个观察器。这种模式将在下一篇文章中介绍。
如果(mAdapter!=null mDataSetObserver==null) {
mDataSetObserver=new AdapterDataSetObserver();
ma dapter . registerdatasetobserver(mDataSetObserver);
//在我们分离时,数据可能已经更改。刷新。
mDataChanged=true
mOldItemCount=mItemCount
//获取项目的数量,调用的是马达普特的获取计数方法
mitem count=ma dapter。get count();
}
错误连接=真
}
/**
* 子类需要覆写layoutChildren()函数来布局子视图,也就是项目视图
*/
@覆盖
protected void onLayout(boolean changed,int l,int t,int r,int b) {
super.onLayout(已更改,l,t,r,b);
mInLayout=true
如果(已更改){
int子计数=get子计数();
for(int I=0;我数孩子;i ) {
getChildAt(i).强制布局();
}
mrecycler。markchildrendirty();
}
if (mFastScroller!=null mItemCount!=mOldItemCount) {
mfastscroller。onitemcountchanged(模具项目计数,mitem计数);
}
//布局子视图
布局子对象();
mInLayout=false
mOverscrollMax=(b-t)/over scroll _ LIMIT _ DIVISOR;
}
//获取一个项目视图
视图获取视图(int position,boolean[] isScrap) {
ISS crap[0]=false;
查看剪贴簿视图
//从缓存的项目视图中获取,列表视图的复用机制就在这里
废料视图=mrecycler。获取废料视图(位置);
查看孩子;
如果(scrapView!=null) {
//代码省略
孩子=妈妈。获取视图(位置,scrapView,this);
//代码省略
}否则{
孩子=妈妈。获取视图(位置,null,this);
//代码省略
}
返回孩子;
}
}
抽象视图定义了集合视图的框架,比如适配器模式的应用、复用项目视图的逻辑、布局项目视图的逻辑等。子类只需要覆写特定的方法即可实现集合视图的功能,例如列表视图。
列表视图中的相关方法。
@覆盖
受保护的void layoutChildren() {
//代码省略
尝试{
超级棒。布局子对象();
invalidate();
//代码省略
//根据布局模式来布局项目视图
开关(mLayoutMode) {
案例布局_设置_选择:
如果(纽塞尔!=null) {
sel=fillFromSelection(newsel。gettop(),childrenTop,children bottom);
}否则{
sel=fillfromiddle(儿童顶部,儿童底部);
}
打破;
案例布局_同步:
sel=fillSpecific(mSyncPosition,mSpecificTop);
打破;
案例布局_强制_底部:
sel=fillUp(mItemCount - 1,children bottom);
adjustViewsUpOrDown();
打破;
案例布局_FORCE_TOP:
mFirstPosition=0;
sel=fillFromTop(children top);
adjustViewsUpOrDown();
打破;
案例布局_具体:
sel=fill specific(reconcileSelectedPosition(),mSpecificTop);
打破;
案例布局_移动_选择:
sel=moveSelection(oldSel,newSel,delta,childrenTop,children bottom);
打破;
默认值:
//代码省略
打破;
}
}
//从上到下填充项目视图[只是其中一种填充方式]
私有视图填充(int pos,int nextTop) {
视图selectedView=null
int end=(mBottom-mTop);
if((mGroupFlags CLIP _ TO _ PADDING _ MASK)==CLIP _ TO _ PADDING _ MASK){
end-=mlistpadding。底部;
}
while(下一个顶端位置项目计数){
//这是选中的项目吗?
boolean selected=pos==mSelectedPosition;
view child=makeadaddview(pos,nextTop,true,mListPadding.left,selected);
下一个顶部=子。getbottom()mDividerHeight;
如果(选中){
selectedView=child
}
刷卡机
}
返回选定视图
}
//添加项目视图
私有视图makeadaddview(int position,int y,boolean flow,int childrenLeft,
布尔选定){
查看孩子;
//代码省略
//为此位置创建一个新视图,或者如果可能,转换一个未使用的视图
child=obtainView(位置,废话小姐);
//这个需要定位和测量
setupChild(child,position,y,flow,childrenLeft,selected,miss crap[0]);
返回孩子;
}
ListView覆盖AbsListView中的layoutChilden函数,在ABS ListView中,项目视图根据布局模式进行布局。项目视图的数量和样式通过与适配器对应的方法获得。在获得了数量和条目视图后,这些条目视图被布局在ListView对应的坐标上,加上条目视图的重用机制,整个ListView基本上就运作起来了。
当然,这里的适配器不是一个经典的适配器模式,但它是对象适配器模式的一个极好的例子,它也体现了一些面向对象的基本原则。这里目标角色和适配器角色合并在一起,适配器中的方法就是目标方法;Adaptee角色是ListView的数据集和Item View和Adapter的数据集,从而获得数据集的个数和元素。
通过增加一层适配器来抽象项目视图的操作,集合视图如ListView可以获得项目的数量、数据元素、项目视图等。通过Adapter对象,从而达到适应各种数据和各种项目视图的效果。由于项目视图和数据类型千变万化,Android架构师将这些变化的部分交给用户处理,通过getCount、getItem、getView等几种方法进行抽象,即把项目视图的构建过程交给用户处理,灵活运用适配器模式,达到无限适配、拥抱变化的目的。
4.杂谈
优点与缺点
优势
更好的可重用性
系统需要使用现有的类,而这个类的接口不符合系统的需要。然后通过适配器模式可以更好地重用这些功能。
更好的可扩展性
在实现适配器功能时,可以调用自主开发的函数,从而自然地扩展了系统的功能。
劣势
过度使用适配器会让系统非常凌乱,整体难以把握。比如很明显调用了接口A,但实际上内部接口是适配接口b的实现,如果这种情况发生在一个系统上太多,无异于灾难。因此,如果没有必要,可以不使用适配器直接重新配置系统。
这就是本文的全部内容。希望对大家的学习有帮助,支持我们。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。