1. 程式人生 > >Android ListView itemType使用Holder原理簡單分析

Android ListView itemType使用Holder原理簡單分析

之前寫過一篇關於Holder複用原理的文章《Android ListView使用Holder優化原理》,此篇著重分析RecycleBin資料結構

原始碼在AbsListView中,RecycleBin一共有兩個儲存結構分別是ActiveViews 和 ScrapViews

ActiveViews儲存當前在介面(手機顯示區域)中顯示View,移出介面會存入ScrapViews ScrapViews儲存當前已經滑動出當前介面(手機顯示區域)顯示的View,這些view儲存起來相當於回收,當再次請求的時候從此儲存中取出反覆使用
    /**
     * The RecycleBin facilitates reuse of views across layouts. The RecycleBin has two levels of
     * storage: ActiveViews and ScrapViews. ActiveViews are those views which were onscreen at the
     * start of a layout. By construction, they are displaying current information. At the end of
     * layout, all views in ActiveViews are demoted to ScrapViews. ScrapViews are old views that
     * could potentially be used by the adapter to avoid allocating views unnecessarily.
     *
     * @see android.widget.AbsListView#setRecyclerListener(android.widget.AbsListView.RecyclerListener)
     * @see android.widget.AbsListView.RecyclerListener
     */
    class RecycleBin {
        private RecyclerListener mRecyclerListener;

        /**
         * The position of the first view stored in mActiveViews.
         */
        private int mFirstActivePosition;

        /**
         * Views that were on screen at the start of layout. This array is populated at the start of
         * layout, and at the end of layout all view in mActiveViews are moved to mScrapViews.
         * Views in mActiveViews represent a contiguous range of Views, with position of the first
         * view store in mFirstActivePosition.
         */
        private View[] mActiveViews = new View[0];

        /**
         * Unsorted views that can be used by the adapter as a convert view.
         */
        private ArrayList<View>[] mScrapViews;

        private int mViewTypeCount;

        private ArrayList<View> mCurrentScrap;

        private ArrayList<View> mSkippedScrap;

        private SparseArray<View> mTransientStateViews;

        public void setViewTypeCount(int viewTypeCount) {
            if (viewTypeCount < 1) {
                throw new IllegalArgumentException("Can't have a viewTypeCount < 1");
            }
            //noinspection unchecked
            ArrayList<View>[] scrapViews = new ArrayList[viewTypeCount];
            for (int i = 0; i < viewTypeCount; i++) {
                scrapViews[i] = new ArrayList<View>();
            }
            mViewTypeCount = viewTypeCount;
            mCurrentScrap = scrapViews[0];
            mScrapViews = scrapViews;
        }

        public void markChildrenDirty() {
            if (mViewTypeCount == 1) {
                final ArrayList<View> scrap = mCurrentScrap;
                final int scrapCount = scrap.size();
                for (int i = 0; i < scrapCount; i++) {
                    scrap.get(i).forceLayout();
                }
            } else {
                final int typeCount = mViewTypeCount;
                for (int i = 0; i < typeCount; i++) {
                    final ArrayList<View> scrap = mScrapViews[i];
                    final int scrapCount = scrap.size();
                    for (int j = 0; j < scrapCount; j++) {
                        scrap.get(j).forceLayout();
                    }
                }
            }
            if (mTransientStateViews != null) {
                final int count = mTransientStateViews.size();
                for (int i = 0; i < count; i++) {
                    mTransientStateViews.valueAt(i).forceLayout();
                }
            }
        }

        public boolean shouldRecycleViewType(int viewType) {
            return viewType >= 0;
        }

        /**
         * Clears the scrap heap.
         */
        void clear() {
            if (mViewTypeCount == 1) {
                final ArrayList<View> scrap = mCurrentScrap;
                final int scrapCount = scrap.size();
                for (int i = 0; i < scrapCount; i++) {
                    removeDetachedView(scrap.remove(scrapCount - 1 - i), false);
                }
            } else {
                final int typeCount = mViewTypeCount;
                for (int i = 0; i < typeCount; i++) {
                    final ArrayList<View> scrap = mScrapViews[i];
                    final int scrapCount = scrap.size();
                    for (int j = 0; j < scrapCount; j++) {
                        removeDetachedView(scrap.remove(scrapCount - 1 - j), false);
                    }
                }
            }
            if (mTransientStateViews != null) {
                mTransientStateViews.clear();
            }
        }

        /**
         * Fill ActiveViews with all of the children of the AbsListView.
         *
         * @param childCount The minimum number of views mActiveViews should hold
         * @param firstActivePosition The position of the first view that will be stored in
         *        mActiveViews
         */
        void fillActiveViews(int childCount, int firstActivePosition) {
            if (mActiveViews.length < childCount) {
                mActiveViews = new View[childCount];
            }
            mFirstActivePosition = firstActivePosition;

            final View[] activeViews = mActiveViews;
            for (int i = 0; i < childCount; i++) {
                View child = getChildAt(i);
                AbsListView.LayoutParams lp = (AbsListView.LayoutParams) child.getLayoutParams();
                // Don't put header or footer views into the scrap heap
                if (lp != null && lp.viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
                    // Note:  We do place AdapterView.ITEM_VIEW_TYPE_IGNORE in active views.
                    //        However, we will NOT place them into scrap views.
                    activeViews[i] = child;
                }
            }
        }

        /**
         * Get the view corresponding to the specified position. The view will be removed from
         * mActiveViews if it is found.
         *
         * @param position The position to look up in mActiveViews
         * @return The view if it is found, null otherwise
         */
        View getActiveView(int position) {
            int index = position - mFirstActivePosition;
            final View[] activeViews = mActiveViews;
            if (index >=0 && index < activeViews.length) {
                final View match = activeViews[index];
                activeViews[index] = null;
                return match;
            }
            return null;
        }

        View getTransientStateView(int position) {
            if (mTransientStateViews == null) {
                return null;
            }
            final int index = mTransientStateViews.indexOfKey(position);
            if (index < 0) {
                return null;
            }
            final View result = mTransientStateViews.valueAt(index);
            mTransientStateViews.removeAt(index);
            return result;
        }

        /**
         * Dump any currently saved views with transient state.
         */
        void clearTransientStateViews() {
            if (mTransientStateViews != null) {
                mTransientStateViews.clear();
            }
        }

        /**
         * @return A view from the ScrapViews collection. These are unordered.
         */
        View getScrapView(int position) {
            if (mViewTypeCount == 1) {
                return retrieveFromScrap(mCurrentScrap, position);
            } else {
                int whichScrap = mAdapter.getItemViewType(position);
                if (whichScrap >= 0 && whichScrap < mScrapViews.length) {
                    return retrieveFromScrap(mScrapViews[whichScrap], position);
                }
            }
            return null;
        }

        /**
         * Put a view into the ScrapViews list. These views are unordered.
         *
         * @param scrap The view to add
         */
        void addScrapView(View scrap, int position) {
            AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();
            if (lp == null) {
                return;
            }

            lp.scrappedFromPosition = position;

            // Don't put header or footer views or views that should be ignored
            // into the scrap heap
            int viewType = lp.viewType;
            final boolean scrapHasTransientState = scrap.hasTransientState();
            if (!shouldRecycleViewType(viewType) || scrapHasTransientState) {
                if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER || scrapHasTransientState) {
                    if (mSkippedScrap == null) {
                        mSkippedScrap = new ArrayList<View>();
                    }
                    mSkippedScrap.add(scrap);
                }
                if (scrapHasTransientState) {
                    if (mTransientStateViews == null) {
                        mTransientStateViews = new SparseArray<View>();
                    }
                    scrap.dispatchStartTemporaryDetach();
                    mTransientStateViews.put(position, scrap);
                }
                return;
            }

            scrap.dispatchStartTemporaryDetach();
            if (mViewTypeCount == 1) {
                mCurrentScrap.add(scrap);
            } else {
                mScrapViews[viewType].add(scrap);
            }

            scrap.setAccessibilityDelegate(null);
            if (mRecyclerListener != null) {
                mRecyclerListener.onMovedToScrapHeap(scrap);
            }
        }

        /**
         * Finish the removal of any views that skipped the scrap heap.
         */
        void removeSkippedScrap() {
            if (mSkippedScrap == null) {
                return;
            }
            final int count = mSkippedScrap.size();
            for (int i = 0; i < count; i++) {
                removeDetachedView(mSkippedScrap.get(i), false);
            }
            mSkippedScrap.clear();
        }

        /**
         * Move all views remaining in mActiveViews to mScrapViews.
         */
        void scrapActiveViews() {
            final View[] activeViews = mActiveViews;
            final boolean hasListener = mRecyclerListener != null;
            final boolean multipleScraps = mViewTypeCount > 1;

            ArrayList<View> scrapViews = mCurrentScrap;
            final int count = activeViews.length;
            for (int i = count - 1; i >= 0; i--) {
                final View victim = activeViews[i];
                if (victim != null) {
                    final AbsListView.LayoutParams lp
                            = (AbsListView.LayoutParams) victim.getLayoutParams();
                    int whichScrap = lp.viewType;

                    activeViews[i] = null;

                    final boolean scrapHasTransientState = victim.hasTransientState();
                    if (!shouldRecycleViewType(whichScrap) || scrapHasTransientState) {
                        // Do not move views that should be ignored
                        if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER ||
                                scrapHasTransientState) {
                            removeDetachedView(victim, false);
                        }
                        if (scrapHasTransientState) {
                            if (mTransientStateViews == null) {
                                mTransientStateViews = new SparseArray<View>();
                            }
                            mTransientStateViews.put(mFirstActivePosition + i, victim);
                        }
                        continue;
                    }

                    if (multipleScraps) {
                        scrapViews = mScrapViews[whichScrap];
                    }
                    victim.dispatchStartTemporaryDetach();
                    lp.scrappedFromPosition = mFirstActivePosition + i;
                    scrapViews.add(victim);

                    victim.setAccessibilityDelegate(null);
                    if (hasListener) {
                        mRecyclerListener.onMovedToScrapHeap(victim);
                    }
                }
            }

            pruneScrapViews();
        }

        /**
         * Makes sure that the size of mScrapViews does not exceed the size of mActiveViews.
         * (This can happen if an adapter does not recycle its views).
         */
        private void pruneScrapViews() {
            final int maxViews = mActiveViews.length;
            final int viewTypeCount = mViewTypeCount;
            final ArrayList<View>[] scrapViews = mScrapViews;
            for (int i = 0; i < viewTypeCount; ++i) {
                final ArrayList<View> scrapPile = scrapViews[i];
                int size = scrapPile.size();
                final int extras = size - maxViews;
                size--;
                for (int j = 0; j < extras; j++) {
                    removeDetachedView(scrapPile.remove(size--), false);
                }
            }

            if (mTransientStateViews != null) {
                for (int i = 0; i < mTransientStateViews.size(); i++) {
                    final View v = mTransientStateViews.valueAt(i);
                    if (!v.hasTransientState()) {
                        mTransientStateViews.removeAt(i);
                        i--;
                    }
                }
            }
        }

        /**
         * Puts all views in the scrap heap into the supplied list.
         */
        void reclaimScrapViews(List<View> views) {
            if (mViewTypeCount == 1) {
                views.addAll(mCurrentScrap);
            } else {
                final int viewTypeCount = mViewTypeCount;
                final ArrayList<View>[] scrapViews = mScrapViews;
                for (int i = 0; i < viewTypeCount; ++i) {
                    final ArrayList<View> scrapPile = scrapViews[i];
                    views.addAll(scrapPile);
                }
            }
        }

        /**
         * Updates the cache color hint of all known views.
         *
         * @param color The new cache color hint.
         */
        void setCacheColorHint(int color) {
            if (mViewTypeCount == 1) {
                final ArrayList<View> scrap = mCurrentScrap;
                final int scrapCount = scrap.size();
                for (int i = 0; i < scrapCount; i++) {
                    scrap.get(i).setDrawingCacheBackgroundColor(color);
                }
            } else {
                final int typeCount = mViewTypeCount;
                for (int i = 0; i < typeCount; i++) {
                    final ArrayList<View> scrap = mScrapViews[i];
                    final int scrapCount = scrap.size();
                    for (int j = 0; j < scrapCount; j++) {
                        scrap.get(j).setDrawingCacheBackgroundColor(color);
                    }
                }
            }
            // Just in case this is called during a layout pass
            final View[] activeViews = mActiveViews;
            final int count = activeViews.length;
            for (int i = 0; i < count; ++i) {
                final View victim = activeViews[i];
                if (victim != null) {
                    victim.setDrawingCacheBackgroundColor(color);
                }
            }
        }
    }

    static View retrieveFromScrap(ArrayList<View> scrapViews, int position) {
        int size = scrapViews.size();
        if (size > 0) {
            // See if we still have a view for this position.
            for (int i=0; i<size; i++) {
                View view = scrapViews.get(i);
                if (((AbsListView.LayoutParams)view.getLayoutParams())
                        .scrappedFromPosition == position) {
                    scrapViews.remove(i);
                    return view;
                }
            }
            return scrapViews.remove(size - 1);
        } else {
            return null;
        }
    }


相關推薦

Android熱補丁原理簡單分析與問題思考

Android熱補丁(熱修復)從2016年微信的Tinker開源後掀起了一個熱潮。時隔一年,阿里推出《深入探索android熱修復技術原理》技術書籍,在推廣其Sophix的同時非常詳細地描述了其方案的實現過程,以及遇到的各種坑,引來大家的關注與分析。可以看到大公

Android ListView itemType使用Holder原理簡單分析

之前寫過一篇關於Holder複用原理的文章《Android ListView使用Holder優化原理》,此篇著重分析RecycleBin資料結構 原始碼在AbsListView中,RecycleBin一共有兩個儲存結構分別是ActiveViews 和 ScrapVie

DWM1000 測距原理簡單分析

DWM1000 超寬頻測距,使用的TOF(time of fly) 的方式,也就是計算無線電磁波傳輸時間,通過傳輸的時間換算成距離。 電磁波傳輸速率和光速一樣,速度是299792.458km/s,可參見百度百科。如果想通過測試這個傳播時間換算距離,那麼就需要非常高的內部時鐘。然。。。並不是有了高速的內部時鐘即

NAT-T技術原理簡單分析及應用實驗解析

1.首先我們就IPSEC VPN的部署場景來做簡要分析: 場景1:如圖所示,企業的總部與分支機構分別架設了VPN裝置,分支機構的需求是同步企業內部的業務資料(屬企業內部的機密資訊),那麼就必須確保資料在公網上是安全包密傳遞的。這種情況下我們可以直接用IPSEC

Android ListView工作原理完全解析,帶你從原始碼的角度徹底理解

在Android所有常用的原生控制元件當中,用法最複雜的應該就是ListView了,它專門用於處理那種內容元素很多,手機螢幕無法展示出所有內容的情況。ListView可以使用列表的形式來展示內容,超出螢幕部分的內容只需要通過手指滑動就可以移動到螢幕內了。另外ListView還

Java 內部類實現原理簡單分析

轉載:原文地址http://www.fzhen.info/?p=300 本文重點不在與內部類的語法及使用,而是試圖解釋一些背後的原理。 內部類簡介 Java支援在類內部定義類,即為內部類。 普通內部類 把類的定義放在類的內部,例如: 程式碼清單1: public class Outer{ priv

android開機啟動流程簡單分析

android啟動 當載入程式啟動Linux核心後,會載入各種驅動和資料結構,當有了驅動以後,開始啟動Android系統同時會載入使用者級別的第一個程序init(system\core\init\init.cpp)程式碼如下: int main(int ar

JAVA程式碼覆蓋率工具JaCoCo-原理簡單分析

作為一個測試人員,保證產品的軟體質量是其工作首要目標,為了這個目標,測試人員常常會通過很多手段或工具來加以保證,覆蓋率就是其中一環比較重要的環節。 我們通常會將測試覆蓋率分為兩個部分,即“需求覆蓋率”和“程式碼覆蓋率”。 需求覆蓋:指的是測試人員對需求的瞭解程度,根據

JVM原理簡單分析

在Java語言裡堆(heap)和棧(stack)裡的區別 1. 棧(stack)與堆(heap)都是Java用來在Ram中存放資料的地方。與C++不同,Java自動管理棧和堆,程式設計師不能直接地設定棧或堆。  2. 棧的優勢是,存取速度比堆要快,僅次於直接位於CPU中的暫存器。但缺點是,存在棧中的資料大小

Spring原理簡單分析

本文知識點主要來自Spring技術內幕:深入解析Spring架構與設計原理(第2版) 。 1.spring設計理念 作業系統關心的是對儲存、計算、通訊、外圍裝置等物理資源的管理,為使用者提供一個統一的服務介面。 而spring其關心的是一些企業應用資源的使用,比如資料的持久

Android Animation動畫原理原始碼分析

Android 平臺提供了三類動畫,一類是 Tween 動畫-Animation,即通過對場景裡的物件不斷做影象變換 ( 平移、縮放、旋轉 ) 產生動畫效果;第二類是 Frame 動畫,即順序播放事先做好的影象,跟電影類似。最後一種就是3.0之後才出現的屬性動畫

Dubbo原理簡單分析

明顯,zookeeper可以被用作一個約會機制,讓參入的程序不在了解其他程序的(或網路)的情況下能夠彼此發現並進行互動,參入的各方甚至不必同時存在,只要在zookeeper留下一條訊息,在該程序結束後,另外一個程序還可以讀取這條資訊,從而解耦了各個節點之間的關係。  zook

AbstractRoutingDataSource 實現動態資料來源切換原理簡單分析

# AbstractRoutingDataSource 實現動態資料來源切換原理簡單分析 > 寫在前面,專案中用到了動態資料來源切換,記錄一下其執行機制。 ## 程式碼展示 下面列出一些關鍵程式碼,後續分析會用到 1. 資料配置 ```java @Configuration @PropertySou

mr原理簡單分析

背景      又是一個週末一天一天的過的好快,今天的任務幹啥呢,索引總結一些mr吧,因為前兩天有面試問過我?我當時也是簡單說了一下,畢竟現在寫mr程式的應該很少很少了,廢話不說了,結合官網和自己理解寫起。   官網 https://hadoop.apache.org/

Android ListView動畫特效實現原理及源代碼

stat 每一個 應該 所有 ner haar .get tde pri Android 動畫分三種,當中屬性動畫為我們最經常使用動畫,且能滿足項目中開發差點兒所有需求,google官方包支持3.0+。我們能夠引用三方包nineoldandr

木馬APP的簡單分析(Android Killer分析)

com smtp pow super text lsm integer ref rar 本文作者:三星s7edge 一.此貼目的:分析一個木馬APP樣本的行為。—————————————————————————————————————————————————-二.分析步驟及

Android查缺補漏(線程篇)-- AsyncTask的使用及原理詳細分析

catch 返回 rri 理解 ams tee ive lean keyword 本文作者:CodingBlock 文章鏈接:http://www.cnblogs.com/codingblock/p/8515304.html 一、AsyncTask的使用 AsyncT

Android XListView實現原理講解及分析

就是 指定 不同 true -h -name 修改 一個 部分 XListview是一個非常受歡迎的下拉刷新控件,但是已經停止維護了。之前寫過一篇XListview的使用介紹,用起來非常簡單,這兩天放假無聊,研究了下XListview的實現原理,學到了很多,今天分享給大家。

Android ListView分析

一.概述 在android開發中ListView是比較常用的元件,它以列表的形式展示具體內容,並且能夠根據資料的長度自適應顯示。本文將著重介紹一個使用BaseAdatper介面卡將資料庫中的資料顯示到介面的小案例。 二.列表的顯示需要三個元素: 1.ListVeiw 用來展示

網口掃盲二:Mac與Phy組成原理簡單分析(轉)

  1. general 下圖是網口結構簡圖.網口由CPU、MAC和PHY三部分組成.DMA控制器通常屬於CPU的一部分,用虛線放在這裡是為了表示DMA控制器可能會參與到網口資料傳輸中.             &