Codelab untuk Tarik lalu Lepas

1. Sebelum memulai

Codelab ini memberikan petunjuk praktis tentang dasar-dasar penerapan fungsi tarik lalu lepas untuk tampilan. Anda akan mempelajari cara mengaktifkan tampilan agar dapat ditarik dan dilepas, baik di dalam aplikasi maupun di aplikasi lainnya. Anda akan mempelajari cara menerapkan interaksi tarik lalu lepas di dalam aplikasi dan bahkan di aplikasi lainnya. Codelab ini akan memandu Anda menggunakan DropHelper untuk mengaktifkan tarik lalu lepas, menyesuaikan masukan visual selama penarikan dengan ShadowBuilder, menambahkan izin untuk penarikan lintas aplikasi, dan menerapkan penerima konten yang berfungsi secara universal.

Prasyarat

Untuk menyelesaikan codelab ini, Anda memerlukan:

Yang akan Anda lakukan

Membuat aplikasi sederhana yang:

  • Menerapkan fungsi Tarik lalu Lepas menggunakan DragStartHelper dan DropHelper
  • Mengubah ShadowBuilder
  • Menambahkan izin untuk Penarikan lintas aplikasi
  • Menerapkan Penerima Konten Jangkauan secara universal.

Yang akan Anda butuhkan

2. Peristiwa tarik lalu lepas

Proses tarik lalu lepas dapat dilihat sebagai peristiwa 4 tahap, yaitu sebagai berikut:

  1. Dimulai: Sistem memulai operasi tarik lalu lepas sebagai respons terhadap gestur tarik pengguna.
  2. Melanjutkan: Pengguna terus menarik, builder dragshadow mulai membuat bayangan saat masuk ke tampilan target.
  3. Berakhir: Pengguna melepaskan operasi tarik di dalam kotak pembatas di area target pelepasan.
  4. Keluar: Sistem mengirimkan sinyal untuk mengakhiri operasi tarik lalu lepas.

Sistem mengirim peristiwa tarik dalam objek DragEvent. Objek DragEvent dapat berisi data berikut

  1. ActionType: Nilai tindakan peristiwa berdasarkan peristiwa siklus proses dari peristiwa tarik lalu lepas, misalnya ACTION_DRAG_STARTED, ACTION_DROP, dll.
  2. ClipData: Data yang ditarik dan dienkapsulasi dalam objek ClipData.
  3. ClipDescription: Informasi meta tentang objek ClipData.
  4. Result: Hasil operasi tarik lalu lepas.
  5. X: Koordinat x dari lokasi objek yang ditarik saat ini.
  6. Y: Koordinat y dari lokasi objek yang ditarik saat ini.

3. Penyiapan

Buat project baru dan pilih template "Empty Views Activity":

2fbd2bca1483033f.png

Biarkan semua parameter seperti default-nya. Biarkan project disinkronkan dan diindeks. Anda akan melihat bahwa MainActivity.kt telah dibuat dengan tampilan activity_main.xml

4. Tarik lalu Lepas Menggunakan View

Di string.xml, tambahkan beberapa nilai string

<resources>
    <string name="app_name">DragAndDropCodelab</string>
    <string name="drag_image">Drag Image</string>
    <string name="drop_image">drop image</string>
 </resources>

Buka file sumber activity_main.xml dan ubah tata letak untuk menyertakan dua ImageViews. Satu akan bertindak sebagai sumber penarikan dan yang lainnya sebagai target pelepasan.

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

    <TextView
        android:id="@+id/tv_greeting"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toTopOf="@id/iv_source"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/iv_source"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:contentDescription="@string/drag_image"
        app:layout_constraintBottom_toTopOf="@id/iv_target"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/tv_greeting" />

    <ImageView
        android:id="@+id/iv_target"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:contentDescription="@string/drop_image"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@id/iv_source" />
</androidx.constraintlayout.widget.ConstraintLayout>

Di build.gradle.kts, aktifkan view binding

buildFeatures{
   viewBinding = true
}

Di build.gradle.kts, tambahkan dependensi untuk Glide

dependencies {
    implementation("com.github.bumptech.glide:glide:4.16.0")
    annotationProcessor("com.github.bumptech.glide:compiler:4.16.0")
    
    //other dependencies
}

Tambahkan URL gambar dan teks ucapan di string.xml

<string name="greeting">Drag and Drop</string>
<string name="target_url">https://services.google.com/fh/files/misc/qq2.jpeg</string>
<string name="source_url">https://services.google.com/fh/files/misc/qq10.jpeg</string>

Di MainActivity.kt, lakukan inisialisasi tampilan.

class MainActivity : AppCompatActivity() {
   val binding by lazy(LazyThreadSafetyMode.NONE) {
       ActivityMainBinding.inflate(layoutInflater)
   }

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(binding.root)
       binding.tvGreeting.text = getString(R.string.greeting)
       Glide.with(this).asBitmap()
           .load(getString(R.string.source_url))
           .into(binding.ivSource)
       Glide.with(this).asBitmap()
           .load(getString(R.string.target_url))
           .into(binding.ivTarget)
   }
}

Pada tahap ini, Aplikasi Anda harus menampilkan teks ucapan dan dua gambar dalam orientasi vertikal.

b0e651aaee336750.png

5. Membuat Tampilan Dapat Ditarik

Agar tampilan tertentu dapat ditarik, tampilan tersebut harus menerapkan metode startDragAndDrop() pada gestur tarik.

Terapkan callback untuk onLongClickListener saat Pengguna mulai menarik pada tampilan.

draggableView.setOnLongClickListener{ v ->
   //drag logic here
   true
}

Meskipun tampilan tidak dapat diklik lama, callback ini akan membuatnya dapat diklik lama. Nilai yang ditampilkan adalah boolean. Nilai benar (true) menandakan bahwa tarikan digunakan oleh callback.

Menyiapkan ClipData: Data yang akan ditarik

Tentukan data yang ingin kita lepas. Data dapat berupa apa saja, mulai dari teks sederhana hingga video. Data ini dienkapsulasi dalam objek ClipData. Objek ClipData berisi satu atau beberapa ClipItem kompleks.

Dengan berbagai jenis MIME yang ditentukan dalam ClipDescription.

Kita menarik URL gambar tampilan sumber. Ada 3 komponen utama dari ClipData

  1. Label: teks sederhana untuk menampilkan kepada pengguna apa yang sedang ditarik
  2. Jenis MIME: MimeType dari item yang ditarik
  3. ClipItem: Item yang ditarik dan dienkapsulasi dalam objek ClipData.Item

Buat ClipData.

val label = "Dragged Image Url"
val clipItem = ClipData.Item(v.tag as? CharSequence)
val mimeTypes = arrayOf(ClipDescription.MIMETYPE_TEXT_PLAIN)
val draggedData = ClipData(
   label, mimeTypes, clipItem
)

Memulai Tarik lalu Lepas

Mulai menarik setelah kita menentukan data yang akan ditarik. Untuk melakukannya, kita akan menggunakan startDragAndDrop

Metode startDragAndDrop menggunakan 4 argumen

  1. data: Data yang ditarik dalam bentuk ClipData.
  2. shadowBuilder: DragShadowBuilder untuk membuat bayangan tampilan.
  3. myLocalState: Objek yang berisi data lokal tentang operasi tarik lalu lepas. Saat mengirim peristiwa tarik ke tampilan dalam aktivitas yang sama, objek ini tersedia melalui DragEvent.getLocalState().
  4. Flag: Flag untuk mengontrol operasi tarik lalu lepas.

Setelah fungsi ini dipanggil, bayangan tarik akan dibuat berdasarkan class View.DragShadowBuilder. Setelah sistem memiliki bayangan tarik, operasi tarik lalu lepas akan dimulai dengan mengirim peristiwa ke tampilan yang terlihat yang telah menerapkan antarmuka OnDragListener.

v.startDragAndDrop(
   draggedData,
   View.DragShadowBuilder(v),
   null,
   0
)

Dengan begitu, kita telah mengonfigurasi tampilan untuk penarikan dan menetapkan data yang akan ditarik. Implementasi akhir akan terlihat seperti ini.

fun setupDrag(draggableView: View) {
   draggableView.setOnLongClickListener { v ->
       val label = "Dragged Image Url"
       val clipItem = ClipData.Item(v.tag as? CharSequence)
       val mimeTypes = arrayOf(ClipDescription.MIMETYPE_TEXT_PLAIN)
       val draggedData = ClipData(
           label, mimeTypes, clipItem
       )
       v.startDragAndDrop(
           draggedData,
           View.DragShadowBuilder(v),
           null,
           0
       )
   }
}

Pada tahap ini, Anda dapat menarik tampilan saat diklik lama.

526e9e2a7f3a90ea.gif

Lanjutkan dengan mengonfigurasi tampilan yang dilepas.

6. Mengonfigurasi View untuk DropTarget

View dapat bertindak sebagai target pelepasan karena telah menerapkan antarmuka OnDragListener.

Konfigurasi tampilan gambar kedua untuk menjadikannya sebagai target pelepasan.

private fun setupDrop(dropTarget: View) {
   dropTarget.setOnDragListener { v, event ->
       // handle drag events here
       true
   }
}

Kita mengganti metode onDrag dari antarmuka OnDragListener. Metode onDrag menggunakan 2 argumen.

  1. Tampilan yang menerima peristiwa tarik
  2. Objek peristiwa untuk peristiwa tarik

Metode ini menampilkan nilai benar (true) jika peristiwa tarik berhasil ditangani. Jika tidak, nilai salah (false) akan ditampilkan.

DragEvent

Paket data ini menandakan paket data yang ditransmisikan oleh sistem pada berbagai tahap operasi tarik lalu lepas. Paket data ini mengenkapsulasi informasi penting terkait operasi dan data yang terlibat.

DragEvent memiliki tindakan penarikan yang berbeda berdasarkan tahap operasi tarik lalu lepas

  1. ACTION_DRAG_STARTED: menandakan dimulainya operasi Tarik lalu Lepas.
  2. ACTION _DRAG_LOCATION: menandakan bahwa pengguna telah melepaskan penarikan dalam status masuk, yakni tidak berada dalam batas area pelepasan target.
  3. ACTION_DRAG_ENTERED: menandakan bahwa tampilan yang ditarik berada dalam batas tampilan pelepasan target.
  4. ACTION_DROP: menandakan bahwa pengguna telah melepaskan penarikan di area pelepasan target.
  5. ACTION_DRAG_ENDED: menandakan bahwa operasi tarik lalu lepas telah selesai.
  6. ACTION_DRAG_EXITED: menandakan akhir dari operasi tarik lalu lepas.

Memvalidasi DragEvent

Anda dapat memilih untuk melanjutkan operasi tarik lalu lepas jika semua batasan terpenuhi dalam peristiwa ACTION_DRAG_STARTED. Misalnya: Dalam contoh ini, kita dapat memeriksa apakah data yang masuk adalah jenis data yang benar atau tidak.

DragEvent.ACTION_DRAG_STARTED -> {
   Log.d(TAG, "ON DRAG STARTED")
   if (event.clipDescription.hasMimeType(ClipDescription.MIMETYPE_TEXT_PLAIN)) {
       (v as? ImageView)?.alpha = 0.5F
       v.invalidate()
       true
   } else {
       false
   }
}

Dalam contoh ini, kita telah memeriksa apakah ClipDescription dari peristiwa memiliki jenis MIME yang dapat diterima atau tidak. Jika dapat diterima, berarti kita memberikan sinyal visual untuk menandakan hal yang sama dan menampilkan nilai true (benar), yang menyatakan bahwa data yang ditarik sedang ditangani. Sebaliknya, kita akan menampilkan nilai false (salah) untuk menandakan bahwa penarikan dibatalkan oleh tampilan target pelepasan.

Menangani Data yang Dilepas

Dalam peristiwa ACTION_DROP, kita dapat memilih tindakan untuk data yang dilepas. Dalam contoh ini, kita mengekstrak URL yang telah ditambahkan ke ClipData sebagai teks. Kita menempatkan gambar ini dari URL ke tampilan gambar target

DragEvent.ACTION_DROP -> {
   Log.d(TAG, "On DROP")
   val item: ClipData.Item = event.clipData.getItemAt(0)
   val dragData = item.text
   Glide.with(this).load(item.text).into(v as ImageView)
   (v as? ImageView)?.alpha = 1.0F
   true
}

Selain menangani pelepasan, kita dapat mengonfigurasi apa yang terjadi saat pengguna menarik tampilan dalam kotak pembatas dari tampilan pelepasan target, dan apa yang terjadi saat pengguna menarik tampilan keluar dari area target.

Tambahkan beberapa petunjuk visual saat item yang ditarik memasuki area target

DragEvent.ACTION_DRAG_ENTERED -> {
   Log.d(TAG, "ON DRAG ENTERED")
   (v as? ImageView)?.alpha = 0.3F
   v.invalidate()
   true
}

Selain itu, tambahkan lebih banyak petunjuk visual saat pengguna menarik tampilan keluar dari kotak pembatas di tampilan pelepasan target.

DragEvent.ACTION_DRAG_EXITED -> {
   Log.d(TAG, "ON DRAG EXISTED")
   (v as? ImageView)?.alpha = 0.5F
   v.invalidate()
   true
}

Tambahkan lagi beberapa petunjuk visual lainnya untuk menandakan akhir dari operasi tarik lalu lepas

DragEvent.ACTION_DRAG_ENDED -> {
   Log.d(TAG, "ON DRAG ENDED")
   (v as? ImageView)?.alpha = 1.0F
   true
}

Pada tahap ini, Anda dapat menarik gambar ke tampilan gambar target. Setelah melepas gambar target, ImageView akan menampilkan perubahan

114238f666d84c6f.gif

7. Tarik lalu Lepas pada Mode Multi-Aplikasi

Item dapat ditarik dari satu aplikasi ke aplikasi lainnya, dengan catatan kedua aplikasi berbagi layar melalui mode multi-aplikasi. Penerapan untuk mengaktifkan tarik lalu lepas di semua aplikasi sama, kecuali kita harus menambahkan flag saat penarikan dan izin saat pelepasan

Mengonfigurasi flag selama Penarikan

Seperti yang kita ingat, startDragAndDrop menggunakan satu argumen untuk menentukan flag, yang berarti mengontrol operasi tarik lalu lepas.

v.startDragAndDrop(
   draggedData,
   View.DragShadowBuilder(v),
   null,
   View.DRAG_FLAG_GLOBAL or View.DRAG_FLAG_GLOBAL_URI_READ
)

View.DRAG_FLAG_GLOBAL menandakan bahwa penarikan dapat melewati batas jendela dan View.DRAG_FLAG_GLOBAL_URI_READ menandakan bahwa penerima penarikan dapat membaca URI konten.

Agar Target Pelepasan membaca data yang ditarik dari aplikasi lain, tampilan target pelepasan harus mendeklarasikan izin untuk membaca.

val dropPermission = requestDragAndDropPermissions(event)

Tampilan target pelepasan akan memberikan izin setelah data yang ditarik ditangani.

dropPermission.release()

Penanganan akhir item yang ditarik terlihat seperti ini

DragEvent.ACTION_DROP -> {
   Log.d(TAG, "On DROP")
   val dropPermission = requestDragAndDropPermissions(event)
   val item: ClipData.Item = event.clipData.getItemAt(0)
   val dragData = item.text
   Glide.with(this).load(item.text).into(v as ImageView)
   (v as? ImageView)?.alpha = 1.0F
   dropPermission.release()
   true
}

Pada tahap ini, Anda dapat menarik gambar ini ke aplikasi lain, dan data yang ditarik dari aplikasi lain juga dapat ditangani dengan benar.

8. Library Tarik lalu Lepas

Jetpack menyediakan library DragAndDrop untuk menyederhanakan penerapan operasi tarik lalu lepas.

Tambahkan dependensi di build.gradle.kts untuk menggunakan Library DragAndDrop

implementation("androidx.draganddrop:draganddrop:1.0.0")

Untuk latihan ini, buat Aktivitas terpisah yang disebut DndHelperActivity.kt yang memiliki 2 ImageView secara vertikal. Satu akan bertindak sebagai sumber penarikan dan yang lainnya akan menjadi target pelepasan.

Ubah strings.xml untuk menambahkan resource string.

<string name="greeting_1">DragStartHelper and DropHelper</string>
<string name="target_url_1">https://services.google.com/fh/files/misc/qq9.jpeg</string>
<string name="source_url_1">https://services.google.com/fh/files/misc/qq8.jpeg</string>

Update activity_dnd_helper.xml untuk menyertakan ImageView

<?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"
   android:padding="24dp"
   tools:context=".DnDHelperActivity">

   <TextView
       android:id="@+id/tv_greeting"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       app:layout_constraintBottom_toTopOf="@id/iv_source"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toTopOf="parent" />

   <ImageView
       android:id="@+id/iv_source"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:contentDescription="@string/drag_image"
       app:layout_constraintBottom_toTopOf="@id/iv_target"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toBottomOf="@id/tv_greeting" />

   <ImageView
       android:id="@+id/iv_target"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:contentDescription="@string/drop_image"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toBottomOf="@id/iv_source" />
</androidx.constraintlayout.widget.ConstraintLayout>

Terakhir, lakukan inisialisasi tampilan di DnDHelperActivity.kt

class DnDHelperActivity : AppCompatActivity() {
   private val binding by lazy(LazyThreadSafetyMode.NONE) {
       ActivityMainBinding.inflate(layoutInflater)
   }

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(binding.root)
       binding.tvGreeting.text = getString(R.string.greeting)
       Glide.with(this).asBitmap()
           .load(getString(R.string.source_url_1))
           .into(binding.ivSource)
       Glide.with(this).asBitmap()
           .load(getString(R.string.target_url_1))
           .into(binding.ivTarget)
       binding.ivSource.tag = getString(R.string.source_url_1)
   }
}

Pastikan untuk mengupdate AndroidManifest.xml guna menjadikan DndHelperActivity sebagai Aktivitas Peluncur

<activity
   android:name=".DnDHelperActivity"
   android:exported="true">
   <intent-filter>
       <action android:name="android.intent.action.MAIN" />
       <category android:name="android.intent.category.LAUNCHER" />
   </intent-filter>
</activity>

DragStartHelper

Sebelumnya kita telah mengonfigurasi tampilan agar dapat ditarik dengan menerapkan onLongClickListener dan memanggil startDragAndDrop. DragStartHelper menyederhanakan penerapan dengan menyediakan metode utilitas

DragStartHelper(draggableView)
{ view: View, _: DragStartHelper ->
   // prepare clipData

   // startDrag and Drop
}.attach()

DragStartHelper menggunakan tampilan yang akan ditarik sebagai argumen. Di sini kita telah menerapkan metode OnDragStartListener yang akan menyiapkan clipdata dan memulai operasi tarik lalu lepas.

Implementasi akhir akan terlihat seperti ini.

DragStartHelper(draggableView)
{ view: View, _: DragStartHelper ->
   val item = ClipData.Item(view.tag as? CharSequence)
   val dragData = ClipData(
       view.tag as? CharSequence,
       arrayOf(ClipDescription.MIMETYPE_TEXT_PLAIN),
       item
   )
   view.startDragAndDrop(
       dragData,
       View.DragShadowBuilder(view),
       null,
       0
   )
}.attach()

DropHelper

DropHelper menyederhanakan konfigurasi tampilan pelepasan target dengan menyediakan metode utilitas yang disebut configureView.

configureView menggunakan 4 argumen

  1. Aktivitas: aktivitas saat ini
  2. dropTarget: tampilan yang sedang dikonfigurasi
  3. mimeTypes: mimeTypes dari item data yang dilepas
  4. Antarmuka OnReceiveContentListener untuk menangani data yang dilepas

Sesuaikan sorotan Target Pelepasan.

DropHelper.configureView(
   This, // Current Activity
   dropTarget,
   arrayOf("text/*"),
   DropHelper.Options.Builder().build()
) {
   // handle the dropped data
}

OnRecieveContentListener menerima konten yang dilepas. Model ini memiliki dua parameter

  1. Tampilan: tempat konten dilepas
  2. Payload: konten sebenarnya yang akan dilepas
private fun setupDrop(dropTarget: View) {
   DropHelper.configureView(
       this,
       dropTarget,
       arrayOf("text/*"),
   ) { _, payload: ContentInfoCompat ->
       // TODO: step through clips if one cannot be loaded
       val item = payload.clip.getItemAt(0)
       val dragData = item.text
       Glide.with(this)
           .load(dragData)
           .centerCrop().into(dropTarget as ImageView)
       // Consume payload by only returning remaining items
       val (_, remaining) = payload.partition { it == item }
       remaining
   }
}

Pada tahap ini, Anda dapat menarik lalu melepas data menggunakan DragStartHelper dan DropHelper.

2e32d6cd80e19dcb.gif

Mengonfigurasi sorotan area Pelepasan

Seperti yang telah Anda lihat, saat item yang ditarik memasuki area pelepasan, area tersebut akan disorot. Dengan DropHelper.Options, kita dapat menyesuaikan cara area pelepasan disorot saat item yang ditarik memasuki batas tampilan.

DropHelper.Options dapat digunakan untuk mengonfigurasi Warna sorotan dan radius sudut sorotan dari area target pelepasan.

DropHelper.Options.Builder()
   .setHighlightColor(getColor(R.color.green))
   .setHighlightCornerRadiusPx(16)
   .build()

Opsi ini harus diteruskan sebagai argumen ke metode configureView dari DropHelper.

private fun setupDrop(dropTarget: View) {
   DropHelper.configureView(
       this,
       dropTarget,
       arrayOf("text/*"),
       DropHelper.Options.Builder()
           .setHighlightColor(getColor(R.color.green))
           .setHighlightCornerRadiusPx(16)
           .build(),
   ) { _, payload: ContentInfoCompat ->
       // TODO: step through clips if one cannot be loaded
       val item = payload.clip.getItemAt(0)
       val dragData = item.text
       Glide.with(this)
           .load(dragData)
           .centerCrop().into(dropTarget as ImageView)
       // Consume payload by only returning remaining items
       val (_, remaining) = payload.partition { it == item }
       remaining
   }
}

Anda dapat melihat warna dan radius sorotan saat melakukan operasi tarik lalu lepas.

9d5c1c78ecf8575f.gif

9. Menerima Konten Lengkap

OnReceiveContentListener adalah API terpadu untuk menerima konten lengkap termasuk teks, HTML, gambar, video, dll. Konten dapat disisipkan ke tampilan baik dari Keyboard, penarikan, atau papan klip. Mempertahankan callback untuk setiap mekanisme input bisa cukup mengganggu. OnReceiveContentListener dapat digunakan untuk menerima konten seperti teks, markup, audio, video, gambar, dan yang lainnya menggunakan satu API. OnReceiveContentListener API menggabungkan berbagai jalur kode dengan membuat satu API untuk diterapkan, sehingga Anda dapat berfokus pada logika khusus aplikasi dan memungkinkan platform menangani logika lainnya.

Untuk latihan ini, buat Aktivitas terpisah yang disebut ReceiveRichContentActivity.kt yang memiliki 2 ImageView secara vertikal. Satu akan bertindak sebagai sumber penarikan dan yang lainnya akan menjadi target pelepasan.

Ubah strings.xml untuk menambahkan resource string.

<string name="greeting_2">Rich Content Receiver</string>
<string name="target_url_2">https://services.google.com/fh/files/misc/qq1.jpeg</string>
<string name="source_url_2">https://services.google.com/fh/files/misc/qq3.jpeg</string>

Update activity_receive_rich_content.xml untuk menyertakan ImageView

<?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=".ReceiveRichContentActivity">

   <TextView
       android:id="@+id/tv_greeting"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       app:layout_constraintBottom_toTopOf="@id/iv_source"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toTopOf="parent" />

   <ImageView
       android:id="@+id/iv_source"
       android:layout_width="320dp"
       android:layout_height="wrap_content"
       android:contentDescription="@string/drag_image"
       app:layout_constraintBottom_toTopOf="@id/iv_target"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toBottomOf="@id/tv_greeting" />

   <ImageView
       android:id="@+id/iv_target"
       android:layout_width="320dp"
       android:layout_height="wrap_content"
       android:contentDescription="@string/drop_image"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toBottomOf="@id/iv_source" />
</androidx.constraintlayout.widget.ConstraintLayout>

Terakhir, lakukan inisialisasi tampilan di ReceiveRichContentActivity.kt

class ReceiveRichContentActivity : AppCompatActivity() {
   private val binding by lazy(LazyThreadSafetyMode.NONE) {
       ActivityReceiveRichContentBinding.inflate(layoutInflater)
   }
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(binding.root)
       binding.tvGreeting.text = getString(R.string.greeting_2)
       Glide.with(this).asBitmap()
           .load(getString(R.string.source_url_2))
           .into(binding.ivSource)
       Glide.with(this).asBitmap()
           .load(getString(R.string.target_url_2))
           .into(binding.ivTarget)
       binding.ivSource.tag = getString(R.string.source_url_2)
   }
}

Pastikan untuk mengupdate AndroidManifest.xml guna menjadikan DndHelperActivity sebagai Aktivitas Peluncur

<activity
   android:name=".ReceiveRichContentActivity"
   android:exported="true">
   <intent-filter>
       <action android:name="android.intent.action.MAIN" />
       <category android:name="android.intent.category.LAUNCHER" />
   </intent-filter>
</activity>

Pertama-tama, buat callback yang menerapkan OnReceiveContentListener.

val listener = OnReceiveContentListener { view, payload ->
   val (textContent, remaining) =
       payload.partition { item: ClipData.Item -> item.text != null }
   if (textContent != null) {
       val clip = textContent.clip
       for (i in 0 until clip.itemCount) {
           val currentText = clip.getItemAt(i).text
           Glide.with(this)
               .load(currentText)
               .centerCrop().into(view as ImageView)
       }
   }
   remaining
}

Di sini, kita telah menerapkan antarmuka OnRecieveContentListener. Metode onRecieveContent menggunakan 2 argumen

  1. Tampilan saat ini yang menerima data
  2. Payload data dari keyboard, penarikan, atau papan klip dalam bentuk ContentInfoCompat

Metode ini menampilkan payload yang tidak ditangani.

Di sini kita telah memisahkan payload ke dalam konten teks dan konten lainnya menggunakan metode Partisi. Kita menangani data teks sesuai kebutuhan dan menampilkan sisa payload

Tangani apa yang ingin kita lakukan dengan data yang ditarik.

val listener = OnReceiveContentListener { view, payload ->
   val (textContent, remaining) =
       payload.partition { item: ClipData.Item -> item.text != null }
   if (textContent != null) {
       val clip = textContent.clip
       for (i in 0 until clip.itemCount) {
           val currentText = clip.getItemAt(i).text
           Glide.with(this)
               .load(currentText)
               .centerCrop().into(view as ImageView)
       }
   }
   remaining
}

Kini pemroses sudah siap. Tambahkan pemroses ini ke tampilan target.

ViewCompat.setOnReceiveContentListener(
   binding.ivTarget,
   arrayOf("text/*"),
   listener
)

Pada tahap ini, Anda dapat menarik lalu melepas gambar ke area target. Setelah dilepas, gambar yang ditarik akan menggantikan gambar asli dalam tampilan target pelepasan.

e4c3a3163c51135d.gif

10. Selamat!

Kini Anda sudah mahir menerapkan fungsi tarik lalu lepas untuk aplikasi Android. Dengan mengikuti codelab ini, Anda telah mempelajari cara membuat interaksi tarik lalu lepas yang interaktif di dalam aplikasi Android maupun di aplikasi lainnya, sehingga meningkatkan pengalaman pengguna dan fungsi aplikasi. Anda telah mempelajari

  • Dasar-Dasar Tarik lalu Lepas: Memahami 4 tahap peristiwa tarik lalu lepas (dimulai, melanjutkan, berakhir, keluar) dan data utama dalam objek DragEvent.
  • Mengaktifkan Tarik lalu Lepas: Membuat tampilan dapat ditarik dan menangani pelepasan dalam tampilan target dengan menangani DragEvent
  • Tarik lalu Lepas pada Mode Multi-Aplikasi: Mengaktifkan tarik lalu lepas lintas aplikasi dengan menetapkan flag dan izin yang sesuai.
  • Menggunakan Library DragAndDrop: Menyederhanakan penerapan tarik lalu lepas menggunakan library Jetpack
  • Menerima Konten Lengkap: Menerapkan operasi untuk menangani beragam jenis konten (teks, gambar, video, dll.) dari berbagai metode input menggunakan API terpadu.

Pelajari lebih lanjut