Android入門(mén)之實(shí)現(xiàn)自定義Adapter
介紹
在上一篇“SimpleAdapter“章節(jié)中,我們看到了把:ListView和Listview內(nèi)部詳細(xì)頁(yè)面進(jìn)行分離的Adapter的設(shè)計(jì)手法。
可是,這個(gè)SimpleAdapter的構(gòu)造函數(shù)不夠錄活、苦澀難懂。很難滿足我們實(shí)際大多生產(chǎn)場(chǎng)景的開(kāi)發(fā)。
因此,今天我們就要來(lái)看一個(gè)更人性化的“自定義BaseAdapter“。實(shí)際生產(chǎn)應(yīng)用場(chǎng)景開(kāi)發(fā)中充斥著自定義BaseAdapter,因此必須要提及它并且圍繞著這個(gè)extends BaseAdapter我們要持續(xù)說(shuō)不少高級(jí)特性。
先來(lái)看一下課程最終要實(shí)現(xiàn)的目標(biāo)
有喵、有汪、有金錢(qián)。還多了表頭和表尾。
我們這次就要使用真正的面向業(yè)務(wù)邏輯、面向?qū)ο蟮氖址▉?lái)實(shí)現(xiàn)這個(gè)界面。
設(shè)計(jì)
上述界面其實(shí)和上一篇例子相仿,使用到了:1個(gè)ImageView、兩個(gè)TextView。
只不過(guò)這次我們用的是標(biāo)準(zhǔn)MVC模式的自定義Adapter。
項(xiàng)目結(jié)構(gòu)
是不是很詳盡?保姆式教程,不詳盡不稱為“保姆”。
先來(lái)看UI端代碼
UI端代碼
這一塊和上一篇幾乎相似,沒(méi)有什么太多變化
activity_main.xml文件
很簡(jiǎn)單,沒(méi)有任何神密可言,就一個(gè)“光桿”Listview的存在。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <ListView android:id="@+id/listView" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
customized_layout.xml文件
內(nèi)容也是very easy,屬于“常規(guī)損人和”,和上一篇無(wú)異。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <!-- 定義一個(gè)用于顯示頭像的ImageView --> <ImageView android:id="@+id/touxiang" android:layout_width="64dp" android:layout_height="64dp" android:baselineAlignBottom="true" android:paddingLeft="8dp" /> <!-- 定義一個(gè)豎直方向的LinearLayout,把QQ呢稱與說(shuō)說(shuō)的文本框設(shè)置出來(lái) --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:id="@+id/name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="8dp" android:textColor="#1D1D1C" android:textSize="20sp" /> <TextView android:id="@+id/description" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="8px" android:textColor="#B4B4B9" android:textSize="14sp" /> </LinearLayout> </LinearLayout>
后端代碼
PetBean.java
package org.mk.android.demo.democustomizedadapter; import java.io.Serializable; public class PetBean implements Serializable { private String name = ""; private int imgId; private String description = ""; public int getImgId() { return imgId; } public void setImgId(int imgId) { this.imgId = imgId; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public String getName() { return name; } public void setName(String name) { this.name = name; } public PetBean(int touxiang, String name, String description) { this.imgId = touxiang; this.name = name; this.description = description; } }
這個(gè)Java Bean里分別就對(duì)應(yīng)著一個(gè)ImageView,兩個(gè)TextView。
始終記得,把Image傳遞給到Adapter用的是一個(gè)int值,它來(lái)源于:R.drawable.圖片名稱(不帶.即postfix)。
PetAdapter.java
package org.mk.android.demo.democustomizedadapter; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import java.util.List; public class PetAdapter extends BaseAdapter { private List<PetBean> data; private Context ctx; public PetAdapter(List<PetBean> data, Context ctx) { this.data = data; this.ctx = ctx; } @Override public int getCount() { if (data != null) { return data.size(); } return 0; } @Override public Object getItem(int i) { return null; } @Override public long getItemId(int i) { return i; } @Override public View getView(int i, View view, ViewGroup viewGroup) { MyViewHolder viewHolder=null; if (view == null) { view = LayoutInflater.from(ctx).inflate(R.layout.customized_layout, viewGroup, false); viewHolder=new MyViewHolder(); viewHolder.touxiang = (ImageView) view.findViewById(R.id.touxiang); viewHolder.name = (TextView) view.findViewById(R.id.name); viewHolder.description = (TextView) view.findViewById(R.id.description); view.setTag(viewHolder); }else{ viewHolder=(MyViewHolder)view.getTag(); } if (data != null) { viewHolder.touxiang.setBackgroundResource(data.get(i).getImgId()); viewHolder.name.setText(data.get(i).getName()); viewHolder.description.setText(data.get(i).getDescription()); return view; } return null; } static class MyViewHolder { public ImageView touxiang; public TextView name; public TextView description; } }
代碼導(dǎo)讀
整個(gè)自定義的Adapter是extends自BaseAdapter,這個(gè)BaseAdapter在extends后有幾個(gè)方法需要進(jìn)行覆蓋:
1.構(gòu)造函數(shù),構(gòu)造函數(shù)里需要兩個(gè)參數(shù):
- 第一個(gè)參數(shù),構(gòu)造函數(shù)里把自定義的數(shù)據(jù)源在上一例里我們用的是List<Map<String,Object>>(不夠面向?qū)ο螅?,而這邊就是List<我們的ViewBean>傳進(jìn)去;
- Context,如果在MainActivity.java里,我們就可以用這樣的形式來(lái)傳這個(gè)參數(shù):Context ctx = MainActivity.this;
2.public int getCount() ,它返回的就是你的ListView里有多少行的這個(gè)size即我們?cè)跇?gòu)造方法里傳入的這個(gè)List<ViewBean>的size;
3.public Object getItem(int i),這個(gè)方法我們?cè)诤笠徊?,高?jí)定制化Adapter里會(huì)進(jìn)一步用到,目前在此我們直接return null就完事了,不用作糾結(jié);
4.public long getItemId(int i),這邊的int i其實(shí)是position,我們可以這么干:直接return i即可,它其實(shí)是一種“一行行從List<ViewBean>取出數(shù)據(jù)做渲染”用的;
5.public View getView(int i, View view, ViewGroup viewGroup) ,這個(gè)函數(shù)是核心,它的故事長(zhǎng)了,來(lái)看一步步導(dǎo)讀:
這個(gè)方法的作用就是一條條把List<ViewBean>數(shù)據(jù)取出來(lái)作渲染用的,它依賴于這一句話:LayoutInflater.from(ctx).inflate(R.layout.customized_layout,
viewGroup, false);這個(gè)語(yǔ)句被調(diào)用的次數(shù)=List.size(),每調(diào)用一次這條語(yǔ)句,Android界面會(huì)渲染一次(一次開(kāi)銷);
每一個(gè)ListView內(nèi)的行顯示的內(nèi)容根據(jù)List<ViewBean>里每一行不同的內(nèi)容會(huì)有不同的顯示,在這邊的一行指的就是:一個(gè)ImageView+兩個(gè)TextView的渲染。因此你要做的就是一個(gè)個(gè)“控制件名.set屬性(List里取出相應(yīng)的該行的這個(gè)數(shù)組的屬性件)”,因此才有了如此的寫(xiě)法:name.setText(data.get(i).getName());如:description.setText(data.get(i).getDescription());如:touxiang.setBackgroundResource(data.get(i).getImgId());這樣的東西。隨便說(shuō)一句:此處的i帶的正是getView里的(int i...)里的這個(gè)i,這個(gè)i對(duì)應(yīng)著你的List<ViewBean>里當(dāng)前的“游標(biāo)”;
全部一個(gè)個(gè)set完了后,把這個(gè)view return出去;
接著我們來(lái)說(shuō),這塊代碼看似沒(méi)邏輯那為什么會(huì)有:View Holder?這是一個(gè)什么鬼?前面我們提到了一句:
LayoutInflater.from(ctx).inflate(R.layout.customized_layout, viewGroup, false);這個(gè)語(yǔ)句被調(diào)用的次數(shù)=List.size(),每調(diào)用一次這條語(yǔ)句,Android界面會(huì)渲染一次,這個(gè)動(dòng)作其實(shí)是很開(kāi)銷資源的。比如說(shuō)我的List<ViewBean>里有100條數(shù)據(jù),Android會(huì)界面渲染100次。其實(shí)這個(gè)渲染只是一個(gè)“一次性”的事,在這邊只要渲染一次就夠了,其余99次是多余重復(fù)的。渲染太多會(huì)造成這個(gè)Android極其吃手機(jī)的“運(yùn)存”。所以我們使用了一個(gè)小技巧:只在這個(gè)View為空時(shí)做一次渲染。渲染過(guò)后就不要再渲染了,直接填充界面控件內(nèi)的屬性值就行了。因此才有了第一個(gè)if (view == null) {的判斷。
那么ViewHolder呢?還是沒(méi)有解釋ViewHolder的作用。我們前面解決了這個(gè)LayoutInflater.from(ctx).inflate的重復(fù)調(diào)用問(wèn)題,但是讀者們知道嗎?你在getView方法里的findViewById(R.id.description)這樣的東西也是會(huì)每次被重復(fù)調(diào)用一次的,舉例來(lái)說(shuō):你有3個(gè)控件,在List<ViewBean>里有3行數(shù)據(jù),你以為你只調(diào)用了3次findViewById?其實(shí)是調(diào)用了總計(jì)3*3=9次,即調(diào)用第二個(gè)控件的findViewById時(shí)它依舊會(huì)重復(fù)調(diào)用第一個(gè)控件的findViewById。這個(gè)動(dòng)作也是開(kāi)銷Android的運(yùn)存和cpu的。那么我們同樣為了減少findViewById的重復(fù)調(diào)用,因此我們使用一個(gè)MyViewHolder,讓其和我們的ViewBean(此處就是PetBean)一樣的結(jié)構(gòu),它專門(mén)是用于保存已經(jīng)被調(diào)用過(guò)findViewById的狀態(tài)(TAG)。然后使用view.setTag和view.getTag來(lái)做狀態(tài)保留。如果這個(gè)Tag存在那么不用再findViewById一次了。如果不存在再findViewById一次;
接著我們就來(lái)看交程序交互后端代碼MainActivity.java
MainActivity.java
package org.mk.android.demo.democustomizedadapter; import androidx.appcompat.app.AppCompatActivity; import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.widget.ListView; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity { private List<PetBean> data = null; private Context ctx; private PetAdapter adapter = null; private ListView listView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ctx = MainActivity.this; listView = (ListView) findViewById(R.id.listView); data = new ArrayList<PetBean>(); data.add(new PetBean(R.drawable.cat,"貓","這是一只貓")); data.add(new PetBean(R.drawable.dog,"狗","這是一只狗")); data.add(new PetBean(R.drawable.jingqianbao,"金錢(qián)豹","這是金錢(qián)豹")); adapter = new PetAdapter((List<PetBean>) data, ctx); final LayoutInflater inflater = LayoutInflater.from(this); View headView = inflater.inflate(R.layout.view_header, null, false); View footView = inflater.inflate(R.layout.view_footer, null, false); listView.addHeaderView(headView); listView.addFooterView(footView); listView.setAdapter(adapter); } }
我們這次為我們的ListView增加了一個(gè)表頭,一個(gè)表尾。表頭表尾分別對(duì)應(yīng)著兩個(gè)layout xml文件,它們位于我們項(xiàng)目的res\layout目錄下。
注:
記得在調(diào)用addHeaderView和addFootView的動(dòng)作必須位于setAdapter(adapter)語(yǔ)句前;
表頭樣式-view_header.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center"> <TextView android:layout_width="match_parent" android:layout_height="48dp" android:textSize="18sp" android:text="表頭" android:gravity="center" android:background="#43BBEB" android:textColor="#FFFFFF"/> </LinearLayout>
表尾樣式-view_footer.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center"> <TextView android:layout_width="match_parent" android:layout_height="48dp" android:textSize="18sp" android:text="表尾" android:gravity="center" android:background="#ECE9E6" android:textColor="#0C0C0C"/> </LinearLayout>
運(yùn)行效果
自己動(dòng)一下手試試就能找到自定義Adapter的感覺(jué)。自定義Adapter的作用很大、使用場(chǎng)景也很多。我們后面會(huì)繼續(xù)強(qiáng)化自定義Adapter的業(yè)務(wù)場(chǎng)景的使用。
到此這篇關(guān)于Android入門(mén)之實(shí)現(xiàn)自定義Adapter的文章就介紹到這了,更多相關(guān)Android自定義Adapter內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android 實(shí)現(xiàn)桌面未讀角標(biāo)
本文主要介紹了Android實(shí)現(xiàn)桌面未讀角標(biāo)的相關(guān)知識(shí)。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-04-04Android布局——Preference自定義layout的方法
PreferenceActivity是一個(gè)方便設(shè)置管理的界面,但是對(duì)于界面顯示來(lái)說(shuō)比較單調(diào),所以自定義布局就很有必要了,下面與大家分享下Preference中自定義layout的方法2013-06-06Android中利用SurfaceView制作抽獎(jiǎng)轉(zhuǎn)盤(pán)的全流程攻略
這篇文章主要介紹了Android中利用SurfaceView制作抽獎(jiǎng)轉(zhuǎn)盤(pán)的全流程,從圖案的繪制到轉(zhuǎn)盤(pán)的控制再到布局,真的非常全面,需要的朋友可以參考下2016-04-04Android ProgressBar 模擬進(jìn)度條效果的實(shí)現(xiàn)
這篇文章主要介紹了Android ProgressBar 模擬進(jìn)度條效果的實(shí)現(xiàn),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-04-04解決Fedora14下eclipse進(jìn)行android開(kāi)發(fā),ibus提示沒(méi)有輸入窗口的方法詳解
本篇文章是對(duì)Fedora14下eclipse進(jìn)行android開(kāi)發(fā),ibus提示沒(méi)有輸入窗口的解決方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05Android實(shí)現(xiàn)定制返回按鈕動(dòng)畫(huà)效果的方法
這篇文章主要介紹了Android實(shí)現(xiàn)定制返回按鈕動(dòng)畫(huà)效果的方法,涉及Android控件及動(dòng)畫(huà)的相關(guān)操作技巧,需要的朋友可以參考下2016-02-02Android實(shí)現(xiàn)3種側(cè)滑效果(仿qq側(cè)滑、抽屜側(cè)滑、普通側(cè)滑)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)多種側(cè)滑效果,包括仿qq側(cè)滑,抽屜側(cè)滑,普通側(cè)滑三種效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04