Android · 2015年6月1日 0

ScrollView下,ListView生存之道

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

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

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

java代码 
  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(00);
  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的一个方法,如下:

java代码 
  1. @Override
  2. public boolean onInterceptTouchEvent(MotionEvent ev) {
  3. return false;
  4. }

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

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

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

java代码 
  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的东西

一. 最初:

java代码 
  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, nullnull);
  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,在这里也可以做到。

二. 升级:

java代码 
  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, nullnull);
  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. }

 

这样就可以添加footerView了。最终我是使用了这种方案。

 

Share this: