Android UI GridView讲解

前言

GridView常用于以网格形式展示数据,九宫格布局就是一种典型的应用。本文将详细讲解GridView的使用方法和常用技巧。

基本使用

GridView的使用和ListView相似,重点在于数据由Adapter(适配器)提供,GridView并不直接访问数据源。因此,可以将GridView的使用分为3步:

  1. 获得数据源(如数组,List等)
  2. 通过数据源建立适配器(如ArrayAdapter等)
  3. 为GridView设置适配器

使用系统提供的布局

针对一些简单的场景(如只需要展示字符串),使用系统提供的ArrayAdapter即可。ArrayAdapter使用数组或List作为数据源,常用的两个构造方法如下:

//resource:列表项的布局文件
//objects:数据源
public ArrayAdapter(Context context,@LayoutRes int resource,T[] objects);
public ArrayAdapter(Context context,@LayoutRes int resource,List<T> objects)

使用GridView的示例代码如下:

<GridView
    android:id="@+id/grid_view_normal"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    android:numColumns="3"/>
//初始化普通布局的GridView
String[] normalArray=new String[]{"coding","ending","Java","Github","coder","Android"};
ArrayAdapter<String> normalAdapter=new ArrayAdapter<>(this,
        android.R.layout.simple_list_item_1,normalArray);
normalGridView.setAdapter(normalAdapter);

ArrayAdapter中使用的android.R.layout.simple_list_item_1是系统提供的布局文件,其实就是一个TextView。

效果截图:

监听点击事件

//监听单击事件
normalGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        String itemMsg= (String) parent.getAdapter().getItem(position);//获取选中对象
        Toast.makeText(GridViewActivity.this,itemMsg,Toast.LENGTH_SHORT).show();
    }
});
//监听长按事件
normalGridView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
    @Override
    public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
        Toast.makeText(GridViewActivity.this,"发生长按事件",Toast.LENGTH_SHORT).show();
        return true;
    }
});

可以看到,在监听器中通过parent.getAdapter().getItem(position)获取选中对象。注意,这个方法的返回值是Object对象,因此需要进行强制转换。

相关属性

android:columnWidth:设置列的宽度。
android:numColumns:设置需要展示的列数。 可选值[数字|auto_fit]
android:stretchMode:设置拉伸模式。 可选值[none|columnWidth|spacingWidth|spacingWidthUniform]
android:horizontalSpacing:设置列与列的水平间距。
android:verticalSpacing:设置行与行的垂直间距。
android:listSelector:设置列表项被选中时的效果。 [color或drawable资源]
android:fastScrollEnabled:是否在快速滑动的是否显示右侧的滑动块。
android:stackFromBottom:是否在初始状态时显示GridView的最底部。 [默认为false]

numColumns属性既可以使用具体的数字,也可以使用auto_fit,GridView会尽可能展示更多的列去填充可视区域。

stackFromBottom这个属性需要简单解释一下:如果设置为true,那么打开GridView首先看到的就是最底部的内容,看起来就像是GridView已经滚动到了最后一行;如果设置为false,就和默认状态一样,首先看到第一行的内容。

stretchMode是拉伸模式,即指定GridView的每一列应该怎样填充可用的空白区域,4种可选值的含义如下:

  1. none:不使用拉伸模式。[默认值]
  2. columnWidth:每一列会被平均拉伸。
  3. spacingWidth:每列之间的间隔会被拉伸。
  4. spacingWidthUniform:每列之间的间隔会被统一拉伸。

注意:如果要使用stretchMode,就需要设置columnWidth属性,否则GridView的内容可能不会显示。

下面给出不同stretchMode属性的具体表现:

none:

columnWidth:

spacingWidth:

spacingWidthUniform:

自定义网格布局

如果需要展示的内容比较复杂(比如图片加文字),我们就应该自定义适配器,使用自己的布局去展示列表项。

首先,建立一个实体类Book:

public class Book {
    private String name;
    private int imageRes;//图片资源

    public Book(String name, int imageRes) {
        this.name = name;
        this.imageRes = imageRes;
    }
    @Override
    public String toString() {
        return name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getImageRes() {
        return imageRes;
    }
    public void setImageRes(int imageRes) {
        this.imageRes = imageRes;
    }
}

接着,自定义一个布局文件(上边图片,下边文字),本例中命名为gridview_custom_item.xml,代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:gravity="center">
    <ImageView
        android:id="@+id/book_image"
        android:layout_width="45dp"
        android:layout_height="45dp"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="6dp"/>

    <TextView
        android:id="@+id/book_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:layout_marginTop="6dp"
        android:layout_marginBottom="6dp"
        android:textAllCaps="false"
        android:textSize="16sp" />
</LinearLayout>

然后,通过继承BaseAdapter实现我们自己的适配器,本例中命名为StyleGridViewAdapter

public class StyleGridViewAdapter extends BaseAdapter{
    private Context context;
    private List<Book> dataList;//数据源

    public StyleGridViewAdapter(Context context,List<Book> dataList) {
        this.dataList = dataList;
        this.context = context;
    }

    @Override
    public int getCount() {
        return dataList.size();
    }
    @Override
    public Object getItem(int position) {
        return dataList.get(position);
    }
    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Book book=dataList.get(position);
        ViewHolder viewHolder;
        if(convertView==null){//只有在无法复用View时再重新实例化列表项布局
            LayoutInflater inflater=LayoutInflater.from(context);
            convertView=inflater.inflate(R.layout.gridview_custom_item,parent,false);
            viewHolder=new ViewHolder();
            viewHolder.bookImageView=convertView.findViewById(R.id.book_image);
            viewHolder.bookNameView=convertView.findViewById(R.id.book_name);
            convertView.setTag(viewHolder);//存储ViewHolder
        }else{//复用已有的View
            viewHolder= (ViewHolder) convertView.getTag();
        }
        viewHolder.bookImageView.setImageResource(book.getImageRes());
        viewHolder.bookNameView.setText(book.getName());
        return convertView;
    }

    //用于在GridView中复用View
    static class ViewHolder{
        ImageView bookImageView;
        TextView bookNameView;
    }
}

可以看到,需要重写getCount、getItem、getItemId、getView这四个方法。此外,还要提供一个构造方法用于外界传入Context和数据源(本例中为List<Book>)。

注意:在自定义的适配器中通常会使用ViewHolder提升GridView的运行效率,这一方式将充分利用GridView中View的复用机制。

随后,在XML文件中定义GridView并设置相应的属性:

<GridView
    android:id="@+id/grid_view_custom"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    android:numColumns="3"
    android:columnWidth="100dp"
    android:stretchMode="columnWidth"/>

最后,在代码中为GridView设置自定义的适配器即可,代码如下:

//初始化自定义布局的GridView
List<Book> dataList=new ArrayList<>();
dataList.add(new Book("《小王子》",R.mipmap.ic_launcher));
dataList.add(new Book("《资本论》",R.mipmap.ic_launcher));
dataList.add(new Book("《三体》",R.mipmap.ic_launcher));
dataList.add(new Book("《必然》",R.mipmap.ic_launcher));
dataList.add(new Book("《创客》",R.mipmap.ic_launcher));
dataList.add(new Book("《平凡世界》",R.mipmap.ic_launcher));
StyleGridViewAdapter styleAdapter=new StyleGridViewAdapter(this,dataList);
customGridView.setAdapter(styleAdapter);

效果截图:

常用技巧

下面只列出了一些常见的技巧,其实很多ListView能够使用的技巧GridView也可以使用,更多的知识点可以参考这篇博客:

《Android UI ListView讲解》

去掉默认的选中颜色

只需将android:listSelector属性设置为#00000000就可以去掉默认的选中颜色(其实是设置为透明色)。

设置分割线

GridView和ListView不同,并没有专门的属性用于设置列表项间的分割线。如果想要在GridView为Item设置分割线,就需要在Item的布局文件中进行设置。

首先,在drawable文件夹下创建一个shape资源,本例中命名为gridview_item_divider.xml,示例代码如下:

<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <stroke android:color="#aaa" android:width="1dp"/>
</shape>

然后,为Item对应的根布局设置这个shape资源为background,示例代码如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/gridview_item_divider"
    android:gravity="center">
    <ImageView
        android:id="@+id/book_image"
        android:layout_width="45dp"
        android:layout_height="45dp"
        android:layout_marginTop="6dp"/>

    <TextView
        android:id="@+id/book_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="6dp"
        android:layout_marginBottom="6dp"
        android:textAllCaps="false"
        android:textSize="16sp" />
</LinearLayout>

效果截图:

注意:这种方法有一个不足之处,那就是内部的网格线会比外部的网格线要粗(因为这是两个Item间的网格线叠加形成的)。

监听滚动状态

只需要为GridView设置OnScrollListener即可,示例代码如下:

//监听ListView的滑动状态
normalGridView.setOnScrollListener(new AbsListView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        //滑动状态发生变化时触发
        //scrollState的可能值:[SCROLL_STATE_IDLE|SCROLL_STATE_TOUCH_SCROLL|SCROLL_STATE_FLING]
    }
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        //滑动完成时触发
        //firstVisibleItem:第一个可见项的索引值
        //visibleItemCount:可见项的个数
        //totalItemCount:列表项的总数
    }
});

onScrollStateChanged中的scrollState可能有三种取值,含义如下:

  1. SCROLL_STATE_IDLE:静止状态
  2. SCROLL_STATE_TOUCH_SCROLL:滑动状态(用户此时触碰着屏幕且在滑动)
  3. SCROLL_STATE_FLING:惯性滑动状态(用户此时未触碰屏幕,GridView借助上一次滑动的惯性滑动)

跳转到指定位置

//跳转到指定位置(让这个Item所在的行成为GridView当前的第一行)
public void setSelection(int position);

平滑滚动到指定位置

//平滑滚动到指定位置
public void smoothScrollToPosition(int position);

//平滑滚动n行高度的距离
//offset:需要滚动的行数(offset为正数时GridView向上滚动,为负数时向下滚动)
public void smoothScrollByOffset(int offset);

注意: smoothScrollToPosition只保证这个列表项在可视范围内,并不保证让这个Item所在的行成为GridView当前的第一行。

常见问题

  1. 子控件抢夺焦点
  2. 异步加载时图片显示错位

请参考这篇博客:《Android UI ListView讲解》

更多博客

《Android UI 常用控件讲解》:包括CheckBox、RadioButton、ToggleButton、Switch、ProgressBar、SeekBar、RatingBar、Spinner、ImageButton。
《Android UI 与文本相关的控件》:包括TextView、EditText、AutoCompleteTextView和MultiAutoCompleteTextView。
《Android UI ListView讲解》:详细讲解ListView的使用和常用技巧。

demo下载地址

https://github.com/CodingEnding/UISystemDemo [ 持续更新中 ]

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值