TextView のredraw

 いろいろとイジくっていたら、TextView が正常表示されなくなった。
 部分的に欠けた表示になるんだ。
 再描画させればいいことはわかるんだけど、その方法がわからない…。
 で、getText してsetText したら、うまいこといったよ。

	setText( (String) getText() );

TextView の再帰ループ

 いま作ってるプログラムでは、TextView を二枚使ってる。
 ひとつはメッセージ表示。
 もうひとつはデバッグ・モニター。

 それでメッセージ側に、タッチを即すアイコンを出しているんだけど…。
 これまた、ひょんなことから表示されなくなってしまったんだ。
 原因を調べていたら、デバッグ・モニターも表示されなくなってたのに気づいた。
 こっちは単純な表示フラグの問題だったんで、直したところ、なんとアイコンも表示されるようになったんだ。
 どうやら、デバッグ・モニターの再描画を、メッセージ側も拾っていたらしい…。
 Σ そんなん、アリですか?! (笑

 でも、デバッグ・モニター側でのsetText って、onDraw の中でしかやってないんだよね。
 ふしぎ…
 って、まてまて。
 ということは、↓ってことだよ。

 onDraw → setText →onDraw →setText →…

 どう見ても、再帰ルーチン。
 つまり、スレッドなしで、安全な描画ループができるってことだよ。
 これはまた、妙な技を見つけてしまった…。

by the way…

 TextView なのに定期描画されてるの、なんか、おかしいと思ってたんだ。(笑

Posted in 裏技, 雑記. Tags: . TextView のredraw はコメントを受け付けていません »

TextViewはスクロールできません…

 表示するテキストが表示枠からはみ出したら、スクロールバーを出そう。
 そう思って調べてみると…
 TextViewはスクロールできません…
 どうやらスクロールさせるためには、ScrollView を使わなきゃならないらしい。

 ScrollViewは、ViewGroup の一種らしい。
 おそらく一種の窓みたいなもので、傘下のView の表示位置を変えて、スクロールさせるんだろうね。
 昔、そんなのを作ったよ…。

 つまりやるには、メッセージ表示のTextView を、ScrollView の傘下に配置しなきゃいけない。
 構造変更しなきゃならないよ… orz

 クリックでのメッセージ送りもあるし、今回は見送るか。

Posted in View, 雑記. Tags: , . TextViewはスクロールできません… はコメントを受け付けていません »

スレッドから別オブジェクトを触ると異常終了?! 解決編

 先日の「スレッドからオブジェクトをイジると異常終了する」件。
 2つの方法で解決した。

 おそらくは邪道な方法だと思うので、参考ぐらいにとどめた方がいいと思う。(^_^;

 前提となる、アプリの構造は以下のような感じだ。

	Activity
	  └GameRun (スレッド)
	     └TextView

※.この他にも、TextView と同列のView があるのだが、ここでは省略。

【動作】
 GameRun がスレッドとして動作し、表示メッセージをTextView へ送る。

【問題】
 GameRunスレッドから、setText や class変数の変更など、TextViewの操作を行うと異常終了する。

 解決するには、Handler を使用するしかないようだ。
 ただそれには不都合があり、今回は、別の方法を探ってみた。

リスナーを使う

 Handler ときて、最初に連想したのがこの方法だ。
 リスナーも通知先をハンドラという。
 ならば、リスナー経由で操作を行えば、根本の原因を回避できるのではないか?
 というワケ。

	GameRun
	 ↓ 通知 ( _Listener.setPadding( padding[] ); )
	TextView
	  method が実行され、変数に値が格納される。
	   setPadding( padding[] ){
	      mesPadding = padding;
	   }

 ただしこの方法は、必ずmethod を介さなければならない。
 上記の例でいえば、スレッドを使わない場合、次のようにシンプルだ。

	TextView.mesPadding = padding;

 ただの代入でも、method を介さなければならないのは、ちょいとシャク。
 ただそれだけの話ではあるんだけどね。

 この方法はことのほか、すんなり問題を解決した。
 ソースもすっきりシンプル。
 値を返すようなものにも使用できる。

リスナー と Handler を使う

 前記方法で問題は解決したように見えたが、別の箇所で再び同じ問題が起きた。
 そこではsetTextやLayoutView のremove を使っており、前記方法では解決されなかったのだ。
 おそらく、描画しているものに対して、直接操作を行おうとしたからだろう。

 解決策としてまず思い浮かんだのが、タイマーである。
 タイマーはキューを受けると、時間をおいて処理を実行する仕掛け。
 View側であらかじめ、setTextやremoveするようなタイマーを作り、スレッド側はキューを送る。
 処理の実行はView側なので、根本原因は回避できるハズだ。
 ……って、待て待て。
 それって、Handler そのものなんじゃね? (^_^;

 そこでView のmethod にHandler を仕掛け、↓のように改良してみた。
 結果は成功である。

View側 method

	private Handler mHandler = new Handler();
	private String mesStr = "";

	public void setMes( String mes ){
		mesStr = mes;
		mHandler.post( new Runnable() {
			public void run() {
				setText( mesStr );
			}
		});
		
	}

スレッド側 method実行

	_MesView.setMes( "うふふ……" );

 View側で、引数 mes を、class変数 mesStr に一旦代入しているのは、そうしないとrun内で認識されないから。(シンタックス・エラーになる)
 というのも、new Runnable() で暗黙的にThread を生成しているからだ。
 つまりrun内は、別class または別method の領域となってしまうのだね。
 それはわかるが、イマイチ好きになれない書き方…。
 まぁ、是非もナシやね。

 これで問題は解決し、もうひとつの問題が発生した。
 先のsetText をappend にした場合である。

	public void appendMes( String mes ){
		mesStr = mes;
		mHandler.post( new Runnable() {
			public void run() {
				append( mesStr );
			}
		});
	}

 append は現在表示しているText に文字列を追加するmethodである。
 たとえば、こんな動作をする。

	[ソース]
	setText( "うふふ……" );
	append( "えっち♪" );

	[表示動作]
	うふふ……
	 ↓append
	うふふ……えっち♪

 ソースからすると、上記のような動作になるハズなのだが。
 実際にやってみると…こうなってしまう。

	うふふ……
	 ↓append
	えっち♪えっち♪

 これはclass変数を介しているために起きてしまう現象だ。
 普段、意識しないが、Javaでの変数は参照が基本。
 内部的にsetText されたのは、class変数のメモリー位置で、class変数の内容が変われば、当然、変わってしまう…。
 おそらくは、スレッドを介しているのも関係があるんだろうね。

 そこでappend は使わず、自前で追加するように変更した。
 これで意図どおりに実行されるようになった。

	public void appendMes( String mes ){
		mesStr = mesStr+mes;
		mHandler.post( new Runnable() {
			public void run() {
				setText( mesStr );
			}
		});
	}

 以上の方法には、リスナーのみの時とは、異なる注意点がある。

1. 即応性がない。
 実行タイミングは、Handler(Looper) 任せなので、いつ実行されるか不明だ。
 付随して、返り値を求めるようなmethod には適応できない。

2. classローカルでも、Handler を介することになる。
 たとえば、例にあげたTextView自身がsetMesを実行すると、Handler を介した実行になる。
 なんだか、自分の尻を他人に拭いてもらうようで、まどろっこしい。(笑
 また、即応性の面からすると、潜在的なバグとなる可能性もある。
 必要なら、ローカル用とHandler用、別々のmethod を作った方がいいだろうね。

3. class変数で引数を保持する。
 先の失敗例のように、Handler が実行する前に、値が変わってしまわないようにしなければならない。

by the way

 setTextは、スレッドからの実行で、ちゃんと動作している場合と、異常終了する場合が見られた。
 これはsetTextだけの話ではない。
 おそらく動作する例は、その時はまだ画面に表示されていないか、UIがひとつだけの状態だったのが原因ではないかと思う。
 一度だけの実行で、不具合ナシと、安心してはいけないね。

補足

 Handler の使い方は、まだよく把握していない。
 見よう見まねの試行錯誤で、「一応、動作している」にすぎない。
 もっとうまく、正しい方法もあると思う。
 ちゃんとした解説を見て、試して、よく理解する必要があるね。(^_^;

 しかし…。
 なんだかJavaというより、Cocoaって感じの構造になったな。(w

Posted in Listener, 雑記. Tags: , , , . スレッドから別オブジェクトを触ると異常終了?! 解決編 はコメントを受け付けていません »