Android SDK从28升级到32后GridView首次加载不显示问题
代码已稳定运行十余年,将Android SDK从28升级至32后,应用首屏的GridView首次加载时无法显示。Item数量为预期的4个,代码已进入getView方法,但视图仍不显示;切换至其他页面后返回,GridView即可正常显示。SDK30及以上版本中GridView是否有变更?应用其余功能均正常。
相关代码
MainActivity 核心代码
public class MainActivity extends BaseActivity implements Constants { public GridView gridView; MainActivityAdapter adapter; private ArrayList<String> topicNames; private ArrayList<Integer> topicIconIds; private ArrayList<OnTouchListener> onClickListeners; @Override protected void onCreation(Bundle savedInstanceState) { setLayout(new MainLayout()); try { configureUI(); } catch (MyException exception) { exception.handleMe(this); } UIUtils.handleLastCrashIfAny(this); } private void configureUI() throws MyException { ((MainLayout) getBaseLayout()).configureUI(); prepareList(); Log.d("MainActivity", " creating mainlayout with topics=" + topicNames.stream().count()); adapter = new MainActivityAdapter(this, topicNames, topicIconIds); gridView.setAdapter(adapter); } class MainActivityAdapter extends BaseAdapter { private final ArrayList<String> adapterTopicNames; private final ArrayList<Integer> adapterTopicIcondIds; public MainActivityAdapter(Activity activity, ArrayList<String> listTopic, ArrayList<Integer> listImage) { super(); this.adapterTopicNames = listTopic; this.adapterTopicIcondIds = listImage; } @Override public int getCount() { return adapterTopicNames.size(); } @Override public String getItem(int position) { return adapterTopicNames.get(position); } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder view = null; if (convertView == null) { convertView = ((MainLayout) getBaseLayout()).getView(); } view = (ViewHolder) convertView.getTag(); // set image view parameters ((MainLayout) getBaseLayout()).setTopicImageParameters(view); view.textView.setText(adapterTopicNames.get(position).replace(" and ", " & ")); view.imageView.setId(position); view.imageView.setImageResource(adapterTopicIcondIds.get(position)); view.position = position; convertView.setId(position); view.imageView.setOnTouchListener(onClickListeners.get(position)); return convertView; } } public ArrayList<String> getTopicNames() { return topicNames; } @Override protected void onRestart() { super.onRestart(); adapter.notifyDataSetChanged(); getBaseLayout().hideBuyFullVersionButtonIfNeeded(); Log.d("MainActivity", "onRestart"); } }
MainLayout.configureUI 方法
public void configureUI() throws MyException { int landscapeRowsPortraitColumns = ApplicationDetails.getLandscapeRowCountPortraitColumnCount(); int landscapeColumsPortraitRows = ApplicationDetails.getLandscapeColumnCountPortraitRowCount(); if (activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { numberOfRows = landscapeColumsPortraitRows; numberOfColumns = landscapeRowsPortraitColumns; } else if (activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { numberOfRows = landscapeRowsPortraitColumns; numberOfColumns = landscapeColumsPortraitRows; } gridView = new GridView(activity) { @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if (!areCellDimensionsCalculated) { getCellDimensions(); } } }; gridView.setVerticalSpacing(1); gridView.setHorizontalSpacing(1); gridView.setOverScrollMode(GridView.OVER_SCROLL_NEVER); gridView.setStretchMode(GridView.NO_STRETCH); gridView.setSelector(android.R.color.transparent); gridView.setBackgroundColor(ApplicationDetails.getMainScreenDividerColor()); RelativeLayout.LayoutParams gridViewParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT); gridViewParams.addRule(RelativeLayout.BELOW, navigationBar.getId()); gridViewParams.addRule(RelativeLayout.ABOVE, footerBar.getId()); contentView.addView(gridView, gridViewParams); ((MainActivity) activity).gridView = gridView; }
问题分析与解决方案
1. 视图布局计算时机不匹配(SDK30+绘制逻辑调整)
从Android 11(SDK30)开始,系统对视图布局调度做了优化,你重写的GridView.onLayout中计算单元格尺寸的逻辑,可能在首次加载时未及时完成,导致Item因尺寸未确定而无法显示。
解决:
在getCellDimensions方法执行完毕后,立即调用gridView.requestLayout()或adapter.notifyDataSetChanged(),强制触发GridView重新测量和绘制。
2. Adapter的View复用逻辑存在漏洞
当前getView方法中,首次创建convertView时未绑定ViewHolder到Tag,导致view = (ViewHolder) convertView.getTag()可能返回null,后续控件赋值操作无效(虽然代码进入了getView,但实际视图未正确渲染)。
解决:
修正getView中的ViewHolder绑定逻辑:
@Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder view; if (convertView == null) { convertView = ((MainLayout) getBaseLayout()).getView(); view = new ViewHolder(); // 这里必须初始化view.textView、view.imageView等控件,比如: // view.textView = convertView.findViewById(R.id.topic_text); // view.imageView = convertView.findViewById(R.id.topic_icon); convertView.setTag(view); } else { view = (ViewHolder) convertView.getTag(); } // 后续设置数据的逻辑不变 ((MainLayout) getBaseLayout()).setTopicImageParameters(view); view.textView.setText(adapterTopicNames.get(position).replace(" and ", " & ")); view.imageView.setId(position); view.imageView.setImageResource(adapterTopicIcondIds.get(position)); view.position = position; convertView.setId(position); view.imageView.setOnTouchListener(onClickListeners.get(position)); return convertView; }
3. 首次加载时数据通知时机过早
设置Adapter的操作可能在GridView完成布局前执行,导致系统未触发绘制。
解决:
在configureUI中设置Adapter后,通过post延迟通知数据变更:
private void configureUI() throws MyException { ((MainLayout) getBaseLayout()).configureUI(); prepareList(); Log.d("MainActivity", " creating mainlayout with topics=" + topicNames.stream().count()); adapter = new MainActivityAdapter(this, topicNames, topicIconIds); gridView.setAdapter(adapter); // 延迟触发绘制 gridView.post(() -> adapter.notifyDataSetChanged()); }
4. StretchMode设置导致尺寸计算异常
你设置了GridView.NO_STRETCH,在SDK30+中,这种模式下如果未明确指定列宽,GridView可能无法正确计算Item的显示空间。
解决:
尝试修改StretchMode为GridView.STRETCH_COLUMN_WIDTH,或者主动设置列宽:
gridView.setStretchMode(GridView.STRETCH_COLUMN_WIDTH); // 或者根据计算的单元格尺寸设置列宽 gridView.setColumnWidth(calculatedCellWidth);
内容的提问来源于stack exchange,提问作者nirav dinmali

