お仕事で極悪Androidアプリを改修していて、 今日得た知見をば。
getTextSize/setTextSize
あるActivityの画面で、 本文とボタンのtext sizeを揃えようとして、 TextView#getTextSizeしてからsetTextSizeしたら、 大きくなるんですよね。何でだろう、調べると、 TextView#getTextSizeとsetTextSizeのデフォルト単位が違う のだそう。びっくりです。
| 1
 |  | 
というように、単位を明示する必要があるそう。
サイズ自動調整TextView
TextViewで、指示通りに改行位置を固定しようと思って。 指示中の、禁則処理に失敗しているところも含めて忠実に再現しようと。 そのためにはtext sizeを随意にせねばならず。 【Android】横幅に合わせてテキストサイズを調整するTextViewそのままで上手く行きました。 あーでもonLayout()の最初の引数changedがtrueの時だけ resize()すればよかとです。
今はAutosizing TextViewsというのがあるそう。 ただ、API 26からなのでまだなかなか使えないでしょうか。
onLayout後の値の取得
上記のようにtext sizeを変えてから、 その結果のtext sizeに合わせて他のViewのtext sizeを 決定しようとすると、 onLayout()が呼ばれ終わってからでないと 目的の値が取得出来ないんですね。 そこで、 How to know when an activity finishes a layout pass? にあるように、 myView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {...}); とすれば良いです。
補足
RobolectricでUnit Test書いてたら、 このclass、testが終わらないんです。 何でかなー、とbreakpointで追ってみると、 延々とonGlobalLayout()が呼ばれ続けてるんですね。 えーっと思って。 RxJavaHooks.setOnIOScheduler(s -> Schedulers.immediate()); しても、 ShadowApplication.runBackgroundTasks(); しても効き目はなく。 androidでheightやwidthが0になって取得できない時 を見ると、用が済んだらすぐremoveするんですね。そっか。 というわけで、Android SDKのversionによって分けて、 removeOnGlobalLayoutListener(this)と removeGlobalOnLayoutListener(this)でremoveするように したんですけど、今度はthisが効かない。 なるほど、lambdaだとthisは外側のclass instanceになるんですね。 じゃぁっていうんでlambda自体をListener instanceとして名前付けて、 lambdaの中でthisじゃなくてその名前で参照しようとしたんですが、 might not have been initializedとか言われ、 nullで初期化すると今度はeffectively finalじゃないと言われて、 うー、とか思って仕方なく諦めて、 初めてlambdaを解いてinner classの記述に戻しました。
Rx Androidにmaxはない?
探したんですけど見つからなかったので自分で集計しました。
| 1 2 3 4 5 6 |  | 
TextViewで白枠
ある段落を白枠で囲って欲しいと言われました。 調べると、[android]xmlで枠を指定するというのがあり、 それ用のdrawable XMLを作ってやってandroid:background="@drawable/..."で、 それを指定すれば、望み通りのものが得られました。 背景色は、これも書いてありますが#00ffffffで透明になります。
| 1 2 3 4 5 6 7 8 9 10 |  | 
Robolectricで次のActivityへの遷移の確認
shadowOf(activity).peekNextStartedActivity()でIntentを取得、 getComponent().getClassName()が目的のclass nameかをassert。
| 1 2 3 |  | 
Local PushでNotification
Local PushでNotificationをして欲しい、と言われました。 調べてみると、要するに、 AlarmManagerにPendingIntentをsetして、 それがset時の引数のUnix Time(millisec)になると、 これもset時引数のBroadcastReceiverの子classの onReceive(context, intent)が呼ばれるので、 そこでNotificationManager.notify()をする、と。
AndroidのNotificationについては、 sample applicationを作って色々と試してみました。
- uninstall/端末再起動すれば登録済みのalarmは解除される
- 多重登録してもPendingIntent.FLAG_UPDATE_CURRENTなら最後のNotificationに上書きされる
- 過去の時日のalarmを登録するとすぐNotifyされてしまう
- 機種によっては挙動が違う(Huaweiでは、アプリが起動していない時/Sleep時にAlarmを発動させるには「保護されたアプリ」でないとならない、等)
- 長いtextは全文出ないで端折られる。出したいなら、.setStyle(new NotificationCompat.BigTextStyle().bigText("..."))する。但し.setBigContentTitle(intent.getStringExtra("..."))も同時に加えるとダメっぽい。