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定義しても後々色々追加したいところが出てきて車輪の再発明になって面倒だろうし…と悩んだ末に結局外部ライブラリに頼ることにした。マテリアルデザインで円形の画像とかたくさん出てくるんだし、そういうのサポートライブラリにも入ってて良さそうなもんだけど…
ottoのハマリポイント
イベントのsubscribeとpublishはottoを使っている。なんか今のところこれが情報多そうだったから。
が、つかっててハマるポイントもあったので覚書
UIへの変更は出来ない
他の遅延タスク系と同じで、UIへの変更はotto経由だとうまくいかない。なので、looperを噛ませないといけない。 Busインスタンスはシングルトンで他のところからも使い回すような形にすることが多いと思う。で、そのシングルトンでインスタンスを作る時にそのlooperを噛ませたラッパークラスを使う。
public class MainThreadBus extends Bus { private final Handler mHandler = new Handler(Looper.getMainLooper()); @Override public void post(final Object event) { if (Looper.myLooper() == Looper.getMainLooper()) { super.post(event); } else { mHandler.post(new Runnable() { @Override public void run() { MainThreadBus.super.post(event); } }); } } public class BusHolder { private static final Bus sBus = new MainThreadBus(); public static Bus getInstance() { return sBus; } }
これでBusHolder.getInstance().post()とかすればUIにも変更が加えられる。 参考: android - How to send event from Service to Activity with Otto event bus? - Stack Overflow
サブクラスでのsubscribeはできない。
ActivityでonResumeの中あたりでBusHolder.getInstance().register(this)とかして、@Subscribeアノテーションをつけたメソッドを作ってイベント発行しても、なぜかsubscribe側のメソッドが呼ばれない場合がある。なんでだろうと思って色々いじってみたけど、そのactivityが別アクティビティのスーパークラスだったのがいけなかった。インターフェースでもだめらしい。なので、@Subscribeなメソッドはサブクラス自体に書かないといけないっぽい。面倒…