ListView嵌套ListView 。 滑动冲突 数据错乱 事件分发(一)

纯个人学习使用,转载请注明出处

先上效果图:

        

   接下来我们实现一下。可能有些人说很简单啊  不就是listView嵌套listView吗,有什么难的。。。

       (额。。。。。?)

   我们自己来写个ListView嵌套ListView来看看是怎么回事(代码我就不贴了,嘿嘿,很简单的事情嘛)

    好 3个小时后战战兢兢的运行了下。。。。各位大哥请看

        

小弟我的审美就是这了,,,先凑合着看?嘿嘿

               把眼睛睁开瞅了一眼 卧槽勒 单选数据怎么只有一条,,,然后又挥动着我那颤抖分双手滑了滑,哎呦卧槽,竟然不能滑动了?。。。肿么办

            算了  毕竟是自己写出来的bug,就算哭着也得改完啊。。。?

           1.为什莫数据只显示一条。。

            答:数据显示一条说明ListView的高度显示不正确,那listView的高度是在哪里计算的呢,当然是在我们的onMeasuure()里面计算的啊,我们点开看看listView的源码

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // Sets up mListPadding
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    int childWidth = 0;
    int childHeight = 0;
    int childState = 0;

    mItemCount = mAdapter == null ? 0 : mAdapter.getCount();
    if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED
            || heightMode == MeasureSpec.UNSPECIFIED)) {
        final View child = obtainView(0, mIsScrap);

        // Lay out child directly against the parent measure spec so that
        // we can obtain exected minimum width and height.
        measureScrapChild(child, 0, widthMeasureSpec, heightSize);

        childWidth = child.getMeasuredWidth();
        childHeight = child.getMeasuredHeight();
        childState = combineMeasuredStates(childState, child.getMeasuredState());

        if (recycleOnMeasure() && mRecycler.shouldRecycleViewType(
                ((LayoutParams) child.getLayoutParams()).viewType)) {
            mRecycler.addScrapView(child, 0);
        }
    }

    if (widthMode == MeasureSpec.UNSPECIFIED) {
        widthSize = mListPadding.left + mListPadding.right + childWidth +
                getVerticalScrollbarWidth();
    } else {
        widthSize |= (childState & MEASURED_STATE_MASK);
    }

    if (heightMode == MeasureSpec.UNSPECIFIED) {
        heightSize = mListPadding.top + mListPadding.bottom + childHeight +
                getVerticalFadingEdgeLength() * 2;
    }

    if (heightMode == MeasureSpec.AT_MOST) {
        // TODO: after first layout we should maybe start at the first visible position, not 0
        heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);
    }

    setMeasuredDimension(widthSize, heightSize);

    mWidthMeasureSpec = widthMeasureSpec;
}        

            各位大官人 ,请看 彩色部分 。当我们的测量模式是UNSPECIFIED的时候我们的高度只有一个条目的高度

            但是我们的测量模式为ATMOST的时候调用了measureheightOfChildren()这个方法,我们进入源码看啊看,打破沙锅问到底

        

final int measureHeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition,
        int maxHeight, int disallowPartialChildPosition) {
    final ListAdapter adapter = mAdapter;
    if (adapter == null) {
        return mListPadding.top + mListPadding.bottom;
    }

    // Include the padding of the list
    int returnedHeight = mListPadding.top + mListPadding.bottom;
    final int dividerHeight = mDividerHeight;
    // The previous height value that was less than maxHeight and contained
    // no partial children
    int prevHeightWithoutPartialChild = 0;
    int i;
    View child;

    // mItemCount - 1 since endPosition parameter is inclusive
    endPosition = (endPosition == NO_POSITION) ? adapter.getCount() - 1 : endPosition;
    final AbsListView.RecycleBin recycleBin = mRecycler;
    final boolean recyle = recycleOnMeasure();
    final boolean[] isScrap = mIsScrap;

    for (i = startPosition; i <= endPosition; ++i) {
        child = obtainView(i, isScrap);

        measureScrapChild(child, i, widthMeasureSpec, maxHeight);

        if (i > 0) {
            // Count the divider for all but one child
            returnedHeight += dividerHeight;
        }

        // Recycle the view before we possibly return from the method
        if (recyle && recycleBin.shouldRecycleViewType(
                ((LayoutParams) child.getLayoutParams()).viewType)) {
            recycleBin.addScrapView(child, -1);
        }

        returnedHeight += child.getMeasuredHeight();

        if (returnedHeight >= maxHeight) {
            // We went over, figure out which height to return.  If returnedHeight > maxHeight,
            // then the i'th position did not fit completely.
            return (disallowPartialChildPosition >= 0) // Disallowing is enabled (> -1)
                        && (i > disallowPartialChildPosition) // We've past the min pos
                        && (prevHeightWithoutPartialChild > 0) // We have a prev height
                        && (returnedHeight != maxHeight) // i'th child did not fit completely
                    ? prevHeightWithoutPartialChild
                    : maxHeight;
        }

        if ((disallowPartialChildPosition >= 0) && (i >= disallowPartialChildPosition)) {
            prevHeightWithoutPartialChild = returnedHeight;
        }
    }

    // At this point, we went through the range of children, and they each
    // completely fit, so return the returnedHeight
    return returnedHeight;
}

        非常清晰 有木有 老铁 ,来 先双击666 ,

        这个里面遍历了子View进行高度的叠加,所以我们总结

            ListView测量模式为UNSPECIFIED的时候高度为一个条目

                        为AT_MOST EXACTLY的时候正常


        那么我们就要分析了究竟是哪里给他设置了UNSPECIFIED这个标签

        我们先动动我们的小脑袋想想,俺们是ListView嵌套ListView那么里层的ListView显示不全那么就是外层的ListView测量的时候给里层的ListView设置了UNSPECIFIED这个标签 

        那么还是在ListView的onMeasure()里面查找我们的答案

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // Sets up mListPadding
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);

    int childWidth = 0;
    int childHeight = 0;
    int childState = 0;

    mItemCount = mAdapter == null ? 0 : mAdapter.getCount();
    if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED
            || heightMode == MeasureSpec.UNSPECIFIED)) {
        final View child = obtainView(0, mIsScrap);

        // Lay out child directly against the parent measure spec so that
        // we can obtain exected minimum width and height.
        measureScrapChild(child, 0, widthMeasureSpec, heightSize);

        childWidth = child.getMeasuredWidth();
        childHeight = child.getMeasuredHeight();
        childState = combineMeasuredStates(childState, child.getMeasuredState());

        if (recycleOnMeasure() && mRecycler.shouldRecycleViewType(
                ((LayoutParams) child.getLayoutParams()).viewType)) {
            mRecycler.addScrapView(child, 0);
        }
    }

    if (widthMode == MeasureSpec.UNSPECIFIED) {
        widthSize = mListPadding.left + mListPadding.right + childWidth +
                getVerticalScrollbarWidth();
    } else {
        widthSize |= (childState & MEASURED_STATE_MASK);
    }

    if (heightMode == MeasureSpec.UNSPECIFIED) {
        heightSize = mListPadding.top + mListPadding.bottom + childHeight +
                getVerticalFadingEdgeLength() * 2;
    }

    if (heightMode == MeasureSpec.AT_MOST) {
        // TODO: after first layout we should maybe start at the first visible position, not 0
        heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);
    }

    setMeasuredDimension(widthSize, heightSize);

    mWidthMeasureSpec = widthMeasureSpec;
}

    大致看下并没有啥问题啊,但是到底是哪里出的问题呢

           矣 好像又一个方法没看,就是这个measureScrapChild我们点进去看看源码

        

private void measureScrapChild(View child, int position, int widthMeasureSpec, int heightHint) {
    LayoutParams p = (LayoutParams) child.getLayoutParams();
    if (p == null) {
        p = (AbsListView.LayoutParams) generateDefaultLayoutParams();
        child.setLayoutParams(p);
    }
    p.viewType = mAdapter.getItemViewType(position);
    p.isEnabled = mAdapter.isEnabled(position);
    p.forceAdd = true;

    final int childWidthSpec = ViewGroup.getChildMeasureSpec(widthMeasureSpec,
            mListPadding.left + mListPadding.right, p.width);
    final int lpHeight = p.height;
    final int childHeightSpec;
    if (lpHeight > 0) {
        childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
    } else {
        childHeightSpec = MeasureSpec.makeSafeMeasureSpec(heightHint, MeasureSpec.UNSPECIFIED);
    }
    child.measure(childWidthSpec, childHeightSpec);

    // Since this view was measured directly aginst the parent measure
    // spec, we must measure it again before reuse.
    child.forceLayout();
}

        哎呦 。好像看见了  我们debug走一下 看他走那个方法

        

兄弟们 怎么样 。来 先双666 给老弟来波关注  嘿嘿?是不是走的UNSPECIFIED这个标记,

    到这里 我们解决就好办了  是吧  我们给他动态的设置下测量模式不就行了嘛

        2.解决办法  重写onMeasure()方法   

public class MyListView extends ListView {
    public MyListView(Context context) {
        super(context);
    }

    public MyListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
                MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    }


}
 

so easy   老铁们   我们遇到问题能解决就很好了  但是我们还要懂问题为什么发生,可能小弟水平有点菜

    大家一起探讨交流    文中有错误欢迎指出 谢谢?

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值