`
亚当爱上java
  • 浏览: 707388 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Android ListView初始化简单分析

阅读更多
下面是分析ListView初始化的源码流程分析,主要是ListVIew.onLayout过程与普通视图的layout过程完全不同,避免流程交代不清楚,以下是一个流程的思维导图。

     思维导图是顺序是从左向右,从上向下。
一、 先看构造函数,上图中1.1就不分析了,主要是读取一些ListView参数,直接来看1.2 ViewGroup构造函数源码
    private void initViewGroup() {
        ......
        // 初始化保存当前ViewGroup中所有View的数组
        mChildren = new View[ARRAY_INITIAL_CAPACITY];
        // 初始时其Child个数为0
        mChildrenCount = 0;
        ......
    }

二、接着2 即 ListView.onMeasure方法,只是获取当前ListView的宽高
    @Override  
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
        // Sets up mListPadding  
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
      
        // 获取当前ListView总宽高  
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);  
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);  
      
        ......  
      
        setMeasuredDimension(widthSize , heightSize);  
        mWidthMeasureSpec = widthMeasureSpec;          
    }  

三、步骤3是重点,ListView的onLayout的流程与普通View的不同
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        mInLayout = true;
        // 初始时changed = true
        if (changed) {
            int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
            	// ?
                getChildAt(i).forceLayout();
            }
            mRecycler.markChildrenDirty();
        }
        
        if (mFastScroller != null && mItemCount != mOldItemCount) {
            mFastScroller.onItemCountChanged(mOldItemCount, mItemCount);
        }

        // ListView实现此方法
        layoutChildren();
        mInLayout = false;

        mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR;
    }

四、步骤4.1 具体分析ListVIew.layoutChildren
    @Override
    protected void layoutChildren() {
    	// 默认为false
        final boolean blockLayoutRequests = mBlockLayoutRequests;
        if (!blockLayoutRequests) {
        	// 目的是为了阻止没必要的layout操作,提交效率
            mBlockLayoutRequests = true;
        } else {
            return;
        }

        try {
            super.layoutChildren();
            // 为什么此处要请求重绘?
            invalidate();

            ......
			
            int childCount = getChildCount();

            ......

            boolean dataChanged = mDataChanged;
            if (dataChanged) {
                handleDataChanged();
            }

            ......

            // 把所有child view放置到RecycleBin
            // 满足条件的话可以重用这些child view
            final int firstPosition = mFirstPosition;
            
            // ListView中Item复用使用此数据结构
            final RecycleBin recycleBin = mRecycler;

            // reset the focus restoration
            View focusLayoutRestoreDirectChild = null;

            // Don't put header or footer views into the Recycler. Those are
            // already cached in mHeaderViews;
            if (dataChanged) {
                for (int i = 0; i < childCount; i++) {
                    recycleBin.addScrapView(getChildAt(i), firstPosition+i);
                }
            } else {
            	// 创建childCount个数的View放入RecycleBin类activeViews数组中
                recycleBin.fillActiveViews(childCount, firstPosition);
            }
	
            ......

            // Clear out old views
            detachAllViewsFromParent();
            recycleBin.removeSkippedScrap();

            switch (mLayoutMode) {
            
            ......
            
            default:
                if (childCount == 0) {
                    if (!mStackFromBottom) {
                        final int position = lookForSelectablePosition(0, true);
                        setSelectedPositionInt(position);
                        // 此方法是重点,以下具体分析
                        sel = fillFromTop(childrenTop);
                    } else {
                        final int position = lookForSelectablePosition(mItemCount - 1, false);
                        setSelectedPositionInt(position);
                        sel = fillUp(mItemCount - 1, childrenBottom);
                    }
                } else {
                    if (mSelectedPosition >= 0 && mSelectedPosition < mItemCount) {
                        sel = fillSpecific(mSelectedPosition,
                                oldSel == null ? childrenTop : oldSel.getTop());
                    } else if (mFirstPosition < mItemCount) {
                        sel = fillSpecific(mFirstPosition,
                                oldFirst == null ? childrenTop : oldFirst.getTop());
                    } else {
                        sel = fillSpecific(0, childrenTop);
                    }
                }
                break;
            }

            // Flush any cached views that did not get reused above
            recycleBin.scrapActiveViews();

             ......

            invokeOnItemScrollListener();
        } finally {
            if (!blockLayoutRequests) {
                mBlockLayoutRequests = false;
            }
        }

五、 分析步骤4.2 ListView.fillFromTop源码
	// 从上向下在ListView中填充Item View
	private View fillFromTop(int nextTop) {
        mFirstPosition = Math.min(mFirstPosition, mSelectedPosition);
        mFirstPosition = Math.min(mFirstPosition, mItemCount - 1);
        if (mFirstPosition < 0) {
            mFirstPosition = 0;
        }
        // 具体操作在此
        return fillDown(mFirstPosition, nextTop);
    }

六、查看步骤4.3 ListView.fillDown源码
    // 在参数pos下面填充Item View
    private View fillDown(int pos, int nextTop) {
        View selectedView = null;

        // ListView getHeight也是这样计算的
        int end = (mBottom - mTop);
        if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) {
            end -= mListPadding.bottom;
        }

        // 初始化时pos = 0,如果item总数少于一屏幕,执行mItemCount - pos次
        // 如果item多余一屏幕,执行end - nextTop次
        while (nextTop < end && pos < mItemCount) {
            // is this the selected item?
            boolean selected = pos == mSelectedPosition;
            // 获取Item View对象
            View child = makeAndAddView(pos, nextTop, true, mListPadding.left, selected);

            nextTop = child.getBottom() + mDividerHeight;
            if (selected) {
                selectedView = child;
            }
            pos++;
        }

        setVisibleRangeHint(mFirstPosition, mFirstPosition + getChildCount() - 1);
        return selectedView;
    }

七、查看步骤4.4 ListView.makeAndAddView源码
    // ListView都是通过此方法获取Item View
    // 具体Item View如何复用,是否需要创建新的Item View都有此方法处理
    private View makeAndAddView(int position, int y, boolean flow, int childrenLeft,
            boolean selected) {
        View child;

		// ListView的数据发生变化,肯定Item View之前已经创建好了,无需重新创建
        if (!mDataChanged) {
            // 当前position复用之前创建的视图
            child = mRecycler.getActiveView(position);
            if (child != null) {
            	// 对复用的View针对当前需要进行配置
                setupChild(child, position, y, flow, childrenLeft, selected, true);

                return child;
            }
        }

        // 创建或者重用视图
        child = obtainView(position, mIsScrap);

        // This needs to be positioned and measured
        setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]);

        return child;
    }

八 查看步骤4.5 ListView.setupChild源码
    private void setupChild(View child, int position, int y, boolean flowDown, int childrenLeft,
            boolean selected, boolean recycled) {
        // 判断当前Item View是否选中状态
        final boolean isSelected = selected && shouldShowSelector();
        final boolean updateChildSelected = isSelected != child.isSelected();
        
        final int mode = mTouchMode;
        
        // 是否处于按下状态
        final boolean isPressed = mode > TOUCH_MODE_DOWN && mode < TOUCH_MODE_SCROLL &&
                mMotionPosition == position;
        final boolean updateChildPressed = isPressed != child.isPressed();
        
        // 是否需要重新measure与layout
        final boolean needToMeasure = !recycled || updateChildSelected || child.isLayoutRequested();

        // Respect layout params that are already in the view. Otherwise make some up...
        // noinspection unchecked
        AbsListView.LayoutParams p = (AbsListView.LayoutParams) child.getLayoutParams();
        if (p == null) {
            p = (AbsListView.LayoutParams) generateDefaultLayoutParams();
        }
        p.viewType = mAdapter.getItemViewType(position);

        if ((recycled && !p.forceAdd) || (p.recycledHeaderFooter &&
                p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER)) {
            attachViewToParent(child, flowDown ? -1 : 0, p);
        } else {
            p.forceAdd = false;
            if (p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {
                p.recycledHeaderFooter = true;
            }
            // 向ListView(ViewGroup子类)添加当前Item View
            addViewInLayout(child, flowDown ? -1 : 0, p, true);
        }

        // 更新选中状态
        if (updateChildSelected) {
            child.setSelected(isSelected);
        }

        // 更新按下状态
        if (updateChildPressed) {
            child.setPressed(isPressed);
        }

        if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) {
            if (child instanceof Checkable) {
                ((Checkable) child).setChecked(mCheckStates.get(position));
            } else if (getContext().getApplicationInfo().targetSdkVersion
                    >= android.os.Build.VERSION_CODES.HONEYCOMB) {
                child.setActivated(mCheckStates.get(position));
            }
        }

        if (needToMeasure) {
            int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec,
                    mListPadding.left + mListPadding.right, p.width);
            int lpHeight = p.height;
            int childHeightSpec;
            if (lpHeight > 0) {
                childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
            } else {
                childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
            }
            // 与普通视图的measure流程不同,ListView是在此处执行具体的当前Item View measure
            child.measure(childWidthSpec, childHeightSpec);
        } else {
            cleanupLayoutState(child);
        }

        final int w = child.getMeasuredWidth();
        final int h = child.getMeasuredHeight();
        final int childTop = flowDown ? y : y - h;

        if (needToMeasure) {
            final int childRight = childrenLeft + w;
            final int childBottom = childTop + h;
            // 大小改变肯定位置也会发生变化,当前Item View重新进行layout
            child.layout(childrenLeft, childTop, childRight, childBottom);
        } else {
            child.offsetLeftAndRight(childrenLeft - child.getLeft());
            child.offsetTopAndBottom(childTop - child.getTop());
        }

        if (mCachingStarted && !child.isDrawingCacheEnabled()) {
            child.setDrawingCacheEnabled(true);
        }

        if (recycled && (((AbsListView.LayoutParams)child.getLayoutParams()).scrappedFromPosition)
                != position) {
            child.jumpDrawablesToCurrentState();
        }
    }

说明:本文为转载,基于android2.3平台。
  • 大小: 31.1 KB
分享到:
评论

相关推荐

    android的listview嵌套listview,列表嵌套列表 android studio版本

    // 初始化父ListView的Adapter ParentListAdapter parentAdapter = new ParentListAdapter(this, parentData); parentListView.setAdapter(parentAdapter); // 对于每个父ListView的条目,我们需要在getView()中...

    Android ListView简单示例

    在MainActivity.java中,我们需要初始化ListView并设置Adapter。首先,通过`setContentView()`加载主布局,然后找到ListView的引用,如`ListView listView = findViewById(R.id.listView)`。接着,创建前面提到的...

    android ListView简单实例

    在Android开发中,ListView是一个非常重要的组件,它用于展示大量数据列表,用户可以通过滚动查看所有条目。本教程将深入讲解如何使用ArrayAdapter和SimpleAdapter这两种方式来创建一个简单的ListView实例。 首先,...

    Android listview嵌套listview

    // 初始化ViewHolder holder.innerListView = convertView.findViewById(R.id.inner_list_view); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } // 设置内部...

    android中一个简单的LIstView用法例子

    // 初始化ListView ListView listView = (ListView) findViewById(R.id.listView); // 创建适配器 ArrayAdapter&lt;String&gt; adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, items); // 设置...

    android listView 适配器demo

    3. **初始化ListView**:在布局文件中添加ListView控件,并在Activity或Fragment中找到这个控件。 4. **设置适配器**:调用ListView的`setAdapter()`方法,传入创建好的适配器。 5. **监听事件**:如果需要,可以为...

    androidlistview里面使用radiobutton

    // 构造函数,初始化数据 public ListRadioButtonAdapter(List&lt;String&gt; items) { this.items = items; } @Override public int getCount() { return items.size(); } @Override public Object getItem...

    android listView 改变选中行的 字体颜色和 图片

    如果是新创建,需要初始化视图;如果是复用,可以直接修改视图内容。 - 使用`holder`模式来提高性能,避免频繁的findViewById操作。 - 根据ListView的`position`参数,判断当前行是否被选中。可以利用`listView....

    Android listView 分页功能

    - **初始化ListView**:在布局文件中添加ListView,设置适配器。 - **定义Adapter**:自定义Adapter,包含数据集和获取数据的方法。 - **监听滚动事件**:为ListView添加OnScrollListener,重写`onScroll()`和`...

    android listView 单选 功能

    在初始化ListView时,可以根据需求设置默认选中的项。 ```java items.get(0).setSelected(true); // 设置第一条为默认选中 adapter = new MyAdapter(this, items); listView.setAdapter(adapter); ``` 三、使用...

    android listview

    初始化convertView ... convertView.setBackgroundResource(R.drawable.list_item_background); // 设置背景 // ... 绑定数据 ... } ``` 另一种方法是利用自定义ViewGroup,例如继承LinearLayout或FrameLayout,...

    android ListView 使用 示例

    在Activity中初始化ListView和Adapter,并设置Adapter: ```java ListView listView = findViewById(R.id.list_view); List&lt;String&gt; items = new ArrayList(); // 初始化数据... MyAdapter adapter = new MyAdapter...

    android listView 模拟物理分页Demo 按键

    - 数据加载:在初始化时加载第一页数据。当用户点击“下一页”按钮时,向服务器请求下一页数据并添加到适配器中,调用notifyDataSetChanged()更新ListView。 - 关注按键事件:在Activity或Fragment中监听“下一页...

    android Listview demo

    2. 初始化ArrayAdapter,传入上下文、布局ID和数据集。 3. 调用ListView的`setAdapter()`方法,传入创建好的Adapter。 五、监听事件 为了响应用户在ListView上的点击操作,我们可以为ListView设置一个....

    android ListView+CheckBox实现单选

    在Activity或Fragment的onCreate()方法中,初始化ListView、数据列表和Adapter,最后将Adapter设置给ListView。 ```java List&lt;ItemModel&gt; itemList = new ArrayList(); // 添加数据到itemList ItemAdapter adapter...

    Android-ListView中嵌套(ListView)控件兼容问题

    这段代码展示了如何创建一个`StatisticsForcastListView`对象,并对其进行初始化。其中的关键点在于设置了`setFocusable(false)`来禁用父`ListView`的焦点功能,确保点击事件能够正常触发。 对应的XML布局文件如下...

    AndroidListView实现时间轴

    - 在Activity的布局文件中添加一个ListView,并在代码中初始化它,设置自定义Adapter。 ```xml &lt;ListView android:id="@+id/timeline_list" android:layout_width="match_parent" android:layout_height="match...

    Android ListVIew复制粘贴的实现

    3. 在Activity中,初始化ClipboardManager并设置监听器,以便在复制或粘贴时获取和设置数据。 4. 设置ListView的OnItemClickListener,监听单击事件,当用户点击某一行时,获取选中行的数据,创建ClipData并调用...

    C# xamarin android listview简单使用

    在MainActivity.cs中,加载布局,初始化ListView,并设置Adapter: ```csharp protected override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); SetContentView(Resource....

    Android实现ListView的增删改查Demo

    在Activity或Fragment中,我们需要初始化ListView并设置其适配器。适配器通常是自定义的,如`WordAdapter`,继承自`BaseAdapter`。适配器负责将数据绑定到ListView的每一项视图上。我们还需要在适配器中实现`getView...

Global site tag (gtag.js) - Google Analytics