- 投稿日:2020-03-17T23:07:29+09:00
コピペでできる一部の文字列でクリックイベントを取得する方法(android)
androidで長いTextViewがあって、一部だけハイパーリンクにするのめちゃくちゃめんどくさいですよね。
ハイパーリンクというか、クリックイベントをとりたかったのですがだいぶめんどくさかったです。なのでコピペでできるようにしました。
ついでにHTMLタグにも対応できるカスタムtextviewを作りました。まずはそれを表示するクラスから
HtmlTextActivity.ktclass HtmlTextActivity: AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_html_text) setHtmlTextView() } private fun setHtmlTextView() { // クリックを検知したいワードをセット val targets = mutableListOf("ここ", "こっち") html_textview.setPartialStringClickListener(targets, object: HtmlTextView.Callback { override fun onClick(tappedText: String) { when (tappedText) { "ここ" -> finish() "こっち" -> { AlertDialog.Builder(this@HtmlTextActivity) .setTitle("tapされました") .setMessage("") .setPositiveButton("OK"){ _, _ -> } .show() } } } }) } }setHtmlTextView()くらいしか特筆すべき点はないです。
クリックイベントをとりたいtargetの文字列を指定して、mutableListに入れて、タップされた文字列がコールバックされるように組みました。次に、肝心なHtmlに対応したTextViewです(今回は改行くらいしかタグを使っていませんがwww)
HtmlTextView.ktclass HtmlTextView: TextView { constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {} constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) {} private var listener: Callback? = null init { // htmlに対応させてます。プレビューでもhtmlが有効になるのでとても見やすいです。 this.text = HtmlCompat.fromHtml(this.text.toString(), FROM_HTML_MODE_COMPACT) } fun setPartialStringClickListener(targets: MutableList<String>, listener: Callback) { this.listener = listener val ss = SpannableString(this.text) for (target in targets) { var start = 0 var end = 0 // リンク化対象の文字列の start, end を算出する val pattern = Pattern.compile(target) val matcher = pattern.matcher(this.text) while (matcher.find()) { start = matcher.start() end = matcher.end() break } ss.setSpan(object: ClickableSpan() { override fun onClick(textView: View) { listener.onClick(target) } }, start, end, Spanned.SPAN_INCLUSIVE_INCLUSIVE) } this.text = ss this.movementMethod = LinkMovementMethod.getInstance() this.highlightColor = Color.TRANSPARENT } interface Callback { fun onClick(clickedStr: String) } }コメントにも書いていますが、プレビューでもhtmlが有効になるのでとても見やすいです(gifはめちゃくちゃめんどくさいのでもう載せませんがww)
一応、HtmlTextViewを使ったときのxmlも載せておきます。
activity_html_text.xml<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <com.free.myapplication.extension.HtmlTextView android:id="@+id/html_textview" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="32dp" android:layout_centerInParent="true" android:textColorLink="@android:color/holo_orange_light" android:text="@string/test_html"/> </RelativeLayout>android:textColorLinkってところで色を指定しないとわけわからん色をつけられちゃうので注意です。
- 投稿日:2020-03-17T22:14:37+09:00
FragmentPagerAdapterのページ切り替えを無効にする方法
FragmentPagerAdapterでスワイプの無効/有効を切り替えたかったので実装をメモ。
ViewPagerでは実装できるけどFragmentPagerAdapterでは難しいみたい。
https://stackoverflow.com/questions/41656902/disable-swipe-in-fragmentpageradapter-android結果、無効というかタブの切り替えは行われないような処理が書けた。
(スワイプしようとしても弾性をもって勝手に元の位置に戻る系の。これはこれでUIとしてありかなと。)
以下、実装。private lateinit var listener:ViewPager.SimpleOnPageChangeListener private lateinit var viewPager:ViewPager : //スワイプを無効にする listener = object : ViewPager.SimpleOnPageChangeListener() { override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { super.onPageScrolled(position, positionOffset, positionOffsetPixels) viewPager.currentItem = 固定したいpageのposition } } viewPager.addOnPageChangeListener(listener) : //スワイプを有効にする viewPager.removeOnPageChangeListener(listener)
- 投稿日:2020-03-17T12:32:20+09:00
fastlaneを使って Internal App SharingにAABファイルをアップロードしてSlackにダウンロードリンクを飛ばす
事前準備
- fastlaneのバージョン
2.139.0以上
を使えるようにする- Internal App Sharingを使ってアプリの配布とダウンロードができるようにしておく
- こちらのスライドを見れば設定やそもそもInternal App Sharingって何?というのは解決できると思います Internal app sharing 完全に理解した
- Google Play Developer APIのPublishing APIを使えるようにしておく
- こちらの記事でやり方は紹介されているので参考にしてください https://qiita.com/itog/items/f382a7cc4a38bd079aab
- 記事内ではKeyにP12を使用する方法になっていますがJSONにします。このJSONが後で必要になります。
実装
desc 'Submit a new build to Google Play Store Internal App Sharing' lane :submit_play_store_internal_app_sharing do # cleanを先にすることでbuildフォルダを一度削除する # これによってbundle[Flavor][Variant]で生成されたaabファイルのみがoutput配下に存在するようになる # [Flavor]や[Variant]は適宜自分たちの環境に合わせて読み替えてください gradle(task: 'clean bundle[Flavor][Variant]') download_url = upload_to_play_store_internal_app_sharing( package_name: '[your.app.package.name]', json_key: 'service-account.json', # 事前準備で用意したjsonを指定します # json_key_data : json_keyでjsonファイルを指定するか生のjsonをこのkeyで指定する aab: lane_context[SharedValues::GRADLE_AAB_OUTPUT_PATH] # 最初のgradleアクションで生成されたaabのファイルパスが格納されている # aab_paths : '' aabを複数アップロードしたい場合はこちらのkeyを使って配列でpathを指定する ) payload = { 'Download URL' => download_url } slack( message: ':android: :google-play: Submit PlayStore Internal App Sharing!', payload: payload, default_payloads: %i[git_branch last_git_commit_message] ) end上記が成功するとSlackにダウンロードURLが投稿されるので、このリンクを端末で開くことでアプリのインストールができます。
アップロードするだけであれば
upload_to_play_store_internal_app_sharing
だけ呼び出せば大丈夫です。注意点
API経由でアップロードした場合一覧( https://play.google.com/apps/publish/internalappsharing/ )に表示されないので注意です。
(なにかやり方が行けないのかも?)おわりに
完全に余談ですがInternal App SharingへのアップロードはGoogle Play Developer API v3が必要で、まずはそちらのバージョンアップが必要でした。
対応してくれた人には感謝しかないです。このへんのはなし
https://github.com/fastlane/fastlane/issues/14573参考URL
https://docs.fastlane.tools/actions/upload_to_play_store_internal_app_sharing/
https://docs.fastlane.tools/actions/gradle/
https://docs.fastlane.tools/actions/slack/
https://github.com/fastlane/fastlane/pull/15822/files
- 投稿日:2020-03-17T11:26:30+09:00
【和訳】Jetpack Navigation(Codelabs for the 2019 Android Dev Summit: Mountain View, October 23-24, 2019.)
Jetpack Navigationの日本語訳
1.はじめに
ナビゲーションアーキテクチャコンポーネントは、ナビゲーションの実装を簡素化すると同時に、アプリのナビゲーションフローを視覚化するのにも役立ちます。 ライブラリには、次のような多くの利点があります。
- フラグメントトランザクションの自動処理
- デフォルトで進むと戻るの適切な処理
- アニメーションと遷移のデフォルト動作
- ファーストクラスの操作としてのディープリンク
- 追加の作業をほとんど必要としないナビゲーションUIパターン(ナビゲーションドロワーやボトムナビゲーションなど)の実装
- ナビゲート中に情報を渡すときの型安全
- アプリのナビゲーションフローを視覚化および編集するためのAndroid Studioツール
構築するもの
すべてのアクティビティとフラグメントはすでに作成されています。 ナビゲーションコンポーネントを使用してそれらを接続し、その際に以下を実装します。
- 視覚的なナビゲーショングラフ
- 宛先とアクションによるナビゲーション
- 遷移アニメーション
- メニューナビゲーション、ボトムナビゲーション、メニュードロワーナビゲーション
- 型安全な引数の受け渡し
- ディープリンク
前提条件
- Kotlinの基本的な知識(このコードラボはKotlinにあります)
- Android Studio 3.2以降
- API 14以上を実行可能なエミュレーターまたはデバイス
2.開始
コードを入手する
GitHubからナビゲーションコードラボを複製します。$ git clone https://github.com/googlecodelabs/android-navigation
または、リポジトリをZipファイルとしてダウンロードできます。
Android Studio 3.3以降を入手してください
Android Studio 3.3以降を使用していることを確認してください。 これは、Android Studioのナビゲーションツールに必要です。
Android Studioの最新バージョンをダウンロードする必要がある場合は、ここからダウンロードできます。ナビゲーションの概要
ナビゲーションコンポーネントは3つの主要な部分で構成され、調和して連携して機能します。 それらです:
1. ナビゲーショングラフ(新しいXMLリソース) - これは、1つの場所にすべてのナビゲーション関連情報を含むリソースです。これには、目的地と呼ばれるアプリ内のすべての場所と、ユーザーがアプリを経由する可能性のあるパスが含まれます。
2. NavHostFragment(レイアウトXMLビュー) - これは、レイアウトに追加する特別なウィジェットです。ナビゲーショングラフのさまざまな目的地が表示されます。
3. NavController(Kotlin / Javaオブジェクト) - これは、ナビゲーショングラフ内の現在の位置を追跡するオブジェクトです。ナビゲーショングラフを移動するときに、NavHostFragment内の宛先コンテンツの交換を調整します。ナビゲートするときは、NavControllerオブジェクトを使用して、ナビゲーショングラフでどこに行きたいか、どのパスをたどるかを伝えます。
NavController
は、NavHostFragmentに適切な宛先を表示します。それが基本的な考え方です。 新しいNavigation Graphリソースから始めて、これが実際にどのように見えるかを見てみましょう。
3.ナビゲーショングラフの紹介
宛先
ナビゲーションコンポーネントは、目的地の概念を導入します。 宛先とは、アプリ内でナビゲートできる任意の場所で、通常はフラグメントまたはアクティビティです。これらはデフォルトでサポートされていますが、必要に応じて独自のカスタム宛先タイプを作成することもできます。
ナビゲーショングラフ
ナビゲーショングラフは、ユーザーがアプリを通過できるすべての可能なパスを定義する新しいリソースタイプです。特定の宛先から到達可能なすべての宛先を視覚的に表示します。Android Studioのナビゲーションエディターにグラフが表示されます。 アプリ用に作成する開始ナビゲーショングラフの一部を次に示します。
ナビゲーションエディターの詳細
1.res/navigation/mobile_navigation.xmlを開きます
2.[デザイン]をクリックしてデザインモードに入ります。表示される内容は次のとおりです。
ナビゲーショングラフには、利用可能な目的地が表示されます。 宛先間の矢印はアクションと呼ばれます。 アクションの詳細については後で学びます。
3.宛先をクリックして、その属性を表示します。
4.矢印で表されているアクションをクリックして、その属性を表示します。
ナビゲーションXMLファイルの構造
グラフィカルナビゲーションエディターで行うすべての変更は、レイアウトエディターがレイアウトXMLを変更する方法と同様に、基になるXMLファイルを変更します。
[テキスト]タブをクリックします。
次のようなXMLが表示されます。
<navigation 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" app:startDestination="@+id/home_dest"> <!-- ...tags for fragments and activities here --> </navigation>お知らせ:
<navigation>
は、すべてのナビゲーショングラフのルートノードです。<navigation>
には、<activity>
または<fragment>
要素で表される1つ以上の宛先が含まれます。app:startDestination
は、ユーザーが最初にアプリを開いたときにデフォルトで起動される宛先を指定する属性です。フラグメントの宛先を見てみましょう。
<fragment android:id="@+id/flow_step_one_dest" android:name="com.example.android.codelabs.navigation.FlowStepFragment" tools:layout="@layout/flow_step_one_fragment"> <argument .../> <action android:id="@+id/next_action" app:destination="@+id/flow_step_two_dest"> </action> </fragment>お知らせ:
android:id
は、このXMLおよびコードの宛先を参照するために使用できるフラグメントのIDを定義します。android:name
は、その宛先に移動するときにインスタンス化するフラグメントの完全修飾クラス名を宣言します。tools:layout
は、グラフィカルエディターに表示するレイアウトを指定します。一部の
<fragment>
タグには、<action>
、<argument>
、および<deepLink>
も含まれています。これらのすべてについては後で説明します。4.ナビゲーショングラフに目的地を追加する
サンプルアプリは、グラフ内のいくつかの目的地から始まります。 このステップでは、新しい目的地を追加します! ナビゲーショングラフに移動するには、ナビゲーショングラフに移動先を追加する必要があります。
注:このコードラボの各ステップのコードは含まれており、ダウンロードしたコードのTODOステートメントの間にコメントアウトされています。
記述したコードを、含まれているコメントアウトされたコードと比較する必要があります。
1.res/navigation/mobile_navigation.xmlを開き、[デザイン]タブをクリックします。
2.新規宛先アイコンをクリックして、「settings_fragment」を選択します。その結果、デザインビューでフラグメントのレイアウトのプレビューをレンダリングする新しい宛先が作成されます。
XMLファイルを直接編集して宛先を追加することもできます。
mobile_navigation.xml<fragment android:id="@+id/settings_dest" android:name="com.example.android.codelabs.navigation.SettingsFragment" android:label="@string/settings" tools:layout="@layout/settings_fragment" />命名規則に従うには、idをデフォルトの
settingsFragment
からsettings_dest
に変更します。5.ナビゲーショングラフを使用してナビゲートする
今、あなたはこの素晴らしいナビゲーショングラフを持っていますが、実際にそれを使ってナビゲートしているわけではありません。
アクティビティとナビゲーション
ナビゲーションコンポーネントは、ナビゲーションの原則に概説されているガイダンスに従います。ナビゲーションの原則では、アクティビティをアプリのエントリポイントとして使用することを推奨しています。 アクティビティには、ボトムナビゲーションなどのグローバルナビゲーションも含まれます。
これに対して、フラグメントは実際の宛先固有のレイアウトになります。
これをすべて機能させるには、アクティビティレイアウトを変更して、NavHostFragmentと呼ばれる特別なウィジェットを含める必要があります。
NavHostFragment
は、ナビゲーショングラフをナビゲートするときに、さまざまなフラグメントの宛先を入出力します。上の図のようなナビゲーションをサポートするシンプルなレイアウトは次のようになります。このコードの例は、
res/layout-470dp/navigation_activity.xml
にあります。res/layout-470dp/navigation_activity.xml<LinearLayout .../> <androidx.appcompat.widget.Toolbar .../> <fragment android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:id="@+id/my_nav_host_fragment" android:name="androidx.navigation.fragment.NavHostFragment" app:navGraph="@navigation/mobile_navigation" app:defaultNavHost="true" /> <com.google.android.material.bottomnavigation.BottomNavigationView .../> </LinearLayout>お知らせ:
- これはアクティビティのレイアウトです。 ボトムナビゲーションとツールバーを含むグローバルナビゲーションが含まれています
android:name="androidx.navigation.fragment.NavHostFragment"
およびapp:defaultNavHost="true"
は、システムの戻るボタンをNavHostFragmentに接続しますapp:navGraph="@navigation/mobile_navigation"
は、NavHostFragment
をナビゲーショングラフに関連付けます。このナビゲーショングラフは、このNavHostFragment
でユーザーがナビゲートできるすべての宛先を指定します。NavController
最後に、ユーザーがボタンをクリックするなどの操作を行う場合、ナビゲートコマンドをトリガーする必要があります。NavControllerと呼ばれる特別なクラスは、
NavHostFragment
のフラグメントスワップをトリガーします。// Command to navigate to flow_step_one_dest findNavController().navigate(R.id.flow_step_one_dest)ナビゲートする宛先またはアクションIDを渡すことに注意してください。これらは、ナビゲーショングラフXMLで定義されているIDです。これは、宛先IDを渡す例です。
NavController
は強力です。なぜなら、navigate()
やpopBackStack()
などのメソッドを呼び出すと、これらのコマンドは、ナビゲート先のタイプに基づいて適切なフレームワーク操作に変換されます。たとえば、アクティビティの宛先でnavigate()
を呼び出すと、NavController
が代わりにstartActivity()
を呼び出します。
NavHostFragment
に関連付けられたNavController
オブジェクトを取得する方法はいくつかあります。Kotlinでは、フラグメント、アクティビティ、またはビュー内からナビゲーションコマンドを呼び出すかどうかに応じて、次の拡張関数のいずれかを使用することをお勧めします。
NavController
はNavHostFragment
に関連付けられています。したがって、どの方法を使用する場合でも、フラグメント、ビュー、またはビューIDがNavHostFragment
自体であるか、親としてNavHostFragment
を持っていることを確認する必要があります。そうしないと、IllegalStateException
が発生します。NavControllerを使用して宛先に移動する
NavController
を使用してナビゲートする番です。宛先にナビゲートボタンをフックして、flow_step_one_dest
宛(FlowStepFragment
である宛先)にナビゲートします。1.HomeFragment.ktを開く
2.onViewCreated()
でnavigate_destination_button
をフックしますHomeFragment.ktval button = view.findViewById<Button>(R.id.navigate_destination_button) button?.setOnClickListener { findNavController().navigate(R.id.flow_step_one_dest, null) }3.アプリを実行し、移動先へ移動ボタンをクリックします。 ボタンが
flow_step_one_dest
宛にナビゲートすることに注意してください。便利なメソッドNavigation.createNavigateOnClickListener(@IdRes destId:int、bundle:Bundle)を使用することもできます。このメソッドは、
OnClickListener
を構築して、宛先に渡される引数のバンドルを使用して、指定された宛先にナビゲートします。クリックリスナーコードは次のようになります。
val button = view.findViewById<Button>(R.id.navigate_destination_button) button?.setOnClickListener( Navigation.createNavigateOnClickListener(R.id.flow_step_one_dest, null) )6.ナビゲーション遷移の変更
以下に示すように、各
navigate()
呼び出しには、それほど刺激的なデフォルト遷移が関連付けられていません。
NavOptions
のセットを含めることにより、デフォルトの遷移および呼び出しに関連付けられた他の属性をオーバーライドできます。NavOptions
は、必要なオプションのみをオーバーライドおよび設定できるBuilder
パターンを使用します。NavOptions用のktx DSLもあります。これを使用します。アニメーション化されたトランジションの場合、
anim
リソースフォルダーでXMLアニメーションリソースを定義し、それらのアニメーションをトランジションに使用できます。アプリコードにはいくつかの例が含まれています。カスタム遷移を追加する
宛先へ移動ボタンを押すとカスタムの遷移アニメーションが表示されるようにコードを更新します。
1.HomeFragment.ktを開きます
2.NavOptions
を定義し、navigate_destination_button
のnavigate()
呼び出しに渡しますval options = navOptions { anim { enter = R.anim.slide_in_right exit = R.anim.slide_out_left popEnter = R.anim.slide_in_left popExit = R.anim.slide_out_right } } view.findViewById<Button>(R.id.navigate_destination_button)?.setOnClickListener { findNavController().navigate(R.id.flow_step_one_dest, null, options) }3.手順5で追加したコードがまだある場合は削除します
4.移動先へ移動ボタンをタップするとフラグメントが画面上にスライドし、バックキーを押すと画面外にスライドすることを確認します7.アクションを使用してナビゲートする
Actions
ナビゲーションシステムでは、アクションを介してナビゲートすることもできます。前述のように、ナビゲーショングラフに表示される線は、アクションの視覚的表現です。
アクションによるナビゲーションには、目的地によるナビゲーションよりも次の利点があります。
- アプリのナビゲーションパスを視覚化できます
- アクションには、遷移アニメーション、引数値、バックスタック動作など、設定可能な追加の関連属性を含めることができます
- プラグインで安全な引数を使用してナビゲートできます
flow_step_one_dest
とflow_step_two_dest
を接続するアクションのビジュアルとXMLは次のとおりです。<fragment android:id="@+id/home_dest" android:name="com.example.android.codelabs.navigation.HomeFragment" .../> <fragment android:id="@+id/flow_step_two_dest" android:name="com.example.android.codelabs.navigation.FlowStepFragment"> <argument .../> <action android:id="@+id/next_action" app:popUpTo="@id/home_dest"> </action> </fragment>お知らせ:
-flow_step_two_dest
をhome_dest
に接続するアクションには、同じIDnext_action
が使用されます。flow_step_one_dest
またはflow_step_two_destのnext_action
idを使用してナビゲートできます。これは、アクションが抽象化のレベルを提供し、コンテキストに応じて異なる場所をナビゲートできる方法の例です。
-popUpTo
属性が使用されます - このアクションは、home_dest
に到達するまでバックスタックからフラグメントをポップしますアクションでナビゲートする
名前を付けてナビゲートボタンを接続して、その名前どおりになるようにします。
1.デザインモードで
mobile_navigation.xml
ファイルを開きます
2.home_dest
からflow_step_one_dest
に矢印をドラッグします。3.アクションの矢印が選択された状態(青)で、アクションのプロパティを次のように変更します。
- ID = next_action
- Transition for Enter = slide_in_right
- Transition for Exit = slide_out_left
- Transitions for Pop Enter = slide_in_left
- Transitions for Pop Exit = slide_out_right
4.テキストタブをクリックします
home_dest宛先の下に新しく追加されたnext_actionアクションに注意してください。mobile_navigation.xml<fragment android:id="@+id/home_dest" ...> <action android:id="@+id/next_action" app:destination="@+id/flow_step_one" app:enterAnim="@anim/slide_in_right" app:exitAnim="@anim/slide_out_left" app:popEnterAnim="@anim/slide_in_left" app:popExitAnim="@anim/slide_out_right" />5.HomeFragment.ktを開く
6.navigate_action_button
にクリックリスナーを追加しますHomeFragment.ktview.findViewById<Button>(R.id.navigate_action_button)?.setOnClickListener( Navigation.createNavigateOnClickListener(R.id.next_action, null) )アクションを使用すると、プログラムで指定するのではなく、ナビゲーションXMLファイルに
NavOptions
を添付できます。7.操作に移動をタップすると、次の画面に移動することを確認します。
8.ナビゲーションに安全な引数を使用する
Safe Args
ナビゲーションコンポーネントには、Safe Argsと呼ばれるGradleプラグインがあり、目的地とアクションに指定された引数へのタイプセーフなアクセスのためのシンプルなオブジェクトとビルダークラスを生成します。
Safe argsを使用すると、宛先間で値を渡すときに次のようなコードを取り除くことができます。
val username = arguments?.getString("usernameKey")そして、代わりに、セッターとゲッターを生成したコードに置き換えます。
val username = args.username型の安全性のため、安全な引数生成クラスを使用し、アクションでナビゲートし、 ナビゲーション間に引数を渡す方法によるナビゲーションが推奨されます。
safe argsを使用して値を渡す
1.プロジェクト
build.gradle
ファイルを開き、safe argsプラグインに注目してください。build.gradledependencies { classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion" //... }2.
app/build.gradle
ファイルを開き、適用されたプラグインに注目してください。app/build.gradleapply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'androidx.navigation.safeargs.kotlin' android { //... }3.
mobile_navigation.xml
を開き、flow_step_one_dest
宛の引数の定義方法に注目してください。mobile_navigation.xml<fragment android:id="@+id/flow_step_one_dest" android:name="com.example.android.codelabs.navigation.FlowStepFragment" tools:layout="@layout/flow_step_one_fragment"> <argument android:name="flowStepNumber" app:argType="integer" android:defaultValue="1"/> <action...> </action> </fragment>
<argument>
タグを使用して、safeargsはFlowStepFragmentArgs
というクラスを生成します。XMLには、
android:name="flowStepNumber"
で指定されたflowStepNumber
という引数が含まれているため、生成されたクラスFlowStepFragmentArgs
には、ゲッターとセッターを持つ変数flowStepNumber
が含まれます。4.
FlowStepFragment.kt
を開きます
5.以下に示すコード行をコメントアウトします。FlowStepFragment.kt// Comment out this line // val flowStepNumber = arguments?.getInt("flowStepNumber")この古いスタイルのコードはタイプセーフではありません。 安全な引数を使用することをお勧めします。
6.
FlowStepFragment
を更新して、コード生成クラスFlowStepFragmentArgs
を使用します。
これにより、タイプセーフな方法でFlowStepFragment
引数が取得されます。FlowStepFragment.ktval safeArgs: FlowStepFragmentArgs by navArgs() val flowStepNumber = safeArgs.flowStepNumberSafe Args Directionクラス
引数を追加してもしなくても、安全な引数を使用して型安全な方法でナビゲートすることもできます。これは、生成されたDirectionsクラスを使用して行います。
Directionsクラスは、アクションを持つすべての個別の宛先に対して生成されます。Directionsクラスには、目的地のすべてのアクションのメソッドが含まれています。
たとえば、
HomeFragment.kt
のnavigate_action_button
クリックリスナーを次のように変更できます。HomeFragment.kt// クリックリスナーラムダを定義しているため、中括弧の使用に注意してください view.findViewById<Button>(R.id.navigate_action_button)?.setOnClickListener { val flowStepNumberArg = 1 val action = HomeFragmentDirections.nextAction(flowStepNumberArg) findNavController().navigate(action) }ナビゲーショングラフXMLでは、各引数に
defaultValue
を提供できることに注意してください。そうしない場合は、次に示すように、アクションに引数を渡す必要があります。
HomeFragmentDirections.nextAction(flowStepNumberArg)
9.メニュー、ドロワー、ボトムナビゲーションを使用したナビゲーション
NavigationUIおよびnavigation-ui-ktx
ナビゲーションコンポーネントには、NavigationUIクラスとnavigation-ui-ktx kotlin拡張が含まれます。
NavigationUI
には、メニュー項目をナビゲーションの宛先に関連付ける静的メソッドがあり、navigation-ui-ktx
は同じことを行う拡張機能のセットです。NavigationUI
は、現在のグラフで目的地と同じIDのメニュー項目を見つけると、メニュー項目を構成してその目的地にナビゲートします。オプションメニューでNavigationUIを使用する
NavigationUI
を使用する最も簡単な方法の1つは、オプションメニューのセットアップを簡素化することです。特に、NavigationUI
はonOptionsItemSelected
コールバックの処理を簡素化します。1.MainActivity.ktを開く
onCreateOptionsMenu
でメニューoverflow_menu
を拡張するためのコードをすでに持っていることに注意してください。
2.res/menu/overflow_menu.xml
を開く
3.オーバーフローメニューを更新して、settings_dest
を含めますoverflow_menu.xml<item android:id="@+id/settings_dest" android:icon="@drawable/ic_settings" android:menuCategory="secondary" android:title="@string/settings" />4.MainActivity.ktを開く
5.NavigationUI
でonOptionsItemSelected
をonNavDestinationSelectedヘルパーメソッドで処理します。メニュー項目がナビゲートすることを意図していない場合は、super.onOptionsItemSelected
で処理しますMainActivity.ktoverride fun onOptionsItemSelected(item: MenuItem): Boolean { return item.onNavDestinationSelected(findNavController(R.id.my_nav_host_fragment)) || super.onOptionsItemSelected(item) }6.アプリを実行します。
SettingsFragment
にナビゲートする機能的なActionBarメニューが必要です。NavigationUIを使用してボトムナビゲーションを構成する
コードには、ボトムナビゲーションを実装するためのXMLレイアウトコードが既に含まれているため、ボトムナビゲーションバーが表示されます。 しかし、どこにも移動しません。
1.
res/layout/navigation_activity/navigation_activity.xml(h470dp)
を開き、テキストタブをクリックします
ボトムナビゲーションのXMLレイアウトコードが存在し、bottom_nav_menu.xml
を参照していることに注意してください。navigation_activity.xml<com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottom_nav_view" android:layout_width="match_parent" android:layout_height="wrap_content" app:menu="@menu/bottom_nav_menu" />2.
res/menu/bottom_nav_menu.xml
を開く
ボトムナビゲーションに2つのアイテムがあり、それらのIDがナビゲーショングラフの宛先と一致しているか注意してください。bottom_nav_menu.xml<menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@id/home_dest" android:icon="@drawable/ic_home" android:title="@string/home" /> <item android:id="@id/deeplink_dest" android:icon="@drawable/ic_android" android:title="@string/deeplink" /> </menu>NavigationUIを使用して、ボトムナビゲーションに実際に何かをさせましょう。
3.
MainActivity.kt
を開く4.setupWithNavController(bottomNavigationView:BottomNavigationView、navController:NavController)を使用して
setupBottomNavMenu
メソッドを実装します。MainActivity.ktprivate fun setupBottomNavMenu(navController: NavController) { val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_nav_view) bottomNav?.setupWithNavController(navController) }これで、ボトムナビゲーションが機能します!
NavigationUIを使用してナビゲーションドロワーを構成する
最後に、
NavigationUI
を使用してサイドナビゲーションとナビゲーションドロワーを構成します。これにはActionBarのとproper up navigationの処理を含みます。これは、画面が十分に大きい場合、または画面が下のナビゲーションには短すぎる場合に表示されます。最初に、適切なレイアウトXMLコードが既にアプリにどのようにあるかを観察します。
1.
navigation_activity.xml
とnavigation_activity.xml (w960dp)
を開きます
両方のレイアウトにnav_drawer_menuに接続されたNavigationViewが含まれていることに注意してください。タブレットバージョン(w960dp)では、NavigationViewは常に画面に表示されます。小さいデバイスでは、NavigationViewはDrawerLayout内にネストされます。次に、NavigationViewナビゲーションの実装を開始します。
2.MainActivity.ktを開く
3.setupWithNavController(navigationView:NavigationView、navController:NavController)を使用してsetupNavigationMenu
メソッドを実装します。
このバージョンのメソッドは、BottomNavigationView
ではなくNavigationView
をどのように使用するかに注意してください。MainActivity.ktprivate fun setupNavigationMenu(navController: NavController) { val sideNavView = findViewById<NavigationView>(R.id.nav_view) sideNavView?.setupWithNavController(navController) }これで、ナビゲーションビューメニューが画面に表示されますが、ActionBarには影響しません。
ActionBar
をセットアップするには、AppBarConfigurationのインスタンスを作成する必要があります。
AppBarConfiguration
の目的は、ツールバー、折りたたみツールバー、およびアクションバーに必要な構成オプションを指定することです。
構成オプションには、バーが引き出しレイアウトを処理する必要があるかどうか、どの宛先が最上位の宛先と見なされるかなどが含まれます。トップレベルの宛先は、アプリのルートレベルの宛先です。これらの宛先は、アプリバーに「上」ボタンを表示せず、宛先がドロワーレイアウトを使用する場合、ドロワーアイコンを表示します。
4.トップレベルの宛先IDとドロワーレイアウトのセットを渡して、
AppBarConfiguration
を作成します。MainActivity.ktval drawerLayout : DrawerLayout? = findViewById(R.id.drawer_layout) appBarConfiguration = AppBarConfiguration( setOf(R.id.home_dest, R.id.deeplink_dest), drawerLayout)トップレベルの宛先を決定する方法
ボトムナビゲーションやサイドナビゲーションなど、グローバルナビゲーションUIを介して到達可能な宛先はすべて、階層の同じトップレベルにあるようにユーザーに表示されます。したがって、これらは最上位の宛先です。
home_dest
およびdeeplink_dest
は下のナビゲーションにあり、これら両方の宛先にドロワーアイコンを表示するため、これらは最上位の宛先です。
開始宛先は常に最上位の宛先と見なされることに注意してください。 最上位の宛先のリストを指定しない場合、唯一の最上位の宛先は開始宛先です。AppBarConfigurationの詳細については、ドキュメントをご覧ください。AppBarConfigurationができたので、NavigationUI.setupActionBarWithNavControllerを呼び出すことができます。これにより、次のことが行われます。
- 宛先のラベルに基づいてActionBarにタイトルを表示します
- 最上位の目的地にいないときはいつでも、上ボタンを表示する
- トップレベルの目的地にいるときに引き出しアイコン(ハンバーガーアイコン)を表示する
5.
setupActionBarWithNavController
を実装するMainActivity.ktprivate fun setupActionBar(navController: NavController, appBarConfig : AppBarConfiguration) { setupActionBarWithNavController(navController, appBarConfig) }また、[上へ]ボタンが押されたときに何が起こるかをNavigationUIに処理させる必要があります。
6.
onAppNNavigationUp
をオーバーライドし、同じAppBarConfiguration
を使用してNavigationUI.navigateUpを呼び出します。MainActivity.ktoverride fun onSupportNavigateUp(): Boolean { return findNavController(R.id.my_nav_host_fragment).navigateUp(appBarConfiguration) }いくつかの異なる
navigateUp
メソッドがあることに注意してください。AppBarConfiguration
パラメーターを受け入れるNavigation UIから次のインポートを使用していることを確認してください。
import androidx.navigation.ui.navigateUp
7.コードを実行します。 分割画面でアプリを開くと、ナビゲーションドロワーが機能するはずです。 適切なタイミングで上アイコンと引き出しアイコンが表示され、正しく機能するはずです。
レイアウトnavigation_activity.xml(h470dp)は、ポートレートモードの電話機で使用されます。このレイアウトにはナビゲーションドロワーは含まれず、代わりにボトムナビゲーションが含まれます。そのため、ナビゲーションドロワーを表示するにはアプリを分割画面で開く必要があります。ナビゲーションドロワーとボトムナビゲーションの両方を備えたレイアウトがない理由は、マテリアルデザインガイドラインがこれに対して警告しているためです。
NavigationViewへの新しい目的地の追加は簡単です。 ナビゲーションドロワーで上下のナビゲーションを操作したら、新しいメニュー項目を追加するだけです。
8.
menu/nav_drawer_menu.xml
を開く
9.settings_destの新しいメニュー項目を追加しますmenu/nav_drawer_menu.xml<item android:id="@+id/settings_dest" android:icon="@drawable/ic_settings" android:title="@string/settings" />これで、ナビゲーションドロワーに設定画面が目的地として表示されます。 よくできました!
10.宛先へのディープリンク
ディープリンクとナビゲーション
ナビゲーションコンポーネントには、ディープリンクのサポートも含まれています。ディープリンクは、実際のURLリンクからのものであれ、通知からの保留中の意図であれ、アプリのナビゲーションの途中にジャンプする方法です。
ナビゲーションライブラリを使用してディープリンクを処理する利点の1つは、ユーザーがアプリウィジェット、通知、またはWebリンク(次の手順で説明)などの他のエントリポイントから適切なバックスタックを使用して適切な目的地で開始できることです。
ナビゲーションは、ユーザーを特定の目的地に導くPendingIntentを構築するNavDeepLinkBuilderクラスを提供します。
ディープリンクを追加する
NavDeepLinkBuilder
を使用して、アプリウィジェットを宛先に接続します。1.
DeepLinkAppWidgetProvider.kt
を開く
2.NavDeepLinkBuilder
で構築されたPendingIntent
を追加します。DeepLinkAppWidgetProvider.ktval args = Bundle() args.putString("myarg", "From Widget"); val pendingIntent = NavDeepLinkBuilder(context) .setGraph(R.navigation.mobile_navigation) .setDestination(R.id.deeplink_dest) .setArguments(args) .createPendingIntent() remoteViews.setOnClickPendingIntent(R.id.deep_link_button, pendingIntent)お知らせ:
setGraph
にはナビゲーショングラフが含まれています。setDestination
は、リンクの移動先を指定します。setArguments
には、ディープリンクに渡す引数が含まれます。デフォルトでは、
NavDeepLinkBuilder
はランチャーアクティビティを開始します。 アクティビティをコンテキストとして渡すか、setComponentName()
を介して明示的なアクティビティクラスを設定することにより、この動作をオーバーライドできます。3.ディープリンクウィジェットをホーム画面に追加します。 ホーム画面を長押しして、ウィジェットを追加するオプションを表示します。
4.ウィジェットをタップし、Androidの宛先が正しい引数で開くことを確認します。 これは、
DeepLinkAppWidgetProvider
で渡した引数であるため、上部に「From Widget」と表示されます。5.戻るボタンを押すと、
home_dest
宛先に移動することを確認します。便宜上、
NavController
のcreateDeepLink()
メソッドを呼び出して、NavController
のコンテキストと現在のナビゲーショングラフを使用することもできます。DeepLinkバックスタック
ディープリンクのバックスタックは、渡したナビゲーショングラフを使用して決定されます。選択した明示的なアクティビティに親アクティビティがある場合、それらの親アクティビティも含まれます。
バックスタックは、
app:startDestination
で指定された宛先を使用して生成されます。このアプリでは、1つのアクティビティと1つのレベルのナビゲーションしかないため、バックスタックはhome_dest
の宛先に移動します。より複雑なナビゲーションには、ネストされたナビゲーショングラフを含めることができます。ネストされたグラフの各レベルで
app:startDestination
がバックスタックを決定します。ディープリンクとネストされたグラフの詳細については、ナビゲーションの原則をご覧ください。11. Webリンクとリンク先の関連付け
要素
ディープリンクの最も一般的な用途の1つは、Webリンクがアプリでアクティビティを開くことを許可することです。従来は、intent-filterを使用して、URLを開きたいアクティビティに関連付けていました。
ナビゲーションライブラリを使用すると、これが非常に簡単になり、URLをナビゲーショングラフの目的地に直接マッピングできます。
<deepLink>
は、グラフの宛先に追加できる要素です。各<deepLink>
要素には、単一の必須属性app:uri
があります。
URIの直接一致に加えて、次の機能がサポートされています。
- スキームのないURIは、httpおよびhttpsと見なされます。 たとえば、
www.example.com
はhttp://www.example.com
およびhttps://www.example.com
に一致します。{placeholder_name}
の形式のプレースホルダーを使用して、1つ以上の文字に一致させることができます。プレースホルダーの文字列値は、同じ名前のキーを持つ引数バンドルで使用できます。 たとえば、http://www.example.com/users/{id}
はhttp://www.example.com/users/4
と一致します。.*
ワイルドカードを使用して、0個以上の文字と一致させることができます。- NavControllerはACTION_VIEWインテントを自動的に処理し、一致するディープリンクを探します。
を使用してURIベースのディープリンクを追加する
この手順では、
www.example.com
へのディープリンクを追加します。1.mobile_navigation.xmlを開く
2.<deepLink>
要素をdeeplink_dest
宛先に追加します。mobile_navigation.xml<fragment android:id="@+id/deeplink_dest" android:name="com.example.android.codelabs.navigation.DeepLinkFragment" android:label="@string/deeplink" tools:layout="@layout/deeplink_fragment"> <argument android:name="myarg" android:defaultValue="Android!"/> <deepLink app:uri="www.example.com/{myarg}" /> </fragment>3.AndroidManifest.xmlを開く
4.nav-graph
タグを追加します。 これにより、適切なインテントフィルターが生成されます。AndroidManifest.xml<activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <nav-graph android:value="@navigation/mobile_navigation" /> </activity>生成されたものを確認したい場合は、出力APKで結果を見つけることができます。
プロジェクトビューで、app -> build -> outputs -> apk ->debug -> app-debug.apkに移動します。
app-debug.apkをダブルクリックしてAPKアナライザーで開きます。 ここでは、生成されたAndroidManifestを見ることができます。
5.ディープリンクを使用してアプリを起動します。 これを行うには2つの方法があります。
- adbを使う:
adb shell am start -a android.intent.action.VIEW -d "http://www.example.com/urlTest"
- Googleアプリ経由で移動します。
www.example.com/urlTest
を検索バーに配置すると、曖昧さ回避ウィンドウが表示されます。ナビゲーションコードラボを選択いずれにしても、画面に「urlTest」というメッセージが表示されるはずです。 これは、URLからフラグメントに渡されました。
12.(オプション)自分でナビゲートする
コードラボアプリには、試してみるためのもう1つの部分があります。それがショッピングカートボタンです。
これは、このコードラボで学んだスキルの要約です。この手順にはコメントが含まれていないため、自分で試してみてください。
- 新しいフラグメントクラスを作成する
- フラグメントをナビゲーショングラフの宛先として追加します
- NavigationUIを使用してメニューを処理し、ショッピングカートアイコンで新しいフラグメントクラスを開きます。
13.おめでとうございます!
ナビゲーションコンポーネントの背後にある基本概念に精通している! このコードラボでは、次のことを学びました。
- ナビゲーショングラフ構造
- NavHostFragmentおよびNavController
- 特定の目的地に移動する方法
- アクションでナビゲートする方法
- 新しいsafeargsプラグインの使用を含む、宛先間で引数を渡す方法
- メニュー、ボトムナビゲーション、ナビゲーションドロワーを使用したナビゲーション
- ディープリンクを介したナビゲーション
このアプリを引き続き使用するか、独自のアプリでナビゲーションを使用してください。
試してみるべきことは他にもたくさんあります。
- バックスタックからの宛先のポップ(またはバックスタック操作)
- ネストされたナビゲーショングラフ
- 条件付きナビゲーション
- 新しい宛先のサポートを追加する
ナビゲーションコンポーネントの詳細については、ドキュメントをご覧ください。他のアーキテクチャコンポーネントについて学びたい場合は、次のコードラボを試してください。
- Room with a View Codelab (LiveData, ViewModel and Room)
- Android WorkManager Codelab
- Android Paging Codelab
- Android Lifecycle-aware components Codelab (LiveData and ViewModel)
- Android Persistence Codelab (Room)