Chiếu nội dung nghe nhìn lên màn hình lớn

API chiếu nội dung nghe nhìn được giới thiệu trong phiên bản Android 5 (API cấp 21) cho phép bạn chụp lại nội dung của màn hình thiết bị dưới dạng luồng nội dung nghe nhìn bạn có thể phát lại, quay hoặc truyền sang các thiết bị khác như TV.

Quá trình chiếu nội dung nghe nhìn bao gồm ba cách biểu thị của màn hình thiết bị:

Màn hình thiết bị thực tế chiếu lên màn hình ảo. Nội dung của màn hình ảo được ghi vào `Bề mặt` (Surface) do ứng dụng cung cấp.
Hình 1. Màn hình thiết bị thực tế chiếu lên màn hình ảo. Nội dung của màn hình ảo được ghi vào Surface do ứng dụng cung cấp.

Tính năng chiếu nội dung nghe nhìn sẽ chụp lại nội dung của màn hình thiết bị, sau đó chiếu hình ảnh được chụp lên màn hình ảo để kết xuất hình ảnh đó trên Surface.

Ứng dụng này cung cấp Surface thông qua SurfaceView hoặc ImageReader, một trong hai sẽ sử dụng nội dung của màn hình được chụp. OnImageAvailableListener của ImageReader cho phép bạn quản lý hình ảnh được kết xuất trên Surface theo thời gian thực. Bạn có thể lưu hình ảnh dưới dạng bản ghi hoặc truyền vào TV hoặc thiết bị khác.

MediaProjection

Bắt đầu phiên chiếu nội dung nghe nhìn bằng cách lấy mã thông báo giúp ứng dụng có thể chụp lại nội dung hiển thị, âm thanh thiết bị hoặc cả hai. Mã thông báo này được biểu thị bằng một thực thể của lớp MediaProjection. Bạn có thể tạo một thực thể của lớp này khi bắt đầu một hoạt động mới.

Phương pháp cũ

Để có được mã thông báo chiếu nội dung nghe nhìn bằng phương pháp cũ, hãy gọistartActivityForResult() với ý định được phương thức createScreenCaptureIntent() của dịch vụ hệ thống MediaProjectionManager trả về:

Kotlin

startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(),
                       REQUEST_MEDIA_PROJECTION)

Java

startActivityForResult(mediaProjectionManager.createScreenCaptureIntent(),
                       REQUEST_MEDIA_PROJECTION);

Lệnh gọi sẽ hiển thị hộp thoại xác nhận để thông báo cho người dùng rằng tính năng chiếu nội dung nghe nhìn chụp lại mọi thông tin hiển thị, bao gồm bất kỳ thông tin nhạy cảm hoặc thông tin nhận dạng cá nhân nào.

Nếu người dùng xác nhận thì startActivityForResult() sẽ truyền một mã kết quả và dữ liệu đến lệnh gọi lại onActivityResult().

Sau đó, bạn có thể truyền dữ liệu và mã kết quả đến phương thức getMediaProjection() từ MediaProjectionManager để tạo một thực thể của MediaProjection:

Kotlin

mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, resultData)

Java

mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, resultData);

Phương pháp này khuyến khích bạn sử dụng API từ thư viện Hoạt động Jetpack để lấy mã thông báo chiếu nội dung nghe nhìn:

Kotlin

val mediaProjectionManager = getSystemService(MediaProjectionManager::class.java)
var mediaProjection : MediaProjection

val startMediaProjection = registerForActivityResult(
  StartActivityForResult()
) { result ->
  if (result.resultCode == RESULT_OK) {
    mediaProjection = mediaProjectionManager
      .getMediaProjection(result.resultCode, result.data!!)
  }
}

startMediaProjection.launch(mediaProjectionManager.createScreenCaptureIntent())

Java

final MediaProjectionManager mediaProjectionManager =
  getSystemService(MediaProjectionManager.class);
final MediaProjection[] mediaProjection = new MediaProjection[1];

ActivityResultLauncher<Intent> startMediaProjection = registerForActivityResult(
  new StartActivityForResult(),
  result -> {
    if (result.getResultCode() == Activity.RESULT_OK) {
      mediaProjection[0] = mediaProjectionManager
        .getMediaProjection(result.getResultCode(), result.getData());
    }
  }
);

Màn hình ảo

Tâm điểm của tính năng chiếu nội dung nghe nhìn là màn hình ảo mà bạn tạo bằng cách gọi createVirtualDisplay() cho thực thể MediaProjection:

Kotlin

virtualDisplay = mediaProjection.createVirtualDisplay(
                     "ScreenCapture",
                     width,
                     height,
                     screenDensity,
                     DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                     surface,
                     null, null)

Java

virtualDisplay = mediaProjection.createVirtualDisplay(
                     "ScreenCapture",
                     width,
                     height,
                     screenDensity,
                     DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
                     surface,
                     null, null);

Các tham số widthheight chỉ định chiều rộng và chiều cao của màn hình ảo. Để có được các giá trị khớp với chiều rộng và chiều cao của hình chiếu nội dung nghe nhìn, hãy sử dụng các API WindowMetrics được giới thiệu trong Android 11 (API cấp 30).

WindowMetrics

Phép chiếu nội dung nghe nhìn sẽ chụp lại toàn bộ màn hình, bất kể ứng dụng có đang tạo phép chiếu nội dung nghe nhìn ở chế độ toàn màn hình hay ở chế độ nhiều cửa sổ hay không.

Để có kích thước của một phép chiếu nội dung nghe nhìn, hãy sử dụng WindowManager#getMaximumWindowMetrics(), theo đó trả về WindowMetrics cho đối tượng toàn màn hình ngay cả khi ứng dụng chiếu nội dung nghe nhìn ở chế độ nhiều cửa sổ, chỉ chiếm một phần màn hình.

Để tương thích xuống đến API cấp 14, hãy sử dụng WindowMetricsCalculator#computeMaximumWindowMetrics() từ thư viện Jetpack WindowManager.

Gọi WindowMetrics#getBounds() để lấy kích thước chiều rộng và chiều cao chính xác cho màn hình ảo của hình chiếu nội dung nghe nhìn (xem Màn hình ảo).

Kích thước ứng dụng chiếu nội dung nghe nhìn phải luôn có thể đổi kích thước. Các ứng dụng có thể thay đổi kích thước hỗ trợ thay đổi cấu hình thiết bị và chế độ nhiều cửa sổ (xem phần Hỗ trợ nhiều cửa sổ).

Nếu ứng dụng không thể thay đổi kích thước thì nó phải truy vấn các ranh giới hiển thị của cửa sổ và truy xuất WindowMetrics của khu vực hiển thị tối đa có sẵn cho ứng dụng bằng WindowManager#getMaximumWindowMetrics():

Kotlin

val windowContext = context.createWindowContext(context.display!!,
      WindowManager.LayoutParams.TYPE_APPLICATION, null)
val projectionMetrics = windowContext.getSystemService(WindowManager::class.java)
      .maximumWindowMetrics

Java

Context windowContext = context.createWindowContext(context.getDisplay(),
      WindowManager.LayoutParams.TYPE_APPLICATION, null);
WindowMetrics projectionMetrics = windowContext.getSystemService(WindowManager.class)
      .getMaximumWindowMetrics();
.

Bề mặt

Bạn nên định kích thước bề mặt chiếu nội dung nghe nhìn để có đầu ra ở độ phân giải mong muốn. Kích thước lớn (độ phân giải thấp) để truyền sang TV hoặc màn hình máy tính và kích thước nhỏ (độ phân giải cao) để quay màn hình thiết bị.

Kể từ 12L (API cấp 32), khi hệ thống kết xuất màn hình ảo trên bề mặt, hệ thống sẽ điều chỉnh tỷ lệ màn hình ảo sao cho vừa với bề mặt bằng quy trình tương tự như tuỳ chọn centerInside trong ImageView.

Phương pháp điều chỉnh tỷ lệ mới cải thiện tính năng truyền màn hình sang TV và các màn hình lớn khác bằng cách tối đa hoá kích thước của hình ảnh bề mặt trong khi vẫn đảm bảo tỷ lệ khung hình phù hợp.

Đề xuất

Để có kết quả tốt nhất khi chiếu nội dung nghe nhìn, hãy thực hiện theo các đề xuất sau:

  • Ứng dụng phải có thể thay đổi kích thước. Các ứng dụng có thể thay đổi kích thước hỗ trợ thay đổi cấu hình thiết bị và chế độ nhiều cửa sổ (xem phần Hỗ trợ nhiều cửa sổ). Trong tệp kê khai ứng dụng, hãy đặt resizeableActivity="true". Trên Android 7.0 (API cấp 24) trở lên, chế độ cài đặt này có giá trị mặc định là đúng.
  • Cho phép các ứng dụng của bạn hỗ trợ hướng ngang và hướng dọc vì cả hai hướng đều phổ biến trên điện thoại, máy tính bảng và các dạng thức có thể gập.
  • Sử dụng WindowManager#getMaximumWindowMetrics() để lấy ranh giới của hình chiếu nội dung nghe nhìn. Để tương thích xuống cấp API 14, hãy sử dụng Jetpack WindowManager. (Xem mục WindowMetrics).
  • Nếu ứng dụng không thể thay đổi kích thước, hãy lấy ranh giới hình chiếu nội dung nghe nhìn thông qua ranh giới cửa sổ. (Xem mục WindowMetrics).

Tài nguyên khác

Để biết thêm thông tin về phép chiếu nội dung nghe nhìn, vui lòng xem nội dung Quay video và phát lại âm thanh.