Android 適配器模式應用及設計原理_Android

來源:互聯網
上載者:User

適配器模式是一種重要的設計模式,在 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即可。

感謝閱讀,希望能協助到大家,謝謝大家對本站的支援!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.