解决getView()方法反复执行

问题描述:在ScrollView中嵌套使用了ListView/GridView,ListView/GridView只显示一行,为了解决这个问题,重写了ListView/GridView的onMeasure()方法。

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        isOnMeasure = true;
        heightMeasureSpec=MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,
        MeasureSpec.AT_MOST);
        //AT_MOST(表示子控件的高度能扩展多高就扩展多高,但要小于给出的size)
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

由此也带来了新的问题,adapter中的getView()方法会执行多次。调试过程中,一个页面ListView20个复杂条目,getView()方法执行了480次,最后更改了布局,为ListView添加header。 
但是另一个页面,ScrollView中嵌套了2个GridView和一个ListView,无法更改布局。问题原因在于onMeasure()方法反复测量,解决方案如下:

public class MyGridView extends GridView {
    //嵌套在ScrollView中全展开,重写onMeasure导致adapter的getView方法反复执行
    //设置一个boolean变量,onMeasure时设为ture,onLayout时设为false
    public boolean isOnMeasure ;
    public MyGridView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
    public MyGridView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyGridView(Context context) {
        super(context);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        isOnMeasure = true;
        heightMeasureSpec=MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE>>2,
        MeasureSpec.AT_MOST);
        //AT_MOST(表示子控件的高度能扩展多高就扩展多高,但要小于给出的size)
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        isOnMeasure = false;
        super.onLayout(changed, l, t, r, b);
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

在adapter的getView()方法中,对isOnMeasure()的值进行判断,如果为ture,提前return convertView;

public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder vh;
        if (convertView == null) {
            convertView = inflater.inflate(R.layout.inflate_griditem_mainpage_wall_and_lock, null);
            vh = new ViewHolder();
            vh.iv = (ImageView) convertView.findViewById(R.id.image_griditem2);
            convertView.setTag(vh);
        }else {
            vh = (ViewHolder) convertView.getTag();
        }
        //关键代码
        if (parent instanceof MyGridView) {
            if (((MyGridView)parent).isOnMeasure) {
                return convertView;
            }
        }
        String url = (String)getItem(position);
        HttpUtil.loadImage(context, Stringutil.get208x312(url), vh.iv);
        vh.iv.setOnClickListener(new MyClickListener(Stringutil.get480x800(url)));

        num++;
        Log.i("Cat", "getView() : Main = "+num+"");
        return convertView;

}

转载于https://blog.csdn.net/caterwind/article/details/51912457




首先讲一下我遇到的需求吧,页面是这样的,上边有东西,中间是列表,下边还有东西。首先我看到列表立刻就想到了用ListView,但是页面有限,只能用ScrollView包一下。想到就做呗。我就在ScrollView里面加了一个ListView, ListView设置的是wapcontent,这样就出现了ListView数据只显示出了一行。好的,解决问题的方案就来了。

一.设置scrollView中的ListView内容全部显示,不能滑动,将滑动交给scrollView去做

做法:在设置adapter之前,重新计算ListView的高度,我这里写了一个方法:

[html]  view plain  copy
  1. /**  
  2. * 动态设置listView的高度  
  3. * count 总条目  
  4. */  
  5. private void setListViewHeight(ListView listView, BaseAdapter adapter,  
  6. int count) {  
  7. int totalHeight = 0;  
  8. for (int i = 0; i < count; i++) {  
  9. View listItem = adapter.getView(i, null, listView);  
  10. listItem.measure(0, 0);  
  11. totalHeight += listItem.getMeasuredHeight();  
  12. }  
  13. ViewGroup.LayoutParams params = listView.getLayoutParams();  
  14. params.height = totalHeight + (listView.getDividerHeight() * count);  
  15. listView.setLayoutParams(params);  
  16. }  

这样做的前提条件是布局文件中ListView的高度要指定,这样才能重新计算,不要设成wapcontent!

二.不全部展示数据,二者皆可滑动。

此方法不用重新计算ListView的高度,只需焦点在Listview上的时候,ScrollView能把滑动权主动交给Listview,这样需要重写ScrollView的一个方法,如下:

[html]  view plain  copy
  1. @Override  
  2. public boolean onInterceptTouchEvent(MotionEvent ev) {  
  3. return false;  
  4. }  

这样Scrollview就会根据焦点而让出滑动事件。

三.  不重新计算ListView的高度,展示所有数据,ListView不可滑动。

这个做法是重写ListView的onMeasure方法,如下:

[html]  view plain  copy
  1. /**  
  2. * 设置不滚动  
  3. */  
  4. public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  
  5. {  
  6. int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,  
  7. MeasureSpec.AT_MOST);  
  8. super.onMeasure(widthMeasureSpec, expandSpec);  
  9. }  

这种方法是同事告诉我的,我没有用过。

做到这里,ScrollView和ListView的问题是解决了,但是ListView的效率问题出现了。

你会发现在ListView的adapter里的getview方法重复执行了很多次,技术使用了缓存技术也是无用的。

有时候数据只有两三个,但是getView方法却被执行了40多次。这样肯定是不行的。但是为什么单独使用ListView的时候却不会出现这种问题呢?

这个原因肯定出在ScrollView和ListView共存上。Google了一下,外国人都不建议他们共存,但是需求是这样的怎么办呢?

我的最终解决方案:自己写一个类似ListView的东西

一. 最初:

[html]  view plain  copy
  1. /**  
  2. * 虚拟listview  
  3. *  
  4. * @author JustMe  
  5. *  
  6. */  
  7. public class MyListView extends LinearLayout {  
  8. private BaseAdapter adapter;  
  9. private MyOnItemClickListener onItemClickListener;  
  10. /**  
  11. * 通知更新listview  
  12. */  
  13. public void notifyChange() {  
  14. int count = getChildCount();  
  15. LayoutParams params = new LayoutParams(LayoutParams.FILL_PARENT,  
  16. LayoutParams.WRAP_CONTENT);  
  17. for (int i = count; i < adapter.getCount(); i++) {  
  18. final int index = i;  
  19. final LinearLayout layout = new LinearLayout(getContext());  
  20. layout.setLayoutParams(params);  
  21. layout.setOrientation(VERTICAL);  
  22. View v = adapter.getView(i, null, null);  
  23. v.setOnClickListener(new OnClickListener() {  
  24. @Override  
  25. public void onClick(View v) {  
  26. if (onItemClickListener != null) {  
  27. onItemClickListener.onItemClick(MyListView.this,  
  28. layout, index, adapter.getItem(index));  
  29. }  
  30. }  
  31. });  
  32. // 每个条目下面的线  
  33. ImageView imageView = new ImageView(getContext());  
  34. imageView.setBackgroundResource(R.drawable.divider_list);  
  35. imageView.setLayoutParams(params);  
  36. layout.addView(v);  
  37. layout.addView(imageView);  
  38. addView(layout, index);  
  39. }  
  40. }  
  41. public MyListView(Context context) {  
  42. super(context);  
  43. initAttr(null);  
  44. }  
  45. public MyListView(Context context, AttributeSet attrs) {  
  46. super(context, attrs);  
  47. initAttr(attrs);  
  48. }  
  49. /**  
  50. * 设置方向  
  51. *  
  52. * @param attrs  
  53. */  
  54. public void initAttr(AttributeSet attrs) {  
  55. setOrientation(VERTICAL);  
  56. }  
  57. public BaseAdapter getAdapter() {  
  58. return adapter;  
  59. }  
  60. /**  
  61. * 设置adapter并模拟listview添加数据  
  62. *  
  63. * @param adpater  
  64. */  
  65. public void setAdapter(BaseAdapter adpater) {  
  66. this.adapter = adpater;  
  67. notifyChange();  
  68. }  
  69. /**  
  70. * 设置条目监听事件  
  71. *  
  72. * @param onClickListener  
  73. */  
  74. public void setOnItemClickListener(MyOnItemClickListener onClickListener) {  
  75. this.onItemClickListener = onClickListener;  
  76. }  
  77. /**  
  78. * 点击事件监听  
  79. *  
  80. * @author JustMe  
  81. *  
  82. */  
  83. public static interface MyOnItemClickListener {  
  84. public void onItemClick(ViewGroup parent, View view, int position,  
  85. Object o);  
  86. }  
  87. }  
这样实现了ListView的最基本的功能,并且提高了效率,例如,全选功能比以上那些方法的速度提高了2-3秒,页面也不卡顿。

缺点是不能一次加载很多的数据,不然数据会显示的很慢,最好分页加载。说到分页,之前都是在ListView上加footerView,在这里也可以做到。

二. 升级:

[html]  view plain  copy
  1. public class MyListView extends LinearLayout{  
  2. private BaseAdapter adapter;  
  3. private MyOnItemClickListener onItemClickListener;  
  4. boolean footerViewAttached = false;  
  5. private View footerview;  
  6. /**  
  7. * 通知更新listview  
  8. */  
  9. public void notifyChange() {  
  10. int count = getChildCount();  
  11. if (footerViewAttached) {  
  12. count--;  
  13. }  
  14. LayoutParams params = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);  
  15. for (int i = count; i < adapter.getCount(); i++) {  
  16. final int index = i;  
  17. final LinearLayout layout = new LinearLayout(getContext());  
  18. layout.setLayoutParams(params);  
  19. layout.setOrientation(VERTICAL);  
  20. View v = adapter.getView(i, null, null);  
  21. v.setOnClickListener(new OnClickListener() {  
  22. @Override  
  23. public void onClick(View v) {  
  24. if (onItemClickListener != null) {  
  25. onItemClickListener.onItemClick(MyListView.this, layout, index,  
  26. adapter.getItem(index));  
  27. }  
  28. }  
  29. });  
  30. ImageView imageView = new ImageView(getContext());  
  31. imageView.setBackgroundResource(R.drawable.divider_list);  
  32. imageView.setLayoutParams(params);  
  33. layout.addView(v);  
  34. layout.addView(imageView);  
  35. addView(layout, index);  
  36. }  
  37. }  
  38. public MyListView(Context context) {  
  39. super(context);  
  40. initAttr(null);  
  41. }  
  42. public MyListView(Context context, AttributeSet attrs) {  
  43. super(context, attrs);  
  44. initAttr(attrs);  
  45. }  
  46. public void initAttr(AttributeSet attrs) {  
  47. setOrientation(VERTICAL);  
  48. }  
  49. /**  
  50. * 初始化footerview  
  51. *  
  52. * @param footerView  
  53. */  
  54. public void initFooterView(final View footerView) {  
  55. this.footerview = footerView;  
  56. }  
  57. /**  
  58. * 设置footerView监听事件  
  59. *  
  60. * @param onClickListener  
  61. */  
  62. public void setFooterViewListener(OnClickListener onClickListener) {  
  63. this.footerview.setOnClickListener(onClickListener);  
  64. }  
  65. public BaseAdapter getAdapter() {  
  66. return adapter;  
  67. }  
  68. /**  
  69. * 设置adapter并模拟listview添加????数据  
  70. *  
  71. * @param adpater  
  72. */  
  73. public void setAdapter(BaseAdapter adpater) {  
  74. this.adapter = adpater;  
  75. removeAllViews();  
  76. if (footerViewAttached)  
  77. addView(footerview);  
  78. notifyChange();  
  79. }  
  80. /**  
  81. * 设置条目监听事件  
  82. *  
  83. * @param onClickListener  
  84. */  
  85. public void setOnItemClickListener(MyOnItemClickListener onClickListener) {  
  86. this.onItemClickListener = onClickListener;  
  87. }  
  88. /**  
  89. * 没有下一页了  
  90. */  
  91. public void noMorePages() {  
  92. if (footerview != null && footerViewAttached) {  
  93. removeView(footerview);  
  94. footerViewAttached = false;  
  95. }  
  96. }  
  97. /**  
  98. * 可能还有下一??  
  99. */  
  100. public void mayHaveMorePages() {  
  101. if (!footerViewAttached && footerview != null) {  
  102. addView(footerview);  
  103. footerViewAttached = true;  
  104. }  
  105. }  
  106. public static interface MyOnItemClickListener {  
  107. public void onItemClick(ViewGroup parent, View view, int position, Object o);  
  108. }  
  109. }

你可能感兴趣的:(android)