BaseAdapter是應用最多的一種適配了。它是一個抽象類,須要重寫方法完成自定義適配器的功能,這就比較自由靈活,能實現各類想要的效果。html
以前講到的SimpleAdapter和ArrayAdapter 就是它的子類。java
下面介紹如何使用BaseAdapter實現自定義適配器。android
使用BaseAdapter,只需繼承它並實現下面4個方法便可:緩存
public int getCount() //item的數目,即適配器要顯示的數據項個數
public Object getItem(int position) //獲取指定位置的數據項
public long getItemId(int position) //獲取指定位置的數據項id
public View getView(int position, View convertView, ViewGroup parent) //獲取每一項顯示內容-viewapp
下面經過簡單示例,詳細講解BaseAdapter的使用以及注意事項ide
此次示例使用GridView做爲顯示組件。佈局
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <GridView android:id="@+id/base_adapter_lv" android:layout_width="match_parent" android:layout_height="match_parent" /> </RelativeLayout>
<array name="anime_name"> <item>海賊王</item> <item>進擊的巨人</item> <item>火影忍者</item> <item>斬赤紅之瞳</item> <item>秦時明月</item> <item>西遊記</item> <item>葫蘆娃</item> </array> <array name="anime_author"> <item>尾田榮一郎</item> <item>諫山創</item> <item>岸本齊史</item> <item>タカヒロ</item> <item>玄機科技</item> <item>央視</item> <item>上海美術電影</item> </array>
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <ImageView android:id="@+id/anime_cover_img" android:layout_width="match_parent" android:layout_margin="1dp" android:layout_height="100dp" android:scaleType="fitXY"/> <LinearLayout android:layout_width="match_parent" android:layout_height="35dp" android:orientation="vertical" android:layout_below="@id/anime_cover_img"> <TextView android:id="@+id/anime_name_txt" android:layout_width="match_parent" android:layout_height="0dp" android:gravity="center" android:textStyle="bold" android:textSize="15sp" android:layout_weight="3"/> <TextView android:id="@+id/anime_author_txt" android:layout_width="match_parent" android:layout_height="0dp" android:gravity="center" android:textStyle="italic" android:layout_weight="2" android:textSize="10sp"/> <View android:layout_width="match_parent" android:layout_height="3dp" /> </LinearLayout> </RelativeLayout>
常識1:android:layout_weight分配的大小=(父控件大小-android:layout_width)*權重比例大小this
若是android:layout_width的大小已經達到或超過父控件的大小,則android:layout_weight是會失效的。spa
package com.flx.adaptertest.baseadapter; import android.app.Activity; import android.content.res.Resources; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.CheckBox; import android.widget.GridView; import android.widget.ListView; import android.widget.SimpleAdapter; import com.flx.adaptertest.R; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class BaseAdapterActivity extends Activity { private static final String TAG = "BaseAdapterActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate( savedInstanceState ); setContentView( R.layout.base_adapter_act ); GridView gridView = findViewById(R.id.base_adapter_lv); gridView.setNumColumns(2); gridView.setOnItemClickListener( new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Log.d( TAG, "onItemClick: position="+ position + ";text=" + parent.getAdapter().getItem(position).toString()); } } ); List<AnimeBean> animeBeans = new ArrayList<>(); Resources resources = this.getResources(); String[] anime_names = resources.getStringArray( R.array.anime_name ); String[] anime_authors = resources.getStringArray( R.array.anime_author ); int[] coverImgs = {R.drawable.hzw1, R.drawable.jjdjr1, R.drawable.hyrz1, R.drawable.zchzt1, R.drawable.qsmy1, R.drawable.xyj1, R.drawable.hlw1}; for (int i = 0; i < anime_names.length; i++) { animeBeans.add(new AnimeBean(anime_names[i], anime_authors[i], coverImgs[i])); } gridView.setAdapter( new AnimeAdapter(this, animeBeans) ); } }
這裏建立了兩個類,AnimeBean和AnimeAdapter。 3d
AnimeBean是一個java bean類,數據以Bean類組織,其中主要是Getter和Setter的方法。適配器顯示的每一項數據在一個AnimeBean對象中。
AnimeAdapter是繼承BaseAdapter的自定義的適配器類,實現了上述講到的4個方法。
package com.flx.adaptertest.baseadapter; import android.support.annotation.NonNull; public class AnimeBean { private String mAnimeName; private String mAnimeAuthor; private int mAnimeCoverImg; public AnimeBean(String animeName, String animeAuthor, int animeCoverImg) { this.mAnimeName = animeName; this.mAnimeAuthor = animeAuthor; this.mAnimeCoverImg = animeCoverImg; } public String getmAnimeName() { return this.mAnimeName; } public void setmAnimeName(String animeName) { this.mAnimeName = animeName; } public String getmAnimeAuthor() { return this.mAnimeAuthor; } public void setmAnimeAuthor(String animeAuthor) { this.mAnimeAuthor = animeAuthor; } public int getmAnimeCoverImg() { return this.mAnimeCoverImg; } public void setmAnimeCoverImg(int animeCoverImg) { this.mAnimeCoverImg = animeCoverImg; } @NonNull @Override public String toString() { return "mAnimeName:"+mAnimeName+";mAnimeAuthor:"+mAnimeAuthor+";mAnimeCoverImg:"+mAnimeCoverImg; } }
最後一個toString用戶打印Bean裏面數據的內容,在BaseAdapterActivity的適配器點擊項監聽處有調用。
package com.flx.adaptertest.baseadapter; import android.content.Context; import android.util.Log; 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 com.flx.adaptertest.R; import java.util.List; public class AnimeAdapter extends BaseAdapter { private static final String TAG = "AnimeAdapter"; private LayoutInflater mLayoutInflater;//佈局加載器對象 private List<AnimeBean> mAnimeBeans;//數據源 //構造方法,包含了數據源和上下文。將數據和適配器關聯起來了。 public AnimeAdapter(Context context, List<AnimeBean> animeBeans) { mLayoutInflater = LayoutInflater.from(context); mAnimeBeans = animeBeans; } //item的數目,即適配器要顯示的數據項個數 @Override public int getCount() { return mAnimeBeans != null ? mAnimeBeans.size() : 0; } //獲取指定位置的數據項 @Override public Object getItem(int position) { return mAnimeBeans != null ? mAnimeBeans.get(position) : null; } //獲取指定位置的數據項id @Override public long getItemId(int position) { return position; } //獲取每一項顯示內容-view /** *方法3: * 這種方法改進了方法1和方法2的弊端,避免了每次都建立新的View對象和經過findViewById查找組件 而形成的耗時 耗資源的問題。 * 建立View對象經過判空避免了:if (convertView == null) * findViewById()經過ViewHolder緩存下來了,經過setTag設置到View了,須要時能夠直接獲取到。 */ @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder viewHolder; //convertView是顯示項的視圖。爲null時即表示未被實例化過,GridView緩存池中沒有緩存 Log.d( TAG, "getView: position=" + position +";convertView="+convertView ); if (convertView == null) { convertView = mLayoutInflater.inflate( R.layout.base_adapter_grid_item, null ); //建立ViewHolder對象,並賦值 viewHolder = new ViewHolder(); viewHolder.animeNameTxt = convertView.findViewById(R.id.anime_name_txt); viewHolder.animeAuthorTxt = convertView.findViewById(R.id.anime_author_txt); viewHolder.animeCoverImg = convertView.findViewById(R.id.anime_cover_img); //經過setTag,設置與convertView關聯的標籤ViewHolder,將convertView與ViewHolder關聯 convertView.setTag(viewHolder); } else { //從緩存中返回convertView設置的標籤:ViewHolder viewHolder = (ViewHolder) convertView.getTag(); } AnimeBean animeBean = mAnimeBeans.get(position); viewHolder.animeNameTxt.setText(animeBean.getmAnimeName()); viewHolder.animeAuthorTxt.setText(animeBean.getmAnimeAuthor()); viewHolder.animeCoverImg.setImageResource(animeBean.getmAnimeCoverImg()); return convertView; } //緩存控件 private class ViewHolder { public TextView animeNameTxt; public TextView animeAuthorTxt; public ImageView animeCoverImg; } /** * 方法1: * 這種方法的弊端在於:很明顯,每次都會建立新的convertView對象,並經過findViewById查找對應組件。 * 當數據項很大且比較複雜時問題很明顯,耗時 耗資源 * 沒有使用GridView ListView的緩存機制。 */ /* @Override public View getView(int position, View convertView, ViewGroup parent) { convertView = mLayoutInflater.inflate( R.layout.base_adapter_grid_item, null ); TextView animeNameTxt = convertView.findViewById(R.id.anime_name_txt); TextView animeAuthorTxt = convertView.findViewById(R.id.anime_author_txt); ImageView animeCoverImg = convertView.findViewById(R.id.anime_cover_img); AnimeBean animeBean = mAnimeBeans.get(position); animeNameTxt.setText(animeBean.getmAnimeName()); animeAuthorTxt.setText(animeBean.getmAnimeAuthor()); animeCoverImg.setImageResource(animeBean.getmAnimeCoverImg()); return convertView; }*/ /** * 方法2: * 該方法時方法1的改進,使用了緩存機制。 * 這種方法的弊端在於:每次都會經過findViewById()去遍歷視圖樹,當佈局很複雜時就會很耗時 */ /*@Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = mLayoutInflater.inflate( R.layout.base_adapter_grid_item, null ); } TextView animeNameTxt = convertView.findViewById(R.id.anime_name_txt); TextView animeAuthorTxt = convertView.findViewById(R.id.anime_author_txt); ImageView animeCoverImg = convertView.findViewById(R.id.anime_cover_img); AnimeBean animeBean = mAnimeBeans.get(position); animeNameTxt.setText(animeBean.getmAnimeName()); animeAuthorTxt.setText(animeBean.getmAnimeAuthor()); animeCoverImg.setImageResource(animeBean.getmAnimeCoverImg()); return convertView; }*/ }
代碼中註釋作了詳細說明,須要注意幾點:
1.自定義的Adapter繼承了BaseAdapter,須要實現最初講到的4個方法。getCount()、getItem()、getItemId()都比較簡單,注意getView()的實現。
2.GridView、ListView等是由緩存機制的,當須要顯示的時候纔會顯示,不須要顯示的時候在緩存池中。當要顯示的信息過多,超過屏幕不少,滑出的信息和滑入的信息 都是從緩存池中獲取的,緩存池中的信息不會建立全部或立刻銷燬。(最後部分有將數據調整爲1000後 也能看出這一點)
3.上述代碼中,getView()列出了3種方法,方法一、方法2有各自明顯缺陷,方法3是一個比較好的方法很好的避免了每次都建立新的View對象和經過findViewById查找組件 而形成的耗時 耗資源的問題。具體請看上述代碼和註釋。
下面是效果圖
點擊其中一項,log以下:
2019-11-27 14:36:33.949 4655-4655/? D/BaseAdapterActivity: onItemClick: position=0;text=mAnimeName:海賊王;mAnimeAuthor:尾田榮一郎;mAnimeCoverImg:2131165272
要看下getView() 方法3的效果,能夠將數據調整爲1000組,能夠在BaseAdapterActivity中作以下修改
// for (int i = 0; i < anime_names.length; i++) { // animeBeans.add(new AnimeBean(anime_names[i], anime_authors[i], coverImgs[i])); // } for (int i = 0; i < 1000; i++) { int ii = i%anime_names.length; animeBeans.add(new AnimeBean(anime_names[ii], anime_authors[ii], coverImgs[ii])); }
後面大部分View是沒有建立的,向下滑動 纔會逐步建立,以下的log,
滑動逐步顯示後面的信息,後面信息都不須要每次建立View而是經過viewHolder = (ViewHolder) convertView.getTag()從緩存中獲取來的,而後填充數據便可。