Android中ListView嵌套GridView的简单消息流UI(解决宽高问题)

      最近搞一个项目,需要用到类似于新浪微博的消息流,即每一项有文字、有九宫格图片,因此这就涉及到ListView或者ScrollView嵌套GridView的问题。其中GridView的高度问题在网上都很容易找到答案,即覆写onMeasure方法,然后设置高度的MeasureSpec。但是宽度问题确实没有什么资料,这里所说的宽度问题是比如GridView的列数为3,那么即使只有一张图片,gridview的宽度也是match_parent的,导致用户点击在图片范围外但是在gridview范围内时ListView的点击事件不能捕获。

如图 :

           


     出现的问题截图如上,当只有一个图标时GridView的宽度(灰色区域)也是match_parent的,这个时候点击gridview区域是ListView是不能响应的,但是如果GridView的背景色与ListView的背景一致,用户是看不到GridView的宽度,当用户点击图片以外的区域,可能的操作就是进入到该条消息的详情页,但是由于此时GridView是match_parent的,所以ListView根本不会获取到点击事件,这样的体验很不好。我们需要的效果是这样的 :


  即GridView的大小刚好能够包含图片的大小,这样当GridView的背景色为默认时,用户点击图片以外的区域就是点击了ListView的Item。

  我们看看如何解决这个问题吧,首先布局方面就不讲了,主要还是讲讲GridView的宽高问题吧。解决GridView的高度问题,需要覆写GridView的onMeasure方法,代码如下 :

  1. public class MGridView extends GridView {  
  2.   
  3.     public boolean hasScrollBar = true;  
  4.   
  5.     /** 
  6.      * @param context 
  7.      */  
  8.     public MGridView(Context context) {  
  9.         this(context, null);  
  10.     }  
  11.   
  12.     public MGridView(Context context, AttributeSet attrs) {  
  13.         super(context, attrs, 0);  
  14.     }  
  15.   
  16.     public MGridView(Context context, AttributeSet attrs, int defStyle) {  
  17.         super(context, attrs, defStyle);  
  18.     }  
  19.   
  20.     @Override  
  21.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  22.   
  23.         int expandSpec = heightMeasureSpec;  
  24.         if (hasScrollBar) {  
  25.             expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,  
  26.                     MeasureSpec.AT_MOST);  
  27.             super.onMeasure(widthMeasureSpec, expandSpec);// 注意这里,这里的意思是直接测量出GridView的高度  
  28.         } else {  
  29.             super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  30.         }  
  31.     }  
  32.   
  33. }  

      在onMeasure方法中,因为hasScrollBase为true, 我们会注意到代码中有注释的分支。这里实际上就是在onMeasure的时候计算出GridView的高度,而不是只计算其中几个View的高度。如果没有这一步,那么GridView的高度将显示不全。将GridView的高度计算出来,这样就不需要上下滑动来显示其它的item了,因为问题所在就是GridView嵌套在了ScrollView或者包含ScrollView的组件中,从而引发的冲突。

   下面解决宽度的问题,思路就是在ListView的getView方法中手动计算每个GridView的图片个数,如果图片个数小于GridView每行的列数,则手动计算每个child view所需的宽度,然后GridView的宽度 = child个数 * 每个child view的宽度。为了避免重复计算,我们会缓存计算结果。计算代码如下 : 

  1. /** 
  2.  * @author mrsimple 
  3.  */  
  4. public final class GridViewUtils {  
  5.     /** 
  6.      * 存储宽度 
  7.      */  
  8.     static SparseIntArray mGvWidth = new SparseIntArray();  
  9.   
  10.     /** 
  11.      * 计算GridView的高度 
  12.      *  
  13.      * @param gridView 要计算的GridView 
  14.      */  
  15.     public static void updateGridViewLayoutParams(MGridView gridView, int maxColumn) {  
  16.         int childs = gridView.getAdapter().getCount();  
  17.   
  18.         if (childs > 0) {  
  19.             int columns = childs < maxColumn ? childs % maxColumn : maxColumn;  
  20.             gridView.setNumColumns(columns);  
  21.             int width = 0;  
  22.             int cacheWidth = mGvWidth.get(columns);  
  23.             if (cacheWidth != 0) {  
  24.                 width = cacheWidth;  
  25.             } else { // 计算gridview每行的宽度, 如果item小于3则计算所有item的宽度;  
  26.                      // 否则只计算3个child宽度,因此一行最多3个child。 (这里我们以3为例)  
  27.                 int rowCounts = childs < maxColumn ? childs : maxColumn;  
  28.                 for (int i = 0; i < rowCounts; i++) {  
  29.                     View childView = gridView.getAdapter().getView(i, null, gridView);  
  30.                     childView.measure(00);  
  31.                     width += childView.getMeasuredWidth();  
  32.                 }  
  33.             }  
  34.   
  35.             ViewGroup.LayoutParams params = gridView.getLayoutParams();  
  36.             params.width = width;  
  37.             gridView.setLayoutParams(params);  
  38.             if (mGvWidth.get(columns) == 0) {  
  39.                 mGvWidth.append(columns, width);  
  40.             }  
  41.         }  
  42.     }  

ListView的getView方法如下 : 

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. public View getView(int position, View convertView, ViewGroup parent) {  
  3.     ViewHolder viewHolder = null;  
  4.   
  5.     if (convertView == null) {  
  6.         convertView = mInflater.inflate(R.layout.listview_item, parent, false);  
  7.         viewHolder = new ViewHolder();  
  8.         viewHolder.mGridView = (MGridView) convertView.findViewById(R.id.my_gridview);  
  9.         viewHolder.mTextView = (TextView) convertView.findViewById(R.id.my_tv);  
  10.         convertView.setTag(viewHolder);  
  11.     } else {  
  12.         viewHolder = (ViewHolder) convertView.getTag();  
  13.     }  
  14.   
  15.     final ListViewItem item = getItem(position);  
  16.     // 设置GridView的Adapter  
  17.     viewHolder.mGridView.setAdapter(new GridViewAdapter(mContext, item.mImages));  
  18.     // 计算GridView宽度, 设置默认为numColumns为3.  
  19.     GridViewUtils.updateGridViewLayoutParams(viewHolder.mGridView, 3);  
  20.     viewHolder.mTextView.setText(item.mText);  
  21.     return convertView;  
  22. }  

    这样,我们就解决了消息流的宽高问题。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值