คำแนะนำใน Android N และรุ่นก่อนหน้า

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

หมายเหตุ: ใช้ API ที่อธิบายไว้ที่นี่เพื่อให้คำแนะนำ ในแอปที่ทำงานใน Android ถึงและรวมถึง Android 7.1 (API ระดับ 25) เท่านั้น การจัดหา คำแนะนำสำหรับแอปที่ทำงานใน Android 8.0 (API ระดับ 26) ขึ้นไป แอปของคุณต้องใช้ ช่องทางแนะนำ

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

รูปที่ 1 ตัวอย่างแถวคําแนะนํา

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

แนวทางปฏิบัติแนะนำสำหรับคำแนะนำ

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

ประเภทของคำแนะนำ

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

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

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

รีเฟรชคำแนะนำ

เมื่อคุณรีเฟรชคำแนะนำ อย่าเพียงแค่นำคำแนะนำออกและโพสต์ซ้ำ เพราะการทำเช่นนี้จะ คำแนะนำที่จะปรากฏที่ท้ายแถวคำแนะนำ เมื่อรายการเนื้อหา เช่น ภาพยนตร์, เล่นแล้ว, นำออกจากคำแนะนำ

ปรับแต่งคำแนะนำ

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

คำแนะนำกลุ่ม

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

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

ใช้ NotificationCompat.Builder.setGroup() เพื่อตั้งค่าสตริงคีย์กลุ่มของคำแนะนำ สำหรับ เช่น เพื่อทำเครื่องหมายคำแนะนำว่าเป็นของกลุ่มที่มีเนื้อหามาแรงใหม่ คุณอาจโทรหา setGroup("trending")

สร้างบริการคำแนะนำ

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

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีขยาย IntentService ไปยัง สร้างบริการคำแนะนำสำหรับแอปพลิเคชันของคุณ:

Kotlin

class UpdateRecommendationsService : IntentService("RecommendationService") {
    override protected fun onHandleIntent(intent: Intent) {
        Log.d(TAG, "Updating recommendation cards")
        val recommendations = VideoProvider.getMovieList()
        if (recommendations == null) return

        var count = 0

        try {
            val builder = RecommendationBuilder()
                    .setContext(applicationContext)
                    .setSmallIcon(R.drawable.videos_by_google_icon)

            for (entry in recommendations.entrySet()) {
                for (movie in entry.getValue()) {
                    Log.d(TAG, "Recommendation - " + movie.getTitle())

                    builder.setBackground(movie.getCardImageUrl())
                            .setId(count + 1)
                            .setPriority(MAX_RECOMMENDATIONS - count)
                            .setTitle(movie.getTitle())
                            .setDescription(getString(R.string.popular_header))
                            .setImage(movie.getCardImageUrl())
                            .setIntent(buildPendingIntent(movie))
                            .build()
                    if (++count >= MAX_RECOMMENDATIONS) {
                        break
                    }
                }
                if (++count >= MAX_RECOMMENDATIONS) {
                    break
                }
            }
        } catch (e: IOException) {
            Log.e(TAG, "Unable to update recommendation", e)
        }
    }

    private fun buildPendingIntent(movie: Movie): PendingIntent {
        val detailsIntent = Intent(this, DetailsActivity::class.java)
        detailsIntent.putExtra("Movie", movie)

        val stackBuilder = TaskStackBuilder.create(this)
        stackBuilder.addParentStack(DetailsActivity::class.java)
        stackBuilder.addNextIntent(detailsIntent)

        // Ensure a unique PendingIntents, otherwise all
        // recommendations end up with the same PendingIntent
        detailsIntent.setAction(movie.getId().toString())

        val intent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)
        return intent
    }

    companion object {
        private val TAG = "UpdateRecommendationsService"
        private val MAX_RECOMMENDATIONS = 3
    }
}

Java

public class UpdateRecommendationsService extends IntentService {
    private static final String TAG = "UpdateRecommendationsService";
    private static final int MAX_RECOMMENDATIONS = 3;

    public UpdateRecommendationsService() {
        super("RecommendationService");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.d(TAG, "Updating recommendation cards");
        HashMap<String, List<Movie>> recommendations = VideoProvider.getMovieList();
        if (recommendations == null) return;

        int count = 0;

        try {
            RecommendationBuilder builder = new RecommendationBuilder()
                    .setContext(getApplicationContext())
                    .setSmallIcon(R.drawable.videos_by_google_icon);

            for (Map.Entry<String, List<Movie>> entry : recommendations.entrySet()) {
                for (Movie movie : entry.getValue()) {
                    Log.d(TAG, "Recommendation - " + movie.getTitle());

                    builder.setBackground(movie.getCardImageUrl())
                            .setId(count + 1)
                            .setPriority(MAX_RECOMMENDATIONS - count)
                            .setTitle(movie.getTitle())
                            .setDescription(getString(R.string.popular_header))
                            .setImage(movie.getCardImageUrl())
                            .setIntent(buildPendingIntent(movie))
                            .build();

                    if (++count >= MAX_RECOMMENDATIONS) {
                        break;
                    }
                }
                if (++count >= MAX_RECOMMENDATIONS) {
                    break;
                }
            }
        } catch (IOException e) {
            Log.e(TAG, "Unable to update recommendation", e);
        }
    }

    private PendingIntent buildPendingIntent(Movie movie) {
        Intent detailsIntent = new Intent(this, DetailsActivity.class);
        detailsIntent.putExtra("Movie", movie);

        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        stackBuilder.addParentStack(DetailsActivity.class);
        stackBuilder.addNextIntent(detailsIntent);
        // Ensure a unique PendingIntents, otherwise all
        // recommendations end up with the same PendingIntent
        detailsIntent.setAction(Long.toString(movie.getId()));

        PendingIntent intent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
        return intent;
    }
}

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

<manifest ... >
  <application ... >
    ...

    <service
            android:name="com.example.android.tvleanback.UpdateRecommendationsService"
            android:enabled="true" />
  </application>
</manifest>

คำแนะนำในการสร้าง

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

การตั้งค่า

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

Kotlin

class RecommendationBuilder {
    ...

    fun setTitle(title: String): RecommendationBuilder {
        this.title = title
        return this
    }

    fun setDescription(description: String): RecommendationBuilder {
        this.description = description
        return this
    }

    fun setImage(uri: String): RecommendationBuilder {
        imageUri = uri
        return this
    }

    fun setBackground(uri: String): RecommendationBuilder {
        backgroundUri = uri
        return this
    }

...

Java

public class RecommendationBuilder {
    ...

    public RecommendationBuilder setTitle(String title) {
            this.title = title;
            return this;
        }

        public RecommendationBuilder setDescription(String description) {
            this.description = description;
            return this;
        }

        public RecommendationBuilder setImage(String uri) {
            imageUri = uri;
            return this;
        }

        public RecommendationBuilder setBackground(String uri) {
            backgroundUri = uri;
            return this;
        }
...

สร้างการแจ้งเตือน

เมื่อกำหนดค่าแล้ว คุณสร้างการแจ้งเตือนโดยกำหนดค่าจากเครื่องมือสร้างได้ ไปยังการแจ้งเตือน และโทรหา NotificationCompat.Builder.build()

และอย่าลืมโทรหา setLocalOnly() ดังนั้นการแจ้งเตือนNotificationCompat.BigPictureStyleจะไม่แสดง บนอุปกรณ์อื่นๆ

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีสร้างคำแนะนำ

Kotlin

class RecommendationBuilder {
    ...

    @Throws(IOException::class)
    fun build(): Notification {
        ...

        val notification = NotificationCompat.BigPictureStyle(
        NotificationCompat.Builder(context)
                .setContentTitle(title)
                .setContentText(description)
                .setPriority(priority)
                .setLocalOnly(true)
                .setOngoing(true)
                .setColor(context.resources.getColor(R.color.fastlane_background))
                .setCategory(Notification.CATEGORY_RECOMMENDATION)
                .setLargeIcon(image)
                .setSmallIcon(smallIcon)
                .setContentIntent(intent)
                .setExtras(extras))
                .build()

        return notification
    }
}

Java

public class RecommendationBuilder {
    ...

    public Notification build() throws IOException {
        ...

        Notification notification = new NotificationCompat.BigPictureStyle(
                new NotificationCompat.Builder(context)
                        .setContentTitle(title)
                        .setContentText(description)
                        .setPriority(priority)
                        .setLocalOnly(true)
                        .setOngoing(true)
                        .setColor(context.getResources().getColor(R.color.fastlane_background))
                        .setCategory(Notification.CATEGORY_RECOMMENDATION)
                        .setLargeIcon(image)
                        .setSmallIcon(smallIcon)
                        .setContentIntent(intent)
                        .setExtras(extras))
                .build();

        return notification;
    }
}

เรียกใช้บริการคำแนะนำ

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

Kotlin

class BootupActivity : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Log.d(TAG, "BootupActivity initiated")
        if (intent.action.endsWith(Intent.ACTION_BOOT_COMPLETED)) {
            scheduleRecommendationUpdate(context)
        }
    }

    private fun scheduleRecommendationUpdate(context: Context) {
        Log.d(TAG, "Scheduling recommendations update")
        val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
        val recommendationIntent = Intent(context, UpdateRecommendationsService::class.java)
        val alarmIntent = PendingIntent.getService(context, 0, recommendationIntent, 0)
        alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                INITIAL_DELAY,
                AlarmManager.INTERVAL_HALF_HOUR,
                alarmIntent
        )
    }

    companion object {
        private val TAG = "BootupActivity"
        private val INITIAL_DELAY:Long = 5000
    }
}

Java

public class BootupActivity extends BroadcastReceiver {
    private static final String TAG = "BootupActivity";

    private static final long INITIAL_DELAY = 5000;

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d(TAG, "BootupActivity initiated");
        if (intent.getAction().endsWith(Intent.ACTION_BOOT_COMPLETED)) {
            scheduleRecommendationUpdate(context);
        }
    }

    private void scheduleRecommendationUpdate(Context context) {
        Log.d(TAG, "Scheduling recommendations update");

        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent recommendationIntent = new Intent(context, UpdateRecommendationsService.class);
        PendingIntent alarmIntent = PendingIntent.getService(context, 0, recommendationIntent, 0);

        alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                INITIAL_DELAY,
                AlarmManager.INTERVAL_HALF_HOUR,
                alarmIntent);
    }
}

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

<manifest ... >
  <application ... >
    <receiver android:name="com.example.android.tvleanback.BootupActivity"
              android:enabled="true"
              android:exported="false">
      <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED"/>
      </intent-filter>
    </receiver>
  </application>
</manifest>

สำคัญ: แอปของคุณต้องได้รับการแจ้งเตือนเมื่อเปิดเครื่องเรียบร้อยแล้ว ขอสิทธิ์ RECEIVE_BOOT_COMPLETED ดูข้อมูลเพิ่มเติมได้ที่ ACTION_BOOT_COMPLETED

ในชั้นเรียนบริการคำแนะนำ onHandleIntent() ให้โพสต์คำแนะนำไปยังผู้จัดการ ดังนี้

Kotlin

val notification = notificationBuilder.build()
notificationManager.notify(id, notification)

Java

Notification notification = notificationBuilder.build();
notificationManager.notify(id, notification);