Androidの扱いにくいポイント

これまでAndroidではNDK+GLES2を使ってC++でゲームを作るなどはやったことがあったが、たまたま仕事でJavaを用いて標準UIのAndroidアプリを書く機会に恵まれた。その時に引っかかったななめ上の仕様に関する恨みつらみを並べ立ててみる。


この統一感に欠け、使うたびにバッドノウハウばかり次々溜まっていくAPI実装は一体何なのか。本当にgoogleはこれでいいと思ったのか。

Contextの引きずり回し

APIが事あるごとにContextを要求してくる。場合によってはActivityでなくてはならない。

このためプログラムを部品化するにあたって Context を引きずり回すような実装になってしまい依存性が拭い去れない。Activity を多機能にすると肥大化しやすい。

ActivityのonCreate()あたりでsingletonに握らせて、以後必要になったらそこから引っ張ってくる、みたいな方法も考えたけど、そういうシステムリソースの管理をユーザプログラムにさせるのっておかしくないだろうか。そういうのは裏でひっそり確実にやっておいてほしいのだけど。

ImageButton が Button クラスではなく ImageView の派生

どうやらAndroidの開発者は、ラベルに画像が使えるボタンは「ボタンであること」よりも「画像を表示できるものであること」のほうが大切だと考えているらしい。

ImageButton  |  Android Developers

findViewById()などでViewをButtonインスタンスに取得し OnClickListener を仕掛けるようなコードがあったとする。こんなの。

		Button button = (Button)findViewById(R.id.button_foobar);
		button.setOnClickListener(new View.OnClickListener() }
			@Override
			public void onClick(View v) }
				:
				:
			}
		}

このID = button_foobar のボタンをレイアウトXML上でImageButtonに置き換えたりするだけで落ちるようになる。せめて Button クラスの派生であるならば Button インスタンスとして受けることができるのだけども。

HorizontalScrollView が ScrollView ではない

Androidにおいて、垂直スクロールと水平スクロールは「全く異なる機能」である。

setOrientation() で VERTICAL と HORIZONTAL を切り替えられる LinearLayout と異なり、ScrollView および HorizontalScrollView なる二つの別クラスとして実装されていて、しかもどちらかがもう一方の派生、というわけでもない。

ScrollView  |  Android Developers
HorizontalScrollView  |  Android Developers

(参考)LinearLayout  |  Android Developers

両方とも FrameLayout の派生であり、この FrameLayout はスクロールに関する機能を持たないため、「縦横兼用のスクロール機能を持った何か」を実装しようとすると、ポリモーフィズムを利用して実装を統一する、ということができず、両者それぞれのコードを書き分けることになりめんどくさい。

FrameLayout  |  Android Developers

せめてFrameLayoutとの間にAbstractScrollView(仮)的なものを挟んでいてくれたらそれを使ってコードを共通化できたりするわけだが。つまり現在

  • FrameLayout
    • ScrollView
    • HorizontalScrollView

となっているクラス階層を

  • FrameLayout
    • AbstractScrollView(仮)
      • ScrollView
      • HorizontalScrollView

みたいにできんものかなぁと。

ScrollView がことごとく TouchEvent に蓋をしている。

onInterceptTouchEvent() も含め、自身のスクロールに必要ない TouchEvent をことごとく阻害する。ScrollViewに限らず、Androidの TouchEvent まわりは、最初の ACTION_DOWN を取ったView以外全てが蚊帳の外になる仕様が腐ってると思うのだけども、とりわけ ScrollView はその腐り方が半端ではない感じ。

とりあえずTouchEventまわりはなんとかできんものだろうか。

ScrollView内部サイズの扱いが面倒

デフォルトでfillViewportがfalseになっていると、内部スクロール領域を縮められるだけ縮めようとするので、スクロール方向のレイアウトサイズ指定をMATCH_PARENTにしてあると縮んでしまう。これ、true をデフォルトにしておいたほうが絶対便利だと思うのだが(そもそもfillViewportに関する情報も意識してないと行き着かない感じ)。あと、ScrollView 自体は ViewTreeObserver で OnGlobalLayoutListener を仕掛けても正しいサイズが取れないっぽい。

  • fillViewport を true にする
  • FrameLayout などを addView() して、そこに対し ViewTreeObserver で OnGlobalLayoutListener を仕掛ける

みたいにしないと確定されたサイズが取れないし、確定してから中身を作らないと色々面倒なことになる。前二つの項目も含め、ScrollView 周りはとかく仕様が想像のななめ上をいっており、ひたすら地雷原である印象。

ScrollView  |  Android Developers(setFillViewport)

fillViewport の true がデフォルトで、ScrollView 本体から ViewTreeObserver で正しくサイズが取れて、HorizontalScrollViewとスクロール機能のインタフェースを基底レベルで共有してくれていれば概ね想像どおりの挙動なのだけども。

アプリ全体で用いるデフォルトフォントの変更ができない

誤解を招かないように言っておくと、
「アプリ全体で標準のTextViewが用いるフォントファミリーを、システムが提供している中から選択すること」
はできる。
「特定箇所のテキスト表示にTextViewをextendsした独自クラスを用いてフォントを変更する」
こともできる。
「OS環境全体で使用するフォントをなんらかの.ttfに置き換える」
こともできるようだ。

ただ、少なくとも

  • 特定のアプリ上で
  • 標準のTextViewが用いるフォントを
  • 開発者が用意した.ttfで
  • 置き換えること

を実現する方法は見つけられなかった。これができないと「先にレイアウトを標準のTextViewで組み上げ、後から全体のデフォルトフォントを変更する」という作り方ができなくてめんどくさいんだけども。

「その方法を提供しない理由」とかは別に聞きたくないし興味もないんだが、ほんとにこれできないの?