当前位置: 首页 > 科技观察

Android设计模式适配器模式及应用场景详解

时间:2023-03-16 01:51:05 科技观察

前言设计模式有时是一道坎,但设计模式非常有用。过了这一关,可以让你的境界更上一层楼。在android开发中,了解一些设计模式是很有必要的,因为设计模式在android源码中可以说是无处不在。今天我们来讲解一下适配器模式1.适配器模式的定义及问题解决1.适配器模式将一个类的接口转换成客户端期望的另一个接口,从而使两个类可以协同工作2、作为桥梁在两个不兼容的接口之间。这类设计模式属于结构型模式,结合了两个独立接口的功能3.将一个类的接口转换成客户想要的另一个接口。适配器模式使那些由于接口不兼容而无法协同工作的类4成为可能。该模式涉及一个单独的类,负责添加独立的或不兼容的接口功能。举一个真实的例子,读卡器作为存储卡和笔记本之间的适配器。你把内存卡插入读卡器,再把读卡器插入笔记本,这样就可以通过笔记本读取内存卡了;5、主要解决方案是在软件系统中,往往需要将一些“已有的对象”放到新的环境中,而新环境所需要的接口,已有的对象无法满足;二、适用场景及优缺点1、使用场景系统需要使用已有的类,而这样的接口不符合系统的需求,即接口不兼容;想创建一个可重用的类,用于与一些彼此关系不大的类一起工作,包括一些将来可能会引入的类;需要一个统一的输出接口,输入端的接口不能预测;2.优点目标类和适配器类解耦,通过引入一个适配器类,在不修改原有结构的情况下,复用已有的适配器类。增加类的透明性和复用性,将具体的业务实现过程封装在适配器类中,对客户端类是透明的,提高了适配器的复用性,同一个适配器类可以在很多不同的系统中复用。灵活性和可扩展性非常好。通过使用配置文件,可以方便地更换适配器,无需修改原有代码就可以添加新的适配器,完全符合开闭原则。3.缺点过多使用适配器会使系统变得非常凌乱,难以从整体上掌握。比如,明明是调用了A接口,实际上是内部适配了B接口的实现。如果这种情况在一个系统中发生太多,无异于一场灾难。因此,如果没有必要,可以不使用适配器直接重构系统。由于JAVA最多继承一个类,因此最多只能适配一个适配器类,目标类必须是抽象类。一次只能适配一个适配器类,不能同时适配多个适配器。目标抽象类只能是接口,不能是类,其使用有一定的局限性;3.适配器的两种模式适配器模式有两种类型:类适配器对象适配器模式涉及的角色有:目标角色:this是期望的接口。注意:由于这里讨论的是类适配器模式,目标不能是类。Source(Adapee)角色:现在需要一个适配接口。适配器(Adaper)作用:适配器类是这个模式的核心。适配器将源接口转换为目标接口。显然,这个角色不能是一个接口,而必须是一个具体的类。场景:如果类A要使用方法M,类X有方法M,但是方法M的结果可能不能完全满足类A的需求,那么类X是硬编码的,不好用,如果设计是不行,那就把classX改成一个接口,做一些B,C,D,E...的中间类,让他们都有一个方法来处理M方法,然后用1,classadapter给classA:设计一个InterfaceI,让他也有M方法,然后设计一个B类,写一个专门满足A类要求的M方法,然后让A类继承B类,实现I接口的M方法,和最后在A类的M方法中使用super方法调用B类的specialM方法2.对象适配器:(多用对象适配器)设计一个接口I,让它也有M方法再设计一个B类,写满足A类要求的specialM方法声明在A类A类B变量中,A类实现了I接口,那么A类也h作为M方法。最后,在A类的M方法中,如果需要,可以选择调用B类的specialM方法或者设计一个B类实现I接口的M方法,然后在A类中声明一个I类变量,以及然后直接调用I接口的M方法。在调用A类的M方法之前,通过setAdapter(IAdapter)等方法将B类设置为A类,这样就保证了A类和I类的成员变量不变。适配不同情况时,写一个类似B类的中间类适配就可以了。简而言之,两端都保持不变。通过不同的选择方式,选择不同的中间类,即适配器模式。三、适配器案例在现实中的实现这里我们用一个例子来模拟适配器模式。需求是这样的:取消了IPhone12的耳机口,如何保证以前的耳机还能用?当然,还需要一个适配器,这个适配器其实和我们的适配器类似。耳机需要的接口是我们的目标角色,手机提供的接口是我们的源角色,适配器当然是适配器角色。classadaptertargetrolepublicinterfaceITarget{//获取需要的接口StringgetRightInterface();}sourcerolepublicclassIPhoneSeven{//获取iphone7提供的接口publicStringgetInterface(){return"iphone7interface";}}adapterpublicclassCAdapterextendsIPhoneSevenimplementsITarget{@OverridepublicStringgetRightInterface(){StringnewInterface=getInterface();returnsuit(newInterface);}/***转换操作*@paramnewInterface*@return*/privateStringsuit(StringnewInterface){return"3.5mminterface";}}对象适配器的目标角色和对象适配器的源角色对象适配器都是一样的,我们不再写了。适配器publicclassAdapterimplementsITarget{privateIPhoneSevenmIPhoneSeven;publicAdapter(IPhoneSevenIPhoneSeven){mIPhoneSeven=IPhoneSeven;}@OverridepublicStringgetRightInterface(){StringnewInterface=mIPhoneSeven.getInterface();returnsuit(newInterface);}/***转换操作*@paramnewStringInterface*@retStringnewInterface){return"3.5mminterface";}}4.Android中的应用场景adapter模式在android中应用广泛,最常见的Adapter为ListView、GridView、RecyclerView等。然而,我们经常使用的ListView是一个模型。在使用ListView时,每一项的布局和数据都不同,但是最终输出可以看作是一个View,对应上面适配器模式应用场景的第三项:需要一个统一的输出接口,以及上的接口输入端是不可预测的。我们来看看ListView中的适配器模式。首先,让我们看一下我们的Adapter类的总体结构classAdapterextendsBaseAdapter{privateListmDatas;publicAdapter(Listdatas){mDatas=datas;}@OverridepublicintgetCount(){returnmDatas.size();}@OverridepubliclonggetItemId(intposition){returnposition;}@OverridepublicObjectgetItem(intposition){returnmDatas.get(position);}@OverridepublicViewgetView(intposition,ViewconvertView,ViewGroupparent){if(convertView==null){//初始化View}//初始化数据returnconvertView;}}可以看出Adapter中的接口主要是getCount()返回的个数子Views,getView()返回我们填充数据的View,ListView通过这些接口来进行具体的布局、缓存等工作。下面简单看一下ListView的实现。首先,这些getCount()等接口在一个接口类Adapter中publicinterfaceAdapter{//省略其他接口intgetCount();ObjectgetItem(intposition);longgetItemId(intposition);ViewgetView(intposition,ViewconvertView,ViewGroupparent);//省略otherInterface}添加一个过渡接口ListAdapterpublicinterfaceListAdapterextendsAdapter{//interfaceomitted}我们在写自己的Adapter时,会继承一个BaseAdapter,我们看一下BaseAdapterpublicabstractclassBaseAdapterimplementsListAdapter,SpinnerAdapter{//BaseAdapter实现了ListAdapter接口和部分AdapterInterfaceslikegetCount()和getView()需要自己实现}ListView的父类AbsListView有一个ListAdapter接口,通过这个接口调用getCount()等方法获取View的个数等。publicabstractclassAbsListViewextendsAdapterViewimplementsTextWatcher,ViewTreeObserver.OnGlobalLayoutListener,Filter.FilterListener,ViewTreeObserver.OnTouchModeChangeListener,RemoteViewsAdapter.RemoteAdapterConnectionCallback{/***包含该视图要显示的数据的适配器*/ListAdapterAdapter;@OverrideprotectedvoidonAttachedToWindow(){super.onAttachedToWindow();finalViewTreeObservertreeObserver=getViewTreeObserver();treeObserver.addOnTouchModeChangeListener(this);如果(mTextFilterEnabled&&mPopup!=null&&!mGlobalLayoutListenerAddedFilter){treeObserver.addOnGlobalLayoutListener(this);}server(mAdapter!=null&&mDataSetObserver==null)server(mAdapter!=null&server&mDataSetOb)setregisterDataSetObserver(mDataSetObserver);//Datamayhavechangedwhileweweredetached.Refresh.mDataChanged=true;mOldItemCount=mItemCount;//通过getCount()获取View元素个数mItemCount=mAdapter.getCount();}}}从上面我们可以看出那AbsListView是一个抽象类,封装了一些固定的逻辑,比如Adapter模式的应用逻辑,布局的复用逻辑,布局子元素逻辑等,具体实现在子类ListView中。下面我们来看看ListView中各个子元素View是如何处理的。@OverrideprotectedvoidlayoutChildren(){//OmitothercodesOmitothercode}在ListView中,会覆盖AbsListView中的layoutChildren()函数,layoutChildren()会根据不同的情况进行布局,比如从上到下或者从下到下到达顶点。我们来看看具体的布局方法fillUp方法。privateViewfillUp(intpos,intnextBottom){//省略其他代码while(nextBottom>end&&pos>=0){//isthistheselecteditem?booleanselected=pos==mSelectedPosition;Viewchild=makeAndAddView(pos,nextBottom,false,mListPadding.left,selected);nextBottom=child.getTop()-mDividerHeight;if(selected){selectedView=child;}pos--;}mFirstPosition=pos+1;setVisibleRangeHint(mFirstPosition,mFirstPosition+getChildCount()-1);returnselectedView;}这里我们看到在fillUp方法中,会通过makeAndAddView()方法获取View。让我们看一下makeAndAddView()方法的实现。privateViewmakeAndAddView(intposition,inty,booleanflow,intchildrenLeft,booleanselected){if(!mDataChanged){//Trytouseanexistingviewforthisposition.finalViewactiveView=mRecycler.getActiveView(position);if(activeView!=null){//Foundit.We'rereusinganexistingchild,/soitjustneeds/tobepositionedlikeascrapview.setupChild(activeView,position,y,flow,childrenLeft,selected,true);returnactiveView;}}//为这个position做一个新的view,orconvertanunusedviewif//possible.finalViewchild=obtainView(position,mIsScrap);//这个需要定位和测量.setup(Child,设置位置,y,流,childrenLeft,selected,mIsScrap[0]);returnchild;}不知道大家怎么看这里?缓存机制出现在makeAndAddView()方法中,是提高ListView加载效率的关键方法。我们可以看到,在获取子View的时候,会先从缓存中找,也就是从mRecycler中找。mRecycler是一个RecycleBin类,用于AbsListView中的缓存。我们来看看缓存的实现。classRecycleBin{privateView[]mActiveViews=newView[0];/***获取指定位置对应的view.Theviewwillberemovedfrom*mActiveViewsifitisfound.**@parampositionThepositiontolookupinmActiveViews*@returnTheviewifitisfound,nulotherwise*/ViewgetActiveView(intposition){intindex=position-mFirstActivePosition;finalView[]activeViews=mActiveViews;if(index>=0&&index