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方法,代码如下 :

public class MGridView extends GridView {

    public boolean hasScrollBar = true;

    /**
     * @param context
     */
    public MGridView(Context context) {
        this(context, null);
    }

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

    public MGridView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int expandSpec = heightMeasureSpec;
        if (hasScrollBar) {
            expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
                    MeasureSpec.AT_MOST);
            super.onMeasure(widthMeasureSpec, expandSpec);// 注意这里,这里的意思是直接测量出GridView的高度
        } else {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
    }

}

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

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

/**
 * @author mrsimple
 */
public final class GridViewUtils {
    /**
     * 存储宽度
     */
    static SparseIntArray mGvWidth = new SparseIntArray();

    /**
     * 计算GridView的高度
     *
     * @param gridView 要计算的GridView
     */
    public static void updateGridViewLayoutParams(MGridView gridView, int maxColumn) {
        int childs = gridView.getAdapter().getCount();

        if (childs > 0) {
            int columns = childs < maxColumn ? childs % maxColumn : maxColumn;
            gridView.setNumColumns(columns);
            int width = 0;
            int cacheWidth = mGvWidth.get(columns);
            if (cacheWidth != 0) {
                width = cacheWidth;
            } else { // 计算gridview每行的宽度, 如果item小于3则计算所有item的宽度;
                     // 否则只计算3个child宽度,因此一行最多3个child。 (这里我们以3为例)
                int rowCounts = childs < maxColumn ? childs : maxColumn;
                for (int i = 0; i < rowCounts; i++) {
                    View childView = gridView.getAdapter().getView(i, null, gridView);
                    childView.measure(0, 0);
                    width += childView.getMeasuredWidth();
                }
            }

            ViewGroup.LayoutParams params = gridView.getLayoutParams();
            params.width = width;
            gridView.setLayoutParams(params);
            if (mGvWidth.get(columns) == 0) {
                mGvWidth.append(columns, width);
            }
        }
    }

ListView的getView方法如下 :

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder viewHolder = null;

    if (convertView == null) {
        convertView = mInflater.inflate(R.layout.listview_item, parent, false);
        viewHolder = new ViewHolder();
        viewHolder.mGridView = (MGridView) convertView.findViewById(R.id.my_gridview);
        viewHolder.mTextView = (TextView) convertView.findViewById(R.id.my_tv);
        convertView.setTag(viewHolder);
    } else {
        viewHolder = (ViewHolder) convertView.getTag();
    }

    final ListViewItem item = getItem(position);
    // 设置GridView的Adapter
    viewHolder.mGridView.setAdapter(new GridViewAdapter(mContext, item.mImages));
    // 计算GridView宽度, 设置默认为numColumns为3.
    GridViewUtils.updateGridViewLayoutParams(viewHolder.mGridView, 3);
    viewHolder.mTextView.setText(item.mText);
    return convertView;
}

这样,我们就解决了消息流的宽高问题。

时间: 2024-08-07 04:33:46

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

Android 中 ListView Adapter getView 被多次调用问题 解决方法

执行多次原因是因为每显示一个VIew,它都去测量view的高度,执行measure方法,导致getView执行多次. 解决方法是将 ListView 的 layout_width 设置为 fill_parent, <ListView android:id="@+id/lv_messages" android:layout_width="fill_parent" android:layout_height="fill_parent" andr

android listView嵌套gridview的使用心得

在开发的过程中可能需要用到listview嵌套gridview的场景,但是在Android中,不能在一个拥有Scrollbar的组件中嵌入另一个拥有Scrollbar的组件,因为这不科学,会混淆滑动事件,导致只显示一到两行数据.那么就换一种思路,首先让子控件的内容全部显示出来,禁用了它的滚动.如果超过了父控件的范围则显示父控件的scrollbar滚动显示内容,思路是这样,一下是代码.具体的方法是自定义GridView组件,继承自GridView.重载onMeasure方法: public cla

Android中ScrollView嵌套ListView只显示一行的解决方案

Android中ScrollView嵌套ListView只显示一行的解决方案 解决方案1: 直接把包含ListView控件的ScrollView控件从布局文件中去除,留下ListView控件,这是最简单快捷的解决办法. 如果一定要在ScrollView中包含ListView,则参考 解决方案2: public void showlist() { List<HashMap<String, String>> dataHashMaps = new ArrayList<HashMap

Android中RecyclerView嵌套RecyclerView或嵌套ListView

Android中RecyclerView嵌套RecyclerView或嵌套ListView

ListView嵌套GridView

首先,我们通过两个实例来了解下本篇文章所讲的重点,看下图: 微博: 陌陌: 大家应该对这两款软件并不陌生,接下来,我将列举下本文将要实现的几个点: 1.ListView嵌套GridView,互不冲突,界面显示及操作正常: 2.GridView中的图片依据屏幕大小,自动扩大或缩小以适应屏幕,并始终保持正方形: 3.图片数量不同时,布局不同(如上图的4张图片和6张图片): 4.点击ListView的item和点击Gridview的item互不冲突: 5.在GridView区域外的任何一点点击都可以触

Android中ListView异步加载图片错位、重复、闪烁问题分析及解决方案

Android中ListView异步加载图片错位.重复.闪烁问题分析及解决方案 我们在使用ListView异步加载图片的时候,在快速滑动或者网络不好的情况下,会出现图片错位.重复.闪烁等问题,其实这些问题总结起来就是一个问题,我们需要对这些问题进行ListView的优化. 比如ListView上有100个Item,一屏只显示10个Item,我们知道getView()中convertView是用来复用View对象的,因为一个Item的对应一个View对象,而ImageView控件就是View对象通

Android实现ListView或GridView首行/尾行距离屏幕边缘距离

Android上ListView&GridView默认行都是置顶的,这样会很丑. 一般为了解决这个问题都会在首行或尾行加上一个隐藏的View,那样实在是太麻烦了.在网上看博客的时候突然看到这个属性真的很有用! 直接上关键属性: 设置ListView或GridView的android:clipToPadding = true, 然后通过paddingTop和paddingBottom设置距离就好了. 博客原文: http://www.cnblogs.com/xitang/p/3606578.htm

Android中的帧动画的简单使用

Android中动画主要有下面几种,帧动画(frame),补间动画(tween),属性动画(property) 我们平时项目中主要用的是帧动画和补间动画 帧动画需要我们准备一组静态图片,这些图片是通过分解动画得来的,静态图片连起来播放形成动画效果 我们在res目录下新建一个drawable目录,用来存放动画资源和xml文件 图片如下: girl.xml 注意android:oneshot="false|true" 该属性控制动画是否重复播放,false代表重复播放 <?xml v

Android中ListView分类

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android = "http://schemas.android.com/apk/res/android" android:orientation = "vertical" android:layout_width = "fill_parent" android:layo