中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

listview詳解

發布時間:2020-07-16 17:21:05 來源:網絡 閱讀:579 作者:zyl111ok 欄目:移動開發

首先,讓我們來了解下系統時怎么繪制listview的:

ListView繪制的過程如下:

首先,系統在繪制ListView之前,將會先調用getCount方法來獲取Item的個數。

之后每繪制一個Item就會調用一次getView方法,在此方法(getView)內就可以引用事先定義好的xml來確定顯示的效果并返回一個View對象作為一個Item顯示出來。也正是在這個過程中完成了適配器的主要轉換功能,把數據和資源以開發者想要的效果顯示出來。也正是getView的重復調用,使得ListView的使用更為簡單和靈活。這兩個方法是自定ListView顯示效果中最為重要的,同時只要重寫好了就兩個方法,ListView就能完全按開發者的要求顯示。而getItem和getItemId方法將會在調用ListView的響應方法的時候被調用到。所以要保證ListView的各個方法有效的話,這兩個方法也得重寫。比如:沒有完成getItemId方法的功能實現的話,當調用ListView的getItemIdAtPosition方法時將會得不到想要的結果,因為該方法就是調用了對應的適配器的getItemId方法。

 

Adapter :

Adapter是連接后端數據和前端顯示的適配器接口,是數據和UIView)之間一個重要的紐帶。在常見的View(ListView,GridView)等地方都需要用到Adapter。如下圖直觀的表達了DataAdapterView三者的關系:


 listview詳解

 

Adapter的類有好幾個,直接看圖:

listview詳解

 

Android ListView理解,BaseAdapter

ListView是Android開發過程中較為常見的組件之一,它將數據以列表的形式展現出來。一般而言,一個ListView由以下三個元素組成:

1.View,用于展示列表,通常是一個xml所指定的。大家都知道Android的界面基本上是由xml文件負責完成的,所以ListView的界面也理所應當的使用了xml定義。例如在ListView中經常用到的“android.R.layout.simple_list_item_1”等,就是Android系統內部定義好的一個xml文件。

2.適配器,用來將不同的數據映射到View上。不同的數據對應不同的適配器,如ArrayAdapter,CursorAdapter,SimpleAdapter等,他們能夠將數組,指針指向的數據,Map等數據映射到View上。也正是由于適配器的存在,使得ListView的使用相當靈活,經過適配器的處理后,在view看來所有的數據映射過來都是一樣的。

3.數據,具體的別映射的數據和資源,可以是字符串,圖片等,通過適配器,這些數據將會被現實到ListView上。所有的數據和資源要顯示到ListView上都通過適配器來完成。

系統已有的適配器可以將基本的數據顯示到ListView上,如:數組,Cursor指向的數據,Map里的數據。但是在實際開發中這些系統已實現的適配器,有時不能滿足我們的需求。而且系統自帶的含有多選功能ListView在實際使用過程中會有一些問題。要實現復雜的ListView可以通過繼承ListView并重寫相應的方法完成,同時也可以通過繼承BaseAdapter來實現。通過文檔可以看出,ArrayAdapter,CursorAdapter,SimpleAdapter都繼承于BaseAdapter。所以通過繼承BaseAdapter就可以完成自己的Adapter,可以將任何復雜組合的數據和資源,以任何你想要的顯示效果展示給大家。

繼承BaseAdapter之后,需要重寫以下四個方法getCount,getItem,getItemId,getView

getView中可以完成自己想要的界面布局

具體的實現方法如下

public ManagerAdapter(List<User> mRegulatorList, Context context, DataBase mDataBase) {

        this.mRegulatorList = mRegulatorList;

        this.mContext = context;

        this.mDataBase = mDataBase;

    }


    public int getCount() {


        return (this.mRegulatorList.size());

    }


    public Object getItem(int position) {

        return (this.mRegulatorList.get(position));

    }


    public long getItemId(int position) {

        return (position);

    }


    /**

     * 加載xml的條目,實現數據的初始化,為自己的控件設置監聽事件

     * @param position

     * @param contentview

     * @param arg2

     * @return

     */

    public View getView(int position, View contentview, ViewGroup arg2) {//加載XML視圖文件


        ViewHolder holder;

        this.regulator = this.mRegulatorList.get(position);

        //如果視圖之間沒有加載過,就加載xml

        if (contentview == null) {

            contentview = LayoutInflater.from(this.mContext).inflate(R.layout.manager_items, null);

            holder = new ViewHolder();

            this.bindID(contentview, holder);

        //保存視圖狀態

            contentview.setTag(holder);

        } else {

            holder = (ViewHolder) contentview.getTag();

            this.setItemInfo( holder, position, mDataBase);

            this.setClickListener(holder, position, mDataBase);

        }

        return contentview;

    }


    /**

     * 設置一個容器用于存放控件

     */

    public class ViewHolder {


        TextView txtManagerItemMarkName;

        TextView txtManagertmeLastTouchTime;

        TextView txtManagerItemLastLocation;

        TextView txtManagertgetDeviceName;

        ImageView imgManagerItemUserHead;

        Button btnManagerItemPhone;

        Button btnManagerItemPosition;


    }

/**

     * 綁定視圖ID,holder是組件容器

     *

     * @param contentview

     * @param holder

     */

    private void bindID(View contentview, ViewHolder holder) {

        holder.imgManagerItemUserHead = (ImageView) contentview.findViewById(R.id.manageractivity_imv1);

        holder.txtManagerItemLastLocation = (TextView) contentview.findViewById(R.id.manageractivity_txt2_location);

        holder.txtManagerItemMarkName = (TextView) contentview.findViewById(R.id.manageractivity_txt2_name);

        holder.txtManagertgetDeviceName = (TextView) contentview.findViewById(R.id.manageractivity_txt2_name);

        holder.txtManagertmeLastTouchTime = (TextView) contentview.findViewById(R.id.manageractivity_txt3_time);

        holder.btnManagerItemPhone = (Button) contentview.findViewById(R.id.manageractivity_btn_phone);

        holder.btnManagerItemPosition = (Button) contentview.findViewById(R.id.manageractivity_btn_position);

    }


ListView的工作原理如下:

                 ListView 針對每個item,要求 adapter “返回一個視圖” (getView),也就是說ListView在開始繪制的時候,系統首先調用getCount()函數,根據他的返回值得到ListView的長度,然后根據這個長度,調用getView()一行一行的繪制ListView的每一項。如果你的getCount()返回值是0的話,列表一行都不會顯示,如果返回1,就只顯示一行。返回幾則顯示幾行。如果我們有幾千幾萬甚至更多的item要顯示怎么辦?為每個Item創建一個新的View?不可能!!!實際上Android早已經緩存了這些視圖,大家可以看下下面這個截圖來理解下,這個圖是解釋ListView工作原理的最經典的圖了大家可以收藏下,不懂的時候拿來看看,加深理解,其實Android中有個叫做Recycler的構件,順帶列舉下與Recycler相關的已經由Google做過N多優化過的東東比如:AbsListView.RecyclerListener、ViewDebug.RecyclerTraceType等等,要了解的朋友自己查下,不難理解,下圖是ListView加載數據的工作原理(原理圖看不清楚的點擊后看大圖):

listview詳解


下面簡單說下上圖的原理:

  1. 如果你有幾千幾萬甚至更多的選項(item)時,其中只有可見的項目存在內存(內存內存哦,說的優化就是說在內存中的優化!!!)中,其他的在Recycler中

  2. ListView先請求一個type1視圖(getView)然后請求其他可見的項目。convertView在getView中是空(null)的

  3. 當item1滾出屏幕,并且一個新的項目從屏幕低端上來時,ListView再請求一個type1視圖。convertView此時不是空值了,它的值是item1。你只需設定新的數據然后返回convertView,不必重新創建一個視圖

  4. 示例代碼如下


private MyCustomAdapter mAdapter;

 

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        mAdapter = new MyCustomAdapter();

        for (int i = 0; i < 50; i++) {

            mAdapter.addItem("item " + i);

        }

        setListAdapter(mAdapter);

    }

 

    private class MyCustomAdapter extends BaseAdapter {

 

        private ArrayList mData = new ArrayList();

        private LayoutInflater mInflater;

 

        public MyCustomAdapter() {

            mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        }

 

        public void addItem(final String item) {

            mData.add(item);

            notifyDataSetChanged();

        }

 

        @Override

        public int getCount() {

            return mData.size();

        }

 

        @Override

        public String getItem(int position) {

            return mData.get(position);

        }

 

        @Override

        public long getItemId(int position) {

            return position;

        }

 

        @Override

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

            System.out.println("getView " + position + " " + convertView);

            ViewHolder holder = null;

            if (convertView == null) {

                convertView = mInflater.inflate(R.layout.item1, null);

                holder = new ViewHolder();

                holder.textView = (TextView)convertView.findViewById(R.id.text);

                convertView.setTag(holder);

            } else {

                holder = (ViewHolder)convertView.getTag();

            }

            holder.textView.setText(mData.get(position));

            return convertView;

        }

 

    }

 

    public static class ViewHolder {

        public TextView textView;

    }

}

執行程序,查看日志:


listview詳解

getView 被調用 9 次 ,convertView 對于所有的可見項目是空值(如下):

listview詳解

然后稍微向下滾動List,直到item10出現:

listview詳解

 

convertView仍然是空值,因為recycler中沒有視圖(item1的邊緣仍然可見,在頂端)再滾動列表,繼續滾動:

listview詳解

 

convertView不是空值了!item1離開屏幕到Recycler中去了,然后item11被創建,再滾動下:

listview詳解

           

此時的convertView非空了,在item11離開屏幕之后,它的視圖(…0f8)作為convertView容納item12了,好啦,結合以上原理,下面來看看今天最主要的話題,主角ListView的優化:

             首先,這個地方先記兩個ListView優化的一個小點:

                       1. ExpandableListView 與 ListActivity 由官方提供的,里面要使用到的ListView是已經經過優化的ListView,如果大家的需求可以用Google自帶的ListView滿足的的話盡量用官方的,絕對沒錯!

                       2.其次,像小馬前面講的,說ListView優化,其實并不是指其它的優化,就是內存是的優化,提到內存…(想到OOM,折騰了我不少時間),很多很多,先來寫下,如果我們的ListView中的選項僅僅是一些簡單的TextView的話,就好辦啦,消耗不了多少的,但如果你的Item是自定義的Item的話,例如你的自定義Item布局ViewGroup中包含:按鈕、圖片、flash、CheckBox、RadioButton等一系列你能想到的控件的話, 你要在getView中單單使用文章開頭提到的ViewHolder是遠遠不夠的,如果數據過多,加載的圖片過多過大,你BitmapFactory.decode的猛多的話,OOM搞死你,這個地方再警告下大家,是警告……….也提醒下自己:

                         小馬碰到的問題大家應該也都碰到過的,自定義的ListView項亂序問題,我很天真的在getView()中強制清除了下ListView的緩存數據convertView,也就是convertView = null了,雖然當時是解決了這個問題讓其它每次重繪,但是犯了大錯了,如果數據太多的話,出現最最惡心的錯,手機卡死或強制關機,關機啊哥哥們……O_O,客戶殺了我都有可能,但大家以后別犯這樣的錯了,單單使用清除緩存convertView是解決不了實際問題的,繼續……
下面是小記:圖片用完了正確的釋放… 

下面來列舉下真正意義上的優化吧:

  1.  ViewHolder   Tag 必不可少,這個不多說!

  2. 如果自定義Item中有涉及到圖片等等的,一定要狠狠的處理圖片,圖片占的內存是ListView項中最惡心的,處理圖片的方法大致有以下幾種:
    2.1:不要直接拿個路徑就去循環decodeFile();這是找死….用Option保存圖片大小、不要加載圖片到內存去;
    2.2:  拿到的圖片一定要經過邊界壓縮
    2.3:在ListView中取圖片時也不要直接拿個路徑去取圖片,而是以WeakReference(使用WeakReference代替強引用。比如可以使        用WeakReference<Context> mContextRef)、SoftReference、WeakHashMap等的來存儲圖片信息,是圖片信息不是圖片哦!
    2.4:在getView中做圖片轉換時,產生的中間變量一定及時釋放,用以下形式:

  3. 盡量避免在BaseAdapter中使用static 來定義全局靜態變量,我以為這個沒影響 ,這個影響很大,static是Java中的一個關鍵字,當用它來修飾成員變量時,那么該變量就屬于該類,而不是該類的實例。所以用static修飾的變量,它的生命周期是很長的,如果用它來引用一些資源耗費過多的實例(比如Context的情況最多),這時就要盡量避免使用了..

  4. 如果為了滿足需求下必須使用Context的話:Context盡量使用Application Context,因為Application的Context的生命周期比較長,引用它不會出現內存泄露的問題

  5. 盡量避免在ListView適配器中使用線程,因為線程產生內存泄露的主要原因在于線程生命周期的不可控制


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

金湖县| 玉溪市| 东阳市| 神农架林区| 牟定县| 石林| 太湖县| 六盘水市| 修武县| 天峻县| 彭泽县| 鲜城| 黄龙县| 项城市| 嘉荫县| 林周县| 平南县| 木兰县| 深州市| 江油市| 广水市| 金溪县| 都昌县| 洞头县| 十堰市| 会理县| 西林县| 兴文县| 阿尔山市| 游戏| 红原县| 上犹县| 昆明市| 承德市| 贵阳市| 怀安县| 钟祥市| 米林县| 宁夏| 榕江县| 静乐县|