輕松理解Android Adapter

先說(shuō)幾句廢話

其實(shí)這個(gè)概念實(shí)在太抽象了,大家可能會(huì)知道他是連接View和數(shù)據(jù)的橋梁,但是具體怎么去理解這個(gè)東西呢。通過(guò)看這篇文章我相信,每個(gè)人對(duì)適配器都會(huì)有個(gè)深入的理解,這個(gè)是我自己,跟市面上能找到的資料完全不一樣。相信你會(huì)喜歡的,如果喜歡就關(guān)注我吧。

基礎(chǔ)知識(shí)

學(xué)這篇文章之前,希望對(duì)ListView有個(gè)簡(jiǎn)單了解,最少能使用一個(gè)簡(jiǎn)單的寫(xiě)一個(gè)列表看看效果嘛。如果不會(huì)看看這篇文章。
Android UI入門(mén)(第二章:ListView控件的使用)
文章之前用系統(tǒng)的功能實(shí)現(xiàn)了一個(gè)ListView并且顯示到了手機(jī)上。這個(gè)過(guò)程中用到了適配器Adapter。那么為什么使用適配器呢?適配器又是一個(gè)什么東西,如何更深入的理解適配器?這個(gè)就是我今天要帶大家學(xué)習(xí)的。
之前我們用系統(tǒng)的封裝的ListView來(lái)實(shí)現(xiàn)顯示一個(gè)列表。
下面我要帶大家不用系統(tǒng)的內(nèi)容實(shí)現(xiàn)一個(gè)列表。通過(guò)實(shí)現(xiàn)列表的過(guò)程去理解什么事Adapter。類(lèi)似于我們自己實(shí)現(xiàn)一個(gè)Adapter。是不是感覺(jué)挺高大上的?別誤會(huì),我們只是通過(guò)簡(jiǎn)易的代碼去模擬一下Adapter而已。

用已知的知識(shí)寫(xiě)一個(gè)ListView 的效果

首先我們來(lái)寫(xiě)一個(gè)布局,大家都知道ListView的布局是線性的,那么我們要實(shí)現(xiàn)的也是一個(gè)線性布局,這里使用了LinearLayout。
布局代碼是這樣的

<LinearLayout
        android:id="@+id/ll_group_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

</LinearLayout>

只是一個(gè)簡(jiǎn)單的LinearLayout添加了一個(gè)id沒(méi)有其他任何東西。我們代碼里引用這個(gè)LinearLayout,然后往里面添加布局文件。這里使用循環(huán)去添加。代碼大概是這樣的

private void addViews() {
    LayoutInflater inflater = LayoutInflater.from(this);
    for (int i = 0; i < 20; i++) {
        View convertView = inflater.inflate(R.layout.item_user, null);
        ll_group_view.addView(convertView);
    }
}

這里插一句LayoutInflater這個(gè)類(lèi)我沒(méi)講,這個(gè)類(lèi)可以讓我們根據(jù)布局文件創(chuàng)建一個(gè)View。創(chuàng)建的View的內(nèi)容跟布局文件是一樣的
這個(gè)item_user就是我昨天寫(xiě)listview時(shí)候使用的,直接拿來(lái)用了,源碼在前面介紹的文章里面有。這個(gè)item_user可以替換成你自己的任何item的視圖。
現(xiàn)在就來(lái)運(yùn)行下看看效果吧
Activity代碼其實(shí)非常簡(jiǎn)單完整代碼大約只有這么寫(xiě)

public class ScollViewActivity extends AppCompatActivity {

    LinearLayout ll_group_view;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scoll_list);
        ll_group_view = (LinearLayout) findViewById(R.id.ll_group_view);
        addViews();
    }

    private void addViews() {
        LayoutInflater inflater = LayoutInflater.from(this);
        for (int i = 0; i < 20; i++) {
            View convertView = inflater.inflate(R.layout.item_user, null);
            ll_group_view.addView(convertView);
        }
    }
}

運(yùn)行起來(lái)的效果,跟我們使用ListView的效果區(qū)別不大,看上去有兩個(gè)大問(wèn)題,一個(gè)是下劃線,還有一個(gè)就是不能滑動(dòng)。下劃線這個(gè)東西不是重點(diǎn),這里先不多介紹了,至于滑動(dòng)確實(shí)是必須解決的問(wèn)題。如何讓我的View可以滑動(dòng)呢,其實(shí)也很簡(jiǎn)單,在LinearLayout 外層嵌套一個(gè)ScrollView就行了。ScrollView是Android封裝好的一個(gè)視圖類(lèi)。修改下布局文件重新運(yùn)行就ok了
新的布局文件是這樣的

<?xml version="1.0" encoding="utf-8"?>
<ScrollView 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">

    <LinearLayout
        android:id="@+id/ll_group_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

    </LinearLayout>
</ScrollView>

運(yùn)行起來(lái)看看效果把,是不是跟ListView除了下劃線都一模一樣呢?還真是,還能滑動(dòng)。這個(gè)過(guò)程我沒(méi)有把List加入到項(xiàng)目里,現(xiàn)在我去把List加進(jìn)去。直接拿昨天的代碼加上。然后布局里根據(jù)用戶(hù)信息去配置。來(lái)個(gè)源碼
完整的Activity源碼就是這個(gè)樣子。

public class ScollViewActivity extends AppCompatActivity {

    LinearLayout ll_group_view;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scoll_list);
        ll_group_view = (LinearLayout) findViewById(R.id.ll_group_view);

        List<UserBean> userList = new ArrayList<UserBean>();
        for (int i = 0; i < 10; i++) {
            UserBean user = new UserBean();
            user.setName("name:" + i);
            userList.add(user);
        }

        addViews(userList);
    }

    private void addViews(List<UserBean> userList) {
        LayoutInflater inflater = LayoutInflater.from(this);
        for (int i = 0; i < userList.size(); i++) {
            View convertView = inflater.inflate(R.layout.item_user, null);
            String userName = userList.get(i).getName();
            TextView tv_name = (TextView) convertView.findViewById(R.id.tv_name);
            tv_name.setText(userName);
            ll_group_view.addView(convertView);
        }
    }
}

好了看看我們運(yùn)行后的效果吧


效果圖

這個(gè)是可以上下滑動(dòng)的。基本跟我們之前的ListView 一模一樣。

通過(guò)上面我們自己寫(xiě)的程序來(lái)對(duì)比ListView。既然我們能這樣寫(xiě)一個(gè)列表為什么還要去使用ListView 這個(gè)控件呢?
第一個(gè)重要的原因就是,效率。即使現(xiàn)在手機(jī)速度很快了,如果無(wú)限制的addView 的話也會(huì)造成手機(jī)卡頓,內(nèi)存溢出,程序崩潰的。如果這個(gè)頁(yè)面有十萬(wàn)條數(shù)據(jù),我們的方式可是創(chuàng)建了10w個(gè)View的。而ListView的實(shí)現(xiàn)就巧妙的多了。他的實(shí)現(xiàn)是只創(chuàng)建屏幕顯示的view。如果我們屏幕能顯示8條數(shù)據(jù),當(dāng)我們往上滑動(dòng)的時(shí)候,第一條出現(xiàn)在屏幕外面的View它會(huì)拿來(lái)復(fù)用,添加到屏幕下面,往下滑動(dòng)的時(shí)候,會(huì)把最下面出屏幕的View添加到屏幕最上面,這樣依次類(lèi)推,無(wú)限的循環(huán)使用View。整個(gè)過(guò)程ListView只創(chuàng)建了8個(gè)item的視圖。而我們的呢?就特別多了。
第二個(gè)作用就是封裝。封裝好了之后我們不管是復(fù)雜的布局還是簡(jiǎn)單的布局都可以根據(jù)一個(gè)統(tǒng)一的模式去使用,這個(gè)模式就是Adapter。封裝好了之后Adapter其實(shí)可以用在任何ListView 之上的,還可以復(fù)用,還能實(shí)現(xiàn)復(fù)雜的布局,而我們的寫(xiě)法,卻達(dá)不到這些需求的。
那么我們繼續(xù)分析一下Adapter吧。
其實(shí)Adapter最重要的方法就是這個(gè)getView方法了。

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.item_user, null);
        }
        String userName = userList.get(position).getName();
        TextView tv_name = (TextView) convertView.findViewById(R.id.tv_name);
        tv_name.setText(userName);
        return convertView;
    }

這個(gè)是我昨天寫(xiě)的,ListView使用的,有沒(méi)有發(fā)現(xiàn)跟今天寫(xiě)的for循環(huán)里面的幾乎一模一樣。其實(shí)這個(gè)方法就是關(guān)聯(lián)item的視圖和數(shù)據(jù)的方法。這里都是通過(guò)LayoutInflater去創(chuàng)建布局的,知識(shí)Adapter里面可以復(fù)用而已。
下面的賦值跟使用普通的View沒(méi)什么區(qū)別,最后返回這個(gè)View就可以了。
那么為什么Adapter里還有那么多方法呢比如下面這幾個(gè)


Adapter.png

這幾個(gè)方法呢, 我們可以讓這個(gè)ListView更加強(qiáng)大,比如我們的List其實(shí)只有5條數(shù)據(jù),有時(shí)候我們希望默認(rèn)第一條數(shù)據(jù)是個(gè)固定的內(nèi)容,不在list里面。這個(gè)實(shí)現(xiàn)比一般的ListView復(fù)雜一些,但是通過(guò)Adapter還是能很容易實(shí)現(xiàn)。如果實(shí)現(xiàn)了大家會(huì)發(fā)現(xiàn)這個(gè)ListView顯示的數(shù)量其實(shí)比List的內(nèi)容多了1,而不是List的長(zhǎng)度。那么我們就應(yīng)該有個(gè)方法去告訴ListView,我們的真實(shí)長(zhǎng)度是多少,這些都封裝到Adapter里面了就是我們看到的getCount方法,他返回的就是ListView顯示的真實(shí)的長(zhǎng)度,我們微信調(diào)用上傳圖片功能的時(shí)候,會(huì)顯示一個(gè)圖片列表,第一個(gè)圖是個(gè)相機(jī)圖案,這個(gè)功能就可以這么去實(shí)現(xiàn),知識(shí)那個(gè)是GridView,GridView和ListView的Adapter其實(shí)是一樣的,可以通用的。另外兩個(gè)方法一個(gè)是根據(jù)索引去獲取我們的數(shù)據(jù),一個(gè)是獲取id。一般情況下我們這個(gè)id位了封裝都直接返回position。
講到這里,大家是不是發(fā)現(xiàn)其實(shí)ListView和Adapter其實(shí)也沒(méi)那么復(fù)雜呢。
在開(kāi)發(fā)過(guò)程中我們一般都會(huì)自己封裝一個(gè)Adapter。把這些方法封裝進(jìn)去,我們使用的時(shí)候是繼承我們自己封裝的Adapter的,一般情況下只去實(shí)現(xiàn)getView方法。就夠了,這里給出一個(gè)最初級(jí)的封裝。大家可以直接拿去使用的。

封裝初級(jí)的Adapter

大家有沒(méi)有發(fā)現(xiàn),每次寫(xiě)Adapter的時(shí)候都要寫(xiě)好多方法,基本都是一樣的,看著都好煩,那么這個(gè)我們就可以把那些同質(zhì)化的東西封裝起來(lái)實(shí)現(xiàn)一個(gè)先的BaseAdapter,然后用戶(hù)只需要實(shí)現(xiàn)getView方法就可以了。
直接給出源碼看看

public abstract class ICBaseAdapter<T> extends BaseAdapter {
    protected Context mContext;
    protected List<T> mList ;
    public ICBaseAdapter(Context context, List<T> list) {
        this.mContext = context;
        this.mList = list;
    }

    @Override
    public int getCount() {
        return mList.size();
    }

    @Override
    public Object getItem(int position) {
        return mList.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public abstract View getView(int position, View convertView, ViewGroup parent) ;
}

這里只是把之前的UserBean用泛型替換掉了,以后使用的時(shí)候傳一個(gè)泛型的列表就行了,我們默認(rèn)實(shí)現(xiàn)那幾個(gè)煩人的方法,只留下getView給子類(lèi)實(shí)現(xiàn)。
看看怎么用吧
我新建了一個(gè)Adapter起名NewUserListAdapter ,然后我通過(guò)繼承ICBaseAdapter來(lái)實(shí)現(xiàn),這個(gè)類(lèi)需要一個(gè)泛型屬性,就是我們list的泛型而已。然后只需要實(shí)現(xiàn)他的getView方法就好了。是不是很方便呢

 
public class NewUserListAdapter extends ICBaseAdapter<UserBean> {

    public NewUserListAdapter(Context context, List<UserBean> list) {
        super(context, list);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = LayoutInflater.from(mContext).inflate(R.layout.item_user, null);
        }
        String userName = mList.get(position).getName();
        TextView tv_name = (TextView) convertView.findViewById(R.id.tv_name);
        tv_name.setText(userName);
        return convertView;
    }
}

看著是不是比之前的清爽了許多。使用起來(lái)已經(jīng)方便不少了,不過(guò)以后還有更好的封裝和優(yōu)化的。如果覺(jué)得不錯(cuò),關(guān)注我吧

聯(lián)系方式
QQ群 121915371 (建議加群咨詢(xún))
QQ號(hào) 1400100300 (個(gè)人QQ)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容