Androidサポートライブラリ28.新機能





長い伝統により、サポートライブラリの更新は新しいバージョンのAndroidでリリースされます。 これまでのところ、ライブラリはアルファ段階に達しましたが、変更のリストはAndroid Pの同じリストよりもはるかに興味深いものです。 ソースコードを読み、新機能の機能とそれらが必要な理由を把握する必要があります。 正義を取り戻し、Googleが私たちにどんなことを喜んでくれたかをお話しします





RecyclerViewの選択



2014年、Lollipopのリリースに伴い、Googleはサポート対象の新しい要素-RecyclerViewを廃止されたListViewの代わりとして追加しました。 彼には何も問題ありませんでしたが、ListViewのメソッドの1つであるsetSelectionMode()が欠落していました。 4年後、このメソッドはライブラリ全体としてRecyclerViewに間接的に実装されました。



選択についてそれほど魅力的なのは何ですか? 選択モード-リスト項目を長押しすると初期化されるモード。 次に、他のいくつかの要素を選択し、それらに対して一般的なアクションを実行できます。 例:Googleフォト選択モードでは、作業がずっと簡単になります。







実際にサポートの仕組みを見てみましょう。



gradleに依存関係を追加します。 興味深いことに、Googleは選択を別のリポジトリにプッシュしました。



dependencies { implementation "com.android.support:recyclerview-selection:28.0.0-alpha1" }
      
      





RecyclerViewの標準アダプターを作成しましょう。



 class WordAdapter(private val items: List<Word>) : RecyclerView.Adapter<WordViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = WordViewHolder( LayoutInflater .from(parent.context) .inflate(R.layout.item_word, parent, false) ) override fun getItemCount() = items.size override fun onBindViewHolder(holder: WordViewHolder, position: Int) { val item = items[position] holder.bind(item) } class WordViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { private val text: TextView = itemView.findViewById(R.id.item_word_text) fun changeText(word: Word) { text.text = word.text } } }
      
      





Wordモデルをデータとして使用します。



 @Parcelize data class Word(val id: Int, val text: String) : Parcelable
      
      





基盤があります;選択の実装を進めましょう。 最初に、リスト項目を識別するものを決定する必要があります。 Googleでは、Long、String、Parcelableの3つのオプションを選択できます。 この目的のために、すでにWordを作成しました。Parcelable実装のみが欠落しています。 @Parcelizeアノテーションを使用して実装を追加します。これは、Kotlinの実験版で使用できます。 Android Studio 3.2では、実験的なKotlinを使用したプロジェクトのビルドにはまだ問題がありますが、スタジオテンプレートをキャンセルした人はいません。



SelectionTrackerは、ライブラリのメインクラスです。 このクラスのオブジェクトには、ユーザーが選択した要素に関する情報があり、コードからこのリストを変更できます。 このクラスを初期化するには、ItemKeyProviderとItemDetailsLookupの2つの抽象クラスの実装が必要です。 1つ目は、コレクション内の要素の位置とキーの双方向通信に必要です。



 //   ItemKeyProvider       : // SCOPE_MAPPED -   .   ,       // SCOPE_CACHED -  ,       .   class WordKeyProvider(private val items: List<Word>) : ItemKeyProvider<Word>(ItemKeyProvider.SCOPE_CACHED) { override fun getKey(position: Int) = items.getOrNull(position) override fun getPosition(key: Word) = items.indexOf(key) }
      
      





ItemDetailsLookupは、x座標とy座標でアイテムとそのキーの位置を取得するために必要です。



 class WordLookup(private val recyclerView: RecyclerView) : ItemDetailsLookup<Word>() { override fun getItemDetails(e: MotionEvent) = recyclerView.findChildViewUnder(ex, ey) ?.let { (recyclerView.getChildViewHolder(it) as? ViewHolderWithDetails<Word>)?.getItemDetail() } }
      
      





また、ViewHolderからデータを受信するためのインターフェイスを作成して実装します。



 interface ViewHolderWithDetails<TItem> { fun getItemDetail(): ItemDetails<TItem> } class WordDetails(private val adapterPosition: Int, private val selectedKey: Word?) : ItemDetails<Word>() { override fun getSelectionKey() = selectedKey override fun getPosition() = adapterPosition } inner class WordViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), ViewHolderWithDetails<Word> { override fun getItemDetail() = WordDetails(adapterPosition, items.getOrNull(adapterPosition)) }
      
      





どこでも標準コード。 サポートライブラリの開発者が従来の実装を自分で追加しなかったのは驚くべきことです。



Activityでトラッカーを作成します。



 val tracker = SelectionTracker .Builder<Word>( //     "someId", recyclerView, //  Long ItemKeyProvider    StableIdKeyProvider WordKeyProvider(items), WordLookup(recyclerView), //     Long  String StorageStrategy.createParcelableStorage(Word::class.java) ).build()
      
      





ViewHolderを修正し、選択状態の変更に対する反応を追加します。



 fun setActivatedState(isActivated: Boolean) { itemView.isActivated = isActivated }
      
      





アダプタにトラッカーを追加し、ペイロードでonBindViewHolderを再定義します。 変更が選択状態のみに関係する場合、ペイロードには定数SelectionTracker.SELECTION_CHANGED_MARKERが含まれます。



 override fun onBindViewHolder(holder: WordViewHolder, position: Int, payloads: List<Any>) { holder.setActivatedState(tracker.isSelected(items[position])) if (SelectionTracker.SELECTION_CHANGED_MARKER !in payloads) { holder.changeText(items[position]) } }
      
      





トラッカーは準備ができており、時計のように機能します。 少し美しさと意味を追加します。 AppBarの色を変更すると、見出しに選択されたアイテムの数が表示され、ユーザーが何かを選択するとメニューに[クリア]ボタンが追加されます。 これにはActionModeがあり、AppCombatActivityでサポートされています。



まず、ActionMode.Callbackの実装を記述します。



 class ActionModeController( private val tracker: SelectionTracker<*> ) : ActionMode.Callback { override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { mode.menuInflater.inflate(R.menu.action_menu, menu) return true } override fun onDestroyActionMode(mode: ActionMode) { tracker.clearSelection() } override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean = true override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean = when (item.itemId) { R.id.action_clear -> { mode.finish() true } else -> false } }
      
      





オブザーバーをSelectionTrackerに追加し、トラッカーの変更をアクティビティのActionModeに関連付けます。



 tracker.addObserver(object : SelectionTracker.SelectionObserver<Any>() { override fun onSelectionChanged() { super.onSelectionChanged() if (tracker.hasSelection() && actionMode == null) { actionMode = startSupportActionMode(ActionModeController(tracker)) setSelectedTitle(tracker.selection.size()) } else if (!tracker.hasSelection()) { actionMode?.finish() actionMode = null } else { setSelectedTitle(tracker.selection.size()) } } }) } private fun setSelectedTitle(selected: Int) { actionMode?.title = "Selected: $selected" }
      
      





確かに。 シンプルさと美しさをお楽しみください。







標準バージョンを作成しました。 簡単に言うと、Builderには選択プロセスをカスタマイズするための多くのメソッドがあります。 たとえば、withSelectionPredicate(述語:SelectionPredicate)メソッドを使用して、選択したアイテムの数を制限したり、特定のアイテムの選択を禁止したりできます。 Builderは、従来の追加方法での選択と競合する可能性のある動作を追加するメソッドも提供します。 たとえば、withOnDragInitiatedListener(リスナー:OnDragInitiatedListener)を使用して、ドラッグアンドドロップを構成できます。



スライス数



最も奇妙な目新しさはスライスでした。 Googleは、コミュニティにどのような好奇心があるのか​​を説明する時間はほとんどありませんでした。 半分のクラスのコードとドキュメントのみがあります。 正しくしましょう。



Android P DP1のPermissionでバグを回避する方法を見つけたので、ここからのコードを基礎として取り上げます。 Slicesは新しいサポートライブラリではないことに注意してください。 この機能はAndroid SDK 28に登場し、サポートのために生息地がSDKの24番目のバージョンに拡張されました。 これで、ストーリーを完成させ、数年後にそれを続けることができます。 minSdkVersionは最大19に設定できますが、一般的にこのテクノロジーのアイデアと、なぜそれが必要なのかについて話しましょう。



スライスは、あるアプリケーション(クライアントまたはホスト)から別のアプリケーション(送信者またはプロバイダー)の一部または静的な部分を要求できるライブラリです。 RemoteViewsの説明によく似ています。これは、カスタムウィジェットと通知をプログラムするためによく使用されます。



スライスは、CSSとJsを使用しないHTMLのように、設計と対話性を持たないフレームワーク内のデータです。 設計は、ホストアプリケーションのテーマに適合します。 サンプルスライス



送信者は、単純なonBindSlice(sliceUri:Uri):Sliceメソッドを実装し、メソッド内にSliceを形成する必要があるContentProviderです。 プロバイダーが時間と電話番号を送信します。



 class SliceContentProvider : SliceProvider() { private var counter = 0 override fun onBindSlice(sliceUri: Uri): Slice { return when (sliceUri.path) { "/time" -> createTimeSlice(sliceUri) else -> throw IllegalArgumentException("Bad url") } } override fun onCreateSliceProvider(): Boolean { Toast.makeText(context, "Slice content provider is launched", Toast.LENGTH_LONG).show() return true } private fun createTimeSlice(sliceUri: Uri): Slice = ListBuilder(context, sliceUri) .apply { counter++ setHeader( ListBuilder.HeaderBuilder(this) .setTitle("What's the time now?") ) addRow( ListBuilder.RowBuilder(this) .setTitle("It is ${SimpleDateFormat("HH:mm").format(Calendar.getInstance().time)}") ) addRow( ListBuilder.RowBuilder(this) .setTitle("Slice has called $counter times") ) } .build() }
      
      





クライアントは、プロバイダーに対してURIで要求を行い、それを介してスライスを要求し、それを受信して​​SliceViewに転送する必要があります。 すべてのアクションはSliceManagerを介して実行されます。 許可を忘れないことが重要です。



 private val baseSliceUri: Uri = Uri.parse("content://ru.touchin.provider/") private val timeSliceUri = baseSliceUri.buildUpon().appendPath("time").build() private lateinit var sliceManager: SliceManager override fun onCreate(savedInstanceState: Bundle?) { //    View sliceManager = SliceManager.getInstance(this) findViewById<View>(R.id.get_slice).setOnClickListener { tryShowingSlice(timeSliceUri) } } override fun onStart() { super.onStart() if (providerAppNotInstalled(packageManager, baseSliceUri.authority)) { showMissingProviderDialog(this, { finish() }, baseSliceUri) return } } private fun tryShowingSlice(sliceUri: Uri) { if (sliceManager.missingPermission(sliceUri, appName = getString(R.string.app_name))) { //  permission   - Android P DP1 } } else { getSliceAndBind(sliceUri) } } private fun getSliceAndBind(sliceUri: Uri) { sliceView.setSlice(sliceManager.bindSlice(sliceUri)) }
      
      





SliceManagerは、SliceLiveDataを使用してプロバイダーの変更をスライスし、サブスクリプション内のSliceViewを更新する機能を提供します。 残念ながら、現時点では機能しません。 あまり反応しないバージョンを使用しました。



プロバイダーを起動し、アプリケーションを起動します。 作業の結果を観察します。 すべてがクールです。 カウンタが2回インクリメントするのは面白いです。







ほとんどの場合、RemoteViewはウィジェットと通知に使用されます。 スライスはこれらの目的にはあまり適しておらず、わずかにカスタマイズ可能であり、すでに書いたように、アプリケーションの設計に適合しています。 他のアプリケーションのデータを使用するアプリケーションに最適です。 音声アシスタント-Googleアシスタント、アリスなど-は包括的なアシスタントのカテゴリに分類されます。 Novadaブログに記載されているように、スライスコンストラクターを使用して、Googleアシスタントの回答に非常に類似したスライスを収集できます。







そして、ここが理論の時間です。



Sliceは、Google Assistantでのプログラミングの回答のために作られているという基礎を考えてみましょう。これは会社にとって戦略的に重要な製品です。 明らかに、グラフィカルインターフェイスが徐々に音声に取って代わられる時代に生きています。ホームアシスタントの人気が高まり、AI、ニューラルネットワーク、その他の誇大宣伝技術による音声人工知能の開発が進歩しています。



Googleにとって、最も論理的なオプションは、Google Assistantを開発して拡張することです。そのため、1、2年で強力なツールになります。 スライスは、サードパーティの開発者からアドオンを提供するための理論的に素晴らしいツールです。 そのため、アシスタントはより強力になり、すべてのアクションを実行できるようになり、デスクトップとアイコンの必要性がなくなります。 その後、Google AssistantがAndroidの基盤になります。



現時点では、彼らはSliceについて何も語っていませんでした。目標でも、RemoteViewに対する利点でもありません。 サポートの新しいバージョンのコードの数はほぼ最初の場所になりますが。 したがって、次のI / Oで、スライスについて詳しく説明されると思います。 そしておそらく、彼らはOSの進化の計画について話したり、開発者向けの音声インターフェースを備えたAndroidのバージョンを紹介したりするでしょう。



しかし、これはすべて憶測であり、陰謀理論を明らかにして真実に到達したいという著者の願望です。 100%と言える唯一のことは、Google I / Oで歴史の否定を待っていることです。



新しいアイテム:



MaterialCardViewおよびMaterialButton



MaterialCardViewはCardViewから継承されており、実質的に違いはありません。 カードの境界線を設定する機能のみが追加され、別のドロアブルが背景として使用されます。 10の違いを見つけます。

MaterialButtonはAppCombatButtonの後継であり、ここで違いが顕著です。 開発者は、ここでボタンをカスタマイズする方法を追加しました:リップル効果の色、異なるボタン半径、MaterialCardViewなどの境界線。







チップとチップグループ



ここに余分な言葉があります。







Bottomappppbar



このコレクションで最も興味深い予想外のウィジェットは、アイデアは非常に単純ですが、AppBarを下に配置します。 小さな手と大きな画面を持つユーザーにとって、メニューボタンまたは最上部のAppBar上のボタンに到達するのは不便です。 しかし、この要素には他の利点はありません。



BottomAppBarのメニューは、人為的に追加する必要があります。これには、replaceMenuメソッド(@MenuRes int newMenu)があります。



デザイナーは、FloatingActionButtonとBottomAppBarを組み合わせる方法を冷静に考え出しました。 しかし、ボタンがなければ、BottomAppBarは不要に見えます。 切り抜きが削除され、片側にメニューボタンがある顎が残ります。 たとえば、FloatingActionButtonを長押しして、画面の下部にあるメニューに変換することで、大きな画面のメニューに関する問題を解決できます。







短い革新のリスト:





より深く掘り下げたい人のために:



  1. Android Studio 3.1以降を使用してください。 これらのバージョンはまだリリースされていませんが、安定して動作するため、3.2で作業しました。
  2. build.gradleのバージョン付きの小さなシャーマン。 もちろん、必要な依存関係を追加する必要があります。



     android { compileSdkVersion 'android-P' defaultConfig { targetSdkVersion 'P' //  28 } }
          
          



  3. これまでのところ、サポート28を使用するコードはAndroid Pのエミュレーターでのみ実行されていました。古いものはすべて呪われ、実行しようとすると大量のエラーが発生しました。


新機能のリストは最終的なものではありません。 過去2〜3年のchangelogライブラリを分析し、今年のデータを推定すると、5月にはさらに多くの興味深いことが予想されます。 待っています。



便利なリンク:






All Articles