20210411のAndroidに関する記事は6件です。

google maps customizationで地図アプリを かわいくしちゃおう!

google maps customizationとは? その名の通り地図のカスタマイズ機能でございます! 約1年前に新しくなりました✨(まだベータ版ですが使用できます) 以前は地図のスタイル設定を行った後JSONに変換されていましたが、 クラウドベースになったことでURL文字の制限がなくなり、より細かいカスタマイズが可能に! 更新も簡単にできるようになりました! 超簡単直感的!しかも無料で設定できちゃうので活用して地図をかわいくしていきましょ〜! やってみよう! 私の個人Androidアプリ「指宿すきすきまっぷ。」を例にgoogle maps customizationの使い方をご説明します! 事前に... GoogleMapを使用したアプリをお持ちでない方はこちらの記事などを参考にご準備ください! ①GCPの準備 https://mapstyle.withgoogle.com/ 「Try Cloud-based Maps Styling」 と書かれた青いボタンをクリックします。 プロジェクト名(私の場合は「ibusukisukisukimap」)を確認して 「+ CREATE NEW MAP STYLE」をクリックします。 ②地図のカスタマイズ ここでは、すでに用意されているスタイルを選択することができます! 気に入ったものがあれば選択して「保存」ボタンを押し、名前と説明をつけて保存しましょう! もっと自由に細かい設定をしたい場合は「スタイルエディッタで開く」をクリック! こんな画面になるで左側のリストからカスタマイズしてみてください! ?小技その1? 特定の地域限定の地図アプリを作っている場合、プレビューが海外だと見づらいので Search Addressに日本の地名や建物の名前を入力すると分かりやすいです! (私は「指宿市」を入力しました?) ?小技その2(mac限定)? 英語が分かりにくい時は、2本指でタップをして日本語に翻訳を選択しちゃってました! たまに意味不明な翻訳になっているところもありますがその時は英語に戻したりしながら色々押してみてください! ↓意味不明な翻訳の例 脳卒中...!? カスタマイズが終わったら「SAVE」ボタンを押し、名前と説明をつけて保存しましょう! ③Androidアプリ側の準備 1.こちらをダウンロードして、Android Studio プロジェクトの app/libs フォルダにコピーします。 places-maps-sdk-3.1.0-beta.aar 2.最上位の build.gradle ファイルの buildscript.repositories セクションで、 次のように google() が表示されていることを確認します。 buildscript { repositories { google() } } 3.アプリレベルの build.gradle ファイルの dependencies セクションに、 以下のライブラリを追加します。 dependencies { implementation 'com.google.android.libraries.maps:maps:3.1.0-beta' implementation 'com.google.android.gms:play-services-gcm:17.0.0' implementation 'com.google.auto.value:auto-value-annotations:1.6.2' implementation 'com.google.maps.android:android-maps-utils-v3:1.3.1' } 4.アプリレベルの build.gradle ファイルの dependencies セクションに、 以下のライブラリがあれば削除します。 implementation 'com.google.android.gms:play-services-maps:17.0.0' implementation 'com.google.maps.android:android-maps-utils:1.3.1' 5.ゾウさん?をクリックして、Gradle プロジェクトを同期します。 6.既存のアプリの com.google.android.gms.maps パッケージへのすべての参照を com.google.android.libraries.maps パッケージに置き換えます。 Android Studio の [Edit] > [Find] > [Replace in Path...] を使用すると一気に置換できて簡単です! ④GCPとアプリをつなごう 1.GCPに戻り、 マップ管理を開き、「+CREATE NEW MAP ID」をクリックします。 2.MAP IDの設定をします。 Map name 好きな名前 Map type Android Description 説明(空白でも大丈夫です!) 入力が終わったら 「NEXT」 をクリック! 2.MAP IDができたのでコピーしておいてください! コピーができたら、 「MANAGE MAP ID」 をクリックします。 3.②で作成したスタイルを選んで、 「SAVE」 をクリック 4.最後にAndroidのマップを表示しているXMLに先ほどコピーしたMAP IDを貼り付けたら完成です? <fragment xmlns:android="http://schemas.android.com/apk/res/android" xmlns:map="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" ~ ~ ~ map:mapId="ここにペースト"/> ちなみに、「指宿すきすきまっぷ。」こんなに可愛くなりました〜♡
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Android】画面スクロール設定をする

簡単だけど知らなかったのでメモ。 レイアウトの要素を下記で囲むだけでスクロールできる。 <androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView> 例 activity_main.xml <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <androidx.core.widget.NestedScrollView android:layout_width="0dp" android:layout_height="0dp" android:scrollbars="none" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" > <TextView android:id="@+id/name_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Android アプリは Kotlin や Java、C++ 言語を使用して記述できます。Android SDK ツールは、コードをデータやリソース ファイルと一緒に APK(Android パッケージ)にコンパイルします。これは、.apk という接尾語の付いたアーカイブ ファイルです。1 つの APK ファイルに Android アプリのすべてのコンテンツが含まれており、Android 端末はアプリをインストールする際にこのファイルを使用します。 各 Android アプリはそれぞれのセキュリティ サンドボックス内で動作し、以下の Android のセキュリティ機能により保護されています。 Android オペレーティング システムはマルチユーザーの Linux システムであり、各アプリがそれぞれ異なるユーザーになります。 デフォルトで、各アプリに一意の Linux ユーザー ID が割り当てられます(ID はシステムのみで使用され、アプリは ID を関知しません)。アプリに割り当てられたユーザー ID のみがアプリ内のすべてのファイルにアクセスできるよう、パーミッションが設定されます。 各プロセスにはそれぞれ独自の仮想マシン(VM)があるため、アプリのコードは他のアプリとは分離して実行されます。 デフォルトで、すべてのアプリは独自の Linux プロセスで実行されます。Android システムはアプリのコンポーネントのいずれかを実行する必要があるときにプロセスを開始し、それが必要なくなったときや、システムが他のアプリ用にメモリを回復させる必要があるときにプロセスをシャットダウンします。 Android システムには「最小権限の原則」が適用されています。つまり、デフォルトでは各アプリにコンポーネントの動作に必要な分だけのアクセス権が与えられます。これにより、パーミッションの与えられていないシステムの部分にアプリはアクセスできないという非常に安全性の高い環境が作り出されます。ただし、次のように、アプリが他のアプリとデータを共有したり、システムのサービスにアクセスしたりするための方法はあります。 2 つのアプリで同一の Linux ユーザー ID を共有して、お互いのファイルにアクセスできるようにすることが可能です。また、システム リソースを節約するため、同じユーザー ID を持つアプリを同じ Linux プロセスで実行し、同じ VM を共有するよう設定することもできます。この場合、これらのアプリは同じ証明書で署名される必要があります。 アプリから、ユーザーの連絡先などの端末データ、SMS メッセージ、マウント可能なストレージ(SD カード)、カメラ、Bluetooth にアクセスするためのパーミッションをリクエストできます。ユーザーはこれらのパーミッションを明示的に付与する必要があります。詳細については、システム パーミッションの使用をご覧ください。 続いて、このドキュメントでは次のコンセプトについて説明します。 アプリを定義するコア フレームワーク コンポーネント。 コンポーネントと、アプリに必要な端末の機能を宣言するマニフェスト ファイル。 さまざまな端末構成に合わせてアプリの動作を最適化するための、アプリコードから分離されたリソース。アクティビティは、ユーザーとやり取りするためのエントリ ポイントです。これは、1 つのユーザー インターフェースを持つ 1 つの画面で表されます。たとえば、メールアプリには、新着メールの一覧を表示するアクティビティ、メールを作成するアクティビティ、そしてメールを閲覧するアクティビティがあります。メールアプリでは、これらの複数のアクティビティが一体となって 1 つのユーザー エクスペリエンスを形成しますが、それぞれのアクティビティは他のものから独立しています。したがって、これらのアクティビティのいずれかを、別のアプリから開始することができます(メールアプリが許可している場合)。たとえば、カメラアプリからメールアプリの新規メールを作成するアクティビティを開始できます。そのようにして、ユーザーが写真をメールで共有できるようにします。アクティビティは、システムとアプリ間における次の重要なインタラクションを行えるようにします。 アクティビティをホストしているプロセスを継続的に実行するために、ユーザーの現在の操作内容(画面の表示)を追跡。 以前に使用されたプロセス(停止されたアクティビティ)のうち、ユーザーが再度アクセスする可能性があるものを検知し、それらの優先順位を上げてプロセスを維持。 アプリがプロセスを強制終了した場合に、ユーザーが以前の状態を復元したアクティビティに戻れるように支援。 アプリ間でのユーザーフローをアプリが実装する手段と、システムがそれらのフローを連携させるための手段を提供(ここでは最も一般的な例を説明します)。 アクティビティは Activity クラスのサブクラスとして実装します。Activity クラスの詳細については、デベロッパー ガイドのアクティビティをご覧ください。" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.core.widget.NestedScrollView> </androidx.constraintlayout.widget.ConstraintLayout>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android Studioでアプリ開発

自身のPCの性能が悪いのもあるのですが、設定に苦労したのでメモを残して起きたいと思います。 Android Studio自体はダウンロードするのに何の問題もなかったのですが、SDKダウンロードする際に何故かエラーメッセージが出力。 それは保存している場所のフォルダに日本語を使っていたからいけないとのことでした。 そしてSDKはダウンロードできたが次にエミュレーターで実行の段階になり、またエラーメッセージ。 The emulator process for AVD Picel 3a_API 30 x86 was killed. 質問サイトを駆使したところ、どうやら容量の問題らしく、この解決に3段階ぐらいの解決法試しました。 Android StudioでHello worldアプリ作成 https://akira-watson.com/android/helloworld.html#1 このエミュレーター実行の際にエラー文章出る様でたので、 HAMXを設定 それでも通らなかったので ↓ BIOSの設定やHyper-vなど確認 https://akeyfn.xyz/osusumesoft/haxm-win10-vt-x-androidstudio/ それでも通らなかったので ↓ Android StudioからNox Playerを利用してアプリケーションを起動する https://qiita.com/shin1007/items/e1a151c5f99435f3ca5c といった風にエラーを回避しました。他の方が同じ轍を踏まないように。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[脱初心者への道]AndroidのリモートデバッグではまるCommandInvokationFailure

謎のエラーでリモート実行できない UnityでAndroid向けにBuildしてリモートで実行しようとすると、以下のようなエラーが出ることがあります。 : Unable to install APK to device. Please make sure the Android SDK is installed and is properly configured in the Editor. See the Console for more details. ただ、この後にBuildが成功しているメッセージは出ていて、Buildは失敗して無さそうだし、SDKも入ってる。という場合の対処方法を以下に示します。 原因1:同じアプリが既に入っている 一番、単純な話ですが、入れようとしているアプリが既にストア等からインストールされているとエラーになってインストールできません。 https://www.crossroad-tech.com/entry/UnityAndroidBuildFailure 原因2:Android側に空きがない これもあり得ます、エラーメッセージから推測しがたいです。 http://blog.lab7.biz/archives/21598575.html 原因3:ADBが邪魔している この現象もありました。 https://qiita.com/r-ngtm/items/13a09e33e6788be42eb8
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

グレー背景に穴を開けてフォーカスっぽい表現をする方法

アプリのチュートリアルなどで、ダイアログを表示したときのように全体をグレーで覆いつつ、強調したいところに穴を開けたくなることがあります。 要するにこんな感じ ダイアログを表示したとき背景がグレーになるのは、WindowManager.LayoutParams.dimAmountで制御可能ですが、これに穴を開けることはできないですね。上で示したように実際は矢印とか説明なども一緒に表示する必要があるでしょうから、それらを含んだレイアウトをDecorViewに追加するのが安直な実装方法ではないでしょうか? DecorViewとは DecorViewはレイアウト全体の親に当たるViewですね。Layout Inspectorで見るとルートがDecorViewになっていますね。 ActivityのsetContentViewするレイアウトの親に当たるのが、content(ContentFrameLayout)ですね。 DecorViewは、Window#getDecorView() でアクセスすることができます。Window#getDecorView()の戻り値の型はViewですが、DecorViewはFrameLayoutのサブクラスです。ViewGroupにキャストすればaddViewをコールしてViewを追加することができます。 DecorViewにViewを貼り付ける まずは適当なViewを貼り付けてみます。 val view = View(this) view.setBackgroundColor(Color.RED) (window.decorView as ViewGroup).addView(view) このように、DecorViewはStatusBarからNavigationBarまで含めて全体を覆う領域を持っています。 そのため、contentViewと同じレイアウトを使って、その座標を使おう、とすると以下のように盛大にズレてしまいます。 指定の場所に穴を開ける 特定の領域を塗りつぶすというのは簡単ですが、穴を開けるような描画は通常のレイアウトでは難しいです。 なので、そういう描画を行うViewを自作することになります。 穴を開ける場所については、先に示したようにDecorViewは画面全体を覆う大きさを持っています。この座標系でのViewの描画位置を取得する必要があります。そのためにはView.getGlobalVisibleRect()を使って、対象となるViewの絶対座標系でのRectを取得します。 private val rectList: MutableList<RectF> = mutableListOf() fun setTarget(start: View, top: View, end: View, bottom: View) { val rect = Rect() rectList.clear() listOf(start, top, end, bottom).forEach { view -> view.getGlobalVisibleRect(rect) rectList += RectF(rect).also { it.inset(-radius, -radius) } } } Canvasへの描画でPaint#.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)としたPaintを使うと消去を行うことができるので、作業領域を塗りつぶしてから、このPaintを使って穴を開け、作業領域を書き出せば穴を開けることができます。 private val erasePaint = Paint().also { it.color = Color.BLACK it.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) it.isAntiAlias = true } override fun dispatchDraw(canvas: Canvas) { ensureBuffer() val buffer = buffer ?: return val bufferCanvas = bufferCanvas ?: return bufferCanvas.drawColor(backgroundColor, PorterDuff.Mode.SRC) rectList.forEach { bufferCanvas.drawRoundRect(it, radius, radius, erasePaint) } canvas.drawBitmap(buffer, 0f, 0f, null) } private fun ensureBuffer() { if (buffer?.let { it.width == width && it.height == height } == true) { return } buffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { bufferCanvas = Canvas(it) } } 穴以外の座標調整を行う 穴を開けるだけなら前述の処理までで良いですが、その穴に合わせて別のViewを配置するのが通常ですね。 この座標調整を行う方法はいろいろあると思いますが、比較的簡単だと思うのは穴に該当する場所に見えないView(Space)を配置して、それに対する相対位置で他のViewを配置するというものです。 以下のように、穴の位置を示すSpaceを配置し、そのSpaceに対する相対座標で各Viewを配置しておきます。 <?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout" > <Space android:id="@+id/top" android:layout_width="200dp" android:layout_height="32dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Space android:id="@+id/start" android:layout_width="32dp" android:layout_height="200dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Space android:id="@+id/end" android:layout_width="32dp" android:layout_height="200dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Space android:id="@+id/bottom" android:layout_width="200dp" android:layout_height="32dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:rotation="270" app:layout_constraintEnd_toEndOf="@id/top" app:layout_constraintStart_toStartOf="@id/top" app:layout_constraintTop_toBottomOf="@id/top" app:srcCompat="@drawable/ic_forward" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:rotation="180" app:layout_constraintBottom_toBottomOf="@id/start" app:layout_constraintStart_toEndOf="@id/start" app:layout_constraintTop_toTopOf="@id/start" app:srcCompat="@drawable/ic_forward" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="@id/end" app:layout_constraintEnd_toStartOf="@id/end" app:layout_constraintTop_toTopOf="@id/end" app:srcCompat="@drawable/ic_forward" /> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:rotation="90" app:layout_constraintBottom_toTopOf="@id/bottom" app:layout_constraintEnd_toEndOf="@id/bottom" app:layout_constraintStart_toStartOf="@id/bottom" app:srcCompat="@drawable/ic_forward" /> </merge> GlobalVisibleRectで座標を取得していますので、そのときにこのレイアウトにも反映させるようにします。(ここではサイズは決め打ちでXMLに書いてしまっていますが、サイズもここで設定するようにしても良いでしょう) fun setTarget(start: View, top: View, end: View, bottom: View) { val rect = Rect() rectList.clear() listOf( start to binding.start, top to binding.top, end to binding.end, bottom to binding.bottom, ).forEach { pair -> pair.first.getGlobalVisibleRect(rect) rectList += RectF(rect).also { it.inset(-radius, -radius) } pair.second.updateLayoutParams<LayoutParams> { updateMarginsRelative(start = rect.left, top = rect.top) } } } そうすると、画面サイズなどでViewの位置などが変わったとしてもきちんと適切な位置に他のViewを配置することができます。 全体のコードとしてはこんな感じになっています。 class OverlayView @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 ) : ConstraintLayout(context, attrs, defStyleAttr) { private val binding: ViewOverlayBinding = ViewOverlayBinding.inflate(LayoutInflater.from(context), this) private val erasePaint = Paint().also { it.color = Color.BLACK it.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR) it.isAntiAlias = true } private val backgroundColor = Color.argb(0x80, 0, 0, 0) private var buffer: Bitmap? = null private var bufferCanvas: Canvas? = null private val radius = 16 * resources.displayMetrics.density private val rectList: MutableList<RectF> = mutableListOf() fun setTarget(start: View, top: View, end: View, bottom: View) { val rect = Rect() rectList.clear() listOf(start, top, end, bottom).forEach { view -> view.getGlobalVisibleRect(rect) rectList += RectF(rect).also { it.inset(-radius, -radius) } } } override fun dispatchDraw(canvas: Canvas) { ensureBuffer() val buffer = buffer ?: return val bufferCanvas = bufferCanvas ?: return bufferCanvas.drawColor(backgroundColor, PorterDuff.Mode.SRC) rectList.forEach { bufferCanvas.drawRoundRect(it, radius, radius, erasePaint) } canvas.drawBitmap(buffer, 0f, 0f, null) } private fun ensureBuffer() { if (buffer?.let { it.width == width && it.height == height } == true) { return } buffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { bufferCanvas = Canvas(it) } } override fun onTouchEvent(event: MotionEvent?): Boolean { (parent as? ViewGroup)?.removeView(this) performClick() return true } } 以上です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DecorViewにOnApplyWindowInsetsListenerをセットするとstatusBarBackgroundとnavigationBarBackgroundがなくなる件

要約 ViewにOnApplyWindowInsetsListenerを登録したときはそのViewのonApplyWindowInsetsをコールする必要が無いのかちゃんと考えましょう。 ViewCompat.setOnApplyWindowInsetsListener(window.decorView) { view, insets -> // ごにょごにょ WindowInsetsCompat.toWindowInsetsCompat(view.onApplyWindowInsets(insets.toWindowInsets())) } 背景 ステータスバーやナビゲーションバーなどの領域情報を受け取るためにはsetOnApplyWindowInsetsListenerを使ってWindowInsetsの値を受け取ります。 WindowInsetsは親Viewから子Viewへと伝搬していきますが、途中でInsetsの情報が消費されることもあります。 親がすでにオフセットを持っているなら子Viewがさらにオフセットを取るとおかしくなるので、当たり前の挙動ではあります。 (OnApplyWindowInsetsListener#onApplyWindowInsetsが戻り値を持つのは値の消費を伝えるためですね) しかし、何らかの理由で消費される前の情報を拾いたいという場合は、decorViewにリスナーを設定すればいいじゃんとなるかもしれません。 ※DecorViewにOnApplyWindowInsetsListenerをsetすることがそもそも良いのかそういうのは置いておきます ViewCompat.setOnApplyWindowInsetsListener(window.decorView) { view, insets -> // insets使ってごにょごにょ insets } しかしそうすると、Listenerを設定しただけだというのに、ステータスバーとナビゲーションバーの表示がおかしくなります。 なにもしない OnApplyWindowInsetsListenerをセット どうなっているのか、layout insepectorで見てみましょう。 navigationBarBackgroundとstatusBarBackgroundが無くなっていますね。 setOnApplyWindowInsetsListenerでリスナーを登録するとどうなるのかを調べてみましょう。 View.java public void setOnApplyWindowInsetsListener(OnApplyWindowInsetsListener listener) { getListenerInfo().mOnApplyWindowInsetsListener = listener; } リスナーはListenerInfoに登録されます。 このリスナーはdispatchApplyWindowInsetsでコールされます。 View.java public WindowInsets dispatchApplyWindowInsets(WindowInsets insets) { try { mPrivateFlags3 |= PFLAG3_APPLYING_INSETS; if (mListenerInfo != null && mListenerInfo.mOnApplyWindowInsetsListener != null) { return mListenerInfo.mOnApplyWindowInsetsListener.onApplyWindowInsets(this, insets); } else { return onApplyWindowInsets(insets); } } finally { mPrivateFlags3 &= ~PFLAG3_APPLYING_INSETS; } } 見ての通り、リスナーが登録されているとOnApplyWindowInsetsListenerのonApplyWindowInsetsがコールされ、登録されていなければ、そのViewのonApplyWindowInsetsがコールされます。 つまり、そのViewがonApplyWindowInsetsにある本来コールされるべき処理がリスナーを登録したことで実行されなくなります。 そして、DecorViewはonApplyWindowInsetsにがっつりと本来実行されるべき処理があり、ここでnavigationBarBackgroundとstatusBarBackgroundといったViewを作り追加する処理もあります。 DecorView.java @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { final WindowManager.LayoutParams attrs = mWindow.getAttributes(); mFloatingInsets.setEmpty(); if ((attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0) { // For dialog windows we want to make sure they don't go over the status bar or nav bar. // We consume the system insets and we will reuse them later during the measure phase. // We allow the app to ignore this and handle insets itself by using // FLAG_LAYOUT_IN_SCREEN. if (attrs.height == WindowManager.LayoutParams.WRAP_CONTENT) { mFloatingInsets.top = insets.getSystemWindowInsetTop(); mFloatingInsets.bottom = insets.getSystemWindowInsetBottom(); insets = insets.inset(0, insets.getSystemWindowInsetTop(), 0, insets.getSystemWindowInsetBottom()); } if (mWindow.getAttributes().width == WindowManager.LayoutParams.WRAP_CONTENT) { mFloatingInsets.left = insets.getSystemWindowInsetTop(); mFloatingInsets.right = insets.getSystemWindowInsetBottom(); insets = insets.inset(insets.getSystemWindowInsetLeft(), 0, insets.getSystemWindowInsetRight(), 0); } } mFrameOffsets.set(insets.getSystemWindowInsetsAsRect()); insets = updateColorViews(insets, true /* animate */); insets = updateStatusGuard(insets); if (getForeground() != null) { drawableChanged(); } return insets; } DecorViewにOnApplyWindowInsetsListenerを登録すると、本来実行されるはずだった、これらの処理が実行されなくなるからおかしくなっていたわけですね。これはDecorViewだけでなく、android:fitsSystemWindowsの処理など、onApplyWindowInsetsで実行されるはずの何らかの処理がある場合も同様です。 解決方法 OnApplyWindowInsetsListenerで情報を拾いつつ、本来実行されるはずの処理を実行するには、OnApplyWindowInsetsListenerの中でonApplyWindowInsetsをコールします。 ViewCompat.setOnApplyWindowInsetsListener(window.decorView) { view, insets -> // ごにょごにょ WindowInsetsCompat.toWindowInsetsCompat(view.onApplyWindowInsets(insets.toWindowInsets())) } ViewCompatを使っている場合WindowInsetsCompatをWindowInsetsに変換して渡し、戻り値のWindowInsetsをWindowInsetsCompatに変換しないといけないので記述が長くなります。 こうすることで、本来の処理を邪魔しないように情報だけ拾うことができるようになります。 なにもしない OnApplyWindowInsetsListenerをセット リスナーを登録するとそのViewが持っている処理より、リスナーコールが優先されるという動作は、OnTouchListenerなども同じ挙動ではあるのですが、中の動作を理解していないとかなり戸惑うことになると思います。というか、私が戸惑いました。 以上です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む