最近做一个项目时遇到一点问题,在这里分享一下解决思路。
首先看效果图:
这里的需求是实现界面中的六个图标,博主后来和同事讨论过这个问题,用 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 不能解决的时候,那么收效就有了。