batchのブログ

知見の備忘録

BottomNavigationViewを使ったときにFragmentの下が隠れるときの対処法というかそもそもConstraintLayout使うときmatch_parentは使わないほうがいい

はじめに

Android 初心者向け Advent Calendar 2019の12日目の記事です.

BottomNavigationViewを使ってFragmentの中にRecyclerViewを設置したとき,一番下の部分だけ下のBottomNavigationViewに隠れて見えなくなる.

みたいな経験したことないですか?

原因はこれ.
f:id:batch08:20191114103856p:plain

NavHostFragmentを管理しているMainActivityのレイアウトです.

このfragmentのlayout_heightがmatch_parentになっているのが問題です.

自分は解決方法がわからなく,Fragmentのbottom marginをいい感じにBottomNavigationViewに隠れないようにしてたりしました…

解決方法

じゃあどうすればよいのか.

結論をいうと,さっきmatch_parentだったlayout_heightを0dpにするだけです.

これで直ります!
f:id:batch08:20191114104903p:plain

直して思ったのですが,match_parentはmatch_parentですよね.

一応,ConstraintLayoutなのでfragmentのbottomはBottomNavigationViewのtopにくるように制約をかけていますが,match_parentにしてしまうとその制約も突っ走ってmatchなparentまでいってしまいます.


なので,基本ConstraintLayoutでlayoutを組むときに制約をかけたとこまでいっぱいに表示したいというときは0dpをつかうと良いみたいです.

ハマってたワケ

なんで僕がこのBottomNavigationViewを使ったときのfragmentのlayoutにハマってた理由なのですが,公式が悪いです…


Android StudioでNew ProjectするときにBottomNavigationViewを使ったサンプルを作ることができますよね.
f:id:batch08:20191114104832p:plain

これですこれ.


これでNew Projectしたときのactivity_mainが一枚目にあげた状態になっているのです.


最初に作られるサンプルがああいう感じだからそれが正解だと思っていました…


初心者には変につまづくポイントになってしまっている気がするので修正してほしいところです.


以上,ミニマリストエンジニアbatchでした.

OUTPUT

はじめに

FUN part2 Advent Calendar 2019 - Adventarの11日目の記事です.


ミニマリストエンジニアなbatchが書きます.


僕はAndroidアプリ開発が好きなのでそのことを書いてもいいのですが,この時期色々な人たちがこのカレンダーのためにOUTPUTをする時期でもあるので,OUTPUTについて語ろうと思います.



結論から言うと,OUTPUT(今回ではテックブログ系が対象)は自分のための備忘録としても大事だし読む人にもメリットがあることだと思うのでアドベントカレンダーがあるから〜だけでなく,それ以外でも日々継続的に溜まった知見はOUTPUTしようということと,そのちょっとの努力はすごい重要なんじゃないかということが言いたいです.

本編

OUTPUTとは

今回では,主に溜まった知見をテックブログとして書いてOUTPUTすることを対象にしますが,一般的にOUTPUTというと他にもたくさんあって例えば,気になったイベントはとりあえず申し込みをして参加してみたり,イベントやカンファレンスで登壇してみたりTwitterで自分の意見をツイートするのもOUTPUTかなと思います.もちろん僕が今書いているアドベントカレンダーもOUTPUTだと思います.

OUTPUTの効果

このセクションではOUTPUTをすることでどのようないいことがあることか話します.
ブログなど文章を書くOUTPUTをするとまず単純に頭の中にある非言語的な情報を言語化する力がつくと思います.これは個人的に結構大事だと思っています.大学で研究していて論文を書くときにも役立つスキルだと思うし,社会に出て仕事の場面でも上司を説得したり,同じチームのメンバーに自分が頭のなかでぼんやり思っていることを言語化して納得してもらえるように伝えるスキルにもつながると思っています.あとはブログでOUTPUTを続けると文章をわかりやすく書く力や文章構成を綺麗に構成するが身についていくと思います.
また僕の場合は,ブログでOUTPUTすることを継続的に続けていこうと思ったときには想像していなかった効果がありました.それは,OUTPUTの全体的に言えることですが,就職活動で自分を伝えるときに「自分はこういうことに力を入れています.それをこういう形でOUTPUTしています.」と言えることです.ただ口で「私はこういうこと考えています.」だと面接官や話を聞く人は確かめる術がありません.でも,なにかしらの形でOUTPUTして残しておくとそれが現実世界に誰でも見えるとことに残るので実際に行動に移して本当に力を入れていることが目に見えてわかります.なので,自分が努力していることが相手に伝わりやすくなりかなり有利になると思っています.実際に自分はかなり効果的でした.
さらに,テックブログを書くと時間が経って自分がまた同じエラーに出くわしたときに自分が書いた記事を見るだけで一瞬で解決できるようになるメリットがあります.「これ前にも見たエラーだな…前はどうやって直したんだっけ…(エラー文をぐぐって前回と同じことをする)」ということがなくなります.純粋に自分の備忘録として効果を発揮してくれます.加えて,世界中で同じエラーで困っている人が自分の記事を読むことでエラーを解決することができることに繋がる可能性があることです.自分だけでなく他の人にエラーの直し方,難しい機能の実装の仕方をレクチャーすることにも繋がります.

OUTPUTを継続的にするモチベーション維持の仕方

OUTPUTめっちゃいいじゃんと思ってもそれを継続的に続けるモチベーションの維持がかなり難しいと自分は思います.なので,周りにも一度始めても途中で途絶えてしまったりする人が多いのかなと思います.他の人に当てはまるかはわかりませんが,僕のモチベーションを維持するための考え方をシェアしたいと思います.
僕は,少しでも多くの人がコーディング中に何かしらのわからないことがあったときにそれをぐぐって少しでも早くそれを解決することに繋がればいいなと思ってテックブログを書いています.
コーディングをしている際,コード書いてる時間と実装するために調べている時間どっちが多いかと考えると調べている時間もかなり多いのではないかと思います.というか自分の場合は調べている時間7割実装している時間3割くらいの感覚です.世界中の人がテックブログを書いて自分の小さな知見でも公開することで世界中のエンジニアがコーディングでわからないことを調べる時間を減らすことにつながれば生産性もめっちゃ上がると思うし,とてもハッピーになるんじゃないかと思っています.
僕の場合,上級者向けの記事は世の中の素晴らしいエンジニアの方たちがすでに記事にしてくれていることが多いのですが,初級〜中級者向けの記事が少ないかなと感じています.自分もまだまだAndroidに関しては初級〜中級者レベルなのですが,調べていてもなかなか自分がこれだ!と思う記事が見つからないことが多々あります.「自分と同じことで困っている人は世の中に絶対いるはず」と思っているのでそういう人たちのために向けても役立つといいなという事も考えてそれをモチベーションに僕はテックブログを継続的にOUTPUTしています.
なので,「なにか継続的に記事書かなきゃ」と毎日思っているのではなく,「この実装調べまくったり人に聞いたりしてやっと実装できたけど,これ結構実装したい人多いんじゃないか.同じことで困っている人もきっといるはず.よしOUTPUTしよう.」という感じです.技術は日々進化していくもので進化すればするだけわからないことも増えてくのでその分継続的にOUTPUTすることもたくさんあり続いているという感じです.

記事を書いてポチッと投稿するボタンを押すだけで世界中のエンジニアに自分の知見を共有することができるんです.世界中の同じことで困っているエンジニアの役に立つことができて生産性を上げることに繋がる可能性があるんです.素敵すぎませんか?
これが僕のモチベーションです.

「OUTPUTが大事なことはわかるんだ.モチベーションもある.だけど時間がないんだ.」という人へ

本当に時間がないですか?もしくは,OUTPUTの優先度が低すぎではないですか?たしかにこうやって文字を書くのは時間がかかるし面倒くさいです.でもOUTPUTしたことの効果はかかる手間に割に合うかそれ以上に大きいです.

あと,僕はなんでも時間がないと言う人が嫌いです.
たしかに,会社で役員職で一日中びっちり会議が入っていてどう考えても時間が取れないとかそういう人はいると思います.それはわかります.でも,世界中の人全員役員じゃないですよね?しかも,時間ないからOUTPUTできないって考えているのに家でダラダラYouTubeばっか見てませんか?
時間がないという人はきっと生活の中で無駄なことが多いです.色んなことが大事だと思ってしまっていてものごとに優先順位がつけられていないことも考えられます.ミニマルじゃないです.
今すぐ「ミニマリスト」と検索してみてください.あなたが今まで潜在的に常識だと思っていたことは実はなくてもいいじゃん.とか無駄だったなと気づくことができるかもしれません.(これは合う合わないあると思うので個人差がありますということを言っておきます)

世界中のエンジニアがちょっとYouTube見るのをやめて各々が「これ同じことではまってる人他にいそうだからちょっと記事書いて残しとくか」と思えるようになれば世界中にその知見が共有されて素敵な世界が待っているんじゃないかなと思っています.



Let's enjoy OUTPUT Life!!

ミニマリストエンジニアbatchでした.

retrofitでGETやPOSTする毎日.そんなときはsocket.io使って双方向にリアルタイム通信しちゃおうか

はじめに

socket.ioというものがあります.

github.com


Androidで外部と通信するとき,ほとんどがRetrofitつかってAPIたたいてJson取得して〜みたいな流れだと思います.

クライアントがサーバに対してなにか要求してそれに反応してサーバが値を返したり,何らかの処理をする場合はそれでじゅうぶん.


アプリ開発する中でそれが一番よくやることだと思います.


先日のハッカソンでは,違う実装の要求があり,socket.ioというライブラリ?概念?を用いて実装しました.

参加したハッカソンについては記事を書きました.
batch.hatenablog.com

どういうユースケースでsocket.ioをつかうのか

僕がsocket.ioをどのようなユースケースに対応するために用いたのかを簡単に説明したいと思います.


登場人物はクライアント(Android),クラウドサーバ,クライアント(Webアプリ)です.

f:id:batch08:20191110141445p:plain
全体概要図
僕たちがやりたかったのは,ざっくり説明すると,Android端末内のセンサを使い,どのような動きをしたかという操作情報をクラウドサーバを経由してWebアプリに通知して,その操作情報に応じてWebアプリ側の処理を動的に変えるというもの.

なので,Webアプリ側ではクラウドサーバから送られてくるAndroid端末の操作情報をサーバのようにずっと待っている状態です.
クライアントだけどサーバみたいにずっとリクエスト待っているってあんまやらないですよね?こういうときにsocket.io使うみたいです.

実装してみた僕の勝手な解釈としては,クライアントでもサーバのようにずっとリクエストを待ち続けて,リクエストが来たときに反応して何かしら処理をしたいときにsocket.ioを使うといった感じです.

一般的な例でいうと,チャットアプリなんかでこういうのが使われるらしいです.
なので,図で見ると全然双方向な感じもリアルタイムな感じも伝わりませんが,チャットアプリなど想像するとユースケースがわかりやすいかなと思います.

ちなみに,socket.ioと別にwebsocketとかってやつがいてまたそれは別です.
github.com


別というか,socket.ioがwebsocketのめんどくさいとこをいい感じにラップしてくれてるみたいらしい.

HTTPのGETとかいろいろめんどくさいことをRetrofitがいい感じに簡単にやってくれてるみたいな感覚.

一応,websocketもHTTPのプロトコル使って動いてほにゃらららって感じらしい.

詳しいことはわかりませんが,Androidでsocket.ioを使うやり方を解説していきたいと思います.

socket.ioつかう

Step1 build.gradle(Module: app)にかく

implementation "io.socket:socket.io-client:1.0.0"

Step2 アクセス先の設定

Activityにかいてゆく
まず,アクセス先のサーバのベースURLてきなものをグローバル変数でセットしてあげます.
今回はAWS使ってたのでそれっぽく例を書いときます.

private val socket = IO.socket("http://ec2-XXXXXXXXX.amazonaws.com")

Step3 コネクションはる

そして,onCreateでコネクト(コネクション張る的な感じ)してあげます.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        socket.connect()
    }

Step4 情報送信

そして次に,Activityにあるとあるボタンを押したときに,socket.ioでなにかしらリクエストするコードを書いていきます.
ここでちょっと,socket.ioのお勉強しなければなりません.
socket.ioではRetrofitなどのようにエンドポイントがサーバに用意されていてそこに向かってリクエスト投げるという感じのことはしません.

毎回アクセスしに行くのは,さきほどsocketに設定したURLに向かってします.
その代わりに,サーバ側ではキーみたいなものを用意して,そのキーに向かってアクセスが来たとき何かしらの処理をするという風な感じで待ち構えています.
そして,socket.ioではemitとonという概念があります.
emitとonは一つのペアの関係をもっています.
サーバ側がAというキーでemitで待っているときはクライアントは同じAというキーでonでアクセスしにいきます.
emitで待っているサーバに対してemitでアクセスしにいくことはしません.emitとonがペアです.
以下で,pingというキーでemitで待機しているサーバに対してボタンを押されたときにonでアクセスしにいく例を紹介します.
実装したコードはこんな感じ.

class MainActivity : AppCompatActivity() {

    private val socket = IO.socket("http://ec2-XXXXXXXXX.amazonaws.com")

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_connect)

        connectButton.setOnClickListener {
                GlobalScope.launch {
                    connection()
                }
         }
    }

    private fun connection() {
        val TAG = "batchSocketIO"
        socket
            .on(io.socket.client.Socket.EVENT_CONNECT) {
                Log.d(TAG, "CONNECT!")
            }
            .on("ping") {
                try {
                    val json = it[0] as JSONObject
                } catch (e: Exception) {
                    Log.d(TAG, e.toString())
                }
            }
            .on(io.socket.client.Socket.EVENT_DISCONNECT) {
                Log.d(TAG, "DISCONNECT!")
            }
    }
}

connection関数の中の, .on(io.socket.client.Socket.EVENT_CONNECT)はお作法的なものです.
その次にpingというキーで待っているemitにonでアクセスしにいきます.今回の場合,結果としてpongというStringが返ってくるようになっており,それをval json = it[0] as JSONObjectで受け取ってます.
最後の.on(io.socket.client.Socket.EVENT_DISCONNECT)もお作法的なものだと思っています.詳しくはわかっていません.

番外編 JSONデータ渡したいとき

今回,Android側でどのような動きをしたのかという情報をJsonにしてサーバに送って,サーバがそれを受け流しWebアプリに通知するということをしました.
そのときにAndroid側で実装したものを共有したいと思います.

val body = JSONObject(
            """{
                |"hoge":"hogehoge",
                |"direction":$direction
                |}""".trimMargin()
        )

        socket
            .emit(endpoint, body, Emitter.Listener {
            })

JSONObjectでまず,送信したい情報を定義します.
そして,socketでサーバ側が今度はonで待っているのでemitでjsonbodyを混ぜて送信しています.
これがWebアプリ側に送信されてdirectionの向きに応じてWebアプリの処理が動的に変わるというわけです.


以上,にわかながら得た知見を詰めて書いてみました.

一応,ハッカソンで実装したリポジトリ貼っておきます.覗いてみてください.(設計などは全く気にしないで書いてるのでヤバいです)
github.com


なにかご指摘ある場合はぜひぜひTwitterでもブログのコメントでもお願いします.
ミニマリストエンジニアbatchでした.
twitter.com

****Directionsクラス使いたいのにgenerateされない貴方へ(私はこれで4時間くらい時間を無駄にした)

Step 1 build.gradle(Project)
dependenciesに以下を追記

classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.2.0-rc02" // 適宜最新のversionに

Step 2 build.gradle(Module: app)
applyです.applyしてあげましょう.

apply plugin: 'androidx.navigation.safeargs'

Step 3
Let's Build>Rebuild Project

これで生成されます.

僕はべつにsafeargs使わないのにこのクラス使いたくて何回もクリーンプロジェクトしてリビルドしまくってました…

Androidのgeneratedされるjavaのクラスたち多すぎて本当にやめてほしい…

ミニマリストエンジニアbatchでした.

MVVM実装のざっくりしたこととシンプルにFragmentにDatabinding

ただ単純にMVVMを用いているときにAPIで取ってきたデータをDatabindingでFragmentに表示させることをしたいと思ったとき,検索してもなかなか出てこなかった.

RecyclerViewに表示するのはたくさん記事が出てくるのですが,Fragmentにおいてある一つのTextViewに値を表示したいと思ったとき全然記事が出てこなかったです.

自分の検索スキルがまだまだだと思うのですが,きっと同じ悩みを抱えている人がいるだろうということでその知見を共有します.

色々説明が文字だと複雑になるので動画撮ったのでご覧ください.

Databindingする前のAPI叩いて値持ってきたいという方は以前書いたブログを御覧ください
batch.hatenablog.com



www.youtube.com




ミニマリストエンジニアbatchでした.

Groupieでitemクリックを実装しちゃう.おまけにNavigationでFragmentに遷移しちゃう

lisawrayさん,お世話になっております.
github.com


ですが,なぜかあまりGroupieを使ったときのitemクリックの実装の仕方が調べても全然出てきませんでした.
なのでその知見を共有.

そして,Groupieでリスト表示してitemをクリックしたとき別のFragmentに遷移するってことを自分はやりたくて,いろんなアプリを開発する上で多用するものだと思うのでそのやり方もここに残しておきます.

まず,GroupAdapterを実装.

val groupAdapter = GroupAdapter<GroupieViewHolder>()

そして,ItemをaddAllとかで入れるときにsetOnItenClickListener()というのがいるのでその子をはやしてあげる

        groupAdapter.apply {
            update(listItem)
            setOnItemClickListener(onItemClickListener)
        }

はやしたsetOnItenClickListener()の()の中にいれてるonItemClickLitenerを実装する.
この中には具体的に,itemがクリックされたときに行いたい処理を書きます.
今回だと,Navigationをつかってnavグラフで管理されている別のFragmentに遷移する処理を書いています.
また,これも調べて全然出てこなかった,どのitemがクリックされたかindexを取得する方法も一緒に書いています.

    private val onItemClickListener = OnItemClickListener { item, view ->
        // どのitemがクリックされたかindexを取得
        val index = groupAdapter.getAdapterPosition(item)
       // Navifationで別のFragmentに移動.navigateの中のidはnavグラフに定義したactionのid
        findNavController().navigate(R.id.actionMusicFragment)
    }

参考までに.


ミニマリストエンジニアbatchでした.

Log.dだって?Timber使うゾ

はじめに

デバッグログを使ってアプリの挙動を確認することはよくあること.

そのとき,デフォルトであるLog.dよく使う.

しかし,Log.dでは毎回TAG入れるのが面倒だったり,valueには毎回toStringしないといけないしめんどくさいことだらけ.

おまけに色々とセキュリティの観点から問題がある.らしい.
qiita.com

そこで,これらの問題を解決してくれるTimberというライブラリの紹介.

つくったのは僕らの神様Jake様.
ありがたく使わせていただきます.ではやっていき
github.com

使い方

Step 1

build.gradleにかく

implementation 'com.jakewharton.timber:timber:4.7.1'

Step 2

Appという名前のKotlinクラスを作成して以下を書く

class App : Application() {
    override fun onCreate() {
        super.onCreate()
        if (BuildConfig.DEBUG) {
            Timber.plant(Timber.DebugTree())
        }
    }
}

Step 3

Manifestをいじってapplicationの階層の中に以下を追記

android:name=".App"

つかうときはTimber.d("Hello %s!", firstName)とかそんな感じ

以上です