คัดลอกและวาง

Android มีเฟรมเวิร์กแบบคลิปบอร์ดที่มีประสิทธิภาพสำหรับการคัดลอกและวาง รองรับ และประเภทข้อมูลที่ซับซ้อน เช่น สตริงข้อความ โครงสร้างข้อมูลที่ซับซ้อน ข้อความและสตรีมไบนารี และข้อมูลแอปพลิเคชัน ข้อมูลข้อความธรรมดาจะเก็บไว้ในคลิปบอร์ดโดยตรงในขณะที่มีความซับซ้อน ข้อมูลจะจัดเก็บเป็นข้อมูลอ้างอิงที่แอปพลิเคชันการวางจะแก้ปัญหากับผู้ให้บริการเนื้อหา กำลังคัดลอก และการวางจะทำงานทั้งภายในแอปพลิเคชันและระหว่างแอปพลิเคชันที่ใช้

เนื่องจากส่วนหนึ่งของกรอบการทำงานใช้ผู้ให้บริการเนื้อหา เอกสารนี้จึงอนุมานว่าคุ้นเคยกับ Android Content Provider API ซึ่งอธิบายไว้ใน ผู้ให้บริการเนื้อหา

ผู้ใช้คาดหวังความคิดเห็นเมื่อคัดลอกเนื้อหาไปยังคลิปบอร์ด ดังนั้น นอกเหนือจากกรอบการทำงานที่ ขับเคลื่อนการคัดลอกและวาง Android จะแสดง UI เริ่มต้นแก่ผู้ใช้เมื่อคัดลอกใน Android 13 (API ระดับ 33) และสูงกว่า และเนื่องจากฟีเจอร์นี้ จึงมีความเสี่ยงที่จะมีการแจ้งเตือนซ้ำ คุณสามารถดูข้อมูลเพิ่มเติมเกี่ยวกับ เคสขอบนี้จะอยู่ใน หลีกเลี่ยงการแจ้งเตือนซ้ำ

วันที่ ภาพเคลื่อนไหวแสดงการแจ้งเตือนคลิปบอร์ดของ Android 13
รูปที่ 1 UI ที่แสดงเมื่อเนื้อหาใส่คลิปบอร์ดใน Android 13 ปีขึ้นไป

แสดงความคิดเห็นแก่ผู้ใช้ด้วยตนเองเมื่อคัดลอกใน Android 12L (API ระดับ 32) และต่ำกว่า โปรดดู คำแนะนำสำหรับเรื่องนี้ในเอกสารนี้

เฟรมเวิร์กคลิปบอร์ด

เมื่อใช้เฟรมเวิร์กคลิปบอร์ด ให้นำข้อมูลไปไว้ในออบเจ็กต์คลิป แล้ววางออบเจ็กต์คลิป บนคลิปบอร์ดของทั้งระบบ ออบเจ็กต์คลิปอาจมี 1 ใน 3 รูปแบบต่อไปนี้

ข้อความ
สตริงข้อความ ใส่สตริงลงในออบเจ็กต์คลิปโดยตรง แล้วนำไปวางใน คลิปบอร์ด หากต้องการวางสตริง ให้เอาออบเจ็กต์คลิปจากคลิปบอร์ดแล้วคัดลอก ลงในพื้นที่เก็บข้อมูลของแอปพลิเคชัน
URI
ออบเจ็กต์ Uri ที่แสดงถึงค่าใดๆ รูปแบบ URI ส่วนนี้ใช้สำหรับการคัดลอกข้อมูลที่ซับซ้อนจากผู้ให้บริการเนื้อหาเป็นหลัก เพื่อคัดลอก ให้นำออบเจ็กต์ Uri ไปไว้ในออบเจ็กต์คลิป แล้ววางออบเจ็กต์คลิปลงใน คลิปบอร์ด หากต้องการวางข้อมูล ให้รับออบเจ็กต์คลิป รับออบเจ็กต์ Uri แปลงเป็นแหล่งข้อมูล เช่น ผู้ให้บริการเนื้อหา และคัดลอกข้อมูลจาก ลงในพื้นที่เก็บข้อมูลของแอปพลิเคชัน
ความตั้งใจ
Intent ช่วงเวลานี้ รองรับการคัดลอกทางลัดไปยังแอปพลิเคชัน หากต้องการคัดลอกข้อมูล ให้สร้าง Intent ป้อน ไว้ในออบเจ็กต์คลิป แล้ววางออบเจ็กต์คลิปไว้บนคลิปบอร์ด หากต้องการวางข้อมูล ให้เรียก ออบเจ็กต์คลิป แล้วคัดลอกออบเจ็กต์ Intent ไปยัง พื้นที่หน่วยความจำ

คลิปบอร์ดจะเก็บออบเจ็กต์คลิปได้ครั้งละ 1 รายการเท่านั้น เมื่อแอปพลิเคชันวางวัตถุคลิปบน คลิปบอร์ด ออบเจ็กต์คลิปก่อนหน้าจะหายไป

ถ้าต้องการให้ผู้ใช้วางข้อมูลลงในแอปพลิเคชันของคุณ คุณไม่จำเป็นต้องจัดการทุกประเภท คุณตรวจสอบข้อมูลในคลิปบอร์ดได้ก่อนให้ผู้ใช้เลือกวางข้อมูลได้ นอกจากจะมีแบบฟอร์มข้อมูลบางอย่างแล้ว ออบเจ็กต์คลิปยังมีข้อมูลเมตาที่บอกคุณว่า MIME ใด ประเภทต่างๆ ได้ ข้อมูลเมตานี้ช่วยให้คุณตัดสินใจได้ว่าแอปพลิเคชันของคุณมีประโยชน์อะไรบ้าง ด้วยข้อมูลคลิปบอร์ด ตัวอย่างเช่น หากคุณมีแอปพลิเคชันที่ทำงานกับข้อความเป็นหลัก คุณ คุณอาจต้องละเว้นออบเจ็กต์คลิปที่มี URI หรือ Intent

นอกจากนี้ คุณอาจต้องให้ผู้ใช้วางข้อความไม่ว่าข้อมูลในคลิปบอร์ดจะเป็นรูปแบบใดก็ตาม ถึง ให้บังคับข้อมูลในคลิปบอร์ดเป็นข้อความ แล้ววางข้อความนี้ นี่คือ ตามที่อธิบายไว้ในส่วนบังคับคลิปบอร์ดเป็นข้อความ

คลาสคลิปบอร์ด

ส่วนนี้จะอธิบายคลาสที่เฟรมเวิร์กคลิปบอร์ดใช้

เครื่องมือจัดการคลิปบอร์ด

คลิปบอร์ดของระบบ Android แสดงโดยผู้คนทั่วโลก ClipboardManager ชั้นเรียน อย่าสร้างอินสแตนซ์ชั้นเรียนนี้โดยตรง แต่ให้รับการอ้างอิงด้วยการเรียกใช้ getSystemService(CLIPBOARD_SERVICE)

ClipData, ClipData.Item และ ClipDescription

หากต้องการเพิ่มข้อมูลลงในคลิปบอร์ด ให้สร้าง ClipData ออบเจ็กต์ที่มี คำอธิบายของข้อมูลและตัวข้อมูล คลิปบอร์ดเก็บ ClipData ไว้ 1 รายการที่ ClipData มี ออบเจ็กต์ ClipDescription รายการ และอย่างน้อย 1 รายการ ออบเจ็กต์ ClipData.Item รายการ

ออบเจ็กต์ ClipDescription มีข้อมูลเมตาเกี่ยวกับคลิป ยิ่งไปกว่านั้น มีอาร์เรย์ของประเภท MIME ที่ใช้ได้กับข้อมูลของคลิป นอกจากนี้ ให้เปิด Android 12 (API ระดับ 31) ขึ้นไป ข้อมูลเมตาจะมีข้อมูลเกี่ยวกับว่าออบเจ็กต์ ประกอบด้วย ข้อความที่ทำสไตไลซ์และเกี่ยวกับ ประเภทของข้อความในออบเจ็กต์ เมื่อคุณวางคลิปในคลิปบอร์ด ข้อมูลนี้จะพร้อมใช้สำหรับการวางแอปพลิเคชัน ซึ่ง จะตรวจสอบว่าตนจัดการข้อมูลคลิปได้หรือไม่

ออบเจ็กต์ ClipData.Item มีข้อความ, URI หรือ Intent ดังนี้

ข้อความ
CharSequence
URI
Uri โดยปกติจะประกอบด้วย URI ผู้ให้บริการเนื้อหา แม้ว่า URI จะเป็น อนุญาต แอปพลิเคชันที่ให้ข้อมูลจะวาง URI บนคลิปบอร์ด การสมัครงาน ที่ต้องการวางข้อมูล ให้รับ URI จากคลิปบอร์ดและใช้เพื่อเข้าถึงเนื้อหา ของผู้ให้บริการหรือแหล่งข้อมูลอื่นๆ และเรียกข้อมูล
ความตั้งใจ
Intent ข้อมูลประเภทนี้จะให้คุณคัดลอกทางลัดของแอปพลิเคชันไปยัง คลิปบอร์ด จากนั้นผู้ใช้จะวางทางลัดลงในแอปพลิเคชันของตนเองเพื่อการใช้งานในภายหลังได้

คุณเพิ่มออบเจ็กต์ ClipData.Item ได้มากกว่า 1 รายการในคลิป ซึ่งจะช่วยให้ผู้ใช้คัดลอกและ วางตัวเลือกหลายรายการเป็นคลิปเดียว ตัวอย่างเช่น หากคุณมีวิดเจ็ตรายการที่ช่วยให้ ผู้ใช้เลือกได้มากกว่า 1 รายการต่อครั้ง คุณสามารถคัดลอกรายการทั้งหมดไปยังคลิปบอร์ดพร้อมกันได้ สิ่งต้องทำ ให้สร้าง ClipData.Item สำหรับแต่ละรายการแยกกัน แล้วเพิ่ม ClipData.Item ออบเจ็กต์ไปยังออบเจ็กต์ ClipData

วิธีการอำนวยความสะดวกของ ClipData

คลาส ClipData มอบวิธีอำนวยความสะดวกแบบคงที่ในการสร้าง ออบเจ็กต์ ClipData ที่มีออบเจ็กต์ ClipData.Item รายการเดียวและ ออบเจ็กต์ ClipDescription รายการ:

newPlainText(label, text)
ส่งคืนวัตถุ ClipData ที่มีออบเจ็กต์ ClipData.Item เดี่ยว ประกอบด้วยสตริงข้อความ ป้ายกำกับของออบเจ็กต์ ClipDescription ได้รับการตั้งค่าเป็น label ประเภท MIME เดียวใน ClipDescription คือ MIMETYPE_TEXT_PLAIN

ใช้ newPlainText() เพื่อสร้างคลิปจากสตริงข้อความ

newUri(resolver, label, URI)
ส่งคืนวัตถุ ClipData ที่มีออบเจ็กต์ ClipData.Item เดี่ยว มี URI ป้ายกำกับของออบเจ็กต์ ClipDescription ได้รับการตั้งค่าเป็น label หาก URI คือ URI เนื้อหา ซึ่งก็คือในกรณีที่ Uri.getScheme() แสดงผล content: ซึ่งเมธอดจะใช้ค่า ContentResolver ที่มีให้ใน resolver เพื่อเรียกประเภท MIME ที่มีอยู่จาก Content Provider แล้วจัดเก็บไว้ใน ClipDescription สำหรับ URI ที่ไม่ใช่ URI content: โดยเมธอดจะกำหนดประเภท MIME เป็น MIMETYPE_TEXT_URILIST

ใช้ newUri() เพื่อสร้างคลิปจาก URI โดยเฉพาะอย่างยิ่ง URI content:

newIntent(label, intent)
ส่งคืนวัตถุ ClipData ที่มีออบเจ็กต์ ClipData.Item เดี่ยว มี Intent ป้ายกำกับของออบเจ็กต์ ClipDescription ได้รับการตั้งค่าเป็น label ตั้งค่าประเภท MIME เป็น MIMETYPE_TEXT_INTENT

ใช้ newIntent() เพื่อสร้างคลิปจากออบเจ็กต์ Intent

บังคับให้ข้อมูลในคลิปบอร์ดเป็นข้อความ

แม้ว่าแอปพลิเคชันของคุณจะจัดการกับข้อความเท่านั้น แต่คุณสามารถคัดลอกข้อมูลที่ไม่ใช่ข้อความจากคลิปบอร์ดได้โดย การแปลงรหัสด้วย ClipData.Item.coerceToText()

วิธีนี้จะแปลงข้อมูลใน ClipData.Item เป็นข้อความและแสดงผล CharSequence ค่าที่ ClipData.Item.coerceToText() แสดงผลนั้นอิงตาม บนรูปแบบข้อมูลใน ClipData.Item:

ข้อความ
หาก ClipData.Item คือข้อความ นั่นคือถ้า getText() ไม่เป็นค่าว่าง—coerceToText() จะแสดงข้อความ
URI
หาก ClipData.Item คือ URI นั่นคือถ้า getUri() ไม่เป็นค่าว่าง coerceToText() พยายามใช้เป็น URI เนื้อหา
  • หาก URI เป็น URI เนื้อหา และผู้ให้บริการสามารถแสดงสตรีมข้อความได้ coerceToText() แสดงผลสตรีมข้อความ
  • หาก URI เป็น URI เนื้อหา แต่ผู้ให้บริการไม่มีสตรีมข้อความ coerceToText() แสดงผลการนำเสนอ URI ตัวแทนคือ ซึ่งเท่ากับที่แสดงผลโดย Uri.toString()
  • หาก URI ไม่ใช่ URI เนื้อหา coerceToText() จะแสดงผลการแทนค่า URI การนำเสนอจะเหมือนกับที่แสดงผลโดย Uri.toString()
ความตั้งใจ
หาก ClipData.Item คือ Intent ซึ่งก็คือ getIntent() มีค่าว่างไม่ได้ coerceToText() จะแปลงค่าเป็น URI ของ Intent แล้วแสดงผล การนำเสนอจะเหมือนกับที่แสดงผลโดย Intent.toUri(URI_INTENT_SCHEME)

เฟรมเวิร์กคลิปบอร์ดสรุปไว้ในรูปที่ 2 ในการคัดลอกข้อมูล แอปพลิเคชันจะวาง ClipData ในคลิปบอร์ดส่วนกลางของ ClipboardManager ClipData มีวัตถุ ClipData.Item อย่างน้อย 1 รายการและอย่างน้อย 1 รายการ ออบเจ็กต์ ClipDescription รายการ แอปพลิเคชันจะได้รับ ClipData เพื่อวางข้อมูล รับประเภท MIME จาก ClipDescription และรับข้อมูลจาก ClipData.Item หรือจากผู้ให้บริการเนื้อหาที่กล่าวถึงโดย ClipData.Item

รูปภาพแสดงแผนภาพบล็อกของเฟรมเวิร์กการคัดลอกและวาง
รูปที่ 2 เฟรมเวิร์กคลิปบอร์ดของ Android

คัดลอกไปยังคลิปบอร์ด

หากต้องการคัดลอกข้อมูลไปยังคลิปบอร์ด ให้ใช้แฮนเดิลของออบเจ็กต์ ClipboardManager ส่วนกลาง สร้างออบเจ็กต์ ClipData และเพิ่ม ClipDescription และอีกอย่างน้อย 1 รายการ ClipData.Item ออบเจ็กต์ จากนั้นเพิ่มออบเจ็กต์ ClipData ที่เสร็จสมบูรณ์แล้วไปยัง ClipboardManager ออบเจ็กต์ ซึ่งอธิบายเพิ่มเติมในขั้นตอนต่อไปนี้

  1. หากคุณคัดลอกข้อมูลโดยใช้ URI เนื้อหา ให้ตั้งค่าผู้ให้บริการเนื้อหา
  2. ดาวน์โหลดคลิปบอร์ดของระบบ

    Kotlin

    when(menuItem.itemId) {
        ...
        R.id.menu_copy -> { // if the user selects copy
            // Gets a handle to the clipboard service.
            val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
        }
    }
    

    Java

    ...
    // If the user selects copy.
    case R.id.menu_copy:
    
    // Gets a handle to the clipboard service.
    ClipboardManager clipboard = (ClipboardManager)
            getSystemService(Context.CLIPBOARD_SERVICE);
    
  3. คัดลอกข้อมูลไปยังออบเจ็กต์ ClipData ใหม่:

    • สำหรับข้อความ

      Kotlin

      // Creates a new text clip to put on the clipboard.
      val clip: ClipData = ClipData.newPlainText("simple text", "Hello, World!")
      

      Java

      // Creates a new text clip to put on the clipboard.
      ClipData clip = ClipData.newPlainText("simple text", "Hello, World!");
      
    • สำหรับ URI

      ข้อมูลโค้ดนี้สร้าง URI โดยการเข้ารหัสรหัสระเบียนลงใน URI เนื้อหาสำหรับ กับผู้ให้บริการ เราจะกล่าวถึงเทคนิคนี้อย่างละเอียดใน การเข้ารหัสตัวระบุใน URI

      Kotlin

      // Creates a Uri using a base Uri and a record ID based on the contact's last
      // name. Declares the base URI string.
      const val CONTACTS = "content://com.example.contacts"
      
      // Declares a path string for URIs, used to copy data.
      const val COPY_PATH = "/copy"
      
      // Declares the Uri to paste to the clipboard.
      val copyUri: Uri = Uri.parse("$CONTACTS$COPY_PATH/$lastName")
      ...
      // Creates a new URI clip object. The system uses the anonymous
      // getContentResolver() object to get MIME types from provider. The clip object's
      // label is "URI", and its data is the Uri previously created.
      val clip: ClipData = ClipData.newUri(contentResolver, "URI", copyUri)
      

      Java

      // Creates a Uri using a base Uri and a record ID based on the contact's last
      // name. Declares the base URI string.
      private static final String CONTACTS = "content://com.example.contacts";
      
      // Declares a path string for URIs, used to copy data.
      private static final String COPY_PATH = "/copy";
      
      // Declares the Uri to paste to the clipboard.
      Uri copyUri = Uri.parse(CONTACTS + COPY_PATH + "/" + lastName);
      ...
      // Creates a new URI clip object. The system uses the anonymous
      // getContentResolver() object to get MIME types from provider. The clip object's
      // label is "URI", and its data is the Uri previously created.
      ClipData clip = ClipData.newUri(getContentResolver(), "URI", copyUri);
      
    • สำหรับ Intent

      ข้อมูลโค้ดนี้จะสร้าง Intent สำหรับแอปพลิเคชัน แล้วใส่ ในออบเจ็กต์คลิป

      Kotlin

      // Creates the Intent.
      val appIntent = Intent(this, com.example.demo.myapplication::class.java)
      ...
      // Creates a clip object with the Intent in it. Its label is "Intent"
      // and its data is the Intent object created previously.
      val clip: ClipData = ClipData.newIntent("Intent", appIntent)
      

      Java

      // Creates the Intent.
      Intent appIntent = new Intent(this, com.example.demo.myapplication.class);
      ...
      // Creates a clip object with the Intent in it. Its label is "Intent"
      // and its data is the Intent object created previously.
      ClipData clip = ClipData.newIntent("Intent", appIntent);
      
  4. วางออบเจ็กต์คลิปใหม่ลงในคลิปบอร์ด

    Kotlin

    // Set the clipboard's primary clip.
    clipboard.setPrimaryClip(clip)
    

    Java

    // Set the clipboard's primary clip.
    clipboard.setPrimaryClip(clip);
    

แสดงความคิดเห็นเมื่อคัดลอกไปยังคลิปบอร์ด

ผู้ใช้คาดหวังการตอบสนองที่เป็นภาพเมื่อแอปคัดลอกเนื้อหาไปยังคลิปบอร์ด เสร็จแล้ว โดยอัตโนมัติสำหรับผู้ใช้ใน Android 13 ขึ้นไป แต่จะต้องติดตั้งใช้งานด้วยตนเองก่อน เวอร์ชันต่างๆ

ตั้งแต่ Android 13 เป็นต้นไป ระบบจะแสดงการยืนยันด้วยภาพแบบมาตรฐานเมื่อมีการเพิ่มเนื้อหา ไปยังคลิปบอร์ด การยืนยันใหม่จะทำสิ่งต่อไปนี้

  • ยืนยันว่าคัดลอกเนื้อหาเรียบร้อยแล้ว
  • แสดงตัวอย่างเนื้อหาที่คัดลอก

วันที่ ภาพเคลื่อนไหวแสดงการแจ้งเตือนคลิปบอร์ดของ Android 13
รูปที่ 3 UI ที่แสดงเมื่อเนื้อหาใส่คลิปบอร์ดใน Android 13 ปีขึ้นไป

ใน Android 12L (API ระดับ 32) และต่ำกว่า ผู้ใช้อาจไม่แน่ใจว่าคัดลอกสำเร็จหรือไม่ เนื้อหาหรือสิ่งที่คัดลอก ฟีเจอร์นี้จะปรับให้การแจ้งเตือนต่างๆ ที่แสดงโดยแอปต่างๆ เป็นมาตรฐานหลังจาก การคัดลอกและทำให้ผู้ใช้ควบคุมคลิปบอร์ดได้มากขึ้น

หลีกเลี่ยงการแจ้งเตือนซ้ำ

ใน Android 12L (API ระดับ 32) และต่ำกว่า เราขอแนะนำให้แจ้งเตือนผู้ใช้เมื่อคัดลอกสำเร็จ ด้วยการแสดงความคิดเห็นในแอปโดยใช้ภาพ โดยใช้วิดเจ็ต เช่น Toast หรือ Snackbar หลังจากคัดลอก

เราขอแนะนำให้นำข้อความโทสต์ออกเพื่อหลีกเลี่ยงการแสดงข้อมูลที่ซ้ำกัน หรือแถบแสดงข้อความที่แสดงหลังข้อความในแอปสำหรับ Android 13 ขึ้นไป

วันที่ โพสต์แถบแสดงข้อความหลังข้อความในแอป
รูปที่ 4 หากแสดงแถบแสดงข้อความยืนยันการคัดลอกใน Android 13 ผู้ใช้เห็นข้อความที่ซ้ำกัน
โพสต์ข้อความโทสต์หลังจากข้อความในแอป
รูปที่ 5 หากแสดงข้อความโทสต์การยืนยันการคัดลอกใน Android 13 ผู้ใช้เห็นข้อความที่ซ้ำกัน

ตัวอย่างวิธีการติดตั้งใช้งานมีดังนี้

fun textCopyThenPost(textCopied:String) {
    val clipboardManager = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager
    // When setting the clipboard text.
    clipboardManager.setPrimaryClip(ClipData.newPlainText   ("", textCopied))
    // Only show a toast for Android 12 and lower.
    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2)
        Toast.makeText(context, Copied, Toast.LENGTH_SHORT).show()
}

เพิ่มเนื้อหาที่ละเอียดอ่อนลงในคลิปบอร์ด

หากแอปของคุณอนุญาตให้ผู้ใช้คัดลอกเนื้อหาที่ละเอียดอ่อนไปยังคลิปบอร์ด เช่น รหัสผ่านหรือเครดิต ข้อมูลบัตร คุณต้องเพิ่มธงไปยังClipDescription ในClipData ก่อนโทรหา ClipboardManager.setPrimaryClip() การเพิ่มการตั้งค่าสถานะนี้ช่วยป้องกันความละเอียดอ่อน เนื้อหาไม่ให้ปรากฏในการยืนยันด้วยภาพของเนื้อหาที่คัดลอกมาใน Android 13 ขึ้นไป

วันที่ ตัวอย่างข้อความที่คัดลอกโดยไม่แจ้งเนื้อหาที่ละเอียดอ่อน
รูปที่ 6 ตัวอย่างข้อความที่คัดลอกโดยไม่มีแฟล็กเนื้อหาที่ละเอียดอ่อน
คัดลอกตัวอย่างข้อความเพื่อแจ้งว่าเนื้อหาที่ละเอียดอ่อนไม่เหมาะสมแล้ว
รูปที่ 7 ตัวอย่างข้อความที่คัดลอกพร้อมแฟล็กเนื้อหาที่ละเอียดอ่อน

หากต้องการแจ้งว่าเนื้อหาที่ละเอียดอ่อนไม่เหมาะสม ให้เพิ่มบูลีนเพิ่มเติมลงใน ClipDescription แอปทั้งหมดจะต้องดำเนินการ โดยไม่คำนึงถึงระดับ API เป้าหมาย

// If your app is compiled with the API level 33 SDK or higher.
clipData.apply {
    description.extras = PersistableBundle().apply {
        putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true)
    }
}

// If your app is compiled with a lower SDK.
clipData.apply {
    description.extras = PersistableBundle().apply {
        putBoolean("android.content.extra.IS_SENSITIVE", true)
    }
}

วางจากคลิปบอร์ด

ตามที่อธิบายไว้ก่อนหน้านี้ ให้วางข้อมูลจากคลิปบอร์ดโดยรับออบเจ็กต์คลิปบอร์ดส่วนกลาง รับออบเจ็กต์คลิป ดูข้อมูลของคลิป และคัดลอกข้อมูลจากออบเจ็กต์คลิปหากเป็นไปได้ ลงในพื้นที่เก็บข้อมูลของคุณเอง ส่วนนี้จะอธิบายรายละเอียดวิธีวางคลิปบอร์ดทั้ง 3 รูปแบบ

วางข้อความธรรมดา

หากต้องการวางข้อความธรรมดา ให้รับคลิปบอร์ดส่วนกลางแล้วยืนยันว่าสามารถแสดงผลข้อความธรรมดาได้ จากนั้นติดตั้ง ออบเจ็กต์คลิปและคัดลอกข้อความไปยังพื้นที่เก็บข้อมูลของคุณโดยใช้ getText() ตามที่อธิบายไว้ใน ขั้นตอนต่อไปนี้

  1. รับออบเจ็กต์ ClipboardManager ส่วนกลางโดยใช้ getSystemService(CLIPBOARD_SERVICE) และประกาศตัวแปรร่วมที่มี ข้อความที่วาง:

    Kotlin

    var clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
    var pasteData: String = ""
    

    Java

    ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
    String pasteData = "";
    
  2. พิจารณาว่าจำเป็นต้องเปิดหรือปิดใช้ "วาง" ตัวเลือกในรายการปัจจุบัน กิจกรรม ยืนยันว่าคลิปบอร์ดมีคลิปและคุณสามารถจัดการประเภทข้อมูลได้ ที่แสดงในคลิป

    Kotlin

    // Gets the ID of the "paste" menu item.
    val pasteItem: MenuItem = menu.findItem(R.id.menu_paste)
    
    // If the clipboard doesn't contain data, disable the paste menu item.
    // If it does contain data, decide whether you can handle the data.
    pasteItem.isEnabled = when {
        !clipboard.hasPrimaryClip() -> {
            false
        }
        !(clipboard.primaryClipDescription.hasMimeType(MIMETYPE_TEXT_PLAIN)) -> {
            // Disables the paste menu item, since the clipboard has data but it
            // isn't plain text.
            false
        }
        else -> {
            // Enables the paste menu item, since the clipboard contains plain text.
            true
        }
    }
    

    Java

    // Gets the ID of the "paste" menu item.
    MenuItem pasteItem = menu.findItem(R.id.menu_paste);
    
    // If the clipboard doesn't contain data, disable the paste menu item.
    // If it does contain data, decide whether you can handle the data.
    if (!(clipboard.hasPrimaryClip())) {
    
        pasteItem.setEnabled(false);
    
    } else if (!(clipboard.getPrimaryClipDescription().hasMimeType(MIMETYPE_TEXT_PLAIN))) {
    
        // Disables the paste menu item, since the clipboard has data but
        // it isn't plain text.
        pasteItem.setEnabled(false);
    } else {
    
        // Enables the paste menu item, since the clipboard contains plain text.
        pasteItem.setEnabled(true);
    }
    
  3. คัดลอกข้อมูลจากคลิปบอร์ด จุดนี้ในโค้ดจะสามารถเข้าถึงได้ต่อเมื่อ "วาง" เปิดใช้งานรายการในเมนูแล้ว คุณจึงสามารถสรุปว่าคลิปบอร์ดมีข้อความธรรมดา ข้อความ คุณจะยังไม่ทราบว่าข้อความมีสตริงข้อความหรือ URI ที่ชี้ไปยังข้อความธรรมดาไหม ข้อมูลโค้ดต่อไปนี้จะทดสอบสิ่งนี้ แต่จะแสดงเฉพาะโค้ดสำหรับจัดการกับข้อความธรรมดาเท่านั้น:

    Kotlin

    when (menuItem.itemId) {
        ...
        R.id.menu_paste -> {    // Responds to the user selecting "paste".
            // Examines the item on the clipboard. If getText() doesn't return null,
            // the clip item contains the text. Assumes that this application can only
            // handle one item at a time.
            val item = clipboard.primaryClip.getItemAt(0)
    
            // Gets the clipboard as text.
            pasteData = item.text
    
            return if (pasteData != null) {
                // If the string contains data, then the paste operation is done.
                true
            } else {
                // The clipboard doesn't contain text. If it contains a URI,
                // attempts to get data from it.
                val pasteUri: Uri? = item.uri
    
                if (pasteUri != null) {
                    // If the URI contains something, try to get text from it.
    
                    // Calls a routine to resolve the URI and get data from it.
                    // This routine isn't presented here.
                    pasteData = resolveUri(pasteUri)
                    true
                } else {
    
                    // Something is wrong. The MIME type was plain text, but the
                    // clipboard doesn't contain text or a Uri. Report an error.
                    Log.e(TAG,"Clipboard contains an invalid data type")
                    false
                }
            }
        }
    }
    

    Java

    // Responds to the user selecting "paste".
    case R.id.menu_paste:
    
    // Examines the item on the clipboard. If getText() does not return null,
    // the clip item contains the text. Assumes that this application can only
    // handle one item at a time.
     ClipData.Item item = clipboard.getPrimaryClip().getItemAt(0);
    
    // Gets the clipboard as text.
    pasteData = item.getText();
    
    // If the string contains data, then the paste operation is done.
    if (pasteData != null) {
        return true;
    
    // The clipboard doesn't contain text. If it contains a URI, attempts to get
    // data from it.
    } else {
        Uri pasteUri = item.getUri();
    
        // If the URI contains something, try to get text from it.
        if (pasteUri != null) {
    
            // Calls a routine to resolve the URI and get data from it.
            // This routine isn't presented here.
            pasteData = resolveUri(Uri);
            return true;
        } else {
    
            // Something is wrong. The MIME type is plain text, but the
            // clipboard doesn't contain text or a Uri. Report an error.
            Log.e(TAG, "Clipboard contains an invalid data type");
            return false;
        }
    }
    

วางข้อมูลจาก URI เนื้อหา

หากออบเจ็กต์ ClipData.Item มี URI เนื้อหาและคุณพิจารณาว่าคุณสามารถ จัดการประเภท MIME อย่างใดอย่างหนึ่ง สร้าง ContentResolver และเรียกเนื้อหาที่เหมาะสม ในการเรียกดูข้อมูล

ขั้นตอนต่อไปนี้อธิบายวิธีรับข้อมูลจากผู้ให้บริการเนื้อหาโดยอิงตาม URI เนื้อหา ในคลิปบอร์ด ระบบจะตรวจสอบว่าประเภท MIME ที่แอปพลิเคชันใช้ได้นั้นมีให้ใช้งานจาก

  1. ประกาศตัวแปรร่วมเพื่อให้มีประเภท MIME:

    Kotlin

    // Declares a MIME type constant to match against the MIME types offered
    // by the provider.
    const val MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact"
    

    Java

    // Declares a MIME type constant to match against the MIME types offered by
    // the provider.
    public static final String MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact";
    
  2. ดาวน์โหลดคลิปบอร์ดส่วนกลาง รวมทั้งใช้รีโซลเวอร์เนื้อหาเพื่อให้คุณเข้าถึงเนื้อหาได้ เป็นผู้ให้บริการคลาวด์

    Kotlin

    // Gets a handle to the Clipboard Manager.
    val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
    
    // Gets a content resolver instance.
    val cr = contentResolver
    

    Java

    // Gets a handle to the Clipboard Manager.
    ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
    
    // Gets a content resolver instance.
    ContentResolver cr = getContentResolver();
    
  3. รับคลิปหลักจากคลิปบอร์ดและรับเนื้อหาเป็น URI ดังนี้

    Kotlin

    // Gets the clipboard data from the clipboard.
    val clip: ClipData? = clipboard.primaryClip
    
    clip?.run {
    
        // Gets the first item from the clipboard data.
        val item: ClipData.Item = getItemAt(0)
    
        // Tries to get the item's contents as a URI.
        val pasteUri: Uri? = item.uri
    

    Java

    // Gets the clipboard data from the clipboard.
    ClipData clip = clipboard.getPrimaryClip();
    
    if (clip != null) {
    
        // Gets the first item from the clipboard data.
        ClipData.Item item = clip.getItemAt(0);
    
        // Tries to get the item's contents as a URI.
        Uri pasteUri = item.getUri();
    
  4. ทดสอบว่า URI เป็น URI เนื้อหาหรือไม่โดยการเรียก getType(Uri) เมธอดนี้จะแสดงค่า Null หาก Uri ไม่ได้ชี้ไปที่ผู้ให้บริการเนื้อหาที่ถูกต้อง

    Kotlin

        // If the clipboard contains a URI reference...
        pasteUri?.let {
    
            // ...is this a content URI?
            val uriMimeType: String? = cr.getType(it)
    

    Java

        // If the clipboard contains a URI reference...
        if (pasteUri != null) {
    
            // ...is this a content URI?
            String uriMimeType = cr.getType(pasteUri);
    
  5. ทดสอบว่าผู้ให้บริการเนื้อหารองรับประเภท MIME ที่แอปพลิเคชันเข้าใจหรือไม่ ถ้า มี เรียก ContentResolver.query() เพื่อรับข้อมูล ผลลัพธ์คือ Cursor

    Kotlin

            // If the return value isn't null, the Uri is a content Uri.
            uriMimeType?.takeIf {
    
                // Does the content provider offer a MIME type that the current
                // application can use?
                it == MIME_TYPE_CONTACT
            }?.apply {
    
                // Get the data from the content provider.
                cr.query(pasteUri, null, null, null, null)?.use { pasteCursor ->
    
                    // If the Cursor contains data, move to the first record.
                    if (pasteCursor.moveToFirst()) {
    
                        // Get the data from the Cursor here.
                        // The code varies according to the format of the data model.
                    }
    
                    // Kotlin `use` automatically closes the Cursor.
                }
            }
        }
    }
    

    Java

            // If the return value isn't null, the Uri is a content Uri.
            if (uriMimeType != null) {
    
                // Does the content provider offer a MIME type that the current
                // application can use?
                if (uriMimeType.equals(MIME_TYPE_CONTACT)) {
    
                    // Get the data from the content provider.
                    Cursor pasteCursor = cr.query(uri, null, null, null, null);
    
                    // If the Cursor contains data, move to the first record.
                    if (pasteCursor != null) {
                        if (pasteCursor.moveToFirst()) {
    
                        // Get the data from the Cursor here.
                        // The code varies according to the format of the data model.
                        }
                    }
    
                    // Close the Cursor.
                    pasteCursor.close();
                 }
             }
         }
    }
    

วางความตั้งใจ

หากต้องการวางความตั้งใจ ให้เริ่มจากคลิปบอร์ดส่วนกลาง ตรวจสอบออบเจ็กต์ ClipData.Item เพื่อดูว่ามี Intent หรือไม่ จากนั้นโทรหา getIntent() เพื่อคัดลอก พื้นที่เก็บข้อมูลของคุณเอง ข้อมูลโค้ดต่อไปนี้แสดงให้เห็นสิ่งต่อไปนี้

Kotlin

// Gets a handle to the Clipboard Manager.
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager

// Checks whether the clip item contains an Intent by testing whether
// getIntent() returns null.
val pasteIntent: Intent? = clipboard.primaryClip?.getItemAt(0)?.intent

if (pasteIntent != null) {

    // Handle the Intent.

} else {

    // Ignore the clipboard, or issue an error if
    // you expect an Intent to be on the clipboard.
}

Java

// Gets a handle to the Clipboard Manager.
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);

// Checks whether the clip item contains an Intent, by testing whether
// getIntent() returns null.
Intent pasteIntent = clipboard.getPrimaryClip().getItemAt(0).getIntent();

if (pasteIntent != null) {

    // Handle the Intent.

} else {

    // Ignore the clipboard, or issue an error if
    // you expect an Intent to be on the clipboard.
}

การแจ้งเตือนของระบบที่แสดงเมื่อแอปของคุณเข้าถึงข้อมูลคลิปบอร์ด

ใน Android 12 (API ระดับ 31) ขึ้นไป โดยปกติระบบจะแสดงข้อความโทสต์เมื่อแอปของคุณ การโทร getPrimaryClip() ข้อความในข้อความจะมีรูปแบบดังนี้

APP pasted from your clipboard

โดยระบบจะไม่แสดงข้อความโทสต์เมื่อแอปดำเนินการอย่างใดอย่างหนึ่งต่อไปนี้

  • การเข้าถึง ClipData จากแอปของคุณเอง
  • เข้าถึง ClipData ซ้ำจากแอปใดแอปหนึ่ง ข้อความโทสต์จะปรากฏเมื่อ แอปของคุณเข้าถึงข้อมูลจากแอปนั้นเป็นครั้งแรก
  • ดึงข้อมูลเมตาสำหรับออบเจ็กต์คลิป เช่น ด้วยการเรียกใช้ getPrimaryClipDescription() แทนที่จะเป็น getPrimaryClip()

ใช้ผู้ให้บริการเนื้อหาเพื่อคัดลอกข้อมูลที่ซับซ้อน

ผู้ให้บริการเนื้อหารองรับการคัดลอกข้อมูลที่ซับซ้อน เช่น บันทึกในฐานข้อมูลหรือสตรีมไฟล์ เพื่อคัดลอก แล้ววาง URI ของเนื้อหาในคลิปบอร์ด การวางแอปพลิเคชันแล้วรับ URI นี้จาก คลิปบอร์ดและใช้เพื่อเรียกข้อมูลฐานข้อมูลหรือข้อบ่งชี้สตรีมไฟล์

เนื่องจากแอปพลิเคชันการวางมีเพียง URI เนื้อหาสําหรับข้อมูลของคุณ แอปพลิเคชันจึงจําเป็นต้องทราบว่า ที่จะดึงออกมา คุณสามารถระบุข้อมูลนี้ได้โดยการเข้ารหัสตัวระบุสำหรับข้อมูล เกี่ยวกับ URI เอง หรือคุณจะระบุ URI ที่ไม่ซ้ำกันซึ่งจะแสดงข้อมูลที่คุณต้องการคัดลอกก็ได้ ซึ่ง เทคนิคที่คุณเลือกจะขึ้นอยู่กับการจัดระเบียบข้อมูล

ส่วนต่อไปนี้อธิบายวิธีตั้งค่า URI, ให้ข้อมูลที่ซับซ้อน และจัดเตรียมไฟล์ สตรีม คำอธิบายเหล่านี้จะถือว่าคุณคุ้นเคยกับหลักการทั่วไปของผู้ให้บริการเนื้อหา การออกแบบอีกด้วย

เข้ารหัสตัวระบุใน URI

เทคนิคที่มีประโยชน์สำหรับการคัดลอกข้อมูลไปยังคลิปบอร์ดด้วย URI คือการเข้ารหัสตัวระบุสำหรับ ข้อมูลใน URI เอง จากนั้นผู้ให้บริการเนื้อหาจะสามารถรับตัวระบุจาก URI และใช้ เพื่อดึงข้อมูล แอปพลิเคชันการวางไม่จำเป็นต้องทราบว่ามีตัวระบุอยู่ ทั้งนี้ ก็เพียงแค่ขอรับ "การอ้างอิง" ซึ่งก็คือ URI และตัวระบุจาก คลิปบอร์ด ให้กับผู้ให้บริการเนื้อหา เพื่อดึงข้อมูลกลับมา

โดยปกติแล้วคุณจะต้องเข้ารหัสตัวระบุลงใน URI เนื้อหาโดยต่อตัวระบุไปยังจุดสิ้นสุดของ URI ตัวอย่างเช่น สมมติว่าคุณกำหนด URI ผู้ให้บริการเป็นสตริงต่อไปนี้

"content://com.example.contacts"

ถ้าคุณต้องการเข้ารหัสชื่อบน URI นี้ ให้ใช้ข้อมูลโค้ดต่อไปนี้

Kotlin

val uriString = "content://com.example.contacts/Smith"

// uriString now contains content://com.example.contacts/Smith.

// Generates a uri object from the string representation.
val copyUri = Uri.parse(uriString)

Java

String uriString = "content://com.example.contacts" + "/" + "Smith";

// uriString now contains content://com.example.contacts/Smith.

// Generates a uri object from the string representation.
Uri copyUri = Uri.parse(uriString);

หากคุณใช้ผู้ให้บริการเนื้อหาอยู่แล้ว คุณอาจต้องการเพิ่มเส้นทาง URI ใหม่ที่ระบุว่า URI ใช้สำหรับการคัดลอก ตัวอย่างเช่น สมมติว่าคุณมีเส้นทาง URI ต่อไปนี้อยู่แล้ว

"content://com.example.contacts/people"
"content://com.example.contacts/people/detail"
"content://com.example.contacts/people/images"

คุณเพิ่มเส้นทางอื่นสำหรับคัดลอก URI ได้โดยทำดังนี้

"content://com.example.contacts/copying"

จากนั้นคุณจะสามารถตรวจหา "สำเนา" URI โดยการจับคู่รูปแบบ และจัดการด้วยโค้ดที่ สำหรับการคัดลอกและวาง

ปกติแล้วคุณจะใช้เทคนิคการเข้ารหัสนี้ หากคุณใช้ผู้ให้บริการเนื้อหาอยู่แล้วภายใน ฐานข้อมูลหรือตารางภายในเพื่อจัดระเบียบข้อมูลของคุณ ในกรณีเหล่านี้ คุณมีข้อมูลหลายชิ้น ที่ต้องการคัดลอก และน่าจะเป็นตัวระบุที่ไม่ซ้ำกันสำหรับแต่ละชิ้น ในการตอบกลับคำถามจาก วางแอปพลิเคชัน คุณจะสามารถค้นหาข้อมูลตามตัวระบุและส่งคืน

หากข้อมูลเหล่านั้นไม่ได้มีหลายส่วน ก็อาจไม่จําเป็นต้องเข้ารหัสตัวระบุ คุณสามารถใช้ URI ที่ไม่ซ้ำกันสำหรับผู้ให้บริการของคุณ ในการตอบกลับคำถาม ผู้ให้บริการจะแสดงแอตทริบิวต์ ที่มีอยู่ในปัจจุบัน

คัดลอกโครงสร้างข้อมูล

ตั้งค่าผู้ให้บริการเนื้อหาสำหรับการคัดลอกและวางข้อมูลที่ซับซ้อนเป็นคลาสย่อยของ ContentProvider คอมโพเนนต์ เข้ารหัส URI ที่คุณใส่ในคลิปบอร์ดเพื่อให้ชี้ไปยังระเบียนที่คุณต้องการ ให้ไว้ นอกจากนี้ ให้พิจารณาสถานะปัจจุบันของใบสมัครดังนี้

  • หากมีผู้ให้บริการเนื้อหาอยู่แล้ว คุณสามารถเพิ่มฟังก์ชันการทำงานของผู้ให้บริการได้ คุณอาจ จำเป็นต้องแก้ไขเมธอด query() เพื่อจัดการ URI ที่มาจากแอปพลิเคชันที่ ต้องการวางข้อมูล คุณอาจต้องการแก้ไขวิธีจัดการ "สำเนา" URI รูปแบบ
  • หากแอปพลิเคชันของคุณมีฐานข้อมูลภายใน คุณอาจต้องย้ายฐานข้อมูลนี้ เป็นผู้ให้บริการเนื้อหาเพื่ออำนวยความสะดวกในการคัดลอก
  • หากคุณไม่ได้ใช้ฐานข้อมูล คุณสามารถใช้ผู้ให้บริการเนื้อหาแบบง่ายที่ จุดประสงค์เดียวคือการนำเสนอข้อมูลให้กับแอปพลิเคชันที่วางจากคลิปบอร์ด

ในผู้ให้บริการเนื้อหา ให้ลบล้างเมธอดต่อไปนี้เป็นอย่างน้อย

query()
การวางแอปพลิเคชันจะถือว่าแอปได้รับข้อมูลของคุณเมื่อใช้วิธีการนี้กับ URI ที่คุณตั้ง ไว้ในคลิปบอร์ด เพื่อรองรับการคัดลอก ให้วิธีนี้ตรวจหา URI ที่มี "คัดลอก" เส้นทาง จากนั้น แอปพลิเคชันของคุณสามารถสร้าง "สำเนา" ได้ URI ที่จะวาง คลิปบอร์ด ซึ่งมีเส้นทางการคัดลอกและตัวชี้ไปยังระเบียนที่คุณต้องการคัดลอก
getType()
เมธอดนี้ต้องแสดงผลประเภท MIME สำหรับข้อมูลที่คุณต้องการคัดลอก วิธีการ newUri() เรียก getType() เพื่อป้อนประเภท MIME ลงใน ClipData ใหม่ ออบเจ็กต์

ประเภท MIME สำหรับข้อมูลที่ซับซ้อนจะอธิบายไว้ใน ผู้ให้บริการเนื้อหา

คุณไม่จำเป็นต้องมีวิธีอื่นสำหรับผู้ให้บริการเนื้อหา เช่น insert() หรือ update() แอปพลิเคชันการวางจะเพียงแค่รับประเภท MIME ที่รองรับและคัดลอกข้อมูลจากผู้ให้บริการ หากคุณมีวิธีการเหล่านี้อยู่แล้ว วิธีการจะไม่รบกวนการคัดลอก

ข้อมูลโค้ดต่อไปนี้จะแสดงวิธีตั้งค่าแอปพลิเคชันให้คัดลอกข้อมูลที่ซับซ้อน

  1. ในค่าคงที่ร่วมสำหรับแอปพลิเคชันของคุณ ให้ประกาศสตริง URI พื้นฐานและเส้นทางที่ ระบุสตริง URI ที่คุณใช้คัดลอกข้อมูล ประกาศประเภท MIME สำหรับสำเนาด้วย

    Kotlin

    // Declares the base URI string.
    private const val CONTACTS = "content://com.example.contacts"
    
    // Declares a path string for URIs that you use to copy data.
    private const val COPY_PATH = "/copy"
    
    // Declares a MIME type for the copied data.
    const val MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact"
    

    Java

    // Declares the base URI string.
    private static final String CONTACTS = "content://com.example.contacts";
    
    // Declares a path string for URIs that you use to copy data.
    private static final String COPY_PATH = "/copy";
    
    // Declares a MIME type for the copied data.
    public static final String MIME_TYPE_CONTACT = "vnd.android.cursor.item/vnd.example.contact";
    
  2. ในกิจกรรมที่ผู้ใช้คัดลอกข้อมูล ให้ตั้งค่ารหัสเพื่อคัดลอกข้อมูลไปยังคลิปบอร์ด วาง URI ในคลิปบอร์ดเพื่อตอบสนองคำขอคัดลอก

    Kotlin

    class MyCopyActivity : Activity() {
        ...
    when(item.itemId) {
        R.id.menu_copy -> { // The user has selected a name and is requesting a copy.
            // Appends the last name to the base URI.
            // The name is stored in "lastName".
            uriString = "$CONTACTS$COPY_PATH/$lastName"
    
            // Parses the string into a URI.
            val copyUri: Uri? = Uri.parse(uriString)
    
            // Gets a handle to the clipboard service.
            val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
    
            val clip: ClipData = ClipData.newUri(contentResolver, "URI", copyUri)
    
            // Sets the clipboard's primary clip.
            clipboard.setPrimaryClip(clip)
        }
    }
    

    Java

    public class MyCopyActivity extends Activity {
        ...
    // The user has selected a name and is requesting a copy.
    case R.id.menu_copy:
    
        // Appends the last name to the base URI.
        // The name is stored in "lastName".
        uriString = CONTACTS + COPY_PATH + "/" + lastName;
    
        // Parses the string into a URI.
        Uri copyUri = Uri.parse(uriString);
    
        // Gets a handle to the clipboard service.
        ClipboardManager clipboard = (ClipboardManager)
            getSystemService(Context.CLIPBOARD_SERVICE);
    
        ClipData clip = ClipData.newUri(getContentResolver(), "URI", copyUri);
    
        // Sets the clipboard's primary clip.
        clipboard.setPrimaryClip(clip);
    
  3. ในขอบเขตรวมของผู้ให้บริการเนื้อหา ให้สร้างเครื่องมือจับคู่ URI และเพิ่มรูปแบบ URI ที่ ตรงกับ URI ที่คุณใส่ในคลิปบอร์ด

    Kotlin

    // A Uri Match object that simplifies matching content URIs to patterns.
    private val sUriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
    
        // Adds a matcher for the content URI. It matches.
        // "content://com.example.contacts/copy/*"
        addURI(CONTACTS, "names/*", GET_SINGLE_CONTACT)
    }
    
    // An integer to use in switching based on the incoming URI pattern.
    private const val GET_SINGLE_CONTACT = 0
    ...
    class MyCopyProvider : ContentProvider() {
        ...
    }
    

    Java

    public class MyCopyProvider extends ContentProvider {
        ...
    // A Uri Match object that simplifies matching content URIs to patterns.
    private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    
    // An integer to use in switching based on the incoming URI pattern.
    private static final int GET_SINGLE_CONTACT = 0;
    ...
    // Adds a matcher for the content URI. It matches
    // "content://com.example.contacts/copy/*"
    sUriMatcher.addURI(CONTACTS, "names/*", GET_SINGLE_CONTACT);
    
  4. ตั้งค่า query() วิธีนี้สามารถจัดการรูปแบบ URI ที่แตกต่างกันขึ้นอยู่กับวิธีที่คุณเขียนโค้ด แต่ต้อง รูปแบบของการดำเนินการคัดลอกคลิปบอร์ดจะแสดง

    Kotlin

    // Sets up your provider's query() method.
    override fun query(
            uri: Uri,
            projection: Array<out String>?,
            selection: String?,
            selectionArgs: Array<out String>?,
            sortOrder: String?
    ): Cursor? {
        ...
        // When based on the incoming content URI:
        when(sUriMatcher.match(uri)) {
    
            GET_SINGLE_CONTACT -> {
    
                // Queries and returns the contact for the requested name. Decodes
                // the incoming URI, queries the data model based on the last name,
                // and returns the result as a Cursor.
            }
        }
        ...
    }
    

    Java

    // Sets up your provider's query() method.
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
        String sortOrder) {
        ...
        // Switch based on the incoming content URI.
        switch (sUriMatcher.match(uri)) {
    
        case GET_SINGLE_CONTACT:
    
            // Queries and returns the contact for the requested name. Decodes the
            // incoming URI, queries the data model based on the last name, and
            // returns the result as a Cursor.
        ...
    }
    
  5. ตั้งค่าเมธอด getType() เพื่อแสดงผลประเภท MIME ที่เหมาะสมสำหรับที่คัดลอก ข้อมูล:

    Kotlin

    // Sets up your provider's getType() method.
    override fun getType(uri: Uri): String? {
        ...
        return when(sUriMatcher.match(uri)) {
            GET_SINGLE_CONTACT -> MIME_TYPE_CONTACT
            ...
        }
    }
    

    Java

    // Sets up your provider's getType() method.
    public String getType(Uri uri) {
        ...
        switch (sUriMatcher.match(uri)) {
        case GET_SINGLE_CONTACT:
            return (MIME_TYPE_CONTACT);
        ...
        }
    }
    

ส่วนวางข้อมูลจาก URI เนื้อหาจะอธิบายถึงวิธีสร้าง URI เนื้อหาจากคลิปบอร์ดและใช้เพื่อรับและวางข้อมูล

คัดลอกสตรีมข้อมูล

คุณคัดลอกและวางข้อความและข้อมูลไบนารีจำนวนมากเป็นสตรีมได้ ข้อมูลนี้อาจมีแบบฟอร์ม เช่น

  • ไฟล์ที่จัดเก็บไว้ในอุปกรณ์จริง
  • สตรีมจากซ็อกเก็ต
  • ข้อมูลจำนวนมากที่จัดเก็บไว้ในระบบฐานข้อมูลที่สำคัญของผู้ให้บริการ

ผู้ให้บริการเนื้อหาสำหรับสตรีมข้อมูลให้สิทธิ์เข้าถึงข้อมูลด้วยออบเจ็กต์ข้อบ่งชี้ไฟล์ เช่น AssetFileDescriptor, แทนที่จะเป็นออบเจ็กต์ Cursor แอปพลิเคชันการวางจะอ่านสตรีมข้อมูลโดยใช้ ข้อบ่งชี้ไฟล์

หากต้องการตั้งค่าแอปพลิเคชันให้คัดลอกสตรีมข้อมูลกับผู้ให้บริการ ให้ทำตามขั้นตอนต่อไปนี้

  1. ตั้งค่า URI เนื้อหาสำหรับสตรีมข้อมูลที่กำลังวางในคลิปบอร์ด ตัวเลือก ในการดำเนินการนี้ ได้แก่
    • เข้ารหัสตัวระบุของสตรีมข้อมูลลงใน URI ตามที่อธิบายไว้ใน เข้ารหัสตัวระบุในส่วน URI จากนั้นคง ตารางในผู้ให้บริการซึ่งมีตัวระบุและชื่อสตรีมที่เกี่ยวข้อง
    • เข้ารหัสชื่อสตรีมใน URI โดยตรง
    • ใช้ URI ที่ไม่ซ้ำกันซึ่งจะแสดงสตรีมปัจจุบันจากผู้ให้บริการเสมอ หากคุณ ใช้ตัวเลือกนี้ อย่าลืมอัปเดตผู้ให้บริการให้ชี้ไปยังสตรีมอื่น ทุกครั้งที่คุณคัดลอกสตรีมไปยังคลิปบอร์ดโดยใช้ URI
  2. ระบุประเภท MIME สำหรับสตรีมข้อมูลแต่ละประเภทที่คุณต้องการนำเสนอ กำลังวางแอปพลิเคชัน ต้องใช้ข้อมูลนี้เพื่อพิจารณาว่าสามารถวางข้อมูลในคลิปบอร์ดได้หรือไม่
  3. ใช้หนึ่งในเมธอด ContentProvider ที่ส่งคืนข้อบ่งชี้ไฟล์สำหรับ สตรีม หากคุณเข้ารหัสตัวระบุใน URI เนื้อหา ให้ใช้วิธีนี้เพื่อระบุ เพื่อเปิด
  4. หากต้องการคัดลอกสตรีมข้อมูลไปยังคลิปบอร์ด ให้สร้าง URI เนื้อหาและวางไว้ใน คลิปบอร์ด

ในการวางสตรีมข้อมูล แอปพลิเคชันจะรับคลิปจากคลิปบอร์ด รับ URI และใช้ ในการเรียกไปยังเมธอดตัวบ่งชี้ไฟล์ ContentResolver ที่เปิดสตรีม เมธอด ContentResolver เรียกใช้เมธอด ContentProvider ที่เกี่ยวข้อง การส่งผ่าน URI เนื้อหา ผู้ให้บริการจะแสดงข้อบ่งชี้ไฟล์กลับไปยัง ContentResolver วิธี จากนั้นแอปพลิเคชันการวางจะมีหน้าที่อ่าน จากสตรีม

รายการต่อไปนี้แสดงเมธอดตัวบ่งชี้ไฟล์ที่สำคัญที่สุดสำหรับผู้ให้บริการเนื้อหา ชิ้น ของรายการเหล่านี้มีเมธอด ContentResolver ที่สอดคล้องกับสตริง "ข้อบ่งชี้" ต่อท้ายชื่อเมธอด ตัวอย่างเช่น ContentResolver แอนะล็อกของ วันที่ openAssetFile() เท่ากับ openAssetFileDescriptor()

openTypedAssetFile()

เมธอดนี้จะแสดงข้อบ่งชี้ไฟล์เนื้อหา แต่เฉพาะในกรณีที่ประเภท MIME ที่ระบุคือ ที่ผู้ให้บริการรองรับ ผู้โทรซึ่งเป็นแอปพลิเคชันที่กำลังวางข้อมูลจะให้ รูปแบบประเภท MIME ผู้ให้บริการเนื้อหาของแอปพลิเคชันที่คัดลอก URI ไปยังฟังก์ชัน คลิปบอร์ดจะแสดงแฮนเดิลไฟล์ AssetFileDescriptor หากสามารถระบุได้ ประเภท MIME และแสดงข้อผิดพลาดหากทำไม่ได้

วิธีนี้จะจัดการส่วนย่อยของไฟล์ คุณสามารถใช้รายงานนี้เพื่ออ่านเนื้อหาที่ ผู้ให้บริการเนื้อหาได้คัดลอกไปยังคลิปบอร์ดแล้ว

openAssetFile()
วิธีนี้เป็นรูปแบบทั่วไปของ openTypedAssetFile() ไม่ได้กรอง สำหรับประเภท MIME ที่อนุญาต แต่สามารถอ่านส่วนย่อยของไฟล์ได้
openFile()
นี่คือรูปแบบที่กว้างขึ้นของ openAssetFile() ไม่สามารถอ่านส่วนย่อยของ

คุณสามารถเลือกใช้ openPipeHelper() กับเมธอดข้อบ่งชี้ไฟล์ ซึ่งช่วยให้แอปพลิเคชันการวางสามารถอ่านข้อมูลสตรีมใน เทรดพื้นหลังโดยใช้ไปป์ หากต้องการใช้วิธีนี้ ให้ใช้เมธอด ContentProvider.PipeDataWriter ของ Google

ออกแบบฟังก์ชันการคัดลอกและวางที่มีประสิทธิภาพ

ในการออกแบบฟังก์ชันคัดลอกและวางที่มีประสิทธิภาพสำหรับแอปพลิเคชันของคุณ ให้จดจำประเด็นเหล่านี้:

  • ในแต่ละครั้งจะมีคลิปเพียงคลิปเดียวในคลิปบอร์ด คัดลอกใหม่โดย แอปพลิเคชันในระบบจะแทนที่คลิปก่อนหน้า เนื่องจากผู้ใช้อาจไปยังส่วนต่างๆ ออกจากแอปพลิเคชันและคัดลอกก่อนส่งคืน คุณจะยึดคลิปบอร์ดไม่ได้ มีคลิปที่ผู้ใช้คัดลอกไว้ก่อนหน้านี้ในแอปพลิเคชันของคุณ
  • วัตถุประสงค์ของออบเจ็กต์ ClipData.Item หลายรายการต่อคลิปคือการ สนับสนุนการคัดลอกและวางรายการที่เลือกหลายรายการ แทนที่จะเป็นรูปแบบที่แตกต่างกัน อ้างอิงถึงตัวเลือกเดียวก็ได้ ปกติแล้วคุณจะต้องการ ClipData.Item ทั้งหมด ในคลิปเพื่อให้อยู่ในรูปแบบเดียวกัน กล่าวคือ ทั้งหมดจะต้องเป็นข้อความธรรมดา เนื้อหา URI หรือ Intent และไม่ผสม
  • เมื่อคุณให้ข้อมูล คุณสามารถเสนอการนำเสนอ MIME ที่แตกต่างกันได้ เพิ่มประเภท MIME ที่คุณรองรับ ClipDescription แล้วใช้ประเภท MIME ใน ผู้ให้บริการเนื้อหาของคุณ
  • เมื่อดึงข้อมูลจากคลิปบอร์ด แอปพลิเคชันของคุณจะทำหน้าที่ตรวจสอบ ประเภท MIME ที่มีอยู่ แล้วจึงค่อยตัดสินใจว่าจะใช้ MIME ประเภทใด (หากมี) แม้ว่าจะมี คลิปบนคลิปบอร์ดและผู้ใช้ขอให้วาง แอปพลิเคชันของคุณจึงไม่จำเป็น เพื่อวางได้ วางหากประเภท MIME เข้ากันได้ คุณอาจบังคับข้อมูล บนคลิปบอร์ดเพื่อส่งข้อความโดยใช้ coerceToText() หากแอปพลิเคชันของคุณรองรับ ประเภท MIME ที่มีอยู่มากกว่า 1 ประเภท คุณสามารถให้ผู้ใช้เลือกประเภทที่ต้องการได้