KITCHEN DRINKER

主にAndroid開発メモとか

EmojiEditTextでOS8.0でcrash時の対応

チーム内で対応したことだけど、同じこと起こる人居そうなのでメモっとく。

OS 8.0がリリースされた時からちょくちょく出てたcrashが、EmojiCompat導入した途端増えだした。

Fatal Exception: java.lang.ArrayIndexOutOfBoundsException: length=3; index=-1
       at android.text.DynamicLayout.getBlockIndex(DynamicLayout.java:646)
       at android.widget.Editor.drawHardwareAccelerated(Editor.java:1695)
       at android.widget.Editor.onDraw(Editor.java:1664)
       at android.widget.TextView.onDraw(TextView.java:6880)
       at android.view.View.draw(View.java:19132)
       at android.view.View.updateDisplayListIfDirty(View.java:18082)
…

発生条件

  • OS 8.0
  • 端末の開発者オプション設定より、GPUレンダリング設定(ハードウェアアクセラレーション)を有効にしている
  • EditTextで絵文字を含んだあるフォーマットの条件下で入力しようとする

原因

OS 8.0のみ発生するGoogle側のバグのよう

https://issuetracker.google.com/issues/67102093

対応

上記のissueにも上がっていた対応策で、OS 8.0.0の端末のみ入力画面時はGPUレンダリング設定を強制的に無効化にするようにする。

editText.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

BindingAdapterで作った。

@BindingAdapter("forceLayerTypeSoftware")
fun EditText.setForceLayerTypeSoftware(forceLayerTypeSoftware: Boolean = false) {
  if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O || forceLayerTypeSoftware) {
    setLayerType(View.LAYER_TYPE_SOFTWARE, null)
  }
}

xml

      <android.support.text.emoji.widget.EmojiEditText
    …
+          app:forceLayerTypeSoftware="@{false}"
           />

おそらくOS 8.1.0では修正されているはずなので、対象ユーザーがいなくなったら消す予定(忘れそう😇)

ProgressBarでちょっとハマった

こんなの作りたかった

残り時間を表すやーつ f:id:nyanyonin:20180207011618p:plainf:id:nyanyonin:20180207022038p:plain

うん。ProgressBarで作れそう…!とやってみた。

ProgressBarおさらい。

ProgressBar  |  Android Developers

種類

  • Determinate Progress:特定量の進行状況を表すときに用いる

f:id:nyanyonin:20180207012551p:plain

  • Indeterminate Progress(default):不特定量の進行状況を表すときに用いる

f:id:nyanyonin:20180207014004p:plain:w80 よく見るのはくるぐるぐるしてるやーつ

ハマったその1

ろくにdocument読まずに形で見てIndeterminate Progressだろうと実装してみて、数値とかそれっぽく書いて実行してみたら、

・・・・ぐるぐるし続ける…!?

じゃCustomView作るしか…?汗 と思ってたけど以下のようにDeterminate Progressで、背景の設定を円形にすれば良いらしい。

<ProgressBar
    style="?android:attr/progressBarStyleHorizontal"
    android:layout_width="36dp"
    android:layout_height="36dp"
    android:background="@drawable/bg_progress"★全体時間描画(グレーの部分)設定
    android:progressDrawable="@drawable/circle_progress"★残り時間の描画設定
    android:rotation="90"// 描画角度(最初は@drawable/circle_progressの中で設定していたがrecyclerViewの中でうまく動かない問題があったためこちらで指定)
    android:scaleX="-1"
    android:max="100"// MAX
    android:progress="40"// 進行値
    />

2つに分けずともprogressDrawablelayer-listで書いたものをいっぺんに指定することも可能。 だけど今回は色を時間に応じて変えたかったため分けた。

<!-- bg_progress.xmll -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="ring"
    android:thickness="5dp"// 線の太さ
    android:useLevel="false">
    <solid android:color="@color/gray" />
</shape>
<!-- circle_progress.xml -->
<shape
    android:shape="ring"
    android:thickness="5dp"
    android:useLevel="true">
    <solid android:color="@color/green" />
</shape>

ハマったその2

progressの色を時間に応じて変えたかったのでBindingAdapterprogressDrawableを設定してみたらなぜか動かない。。

結論、順番入れ替えたら動いた・・・。

<!-- うごかない -->
    android:progress="@{result.progress}"
    app:progressDrawable="@{result.progress}"
<!-- うごいた-->
    app:progressDrawable="@{result.progress}"
    android:progress="@{result.progress}"

うーーん。。これフォーマッターにかけると入れ替わっちゃうんですよね泣

<!-- ちなみにBindingAdapter使わず直指定だとうごく。。-->
    android:progress="@{result.progress}"
    android:progressDrawable="@drawable/circle_progress_green"

xmlじゃなくてプログラム上からも同様で、progressDrawableを設定後にprogressを設定しなきゃうまく反映されない模様。。

なのでProgressBar側の仕様ってことなのかな;時間ある時src追ってみます。

勿論ご存知の方いらっしゃれば教えてください🙏🙇

端末依存めも

Samsung端末の省電力モードはデフォルトだとバックグラウンドのネットワーク使用がOFFられるので、通知系が受信出来なくなる。(端末仕様)

ただし、省電力モードをカスタマイズ出来るので省電力モードに入ってもバックグラウンドのネットワーク使用をONにするように設定すれば受信出来るようになる。

親branchをsquash margeしたら子branchで大量のdiffが出たとき

Git力弱すぎぃで反省したのでメモ

squash で マージをすると複数のコミットが1コミットに纏めてマージされるのですっきりして良い。

だけど、こんな場合、

master
  |--A
      |--B

masterからAのbranchをつくり諸々コミットする。

Aで全体に影響する大きな変更をしているため、BのbranchをAからきる。

Aをsquashでマージする。

と、Bとmasterの間に大量のdiffが・・・なぜ!😱

理由は、 squash でのマージは、今までのAのコミットと別物として上書きされて扱われるので、 Bに入ってるAのコミットはmasterにマージされたAのコミットとは別物扱いになってしまう。

焦ってgit rebase masterとかやりだしたけど、Aのコミットが多く果てしない作業に…


しかし、これで解決することがわかった

git rebase --onto master A


今日の作業時間半分くらい費やしてしまった…(;ω;)

2017年振り返り&2018年目標

2017年振り返り

引っ越し

年明け早々に引っ越した。前住んでたところの下町感が好きで中々動けなかったけど、転職したし会社近い方が何かと都合良いよねーってので重い腰を上げた。
前の家より狭くなったけど割と気に入ってる。

徒歩通勤 & 手ぶら通勤

引越しに合わせて始めてみた。
片道30分弱程。続いてるし地味に良い運動だと思う。
徒歩通勤に合わせて身軽になりたいなと、カバンをリュックにする→財布やめる→極力手ぶら(←イマココ)
手ぶら通勤は限られてる条件↓下だけど出来る日は凄くラク。(でもお弁当作るのも好きなので悩ましいところはある・・・)

if (服にポケットが付いている && ランチの約束がある(弁当持参でない)日) {
    手ぶら通勤
}

お財布やめてキャッシュレス生活にしたのも良い感じ。
とはいっても、ランチとか諸々現金の必要にせまられることもあるので、マネークリップ買ったけど、煩わしいので良い感じに諸々一体型にしたい。

担当プロジェクトcloseのお知らせ

春に担当していたプロジェクトのcloseを告げられた。
Android一人開発体制だったけど改善の時間をなんとかネゴって手に入れて、よっしゃ!やってやんぞ!!!って矢先だったので割と抜け殻になってた気がする。
そこからはクローズに向けた作業。
アプリ起動したらクローズのお知らせダイアログ出すリリースとか辛かった。
アプリの公開停止ボタン押すのも辛かった。。

エンジニアとして働きはじめて初めての案件だったので学びは沢山。。

新チームへ異動

プロジェクトcloseに伴い新チームへ異動となった。
正直、とても運が良かったと思う。温度感が合う良いチームにjoinさせてもらえた。
職種関係なくメンバー全員サービス愛が強くてユーザーファーストな視点で、問題解決に対するフットワークも軽い。
joinする前は、とはいって課金サービスなので売上重視なんだろうなーと思ってたけど、入ってみたら凄く健全な思想でやってて(それが伝わってない悔しさすらある)、素直に素敵だなと思った。
その視点から申請文言の見直しを提案させてもらったりもした。 developers.cyberagent.co.jp

マネジメント3.0を意識したチーム体制とか開発体制が素晴らしいと思うのでどこかでアウトプットできたら

(※ちなみに今大採用期突入でどの職種もメンバー募集してるので興味ある方いたら声かけてください!)


一人開発→チーム開発

新チームで複数人でAndroidアプリ開発することになった。
レビューしてもらえたりとか、実装の些細な相談のってもらったりとか、定期的に振り返りやったりとか、命名でわいわいしたりとか、行動指針決めたりとか、チーム開発って楽しいですね(;▽;)
この幸せを幸せって思える気持ち、大事にしていきたい。。

はじめ数ヶ月は負債返却とかスタンダード化がメインの任務だったので、kotlin導入とか新アーキテクチャ導入のために動いてた。
最近は新施策の開発メンバーとしても動いてるけど、仕様検討段階から関わっていて、今までそういう経験がなかったので、0→1での開発の進め方を学んでるところ。むつかしー!けど面白い!

前チームでは余裕がなくてひたすらに手を動かしていた。
今は手を動かすより“考える”っていう時間の方が何倍も増えたように思う。


会社への帰属意識の芽生え

何言ってんだ今更だと思うけど、中途入社って会社が大きければ大きいほど帰属意識みたいなものが薄いと思う。
今の会社は良くも悪くも自由で、細かいことはチーム内でよしなに〜って風土なので、チームへの帰属意識はあったけど正直会社に対しては薄かった。
それが運良く?会社が定期的にやってる会社を良くしてく案出し会議のメンバーに入ったことで、 『こんなに自分の会社のこと知らなかった…!』って反省するに機に至った。

またその会議に参加したことで、エンジニア女性が出産後もエンジニアとして働くことの難しさを目の当たりにした。
これに関しての課題感は業界全体でもあると思うので、良い仕組み作り考えていきたいなと。 引き続き取り組んでく事項・・・!


筋トレ

良い。健康になった。生活にプロテインを摂り入れることも含めて女性にこそ薦めたい。
晩酌(趣味)が週7→週3くらいになった。
ご飯ちゃんと食べるようになった。
この話長くなりそうなので(気が向いたら)別途書く。
でも一言だけ

筋トレはいいぞ。

2017年目標から振り返ってみる

去年の振り返りより

お仕事編

  • RxJava導入:取り入れたくて勉強しだしたところ。こんなこと出来るのか…!もっと早く知っていれば…!と感動してる。これからベストな書き方を模索してく
  • kotlin:Rxが一区切りしたらやるぞ

◯:新チームへの異動になったことで自動的に達成できた。


  • 人の前に立つ機会をつくる:コミュニケーション力課題なので克服したい

◯:機会は増やした。(けどまだまだ。。そして前に立って気づいたけどプレゼン力の低さ…泣)


  • ブログ書く:勉強会参加後、問題解決後、読書後などアウトプット心がける

△:問題解決後は少し書けたけどその他のインプット後にはあまり書けてなかった


私生活編

  • 休肝日を3週間に1日は儲ける

◯:前半は厳しさあったけど筋トレのおかげで少なくとも1週間に1日はとれてる!


  • 食事を抜くダイエットはしない

◯:筋トレのおかげでちゃんと食べてる。 食べなきゃ筋肉つかない&維持できないので!


  • 引き続き毎日お弁当作る

△:新チームになってランチの機会が増えたので毎日じゃなくなったけど、作るときは作ってる。


健康第一

◯◯◯:筋トレのおかげで(以下略


総括

振り返ってみると2017年の約半分は前チームだった…あっという間だ。
目標に対しては概ね達成🙆でした🎉
ただ後半は業務外タスクでいっぱいいっぱいになってしまった期間もあって、エンジニアとしてのインプットが少なかったように思う。
諸々巻き込まれやすいし芸人にされ易いタイプなのは把握できたので、引き受けるのは腹に落ちたものだけにしたい。 流されないぞ


2018年目標

振り返りで力尽きたので年明けくらいに追記します

javaからkotlinのdefault引数付き関数呼び出しについて

javaからkotlin classのcompanion object内の関数を呼び出すとき書き方が二つあると思います。

(例)HogeActivity.ktのcreateIntentをjavaから呼び出すとき

class HogeActivity {
  companion object {
    fun createIntent(context: Context) = Intent(context, HogeActivity::class.java)
  }
// 以下略

java側にCompanionをつけて呼び出す

HogeActivity.Companion.createIntent(getContext(), false);

② kotlin側に@JvmOverloadsをつける

class HogeActivity {
  @JvmOverloads 
  companion object {
    fun createIntent(context: Context) = Intent(context, HogeActivity::class.java)
  }

現在javaプロジェクトをkotlinに以降中なのですが一応100%kotlin目指しているので、 全部Kotlinに置き換わった時のことを考えて①の方を使った書き方で統一をしていました。

が、companion object内の関数がdefault引数を利用している場合 パターン②の書き方でないと呼び出せないことがわかりました。

というか超基礎的な話ですがcompanion object内かに関わらず、default引数を利用している場合はjava側に@JvmOverloads付けないとダメ🙅

@JvmOverloads
fun test01(arg01: String = "AAA") {
}

fun test02(arg02: String = "BBB") {
}

試しに上記関数をDecompileしてみます。

// test01をDecompile
@JvmOverloads
public final void test01(@NotNull String arg01) {
   Intrinsics.checkParameterIsNotNull(arg01, "arg01");
}

@JvmOverloads
public static void test01$default(UserProfileFragment var0, String var1, int var2, Object var3) {
   if((var2 & 1) != 0) {
      var1 = "AAA";
   }
   var0.test01(var1);
}

@JvmOverloads
public final void test01() {
   test01$default(this, (String)null, 1, (Object)null);
}

@JvmOverloadsをつけてるtest01は引数なしのものが自動生成されているのがわかります。

// test02をDecompile
public final void test02(@NotNull String arg02) {
    Intrinsics.checkParameterIsNotNull(arg02, "arg02");
}

public static void test02$default(UserProfileFragment var0, String var1, int var2, Object var3) {
   if((var2 & 1) != 0) {
      var1 = "BBB";
   }
   var0.test02(var1);
}

一方test02は引数を指定しないと呼び出せないようになっています。

基礎的な話ですが、きちんと理解出来ていなかったので書きました。

ImageViewのScaleType

いつも忘れてググってるので自分用めも

  • android:adjustViewBounds:描画の縦横比を維持するかどうかを指定
  • android:cropToPadding:余白領域内に描画されている範囲を切り取るかどうかを指定
  • android:scaleType:画像がImageViewのサイズに応じてどのようにリサイズまたは配置されるかを指定

ImageView.ScaleType  |  Android Developers

scaleType
CENTER Viewの中央に表示、拡大縮小無し
CENTER_CROP View内で画像の縦横比を維持し、Cropして中央に配置
CENTER_INSIDE View内で画像の縦横比を維持し画像すべてをView内の中央に配置
FIT_CENTER View内で画像の縦横比を維持し中央に配置   ※default
FIT_END View内で画像の縦横比を維持し、右下に寄せて配置
FIT_START View内で画像の縦横比を維持し、左上に寄せて配置
FIT_XY View内で縦横を独立してリサイズし、フル画面にする
MATRIX Image Matrix を使うときに使用する

f:id:nyanyonin:20170918101629p:plain:w380

CENTER と CENTER_CROP の違い

f:id:nyanyonin:20170918102244p:plain:w220f:id:nyanyonin:20170918102249p:plain:w200

CENTER_INSIDE と FIT_CENTER の違い

f:id:nyanyonin:20180722165034p:plain:w220f:id:nyanyonin:20180722165053p:plain:w220

参考&画像拝借mm:

https://akira-watson.com/android/imageview_scaletype.html

https://robots.thoughtbot.com/android-imageview-scaletype-a-visual-guide

http://abhiandroid.com/ui/scaletype-imageview-example.html

https://stackoverflow.com/questions/11353973/whats-the-difference-between-center-inside-and-fit-center-scale-types#