和 ListView 一樣,GridView 也是一個(gè) ViewGroup,它用來將各種不同的控件整合到一起,按照一個(gè)二維可以滾動(dòng)的網(wǎng)格視圖展示出來。同時(shí)也遵循 MVC 模式,依靠 Adapter 自動(dòng)幫我們完成 UI 和數(shù)據(jù)的綁定。
1. GridView 的特性
GridView 在 Android App 中運(yùn)用非常廣泛,比如我們手機(jī)的系統(tǒng)相冊(cè)將我們的照片及照片名稱按照網(wǎng)格的樣式排列起來,并且可以上下滾動(dòng),這種效果非常適合用 GridView 實(shí)現(xiàn)。
為了實(shí)現(xiàn) MVC 模式,更方便的管理數(shù)據(jù)與 UI,GridView 通過 Adapter 完成數(shù)據(jù)的填充,Adapter 的使用幾乎和 ListView 一樣,另外系統(tǒng)提供了幾種簡(jiǎn)單的 Adapter,具體可以參考 23 節(jié)和 24 節(jié)的內(nèi)容。
2. GridView 的基本用法
GridView 和 上一節(jié)所學(xué)的 ListView 極其相似,主要還是從屬性、API 及事件監(jiān)聽器三個(gè)方面來介紹基本用法。
2.1 GridView 的常用屬性
- android:columnWidth:
設(shè)置網(wǎng)格的列寬 - android:gravity:
網(wǎng)格內(nèi)各個(gè) item 的對(duì)齊方式 - android:horizontalSpacing:
網(wǎng)格的各個(gè) item 在水平方向上的間距 - android:verticalSpacing:
網(wǎng)格的各個(gè) item 在垂直方向上的間距 - android:numColumns:
設(shè)置列數(shù),可以直接設(shè)置距離也可以設(shè)置成“auto_fit”讓系統(tǒng)自適應(yīng) - android:stretchMode:
設(shè)置網(wǎng)格拉伸模式,可選值如下:- none: 不拉伸
- spacingWidth: 將多余空間分?jǐn)偨o網(wǎng)格的間隔空隙
- columnWidth: 將多余空間分?jǐn)偨o網(wǎng)格的各個(gè)列
- spacingWidthUniform: 將多余空間分?jǐn)偨o網(wǎng)格的各個(gè)列及其間隔空隙
2.2 GridView 的常用 API
- smoothScrollByOffset:
平滑滾動(dòng)一個(gè)相對(duì)距離 - smoothScrollToPosition:
平滑滾動(dòng)到某個(gè)位置
2.3 GridView 的事件監(jiān)聽器
- setOnItemClickListener:
設(shè)置 GridView 的 item 點(diǎn)擊事件回調(diào),此接口與 ListView 完全一樣。
3. GridView 的使用示例
為了和 ListView 以及 Adapter 兩節(jié)的內(nèi)容作對(duì)比,本節(jié)依舊實(shí)現(xiàn)“水果列表”的例子,只不過本節(jié)會(huì)把單維列表改成二維的網(wǎng)格樣式,代碼可基于上一節(jié)直接修改。
3.1 布局文件編寫
首先修改布局文件,將 ListView 替換成 GridView 并添加一些 GridView 特有的屬性,如下:
<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/gridview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:columnWidth="110dp"
android:numColumns="auto_fit"
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
android:stretchMode="columnWidth"
android:gravity="center" />
3.2 GridView 適配器
以上屬性都在 25.2.1 小節(jié)有描述,也比較好理解,接著修改 MyAdapter 類,它是繼承自 BaseAdapter 實(shí)現(xiàn)的自定義適配器,因?yàn)槎S列表更節(jié)省空間,上一節(jié)的水果數(shù)目已經(jīng)沒法占滿一屏,這樣會(huì)導(dǎo)致列表不能滑動(dòng),不利于體驗(yàn) GridView 的效果。為此有兩種解決方法:一個(gè)是我們可以手動(dòng)擴(kuò)展我們的列表數(shù)組,增加一些水果名稱和圖片;第二種方法是直接修改適配器,不斷地循環(huán)從之前的列表中取水果數(shù)據(jù)。為了讓大家更好的理解適配器的原理,我們采用第二種方法來擴(kuò)展列表,我們需要修改兩個(gè)回調(diào)方法:getCount
和getView
。
getCount
表示列表的總 item 數(shù),我們直接將水果列表長度乘以10:@Override public int getCount() { return mName.length * 10; }
getView
中我們完成 View 和數(shù)據(jù)的綁定,我們需要循環(huán)取數(shù),所以只需要將 item 的位置對(duì)數(shù)組大小取模即可:@Override public View getView(int position, View convertView, ViewGroup parent) { convertView = LayoutInflater.from(mContext).inflate(R.layout.list_view, null); TextView name = convertView.findViewById(R.id.textView); ImageView image = convertView.findViewById(R.id.imageView); // item 的位置對(duì)數(shù)組長度取模,實(shí)現(xiàn)循環(huán)取值 name.setText(mName[position % mName.length]); image.setImageResource(mResId[position % mResId.length]); return convertView; }
完整的 MyAdapter 代碼如下:
package com.emercy.myapplication;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class MyAdapter extends BaseAdapter {
private Context mContext;
private String[] mName;
private int[] mResId;
public MyAdapter(Context context) {
mContext = context;
}
public void setData(String[] name, int[] resId) {
mName = name;
mResId = resId;
}
@Override
public int getCount() {
return mName.length * 10;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.list_view, null);
TextView name = convertView.findViewById(R.id.textView);
ImageView image = convertView.findViewById(R.id.imageView);
name.setText(mName[position % mName.length]);
image.setImageResource(mResId[position % mResId.length]);
return convertView;
}
}
3.3 主 Activity 邏輯
Activity 的邏輯其實(shí)和 ListView 中的例子完全一樣,只需要將所有的 ListView 類型改成 GridView 即可。這里體現(xiàn)了 MVC 設(shè)計(jì)思路的靈活性,我們想要替換一個(gè)樣式其實(shí)只需要修改布局文件,主邏輯和數(shù)據(jù)層完全不需要修改,這就是前面所說的 UI 和數(shù)據(jù)解耦的強(qiáng)大優(yōu)勢(shì)。MainActivity 代碼如下:
package com.emercy.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.Toast;
public class MainActivity extends Activity {
GridView mGridView;
String[] mDataName = {"蘋果", "梨", "香蕉", "桃子", "西瓜", "荔枝", "橘子"};
int[] mDataImage = {R.drawable.apple, R.drawable.pear, R.drawable.banana, R.drawable.peach,
R.drawable.watermelon, R.drawable.lychee, R.drawable.orange, R.drawable.orange};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mGridView = findViewById(R.id.gridview);
MyAdapter adapter = new MyAdapter(this);
adapter.setData(mDataName, mDataImage);
mGridView.setAdapter(adapter);
mGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Toast.makeText(getApplicationContext(), mDataName[i % mDataName.length], Toast.LENGTH_LONG).show();
}
});
}
}
編譯之后,可以發(fā)現(xiàn)從一維列表變成了網(wǎng)格列表,水果的樣式循環(huán) 10 次,效果如下:
4. 小結(jié)
本節(jié)介紹了 Adapter 常用的第二個(gè)控件,它與 ListView 的用法及其相似,作為網(wǎng)格樣式,相比 ListView 有一些二維的屬性,其余的 API 和事件回調(diào)都和 ListView 完全一樣。最后我們完成了和 ListView 類似的一個(gè)“水果”列表樣式,通過修改 Adapter 可以達(dá)到擴(kuò)展列表的效果。
這里也希望大家能夠注意到 Adapter 的幾個(gè)回調(diào)接口具體的含義,因?yàn)楹芏嗳穗m然經(jīng)常用 Adpater,但其實(shí)都是墨守成規(guī)的去實(shí)現(xiàn)那幾個(gè)回調(diào),沒有真正的理解其內(nèi)涵,這樣在今后的實(shí)際開發(fā)中往往會(huì)遇到很多不可控的問題,希望大家能夠做到知其然并知其所以然。