键盘收起后部分布局无法正常绘制的问题排查求助
嘿,我来帮你拆解下这个问题——你遇到的键盘收起后Header持续隐藏的问题,确实和你怀疑的SurfaceView脱不了干系,不过还有几个布局和UI线程的细节需要调整,咱们一步步来看:
核心问题1:SurfaceView的绘制逻辑阻塞了UI线程
你现在用主线程的Handler来执行SurfaceView的绘制,这完全违背了SurfaceView的设计初衷!SurfaceView就是为了在后台线程绘制,避免阻塞UI而生的。一旦renderer.draw(canvas)有任何耗时操作(哪怕只是几百毫秒),都会卡住主线程,导致系统的布局重绘、用户交互事件无法及时处理——这就解释了为什么键盘收起后Header无法恢复,只有用户再次交互时,系统才会强行优先处理UI更新。
修复方案:把绘制逻辑移到后台线程
重构你的CustomPreview,用独立的后台线程来处理绘制循环,而不是依赖主线程的Handler:
public class CustomPreview extends SurfaceView implements SurfaceHolder.Callback { private RenderThread renderThread; private Renderer renderer; private boolean visible; private boolean canLoadData; private HashMap<String, String> data; public CustomPreview(Context context, AttributeSet attrs) { super(context, attrs); getHolder().addCallback(this); getHolder().setFormat(PixelFormat.RGBA_8888); visible = false; canLoadData = false; renderer = new Renderer(context); data = new HashMap<>(); } @Override public void surfaceCreated(SurfaceHolder holder) { visible = true; // 启动后台绘制线程 renderThread = new RenderThread(getHolder()); renderThread.start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { renderer.updateDimensions(width, height); canLoadData = true; renderer.updateData(data); // 通知线程立即重绘 if (renderThread != null) { renderThread.requestRender(); } } @Override public void surfaceDestroyed(SurfaceHolder holder) { renderer.clearData(); visible = false; canLoadData = false; // 安全停止后台线程 if (renderThread != null) { renderThread.quit(); try { renderThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } renderThread = null; } } public void setVisibility(boolean visible) { this.visible = visible; if (renderThread != null) { renderThread.requestRender(); } } public void saveData(String key, String value) { data.put(key, value); if (canLoadData) { renderer.updateData(data); } if (renderThread != null) { renderThread.requestRender(); } } public String loadData(String key) { return data.get(key); } // 独立的后台绘制线程 private class RenderThread extends Thread { private final SurfaceHolder surfaceHolder; private boolean running = true; private boolean needsRender = true; public RenderThread(SurfaceHolder holder) { this.surfaceHolder = holder; } public void requestRender() { needsRender = true; interrupt(); // 唤醒线程立即执行绘制 } public void quit() { running = false; interrupt(); } @Override public void run() { while (running) { if (visible && needsRender) { Canvas canvas = null; try { canvas = surfaceHolder.lockCanvas(); if (canvas != null) { renderer.draw(canvas); } } finally { if (canvas != null) { surfaceHolder.unlockCanvasAndPost(canvas); } } needsRender = false; } // 等待下一次绘制请求或自动重绘 try { Thread.sleep(60000); // 保持你原来的每分钟自动重绘逻辑 } catch (InterruptedException e) { // 被唤醒,立即检查是否需要绘制 } } } } }
核心问题2:系统UI标志的临时特性导致Header显示异常
你设置的SYSTEM_UI_FLAG_HIDE_NAVIGATION和SYSTEM_UI_FLAG_FULLSCREEN是临时生效的——当键盘弹出、用户点击屏幕等事件发生时,系统会自动清除这些标志,导致你为Header设置的状态栏padding失效,Header要么被状态栏遮挡,要么被移出屏幕。
修复方案:使用沉浸式粘性模式+监听UI变化
改用SYSTEM_UI_FLAG_IMMERSIVE_STICKY保持沉浸式状态,同时监听系统UI的显示/隐藏,动态调整布局padding:
@Override protected void onResume() { super.onResume(); setupImmersiveMode(); } @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus) { setupImmersiveMode(); } } private void setupImmersiveMode() { getWindow().getDecorView().setSystemUiVisibility( View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); // 初始化padding updateLayoutPadding(); } // 动态调整Header和Footer的padding private void updateLayoutPadding() { boolean isSystemUiVisible = (getWindow().getDecorView().getSystemUiVisibility() & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0; int statusBarHeight = isSystemUiVisible ? getStatusBarHeight() : 0; findViewById(R.id.container_header).setPadding(0, statusBarHeight, 0, 0); int navBarHeight = isSystemUiVisible ? getNavigationBarHeight() : 0; findViewById(R.id.container_footer).setPadding(0, 0, 0, navBarHeight); } // 获取状态栏高度 private int getStatusBarHeight() { int result = 0; int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android"); if (resourceId > 0) { result = getResources().getDimensionPixelSize(resourceId); } return result; } // 获取导航栏高度 private int getNavigationBarHeight() { int result = 0; int resourceId = getResources().getIdentifier("navigation_bar_height", "dimen", "android"); if (resourceId > 0) { result = getResources().getDimensionPixelSize(resourceId); } return result; }
核心问题3:软键盘模式设置不合理
默认的软键盘模式可能导致键盘弹出时整个布局被向上平移,直接把Header顶出屏幕。建议设置adjustResize,让系统自动调整布局大小,而不是平移:
在AndroidManifest.xml的对应Activity标签中添加:
android:windowSoftInputMode="adjustResize"
总结
按照上面的步骤修改后,SurfaceView的绘制不会再阻塞UI线程,系统UI的状态变化也能被正确处理,软键盘弹出/收起时的布局调整也会更合理,Header应该就能正常显示了。
内容的提问来源于stack exchange,提问作者JudgedPluto

