20200317のAndroidに関する記事は4件です。

コピペでできる一部の文字列でクリックイベントを取得する方法(android)

ezgif.com-video-to-gif.gif

androidで長いTextViewがあって、一部だけハイパーリンクにするのめちゃくちゃめんどくさいですよね。
ハイパーリンクというか、クリックイベントをとりたかったのですがだいぶめんどくさかったです。

なのでコピペでできるようにしました。
ついでにHTMLタグにも対応できるカスタムtextviewを作りました。

まずはそれを表示するクラスから

HtmlTextActivity.kt
class 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.kt

class 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ってところで色を指定しないとわけわからん色をつけられちゃうので注意です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 = 固定したいpageposition
     }
}
viewPager.addOnPageChangeListener(listener)

                :

//スワイプを有効にする
viewPager.removeOnPageChangeListener(listener)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

fastlaneを使って Internal App SharingにAABファイルをアップロードしてSlackにダウンロードリンクを飛ばす

事前準備

  • fastlaneのバージョン2.139.0以上を使えるようにする
  • 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が投稿されるので、このリンクを端末で開くことでアプリのインストールができます。
Screen_Shot_2020-03-17_at_12_27_09.png

アップロードするだけであれば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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【和訳】Jetpack Navigation(Codelabs for the 2019 Android Dev Summit: Mountain View, October 23-24, 2019.)

Android Dev Summit 2019
Codelabs for the 2019 Android Dev Summit: Mountain View, October 23-24, 2019.

Jetpack Navigationの日本語訳

1.はじめに

ナビゲーションアーキテクチャコンポーネントは、ナビゲーションの実装を簡素化すると同時に、アプリのナビゲーションフローを視覚化するのにも役立ちます。 ライブラリには、次のような多くの利点があります。

  • フラグメントトランザクションの自動処理
  • デフォルトで進むと戻るの適切な処理
  • アニメーションと遷移のデフォルト動作
  • ファーストクラスの操作としてのディープリンク
  • 追加の作業をほとんど必要としないナビゲーションUIパターン(ナビゲーションドロワーやボトムナビゲーションなど)の実装
  • ナビゲート中に情報を渡すときの型安全
  • アプリのナビゲーションフローを視覚化および編集するためのAndroid Studioツール

構築するもの

このコードラボでは、以下に示すサンプルアプリを使用します。
image.png

すべてのアクティビティとフラグメントはすでに作成されています。 ナビゲーションコンポーネントを使用してそれらを接続し、その際に以下を実装します。

  • 視覚的なナビゲーショングラフ
  • 宛先とアクションによるナビゲーション
  • 遷移アニメーション
  • メニューナビゲーション、ボトムナビゲーション、メニュードロワーナビゲーション
  • 型安全な引数の受け渡し
  • ディープリンク

前提条件

  • 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のナビゲーションエディターにグラフが表示されます。 アプリ用に作成する開始ナビゲーショングラフの一部を次に示します。

image.png

ナビゲーションエディターの詳細

1.res/navigation/mobile_navigation.xmlを開きます
2.[デザイン]をクリックしてデザインモードに入ります。

image.png

表示される内容は次のとおりです。

image.png

ナビゲーショングラフには、利用可能な目的地が表示されます。 宛先間の矢印はアクションと呼ばれます。 アクションの詳細については後で学びます。

3.宛先をクリックして、その属性を表示します。

image.png

4.矢印で表されているアクションをクリックして、その属性を表示します。

image.png

ナビゲーションXMLファイルの構造

グラフィカルナビゲーションエディターで行うすべての変更は、レイアウトエディターがレイアウトXMLを変更する方法と同様に、基になるXMLファイルを変更します。

[テキスト]タブをクリックします。

image.png

次のような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」を選択します。

image.png

その結果、デザインビューでフラグメントのレイアウトのプレビューをレンダリングする新しい宛先が作成されます。

image.png

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は、ナビゲーショングラフをナビゲートするときに、さまざまなフラグメントの宛先を入出力します。

image.png

上の図のようなナビゲーションをサポートするシンプルなレイアウトは次のようになります。このコードの例は、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では、フラグメント、アクティビティ、またはビュー内からナビゲーションコマンドを呼び出すかどうかに応じて、次の拡張関数のいずれかを使用することをお勧めします。

NavControllerNavHostFragmentに関連付けられています。したがって、どの方法を使用する場合でも、フラグメント、ビュー、またはビューIDがNavHostFragment自体であるか、親としてNavHostFragmentを持っていることを確認する必要があります。そうしないと、IllegalStateExceptionが発生します。

NavControllerを使用して宛先に移動する

NavControllerを使用してナビゲートする番です。宛先にナビゲートボタンをフックして、flow_step_one_dest宛(FlowStepFragmentである宛先)にナビゲートします。

1.HomeFragment.ktを開く
2.onViewCreated()navigate_destination_buttonをフックします

HomeFragment.kt
val 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()呼び出しには、それほど刺激的なデフォルト遷移が関連付けられていません。

image.png

NavOptionsのセットを含めることにより、デフォルトの遷移および呼び出しに関連付けられた他の属性をオーバーライドできます。NavOptionsは、必要なオプションのみをオーバーライドおよび設定できるBuilderパターンを使用します。NavOptions用のktx DSLもあります。これを使用します。

アニメーション化されたトランジションの場合、animリソースフォルダーでXMLアニメーションリソースを定義し、それらのアニメーションをトランジションに使用できます。アプリコードにはいくつかの例が含まれています。

image.png

カスタム遷移を追加する

宛先へ移動ボタンを押すとカスタムの遷移アニメーションが表示されるようにコードを更新します。

1.HomeFragment.ktを開きます
2.NavOptionsを定義し、navigate_destination_buttonnavigate()呼び出しに渡します

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.移動先へ移動ボタンをタップするとフラグメントが画面上にスライドし、バックキーを押すと画面外にスライドすることを確認します

image.png

7.アクションを使用してナビゲートする

Actions

ナビゲーションシステムでは、アクションを介してナビゲートすることもできます。前述のように、ナビゲーショングラフに表示される線は、アクションの視覚的表現です。

image.png

アクションによるナビゲーションには、目的地によるナビゲーションよりも次の利点があります。

  • アプリのナビゲーションパスを視覚化できます
  • アクションには、遷移アニメーション、引数値、バックスタック動作など、設定可能な追加の関連属性を含めることができます
  • プラグインで安全な引数を使用してナビゲートできます

flow_step_one_destflow_step_two_destを接続するアクションのビジュアルとXMLは次のとおりです。

image.png

<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_desthome_destに接続するアクションには、同じID next_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に矢印をドラッグします。

image.png

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

image.png

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.kt
view.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.gradle
dependencies {
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
    //...
    }

2.app/build.gradleファイルを開き、適用されたプラグインに注目してください。

app/build.gradle
apply 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というクラスを生成します。

image.png

XMLには、android:name="flowStepNumber"で指定されたflowStepNumberという引数が含まれているため、生成されたクラスFlowStepFragmentArgsには、ゲッターとセッターを持つ変数flowStepNumberが含まれます。

image.png

4.FlowStepFragment.ktを開きます
5.以下に示すコード行をコメントアウトします。

FlowStepFragment.kt
// Comment out this line
// val flowStepNumber = arguments?.getInt("flowStepNumber")

この古いスタイルのコードはタイプセーフではありません。 安全な引数を使用することをお勧めします。

6.FlowStepFragmentを更新して、コード生成クラスFlowStepFragmentArgsを使用します。
これにより、タイプセーフな方法でFlowStepFragment引数が取得されます。

FlowStepFragment.kt
val safeArgs: FlowStepFragmentArgs by navArgs()
val flowStepNumber = safeArgs.flowStepNumber

Safe Args Directionクラス

引数を追加してもしなくても、安全な引数を使用して型安全な方法でナビゲートすることもできます。これは、生成されたDirectionsクラスを使用して行います。

image.png

Directionsクラスは、アクションを持つすべての個別の宛先に対して生成されます。Directionsクラスには、目的地のすべてのアクションのメソッドが含まれています。

image.png

たとえば、HomeFragment.ktnavigate_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つは、オプションメニューのセットアップを簡素化することです。特に、NavigationUIonOptionsItemSelectedコールバックの処理を簡素化します。

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.NavigationUIonOptionsItemSelectedonNavDestinationSelectedヘルパーメソッドで処理します。メニュー項目がナビゲートすることを意図していない場合は、super.onOptionsItemSelectedで処理します

MainActivity.kt
override fun onOptionsItemSelected(item: MenuItem): Boolean {
    return item.onNavDestinationSelected(findNavController(R.id.my_nav_host_fragment))
            || super.onOptionsItemSelected(item)
}

6.アプリを実行します。 SettingsFragmentにナビゲートする機能的なActionBarメニューが必要です。

image.png

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.kt
private 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.xmlnavigation_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.kt
private fun setupNavigationMenu(navController: NavController) {
    val sideNavView = findViewById<NavigationView>(R.id.nav_view)
    sideNavView?.setupWithNavController(navController)
}

これで、ナビゲーションビューメニューが画面に表示されますが、ActionBarには影響しません。

ActionBarをセットアップするには、AppBarConfigurationのインスタンスを作成する必要があります。
AppBarConfigurationの目的は、ツールバー、折りたたみツールバー、およびアクションバーに必要な構成オプションを指定することです。
構成オプションには、バーが引き出しレイアウトを処理する必要があるかどうか、どの宛先が最上位の宛先と見なされるかなどが含まれます。

トップレベルの宛先は、アプリのルートレベルの宛先です。これらの宛先は、アプリバーに「上」ボタンを表示せず、宛先がドロワーレイアウトを使用する場合、ドロワーアイコンを表示します。

image.png

4.トップレベルの宛先IDとドロワーレイアウトのセットを渡して、AppBarConfigurationを作成します。

MainActivity.kt
val 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.kt
private fun setupActionBar(navController: NavController,
                           appBarConfig : AppBarConfiguration) {
    setupActionBarWithNavController(navController, appBarConfig)
}

また、[上へ]ボタンが押されたときに何が起こるかをNavigationUIに処理させる必要があります。

6.onAppNNavigationUpをオーバーライドし、同じAppBarConfigurationを使用してNavigationUI.navigateUpを呼び出します。

MainActivity.kt
override 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)は、ポートレートモードの電話機で使用されます。このレイアウトにはナビゲーションドロワーは含まれず、代わりにボトムナビゲーションが含まれます。そのため、ナビゲーションドロワーを表示するにはアプリを分割画面で開く必要があります。ナビゲーションドロワーとボトムナビゲーションの両方を備えたレイアウトがない理由は、マテリアルデザインガイドラインがこれに対して警告しているためです。

image.png

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" />

これで、ナビゲーションドロワーに設定画面が目的地として表示されます。 よくできました!

image.png

10.宛先へのディープリンク

ディープリンクとナビゲーション

ナビゲーションコンポーネントには、ディープリンクのサポートも含まれています。ディープリンクは、実際のURLリンクからのものであれ、通知からの保留中の意図であれ、アプリのナビゲーションの途中にジャンプする方法です。

ナビゲーションライブラリを使用してディープリンクを処理する利点の1つは、ユーザーがアプリウィジェット、通知、またはWebリンク(次の手順で説明)などの他のエントリポイントから適切なバックスタックを使用して適切な目的地で開始できることです。

ナビゲーションは、ユーザーを特定の目的地に導くPendingIntentを構築するNavDeepLinkBuilderクラスを提供します。

ディープリンクを追加する

NavDeepLinkBuilderを使用して、アプリウィジェットを宛先に接続します。

1.DeepLinkAppWidgetProvider.ktを開く
2.NavDeepLinkBuilderで構築されたPendingIntentを追加します。

DeepLinkAppWidgetProvider.kt
val 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.ディープリンクウィジェットをホーム画面に追加します。 ホーム画面を長押しして、ウィジェットを追加するオプションを表示します。

image.png
タップ&ホールド

image.png
下にスクロールしてウィジェットを見つけます

完了すると、ディープリンクウィジェットが表示されます。
image.png

4.ウィジェットをタップし、Androidの宛先が正しい引数で開くことを確認します。 これは、DeepLinkAppWidgetProviderで渡した引数であるため、上部に「From Widget」と表示されます。

image.png

image.png

5.戻るボタンを押すと、home_dest宛先に移動することを確認します。

便宜上、NavControllercreateDeepLink()メソッドを呼び出して、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.comhttp://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に移動します。
image.png

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を検索バーに配置すると、曖昧さ回避ウィンドウが表示されます。ナビゲーションコードラボを選択

image.png
検索バーを使用して開いた(Chromeではない)

image.png
Disambiguationダイアログ

いずれにしても、画面に「urlTest」というメッセージが表示されるはずです。 これは、URLからフラグメントに渡されました。
image.png

12.(オプション)自分でナビゲートする

コードラボアプリには、試してみるためのもう1つの部分があります。それがショッピングカートボタンです。
image.png

これは、このコードラボで学んだスキルの要約です。この手順にはコメントが含まれていないため、自分で試してみてください。

  1. 新しいフラグメントクラスを作成する
  2. フラグメントをナビゲーショングラフの宛先として追加します
  3. NavigationUIを使用してメニューを処理し、ショッピングカートアイコンで新しいフラグメントクラスを開きます。

13.おめでとうございます!

ナビゲーションコンポーネントの背後にある基本概念に精通している! このコードラボでは、次のことを学びました。

  • ナビゲーショングラフ構造
  • NavHostFragmentおよびNavController
  • 特定の目的地に移動する方法
  • アクションでナビゲートする方法
  • 新しいsafeargsプラグインの使用を含む、宛先間で引数を渡す方法
  • メニュー、ボトムナビゲーション、ナビゲーションドロワーを使用したナビゲーション
  • ディープリンクを介したナビゲーション

このアプリを引き続き使用するか、独自のアプリでナビゲーションを使用してください。

試してみるべきことは他にもたくさんあります。

  • バックスタックからの宛先のポップ(またはバックスタック操作)
  • ネストされたナビゲーショングラフ
  • 条件付きナビゲーション
  • 新しい宛先のサポートを追加する

ナビゲーションコンポーネントの詳細については、ドキュメントをご覧ください。他のアーキテクチャコンポーネントについて学びたい場合は、次のコードラボを試してください。

- Android Persistence Codelab (Room)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む