コードから生成したViewにstyleを適用してもLayoutParamsについては無視される

コードから動的にViewを生成したい時がある。そしてそのとき見た目をカスタマイズしたいなんてときがある。もちろんsetBackground()とかsetPadding()を呼び出して設定することは可能であるが、どうせならXMLでやるときのようにstyleを適用したい、なんて場面があるだろう。・・・私にはあった。

さてそんなときに、どうやったらJavaのコードでnewしたTextViewにstyleを適用できるのだろうか、という話。2つ方法があって、どちらもコンストラクタでstyleを指定する。

1つはViewのコンストラクタに引数を4つとるものを使う方法。new TextView(context, null, 0, R.style.some_style);という感じでTextViewを生成する。ただし引数4つのコンストラクタはAPI21からしか存在しないので注意1

もう1つはContextThemeWrapperを使う方法。new TextView(new ContextThemeWrapper(context, R.style.some_style));という感じで生成する。こちらも同じくJavaコードから生成したViewに、styleを適用することができる2。droidkaigiのアプリにコントリビュートしたら学べた方法3

ただし、どっちの方法でstyleを適用しようとも、LayoutParamsに関する設定だけは無視されることに気をつけたい。私はstyleにandroid:layout_marginを指定していたのだが、Javaのコードから生成した場合、marginが無視された。

レイアウトXMLでstyleを適用した場合は、marginも含めてstyleが適用される。しかしコードからだと適用されない。これはコードからstyleを適用した場合、コンストラクタでViewを生成した時点ではViewがLayoutParamsを持たないからだと思われる。

そもそもLayoutParamsはView自身が使う情報ではなく、そのViewを配置するViewGroupが利用する情報になる。コードから生成した場合、このLayoutParamsは親レイアウトにaddView()を行った時点で設定される。もちろんsetLayoutParams()を呼び出すことで事前に設定することも可能だが、コンストラクタを呼び出しただけでは生成されないことがポイント。つまり、View自身に関わるpaddingなどの情報はstyleの適用で設定されるけれども、LayoutParamsに関する情報は設定されないということである。

コンストラクタでLayoutParamsも持たせればいいのにと思うかもしれないが(私も思ったが)、どのViewGroupに配置されるのかがわからないので、コンストラクタの時点でLayoutParamsを設定することは無駄なのだと思う。プログラマが事前にどのViewGroupに配置するか決めているのであれば、setLayoutParams()を使えということなのだろう。

そもそもstyleにLayoutParamsに関する情報をもたせることが間違いなのかもしれない。今まで特に気にせずにstyleでLayoutParamsに関する情報を持たせていたが、実は推奨されないやり方だったのだろうか。

ちなみに、今回の出来事ではじめて知ったのだが、XMLでandroid:layout_xxxとなるのがLayoutParamsらしい。どれがLayoutParamsなのかわからないじゃないかとか思ったけど、自明だった。

ちなみにViewクラスにはsetStyle()みたいなメソッドは存在しない。よく考えてみると、XMLファイルでstyleを適用する際に、なぜかstyleだけはnamespaceがつかない。基本的にViewに要素を書くときは、android:xxxだったりapp:xxxといった感じで頭に必ずnamespaceをつけるのに、直接style="xxx"と書く。つまりstyleの適用だけは特殊な扱いなのだろう。

setContentView()からだとstyleに書いたLayoutParamsが有効になるが、addView()だと無視されることも、このあたりが関係しているのかもしれない。


  1. ちなみに引数3つのコンストラクタの第3引数はdefStyleAttrで、`R.styleable.xxx`を指定するためのものであり、styleを渡したところで適用されない。 
  2. LayoutInflaterがViewをinflateするときにこの方法でinflateしている。 
  3. https://github.com/DroidKaigi/conference-app-2017/pull/401/commits/1812e77a4e3cb598e94714cf12cd83b01d716c79#diff-13d7c85c29370a83d0d27462c1d57f2aR76 

Amazonのほしいものリストを公開しています。仕事で欲しいもの、単なる趣味としてほしいもの、リフレッシュのために欲しいものなどを登録しています。 寄贈いただけると泣いて喜びます。大したお礼はできませんが、よりよい情報発信へのモチベーションに繋がりますので、ご検討いただければ幸いです。