Android GridView 開發中常見「黑屏退出」問題,多因 setContentView 重複調用、適配器 getView 實現錯誤、項目布局過度嵌套導致。本文分析典型錯誤代碼,給出正確實現方案。
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); // 第一次
inflater = LayoutInflater.from(getApplicationContext());
GridView dev_listview = (GridView) findViewById(R.id.dev_listview);
MyAdapter gAdapter = new MyAdapter(this);
dev_listview.setAdapter(gAdapter);
setContentView(R.id.dev_listview); // 錯誤!參數應為布局資源 ID,且重複設置導致 UI 重置
}
後果: 第二次 setContentView 參數錯誤(傳入 View ID 而非 Layout ID),導致 Activity 內容視圖丟失,黑屏/崩潰。
public View getView(int position, View grid, ViewGroup parent) {
inflater = (LayoutInflater) mcontext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (grid != null) { // 錯誤:convertView 非空時應復用,而非重新 inflate
inflater = LayoutInflater.from(mcontext);
grid = inflater.inflate(R.layout.item_9, parent); // 缺少 attachToRoot=false
}
return grid; // grid 可能為 null(首次調用時)
}
問題:
convertView == null 時才應 inflateViewHolder 模式,頻繁 findViewById 導致卡頓inflate 缺少第三參數 false,導致布局參數錯誤null,引發 NPE布局嵌套 10+ 層 LinearLayout,包含大量 ImageView/TextView/ProgressBar,嚴重拖慢渲染,極易 OOM。
public class MainActivity extends AppCompatActivity {
private GridView gridView;
private MyAdapter adapter;
private List dataList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); // 僅調用一次
gridView = findViewById(R.id.dev_listview);
// 初始化測試數據
for (int i = 0; i < 20; i++) {
dataList.add(new ItemData("Item " + i, R.drawable.unpress9));
}
adapter = new MyAdapter(this, dataList);
gridView.setAdapter(adapter);
}
}
public class MyAdapter extends BaseAdapter {
private Context context;
private List dataList;
private LayoutInflater inflater;
public MyAdapter(Context context, List dataList) {
this.context = context;
this.dataList = dataList;
this.inflater = LayoutInflater.from(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) {
ViewHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.grid_item, parent, false); // 關鍵:false
holder = new ViewHolder();
holder.imageView = convertView.findViewById(R.id.item_image);
holder.textView = convertView.findViewById(R.id.item_text);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
ItemData item = dataList.get(position);
holder.imageView.setImageResource(item.imageRes);
holder.textView.setText(item.title);
return convertView;
}
static class ViewHolder {
ImageView imageView;
TextView textView;
}
}
public class ItemData {
public String title;
public int imageRes;
public ItemData(String title, int imageRes) {
this.title = title;
this.imageRes = imageRes;
}
}
題目提到「item 有 9 行,下個 item 調用 5 行的 item5.xml,保證每行高度一致」。
方案: 使用 RecyclerView + GridLayoutManager + SpanSizeLookup,或自定義 GridView 適配器通過 getViewTypeCount() + getItemViewType() 實現多佈局,行高由最內層容器 layout_height="wrap_content" 自然撐開,配合 android:gravity="center" 對齊。
| 錯誤 | 後果 | 修正 |
|---|---|---|
| 重複 setContentView | UI 重置、黑屏、崩潰 | 生命周期中僅調用一次 |
| getView 邏輯顛倒 | 項目不顯示、NPE、閃爍 | convertView==null 時 inflate,否則復用 |
| inflate 缺少 false | 布局參數失效、項目尺寸異常 | inflater.inflate(res, parent, false) |
| 無 ViewHolder | 滑動卡頓、頻繁 GC | 靜態內部類緩存 findViewById 結果 |
| 布局過度嵌套 | 渲染慢、OOM、層級過深警告 | 扁平化、使用 ConstraintLayout |
GridView 核心三要素:正確的 Activity 初始化、高性能適配器(ViewHolder+復用)、扁平化項目布局。新項目建議直接使用 RecyclerView + GridLayoutManager,功能更強、性能更好、API 更現代。