算譜王におれはなる!!!!

偏りはあると思うけど情報技術全般についてマイペースに書くよ。

Android2.3以降でViewのVisibilityの設定がうまくいかない

View#setVisibility()が上手く働かないことがある!
GLSurfaceViewをActivity#setContentView()でセットした後に、他のビューをActivity#addContentView()でセットします。そのビューに対してView#setVisibility()で表示・非表示設定を行っても、期待通り結果が得られないことがあります。

addContentView()って?
僕はAndroidでゲームを作るときには、よくGLSurfaceViewの上にButtonなどのウィジェットを配置したりします。それを実現する仕組みがActivity#addContentView(View, LayoutParams)です。
次の図は、Activity#addContentView()を使用するときのイメージです。

f:id:ngsw_taro:20110721233135p:image:w360

GLSurfaceViewはActivity#setContentView()で既に配置されています。その上にActivity#addContentView()でLinearLayoutなどのViewを配置できます。

実際のコードは次のようになります。

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    this.glSurfaceView = new GLSurfaceView(this);
    this.glSurfaceView.setRenderer(new MyRenderer());
    this.setContentView(this.glSurfaceView);

    final View view = this.getLayoutInflater().inflate(R.layout.main, null);
    this.addContentView(view, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
}

setVisibility()って?
View#setVisibility(int)は、ビューの表示・非表示を設定するメソッドです。渡せる引数はView.VISIBLE, View.INVISIBLE, View.GONEの3つです。
また僕のゲーム開発時の話ですが、ゲーム画面をGLSurfaceViewとして配置しておき、ゲームオーバになったときに「戻るボタン」などをユーザに使わせたいので、それらのウィジェットを上記のようにActivity#addContentView()を使用して配置します。
当然、プレイ中にボタンが見えていては邪魔なので非表示にしておきます。その際に、例えば次のようにしてボタンを見えないようにしておきます。

returnButton.setVisibility(View.INVISIBLE);

そして、ゲームオーバになったときに

returnButton.setVisibility(View.VISIBLE);

と記述した部分を呼び出してやれば、ボタンが表示されるという仕組みになってます。

setVisibility()を呼び出しても表示されない!
ここまでの説明で問題なく動作するのはAndroid2.2までです。Android2.3以降では、returnButton.setVisibility(View.VISIBLE)を呼び出してもreturnButtonは表示されません!
setContentView()されてるビューがGLSurfaceViewで、かつViewが初期状態で(というかViewが表示されるチャンスが訪れる前に)INVISIBLEになってる状況のみで起こる症状です。一度Viewが表示されてから、その後にINVISIBLEの設定、さらにその後にVISIBLEの設定にすると、きちんと表示されます。また、setContentViewでGLSurfaceView以外のViewをセットして実験したら、こちらもきちんと表示されます。

解決策

returnButton.setVisibility(View.INVISIBLE);

ではなく

returnButton.setVisibility(View.GONE);

とします。
これで解決しました。