初識(shí)Android時(shí)诡宗,我對(duì)ListView、GradView中的Adapter一直半懂非懂瞳步,每次寫Adapter都覺得異常痛苦闷哆,故而有了此文,希望能幫到一些初學(xué)者单起。
先來看下適配器模式的官方解釋抱怔,將一個(gè)類的接口轉(zhuǎn)換成客戶希望的另外一個(gè)接口,Adapter模式使得原本由于接口不兼容而不能一起工作的那些類可以一起工作嘀倒。乍看肯定不知所云屈留,通俗說下我個(gè)人的理解:當(dāng)我們創(chuàng)建一個(gè)View之后,肯定要接著規(guī)定該view的大小测蘑、位置灌危、顯示的內(nèi)容信息、觸摸事件的回調(diào)等等碳胳,我們發(fā)現(xiàn)將這些東西放一起導(dǎo)致代碼太耦合甚至冗余了勇蝙,這時(shí)候就可以使用Adapter模式,將這個(gè)流程分開挨约,首先我們只管創(chuàng)建一個(gè)View,至于該View的內(nèi)容樣式我們完全不用關(guān)心浅蚪,交給我們的小弟Adapter去做(前提是這個(gè)人是我們的小弟藕帜,和我們的View能聯(lián)系起來),當(dāng)小弟完成了我們分配給他的任務(wù)后(安排子View的樣式惜傲,信息的解析顯示洽故,Event回調(diào)等),我們只需通過setAdapter()將他的工作內(nèi)容竊取過來就行盗誊。
或者再直觀點(diǎn)說时甚,我們要買回來了一部Iphone7,我們只需要輕松愉快地用它來聽歌哈踱,看電影荒适,聊微信,至于當(dāng)他沒電了怎么辦开镣?如何使用110V刀诬、220V電壓充電?怎么使用二孔、三孔插頭邪财?如何快充如何慢充陕壹?這些都不需要我們關(guān)心,交給我們的充電器(Adapter)即可树埠,我們要做的只需連上usb線和找到插座(setAdapter)就行了糠馆。
當(dāng)你不清楚適配的具體流程時(shí),寫Adapter是非常痛苦的怎憋,接下來我們就舉栗子詳細(xì)分析一個(gè)完整的適配器模式工作流程(現(xiàn)在除了我的奇葩公司又碌,應(yīng)該沒人會(huì)用ListView了吧,所以這里直接以RecyclerView為栗绊袋,其實(shí)ListView的adapter也是一樣的原理)毕匀。
- 1.創(chuàng)建一個(gè)RecyclerView并實(shí)例化它,然后等著我們的小弟(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使用皂岔,我們先從源碼了解每個(gè)方法的回調(diào)周期。
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
的一個(gè)抽象內(nèi)部類规个,我們只需要重寫它暴露出來的各種回調(diào)方法(創(chuàng)建View,綁定View,獲取數(shù)據(jù)內(nèi)容,通知數(shù)據(jù)變化......)姓建,就可以達(dá)到創(chuàng)建及控制itemView內(nèi)容的目的诞仓。這里挑我們創(chuàng)建Adapter時(shí)常用的重寫方法講解:
onCreateViewHolder:
根據(jù)需求,創(chuàng)建自定義樣式的itemViw速兔,最終return一個(gè)ViewHolder類型墅拭。由Adapter
內(nèi)部類中的createViewHolder
調(diào)用。
//根據(jù)需求涣狗,創(chuàng)建自定義樣式的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:
將我們傳遞進(jìn)來的數(shù)據(jù)bean與view綁定在一起谍婉,即決定我們的itemView的具體內(nèi)容如何展示舒憾,由Adapter
內(nèi)部類中的bindViewHolder
調(diào)用。同時(shí)也可以在這里處理觸摸事件的回調(diào)穗熬,后面會(huì)講到镀迂。
//將數(shù)據(jù)與界面進(jìn)行綁定的操作
@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
viewHolder.mTextView.setText(datas[position]);
}
getItemCount:
返回?cái)?shù)據(jù)bean的數(shù)量,即需要的itemView的個(gè)數(shù)唤蔗。
//獲取數(shù)據(jù)的數(shù)量
@Override
public int getItemCount() {
return datas.length;
}
一般情況我們重寫上述三個(gè)方法即可探遵。
onViewAttachedToWindow onViewDetachedFromWindow:
在View依附/脫離window的時(shí)候回調(diào)
registerAdapterDataObserver unregisterAdapterDataObserver:
主要用于注冊(cè)與解綁適配器數(shù)據(jù)的觀察者模式
notifyDataSetChanged notifyItemMoved:
通過Adapter來通知數(shù)據(jù)或item的變化,請(qǐng)求更新view.
那怎么讓Adapter來為我們處理觸摸事件妓柜?通過接口回調(diào)的方法就能很簡(jiǎn)單地完成箱季。
首先在我們的Adapter中添加一個(gè)內(nèi)部接口,其中的方法在第一步實(shí)例化View的時(shí)候?qū)崿F(xiàn)棍掐。
public interface OnItemClickListener {
void ItemClickListener(View view,int postion);
void ItemLongClickListener(View view,int postion);
}
然后在onBindViewHolder中回調(diào)藏雏。
if(mListener!=null){//如果設(shè)置了監(jiān)聽那么它就不為空,然后回調(diào)相應(yīng)的方法
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int pos = holder.getLayoutPosition();//得到當(dāng)前點(diǎn)擊item的位置pos
mListener.ItemClickListener(holder.itemView,pos);//把事件交給我們實(shí)現(xiàn)的接口那里處理
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
int pos = holder.getLayoutPosition();//得到當(dāng)前點(diǎn)擊item的位置pos
mListener.ItemLongClickListener(holder.itemView,pos);//把事件交給我們實(shí)現(xiàn)的接口那里處理
return true;
}
});
}
- 3.將RecyclerView與Adapter通過
setAdapter
聯(lián)系起來作煌,并實(shí)現(xiàn)Adapter內(nèi)部的回調(diào)接口掘殴。
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,"點(diǎn)擊了:"+postion, Toast.LENGTH_SHORT).show();
}
@Override
public void ItemLongClickListener(View view, int postion) {
list.remove(postion);
adapter.notifyItemRemoved(postion);
}
});
至此一個(gè)最簡(jiǎn)單的RecyclerViewAdapter就完成了,文末給出一個(gè)自己簡(jiǎn)單封裝過后的CommonRecyclerAdapter最疆,使用時(shí)我們只需要傳入 context
, layoutResId
,List<T> data
即可杯巨。