Android入門之實(shí)現(xiàn)自定義Adapter
介紹
在上一篇“SimpleAdapter“章節(jié)中,我們看到了把:ListView和Listview內(nèi)部詳細(xì)頁面進(jìn)行分離的Adapter的設(shè)計手法。
可是,這個SimpleAdapter的構(gòu)造函數(shù)不夠錄活、苦澀難懂。很難滿足我們實(shí)際大多生產(chǎn)場景的開發(fā)。
因此,今天我們就要來看一個更人性化的“自定義BaseAdapter“。實(shí)際生產(chǎn)應(yīng)用場景開發(fā)中充斥著自定義BaseAdapter,因此必須要提及它并且圍繞著這個extends BaseAdapter我們要持續(xù)說不少高級特性。
先來看一下課程最終要實(shí)現(xiàn)的目標(biāo)

有喵、有汪、有金錢。還多了表頭和表尾。
我們這次就要使用真正的面向業(yè)務(wù)邏輯、面向?qū)ο蟮氖址▉韺?shí)現(xiàn)這個界面。
設(shè)計
上述界面其實(shí)和上一篇例子相仿,使用到了:1個ImageView、兩個TextView。
只不過這次我們用的是標(biāo)準(zhǔn)MVC模式的自定義Adapter。
項(xiàng)目結(jié)構(gòu)

是不是很詳盡?保姆式教程,不詳盡不稱為“保姆”。
先來看UI端代碼
UI端代碼
這一塊和上一篇幾乎相似,沒有什么太多變化
activity_main.xml文件
很簡單,沒有任何神密可言,就一個“光桿”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ī)損人和”,和上一篇無異。
<?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">
<!-- 定義一個用于顯示頭像的ImageView -->
<ImageView
android:id="@+id/touxiang"
android:layout_width="64dp"
android:layout_height="64dp"
android:baselineAlignBottom="true"
android:paddingLeft="8dp" />
<!-- 定義一個豎直方向的LinearLayout,把QQ呢稱與說說的文本框設(shè)置出來 -->
<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;
}
}這個Java Bean里分別就對應(yīng)著一個ImageView,兩個TextView。
始終記得,把Image傳遞給到Adapter用的是一個int值,它來源于: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)讀
整個自定義的Adapter是extends自BaseAdapter,這個BaseAdapter在extends后有幾個方法需要進(jìn)行覆蓋:
1.構(gòu)造函數(shù),構(gòu)造函數(shù)里需要兩個參數(shù):
- 第一個參數(shù),構(gòu)造函數(shù)里把自定義的數(shù)據(jù)源在上一例里我們用的是List<Map<String,Object>>(不夠面向?qū)ο螅?,而這邊就是List<我們的ViewBean>傳進(jìn)去;
- Context,如果在MainActivity.java里,我們就可以用這樣的形式來傳這個參數(shù):Context ctx = MainActivity.this;
2.public int getCount() ,它返回的就是你的ListView里有多少行的這個size即我們在構(gòu)造方法里傳入的這個List<ViewBean>的size;
3.public Object getItem(int i),這個方法我們在后一步,高級定制化Adapter里會進(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) ,這個函數(shù)是核心,它的故事長了,來看一步步導(dǎo)讀:
這個方法的作用就是一條條把List<ViewBean>數(shù)據(jù)取出來作渲染用的,它依賴于這一句話:LayoutInflater.from(ctx).inflate(R.layout.customized_layout,
viewGroup, false);這個語句被調(diào)用的次數(shù)=List.size(),每調(diào)用一次這條語句,Android界面會渲染一次(一次開銷);
每一個ListView內(nèi)的行顯示的內(nèi)容根據(jù)List<ViewBean>里每一行不同的內(nèi)容會有不同的顯示,在這邊的一行指的就是:一個ImageView+兩個TextView的渲染。因此你要做的就是一個個“控制件名.set屬性(List里取出相應(yīng)的該行的這個數(shù)組的屬性件)”,因此才有了如此的寫法:name.setText(data.get(i).getName());如:description.setText(data.get(i).getDescription());如:touxiang.setBackgroundResource(data.get(i).getImgId());這樣的東西。隨便說一句:此處的i帶的正是getView里的(int i...)里的這個i,這個i對應(yīng)著你的List<ViewBean>里當(dāng)前的“游標(biāo)”;
全部一個個set完了后,把這個view return出去;
接著我們來說,這塊代碼看似沒邏輯那為什么會有:View Holder?這是一個什么鬼?前面我們提到了一句:
LayoutInflater.from(ctx).inflate(R.layout.customized_layout, viewGroup, false);這個語句被調(diào)用的次數(shù)=List.size(),每調(diào)用一次這條語句,Android界面會渲染一次,這個動作其實(shí)是很開銷資源的。比如說我的List<ViewBean>里有100條數(shù)據(jù),Android會界面渲染100次。其實(shí)這個渲染只是一個“一次性”的事,在這邊只要渲染一次就夠了,其余99次是多余重復(fù)的。渲染太多會造成這個Android極其吃手機(jī)的“運(yùn)存”。所以我們使用了一個小技巧:只在這個View為空時做一次渲染。渲染過后就不要再渲染了,直接填充界面控件內(nèi)的屬性值就行了。因此才有了第一個if (view == null) {的判斷。
那么ViewHolder呢?還是沒有解釋ViewHolder的作用。我們前面解決了這個LayoutInflater.from(ctx).inflate的重復(fù)調(diào)用問題,但是讀者們知道嗎?你在getView方法里的findViewById(R.id.description)這樣的東西也是會每次被重復(fù)調(diào)用一次的,舉例來說:你有3個控件,在List<ViewBean>里有3行數(shù)據(jù),你以為你只調(diào)用了3次findViewById?其實(shí)是調(diào)用了總計3*3=9次,即調(diào)用第二個控件的findViewById時它依舊會重復(fù)調(diào)用第一個控件的findViewById。這個動作也是開銷Android的運(yùn)存和cpu的。那么我們同樣為了減少findViewById的重復(fù)調(diào)用,因此我們使用一個MyViewHolder,讓其和我們的ViewBean(此處就是PetBean)一樣的結(jié)構(gòu),它專門是用于保存已經(jīng)被調(diào)用過findViewById的狀態(tài)(TAG)。然后使用view.setTag和view.getTag來做狀態(tài)保留。如果這個Tag存在那么不用再findViewById一次了。如果不存在再findViewById一次;
接著我們就來看交程序交互后端代碼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,"金錢豹","這是金錢豹"));
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增加了一個表頭,一個表尾。表頭表尾分別對應(yīng)著兩個layout xml文件,它們位于我們項(xiàng)目的res\layout目錄下。
注:
記得在調(diào)用addHeaderView和addFootView的動作必須位于setAdapter(adapter)語句前;
表頭樣式-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)行效果

自己動一下手試試就能找到自定義Adapter的感覺。自定義Adapter的作用很大、使用場景也很多。我們后面會繼續(xù)強(qiáng)化自定義Adapter的業(yè)務(wù)場景的使用。
到此這篇關(guān)于Android入門之實(shí)現(xiàn)自定義Adapter的文章就介紹到這了,更多相關(guān)Android自定義Adapter內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android的TextView與Html相結(jié)合的具體方法
Android的TextView與Html相結(jié)合的具體方法,需要的朋友可以參考一下2013-06-06
Android開發(fā)實(shí)現(xiàn)文件存儲功能
這篇文章主要為大家詳細(xì)介紹了Android開發(fā)實(shí)現(xiàn)文件存儲功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2020-07-07
eclipse導(dǎo)入appcompat項(xiàng)目報錯解決辦法
這篇文章主要介紹了eclipse導(dǎo)入appcompat項(xiàng)目報錯解決辦法的相關(guān)資料,需要的朋友可以參考下2017-04-04
Apache?Cordova?Android原理應(yīng)用實(shí)例詳解
這篇文章主要為大家介紹了Apache?Cordova?Android原理應(yīng)用實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11
Android ListView與ScrollView沖突的解決方法總結(jié)
這篇文章主要介紹了Android ListView與ScrollView沖突的解決方法總結(jié)的相關(guān)資料,需要的朋友可以參考下2017-04-04
Android ProgressDialog用法之實(shí)現(xiàn)app上傳文件進(jìn)度條轉(zhuǎn)圈效果
這篇文章主要介紹了Android ProgressDialog用法之實(shí)現(xiàn)app上傳文件進(jìn)度條轉(zhuǎn)圈效果,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-03-03
Android 進(jìn)階實(shí)現(xiàn)性能優(yōu)化之OOM與Leakcanary詳解原理
LeakCanary 是大名鼎鼎的 square 公司開源的內(nèi)存泄漏檢測工具。目前上大部分App在開發(fā)測試階段都會接入此工具用于檢測潛在的內(nèi)存泄漏問題,做的好一點(diǎn)的可能會搭建一個服務(wù)器用于保存各個設(shè)備上的內(nèi)存泄漏問題再集中處理2021-11-11
Android中ListView + CheckBox實(shí)現(xiàn)單選、多選效果
這篇文章主要介紹了Android中ListView + CheckBox實(shí)現(xiàn)單選、多選效果,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2017-02-02
Android 使用AsyncTask實(shí)現(xiàn)多線程斷點(diǎn)續(xù)傳
本文將詳細(xì)講解如何使用AsyncTask來實(shí)現(xiàn)多線程的斷點(diǎn)續(xù)傳下載功能,感興趣的朋友跟隨腳本之家小編一起學(xué)習(xí)吧2018-05-05

