Android source code: ListView adapter Mode
Mode definition
The adapter mode converts an interface of a class into another interface that the client expects, so that the two classes that cannot work together due to interface mismatch can work together.
Use Cases
Using Power interfaces as an example, the power supply of laptops generally accepts 5 V voltage, but the voltage of wires in our daily life is generally V output. At this time, there is a mismatch. in software development, we call it interface incompatibility. In this case, an adapter is required to perform an Interface Conversion. One sentence in software development exactly reflects this: Any problem can be solved by adding an intermediate layer. This layer can be understood as the Adapter layer here. Using this layer to perform an Interface Conversion achieves the purpose of compatibility.
There are two types of adapter modes: Class adapter mode and Object Adapter mode. The structure diagram 1 and figure 2.
The class adapter implements Interface Conversion by implementing the Target Interface and inheriting the Adaptee class, while the Object Adapter mode is implemented by implementing a method of the Target Interface and proxy Adaptee. The structure is slightly different.
Role description Target role: This is the expected interface. Note: The class adapter mode is discussed here, so the target cannot be a class. Source (Adapee) role: the interface to be adapted now.
Adapter (Adaper) role: the adapter class is the core of this mode. The adapter converts the source interface to the target interface. Obviously, this role cannot be an interface, but must be a specific class.
In the example of the above power supply interface, the 5 V voltage is the Target interface, the 220v voltage is the Adaptee class, and the conversion from 220V to 5 V is the Adapter.
Example: Class adapter Mode
/*** Target role */public interface FiveVolt {public int getVolt5 ();}/*** Adaptee role, object To be converted */public class Volt220 {public int getVolt220 () {return 220 ;}} // adapter role public class ClassAdapter extends Volt220 implements FiveVolt {@ Override public int getVolt5 () {return 5 ;}}
The Target role provides the required Target Interface, while the Adaptee class is the object to be converted. Adapter is the interface for converting Volt220 to Target. The Target is to obtain the 5 V output voltage, while Adaptee means that the normal output voltage is 220 V. At this time, we need the power adapter class to convert the V voltage to the 5 V voltage, solve the Problem of interface incompatibility.
Public class Test {public static void main (String [] args) {ClassAdapter adapter = new ClassAdapter (); System. out. println ("output voltage:" + adapter. getVolt5 ());}}
Object Adapter Mode
Like the adapter mode of the class, the Object Adapter mode converts the API of the adapted class into the API of the target class. Unlike the adapter mode of the class, the adapter mode of the object is not to connect to the Adaptee class by using an inheritance relationship, but to connect to the Adaptee class by using a proxy relationship.
As shown in figure 2, The Adaptee class (Volt220) does not have the getVolt5 () method, and the client expects this method. To enable the client to use the Adaptee class, a packaging class Adapter is required. This packaging class encapsulates an Adaptee instance, so that this packaging class can link the Adaptee API with the Target class API. The Adapter and Adaptee are delegates, which determines that the Adapter mode is object.
The sample code is as follows:
/*** Target role */public interface FiveVolt {public int getVolt5 ();}/*** Adaptee role, object To be converted */public class Volt220 {public int getVolt220 () {return 220 ;}// Object Adapter mode public class ObjectAdapter implements FiveVolt {Volt220 mVolt220; public ObjectAdapter (Volt220 adaptee) {mVolt220 = adaptee;} public int getVolt220 () {return mvolt21_getvolt220 () ;}@ Override public int getVolt5 () {return 5 ;}}
Note: To save code, we have not followed some basic object-oriented principles.
Example:
Public class Test {public static void main (String [] args) {ClassAdapter adapter = new ClassAdapter (); System. out. println ("output voltage:" + adapter. getVolt5 ());}}
Balance between class adapters and object adapters
* The class adapter uses the Object Inheritance method, which is a static definition method. The Object Adapter uses the object combination method, which is a dynamic combination method.
* For a class adapter, because the adapter directly inherits Adaptee, the adapter cannot work with the Adaptee subclass. Because the inheritance is a static relationship, after the adapter inherits the Adaptee, it is impossible to process the sub-classes of Adaptee. For object adapters, an adapter can adapt multiple sources to the same target. In other words, the same adapter can adapt both the source class and its subclass to the target interface. Because the Object Adapter uses the relationship between object combinations, it doesn't matter if the object type is correct or not.
* For a class adapter, the adapter can redefine partial behavior of the Adaptee, which is equivalent to partial implementation of a subclass that overwrites the parent class. For object adapters, it is difficult to redefine the behavior of Adaptee. In this case, you need to define a subclass of Adaptee to implement redefinition, and then let the adapter compose the subclass. Although it is difficult to redefine Adaptee behavior, it is convenient to add some new behavior, and the newly added behavior can be applied to all sources at the same time.
* For Class adapters, only one object is introduced, and no additional reference is required to indirectly obtain Adaptee. For object adapters, additional references are required to indirectly obtain Adaptee.
We recommend that you use the Object Adapter implementation method as much as possible, and use synthesis/aggregation instead of inheritance. Of course, you can select the implementation method based on the specific analysis of specific problems. The most suitable is the best.
Adapter mode in ListView
In the development process, the ListView Adapter is one of the most common types. The general usage is roughly as follows:
// Code omitting ListView myListView = (ListView) findViewById (listview_id); // sets the adapter myListView. setAdapter (new MyAdapter (context, myDatas); // adapter public class MyAdapter extends BaseAdapter {private LayoutInflater mInflater; List
MDatas; public MyAdapter (Context context, List
Datas) {this. mInflater = LayoutInflater. from (context); mDatas = datas;} @ Override public int getCount () {return mDatas. size () ;}@ Override public String getItem (int pos) {return mDatas. get (pos) ;}@ Override public long getItemId (int pos) {return pos ;}// parse, set, cache convertView and related content @ Override public View getView (int position, view convertView, ViewGroup parent) {ViewHolder holder = null; // reusable if (convertView = null) {holder = new ViewHolder (); convertView = mInflater. inflate (R. layout. my_listview_item, null); // obtain the title holder. title = (TextView) convertView. findViewById (R. id. title); convertView. setTag (holder);} else {holder = (ViewHolder) convertView. getTag ();} holder. title. setText (mDatas. get (position); return convertView ;}}
This seems quite troublesome. We can't help but ask why the ListView uses the Adapter mode?
As we know, as the most important View, ListView needs to be able to display all kinds of views. The display effects vary with each other, and the types and quantities of displayed data are also changing. How to isolate such changes is particularly important.
Android adds an Adapter layer to respond to changes and abstracts the interfaces required by ListView into the Adapter object so that as long as the user implements the Adapter interface, listView can display a specific Item View based on the user-defined display effect, quantity, and data.
The number of ListView data (getCount function) and the type of each data (getItem function) are notified through the proxy dataset. The most important thing is to solve the output of Item View. The Item View is ever-changing, but after all, it is a View type. The Adapter outputs the Item View as the View (getView function) in a unified manner, so that the variability of the Item View is well matched.
So how does a ListView work in the Adapter mode (not just the Adapter mode? Let's take a look.
ListView inherits from AbsListView. The Adapter is defined in AbsListView. Let's take a look at this class.
Public abstract class AbsListView extends AdapterView
Implements TextWatcher, ViewTreeObserver. onGlobalLayoutListener, Filter. filterListener, ViewTreeObserver. onTouchModeChangeListener, RemoteViewsAdapter. remoteAdapterConnectionCallback {ListAdapter mAdapter; // The function called when it is associated with the Window @ Override protected void onAttachedToWindow () {super. onAttachedToWindow (); // code is omitted // register an observer for the adapter. This mode is introduced in the next article. If (mAdapter! = Null & mDataSetObserver = null) {mDataSetObserver = new AdapterDataSetObserver (); mAdapter. registerDataSetObserver (mDataSetObserver); // Data may have changed while we were detached. refresh. mDataChanged = true; mOldItemCount = mItemCount // gets the number of items. The getCount method mItemCount = mAdapter is called. getCount () ;} mIsAttached = true;}/*** subclass needs to overwrite the layoutChildren () function to layout the child view, that is, Item View */@ Override protected void onLayout (boolean changed, int l, int t, int r, int B) {super. onLayout (changed, l, t, r, B); mInLayout = true; if (changed) {int childCount = getChildCount (); for (int I = 0; I <childCount; I ++) {getChildAt (I ). forceLayout ();} mRecycler. markChildrenDirty ();} if (mFastScroller! = Null & mItemCount! = MOldItemCount) {mFastScroller. onItemCountChanged (mOldItemCount, mItemCount);} // layout Child View layoutChildren (); mInLayout = false; mOverscrollMax = (B-t)/OVERSCROLL_LIMIT_DIVISOR ;} // obtain an Item View obtainView (int position, boolean [] isScrap) {isScrap [0] = false; View scrapView; // obtain it from the cached Item View, the reuse mechanism of ListView is here scrapView = mRecycler. getScrapView (position); View child; if (scrapVi Ew! = Null) {// The code is omitted child = mAdapter. getView (position, scrapView, this); // Code omitted} else {child = mAdapter. getView (position, null, this); // Code omitted} return child ;}}
AbsListView defines the framework of the set View, such as the application of the Adapter mode, the logic of reusing the Item View, and the logic of layout the Item View. Subclass only needs to override a specific method to implement the set view function, such as ListView.
Related methods in ListView.
@ Override protected void layoutChildren () {// The Code omitting try {super. layoutChildren (); invalidate (); // Code omitted // layout Item View switch (mLayoutMode) {case LAYOUT_SET_SELECTION: if (newSel! = Null) {sel = fillFromSelection (newSel. getTop (), childrenTop, childrenBottom);} else {sel = upper (childrenTop, childrenBottom);} break; case LAYOUT_SYNC: sel = fillSpecific (mSyncPosition, mSpecificTop); break; case when: sel = fillUp (mItemCount-1, childrenBottom); adjustViewsUpOrDown (); break; case LAYOUT_FORCE_TOP: mFirstPosition = 0; sel = fillFromTop (childrenTop ); Upper (); break; case LAYOUT_SPECIFIC: sel = fillSpecific (upper (), mSpecificTop); break; case lower: sel = moveSelection (oldSel, newSel, delta, childrenTop, childrenBottom ); break; default: // The Code omitting break;} // fill in Item View from top to bottom [only one filling method] private View fillDown (int pos, int nextTop) {View selectedView = null; int end = (mBottom-mTop); if (mGr OupFlags & CLIP_TO_PADDING_MASK) = CLIP_TO_PADDING_MASK) {end-= mListPadding. bottom;} while (nextTop <end & pos <mItemCount) {// is this the selected item? Boolean selected = pos = mSelectedPosition; View child = makeAndAddView (pos, nextTop, true, mListPadding. left, selected); nextTop = child. getBottom () + mDividerHeight; if (selected) {selectedView = child;} pos ++;} return selectedView ;}// add Item View private View makeAndAddView (int position, int y, boolean flow, int childrenLeft, boolean selected) {View child; // Code omitted // Make a new view for this position, or convert an unused view if possible child = obtainView (position, mIsScrap); // This needs to be positioned and measured setupChild (child, position, y, flow, childrenLeft, selected, mIsScrap [0]); return child ;}
ListView overwrites the layoutChilden function in the AbsListView. In this function, the Item View is displayed based on the layout mode. The number and style of Item views are obtained through the method corresponding to the Adapter. After obtaining the number and Item View, these Item views are arranged on the corresponding coordinates of the ListView, coupled with the reuse mechanism of Item View, the entire ListView is basically running.
Of course, the Adapter here is not a classic Adapter mode, but it is an excellent example of the Object Adapter mode, it also reflects some basic principles of object-oriented. Here, the Target role and the Adapter role are integrated, and the method in the Adapter is the Target method. The Adaptee role is the ListView dataset and the Item View and Adapter proxy dataset, to obtain the number and element of the dataset.
Abstract The operation of the Item View by adding an Adapter layer. The ListView and other collection views obtain the number of items, data elements, and Item View through the Adapter object, to adapt to various data and various Item views. Because the Item View and data types are ever changing, the Android architects hand over the changes to the users for processing and abstract them through getCount, getItem, getView, and other methods, that is to say, the construction process of the Item View is handed over to the user for handling, and the adapter mode is flexibly applied to achieve the goal of infinite adaptation and embracing change.
Advantages of the adapter Mode
* Better reusability
The system needs to use existing classes, and such interfaces do not meet the requirements of the system. The adapter mode enables better reuse of these functions.
* Better scalability
When implementing the adapter function, you can call your own developed functions to naturally expand the system functions.
Disadvantages of adapter Mode
Using too many adapters will make the system messy and difficult to grasp as a whole. For example, we can see that the called a interface is actually adapted to the implementation of B interface internally. If there are too many systems, this would be a disaster. Therefore, if not necessary, you can directly refactor the system without using the adapter.