TabLayoutにアイコンを設定する
androidのTabLayoutは文字だけでなく、画像も設定できる。画像の設定方法にはいくつか方法がある。とりあえず分かるものだけ覚書。
setIconで設定
多分いちばん楽な方法。でもデフォルトでいい感じにアイコンの見た目のレイアウトまで行ってくれるので、大抵はこれで十分な気もする。
TabLayout tab = (TabLayout)findViewById(R.id.tab);
tab.getTabAt(0).setIcon(R.drawable.icon);
ImageSpanを使う
PagerAdapterのgetPageTitleメソッドを上書きとかする。多分しなくてもいいと思うけど。なんにしてもSpannableStringにImageSpanというオブジェクトを入れ込めばアイコンも一緒に追加できるっぽい。setIconだけじゃ物足りない時につかうといい。
private int[] imageResId = { R.drawable.ic_one, R.drawable.ic_two, R.drawable.ic_three }; @Override public CharSequence getPageTitle(int position) { Drawable image = ContextCompat.getDrawable(context, imageResId[position]); image.setBounds(0, 0, image.getIntrinsicWidth(), image.getIntrinsicHeight()); SpannableString sb = new SpannableString(" "); ImageSpan imageSpan = new ImageSpan(image, ImageSpan.ALIGN_BOTTOM); sb.setSpan(imageSpan, 0, 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); return sb; }
参考:
Google Play Style Tabs using TabLayout | CodePath Android Cliffnotes
customViewを自分で設定
正直spannebleStringとか使うのはかったるいし、そこまでいじるならlayoutを自分で作ってそれをinflateしたほうが速い気がするので、こっちのほうが好み。
TabLayout tab = (TabLayout)findViewById(R.id.tab);
tab.getTabAt(0).setCustomView(R.layout.custom_view);
jacksonでの命名規則変換
jacksonはretrofitでサーバ側とやり取りするときに、APIサーバから返ってきたjsonのレスポンスをjavaのオブジェクトに変換するために使っている。
僕の場合、APIサーバをrailsで作っているため基本変数名などはスネークケースで書いている。でもjava側というかandroid側はキャメルケースになっている。どっちかをどっちかに合わせなきゃいけないのは面倒。なのでjacksonでスネークケース=>キャメルケースに命名規則を変換してほしいのだけど、結構簡単にできる。
new ObjectMapper().setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES)
これでOK。オブジェクトマッパー作成時にそういう感じのメソッドを呼べばいい。 retrofitで使う場合は、builder作成時に指定すればいい。
private Retrofit.Builder builder = new Retrofit.Builder() .baseUrl("http://192.168.1.1./") .addConverterFactory(JacksonConverterFactory.create( new ObjectMapper().setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES) ));
参考: java - Jackson overcoming underscores in favor of camel-case - Stack Overflow
androidでheightやwidthが0になって取得できない時
は、ViewTreeObserverを使う。
inflateしたviewの高さや幅を動的に設定したいとか、inflate済みの別のviewの高さや幅を参考にしたいときは以下のようにViewのheightやwidthを取得する。
LayoutParams param = view.getLayoutParams();
param.width = 100;
view.setLayoutParams(param);
が、activityのonCreateとかのなかでgetLayoutParams()してviewのwidthを取得しても、0が返ってくることがある。何でだろうと思って調べてみたところ、その時点ではまだviewはinflateされていないっぽい。タイミングの問題。なので、そのviewがinflateされて高さとか幅を持った時にさせたいことをすればいい。 で、そのタイミングのフックになるのがViewTreeObserver。それにリスナーを追加することで、viewが高さを持ってレンダリングされた直後に呼ばれるメソッドを書いてやればいい。そのリスナーの中ならば、getLayoutParams()をしてもちゃんと高さや幅が取得できる。
final TextView tv = (TextView)findViewById(R.id.image_test); ViewTreeObserver vto = tv.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { ViewGroup.LayoutParams params =tv.getLayoutParams(); params.width = 100; tv.setLayoutParams(params); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { vto.removeOnGlobalLayoutListener(this); } else { vto.removeGlobalOnLayoutListener(this); } } });
ターゲットとなるviewまたはそのviewをラップしている親viewから、getViewTreeObserver()でViewTreeObserverを取得して、それにリスナーを追加する。で、そのonGlobalLayoutのなかで任意の処理を行えばいい。
removeOnGlobalLayoutListenerはその名の通り、リスナーを消去する。リスナーを登録したviewがinflateする度にこのonGlobalLayoutが呼ばれてしまうので、1回計算すればいいような処理なんかは何度も呼ばれたら面倒なので、リスナーを消去してしまう。これでいい感じのタイミングでviewのwidthやheightを取得して、それに合わせた処理をできる。
参考:
android.support.v4.widget.CircleImageView
を使いたいけど使えない。結論から言えば、これは開発者側からは使えないっぽい。
なんかユーザーサムネイルとかを○で表現するのが流行ってるし、かっこいいのでそういう感じにしてみたい。けど普通のImageViewをいじった所で縁取りくらいはつけれるけど、丸くclipすることまでは出来ない。なんか無いかな…と思いながらandroid studioで色々打ち込んでたら、android.support.v4.widget.CircleImageViewという文字列が予測候補に出てきた。なんかこれすごく使えそうだとおもって早速xml内に書いてみてエミュレータを起動しても、表示されない。なんでだと思って調べてみたら、同じく困っている人が居た。
このCircleImageView、なんかサポートライブラリの中のprivateなクラスだったらしいので利用できなかったみたい。がっくり。 しょうがないのでこのCircleImageViewを使うことにした。
あんまり外部のライブラリには頼りたくないし、かと言って自分でxml定義しても後々色々追加したいところが出てきて車輪の再発明になって面倒だろうし…と悩んだ末に結局外部ライブラリに頼ることにした。マテリアルデザインで円形の画像とかたくさん出てくるんだし、そういうのサポートライブラリにも入ってて良さそうなもんだけど…