Android GridView之添加分隔线,动态设置高度,实现高度自适应,并解决第一个item不显示的问题

最近做一个项目时遇到一点问题,在这里分享一下解决思路。

首先看效果图:


这里的需求是实现界面中的六个图标,博主后来和同事讨论过这个问题,用 GridView 实现费时费力好嘛,同事认为做6个 Button 就

好了,可能博主就爱钻牛角尖吧,一开始认为怎么办只要还有办法那就按自己想的去做出来,好了不多废话了,下面来讲思路。


首先是分割线的问题,我们都知道 ListView 中有

分割线颜色  
android:divider="#FFFFFF"
分割线高度
android:dividerHeight="1px"

通过这两个属性可以轻松给 ListView 添加分隔线,于是博主也查了查文档,发现 GridView 中没有类似的属性,那怎么办好呢,继续

翻翻文档吧,发现在 GriView 中有这两个属性:

垂直间距
android:verticalSpacing="10dp"
水平间距
android:horizontalSpacing="10dp"

那么想法就来了,用一个 LinearLayout 布局包裹 GridView ,将 LinearLayout 的背景色设为分隔线的颜色,然后给GridView 设置垂

直和水平间距,那么不就达到分隔线的效果了嘛,那说干就干,首先是 GridView 布局文件:

    <LinearLayout
        android:id="@+id/gridViewLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/color3"
        android:orientation="vertical">

        <GridView
            android:id="@+id/gridView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:horizontalSpacing="1dp"
            android:listSelector="@android:color/transparent"
            android:numColumns="3"
            android:stretchMode="columnWidth"
            android:verticalSpacing="1dp" />

    </LinearLayout>

然后是 item 的布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    android:background="@color/color1">

    <ImageView
        android:id="@+id/image"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:adjustViewBounds="true"
        android:scaleType="fitXY" />

    <TextView
        android:id="@+id/text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="3dp"
        android:textColor="@color/color2"
        android:textSize="13sp" />

</LinearLayout>

接下来是适配器:

public class GridAdapter extends BaseAdapter {

    private Context context = null;
    private int[] image;
    private String[] text;

    public GridAdapter(Context context, int[] image, String[] text) {
        this.context = context;
        this.image = image;
        this.text = text;
    }

    @Override
    public int getCount() {
        return image.length;
    }

    @Override
    public Object getItem(int position) {
        return position;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final ViewHolder viewHolder;
        if (convertView == null) {
            viewHolder = new ViewHolder();
            convertView = LayoutInflater.from(context).inflate(R.layout.gridview_item, parent, false);
            viewHolder.imageView = (ImageView) convertView.findViewById(R.id.image);
            viewHolder.textView = (TextView) convertView.findViewById(R.id.text);
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }
        viewHolder.imageView.setImageResource(image[position]);
        viewHolder.textView.setText(text[position]);
        return convertView;
    }

    public class ViewHolder {
        ImageView imageView;
        TextView textView;
    }
}
到这里感觉要大功告成了,运行一看,出问题了:


GridView 的高度并没有填充整个 LinearLayout ,可以看到下面灰色的空白处都是LinearLayout 的空间,而 GridView 只是刚刚好包

裹了内容。于是想了想,是不是因为加载适配器的时候,item 的属性被重画了,那就先试试在适配器中动态设置 item 的高度看看,

于是在 item 加载后添加如下的代码,同时在构造器中将 LinearLayout 传入:

convertView.setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                    gridViewLayout.getHeight() / 2));
再运行看看,看到结果的那一瞬间,博主内心不禁吐槽WTF!

第一个 item 去哪里了,就这样被吃掉了嘛,查了查资料,说是 GridView 在绘画时是根据第一个 item 定位其余 item 的,动态设置

item 高度,会导致第一个 item 丢失。解决方法呢,其实也不难,如果凑巧你可能还不会发现这个问题。方法是不要用ViewHolder 写

法,删除 item 布局,改成每次 getView 时创建新的 View 。

修改后代码如下:

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        LinearLayout item = new LinearLayout(context);
        item.setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                gridViewLayout.getHeight() / (image.length / COL_NUMBER)));
        item.setOrientation(LinearLayout.VERTICAL);
        item.setBackgroundColor(ContextCompat.getColor(context, R.color.color1));
        item.setGravity(Gravity.CENTER);

        ImageView imageView = new ImageView(context);
        imageView.setLayoutParams(new AbsListView.LayoutParams(120, 120));
        imageView.setAdjustViewBounds(true);
        imageView.setScaleType(ImageView.ScaleType.FIT_XY);
        imageView.setImageResource(image[position]);
        item.addView(imageView);

        TextView textView = new TextView(context);
        LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);
        lp.setMargins(0, 15, 0, 0);
        textView.setLayoutParams(lp);
        textView.setTextSize(13);
        textView.setTextColor(ContextCompat.getColor(context, R.color.color2));
        textView.setText(text[position]);
        item.addView(textView);

        return item;
    }

到这里总算是大功告成了:


这边经别人提点,还有一种解决方法,不需要每次 new 一个 View ,依然采用 ViewHolder 写法:

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final ViewHolder viewHolder;
        if (convertView == null) {
            Log.i("convertView","null");
            viewHolder = new ViewHolder();
            convertView = LayoutInflater.from(context).inflate(R.layout.gridview_item, parent, false);
            convertView.setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                    gridViewLayout.getHeight() / 2));
            viewHolder.imageView = (ImageView) convertView.findViewById(R.id.image);
            viewHolder.textView = (TextView) convertView.findViewById(R.id.text);
            convertView.setTag(viewHolder);
        } else {
            Log.i("convertView","not null");
            convertView.setLayoutParams(new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                    gridViewLayout.getHeight() / 2));
            viewHolder = (ViewHolder) convertView.getTag();
        }
        viewHolder.imageView.setImageResource(image[position]);
        viewHolder.textView.setText(text[position]);
        return convertView;
    }

但是这里博主也不是很明白,第一次已经为 convertView 设置了 LayoutParams ,在后面 LayoutParams 的信息就丢失了,控制台打

印的信息如下:

10-30 15:42:23.387 4158-4158/com.liuwan.mydesign.activity I/convertView: null
10-30 15:42:23.407 4158-4158/com.liuwan.mydesign.activity I/convertView: not null
10-30 15:42:23.450 4158-4158/com.liuwan.mydesign.activity I/convertView: not null
10-30 15:42:23.452 4158-4158/com.liuwan.mydesign.activity I/convertView: null
10-30 15:42:23.465 4158-4158/com.liuwan.mydesign.activity I/convertView: null
10-30 15:42:23.475 4158-4158/com.liuwan.mydesign.activity I/convertView: null
10-30 15:42:23.489 4158-4158/com.liuwan.mydesign.activity I/convertView: null
10-30 15:42:23.502 4158-4158/com.liuwan.mydesign.activity I/convertView: null
10-30 15:42:23.515 4158-4158/com.liuwan.mydesign.activity I/convertView: null
10-30 15:42:23.520 4158-4158/com.liuwan.mydesign.activity I/convertView: not null
10-30 15:42:23.666 4158-4158/com.liuwan.mydesign.activity I/convertView: not null
10-30 15:42:23.674 4158-4158/com.liuwan.mydesign.activity I/convertView: not null
6个 item 调用了12次 getView ,这里的顺序也是没看懂什么意思,可能和 GridView 底层的绘制方法有关吧,如果有人明白希望可以

解答。

最后,博主还是想说一下,这个问题前前后后花了我两个小时解决,要是用 Button 就是几分钟的事,但是我觉得值得,我多学会了

一种设计思路,以后遇到更复杂的设计,以至于 Button 不能解决的时候,那么收效就有了。


源码下载


评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值