Tích hợp Compose với giao diện người dùng hiện có

Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.

Nếu có một ứng dụng có giao diện người dùng dựa trên Chế độ xem, bạn có thể không muốn viết lại toàn bộ giao diện người dùng cho ứng dụng đó cùng một lúc. Trang này sẽ giúp bạn thêm các thành phần Soạn thư mới vào giao diện người dùng hiện có.

Di chuyển giao diện người dùng được chia sẻ

Nếu đang chuyển dần sang ứng dụng Soạn thư, bạn có thể cần sử dụng các phần tử giao diện người dùng dùng chung trong cả tính năng Soạn thư và Hệ thống chế độ xem. Ví dụ: nếu ứng dụng của bạn có thành phần CallToActionButton tùy chỉnh, bạn có thể cần sử dụng thành phần đó trong cả màn hình Soạn thư và chế độ xem.

Trong chế độ Compose, các thành phần trên giao diện người dùng được chia sẻ trở thành các thành phần có thể sử dụng lại trên ứng dụng bất kể thành phần đó được tạo kiểu bằng XML hay chế độ xem tùy chỉnh. Ví dụ: bạn sẽ tạo một CallToActionButton có thể kết hợp cho thành phần Button gọi hành động tùy chỉnh của mình.

Để sử dụng trình kết hợp trong màn hình dựa trên Chế độ xem, bạn cần tạo trình bao chế độ xem tùy chỉnh mở rộng từ AbstractComposeView. Trong thành phần kết hợp Content bị ghi đè, hãy đặt thành phần kết hợp mà bạn đã tạo và gói trong giao diện Compose như thể hiện ở ví dụ sau:

@Composable
fun CallToActionButton(
    text: String,
    onClick: () -> Unit,
    modifier: Modifier = Modifier,
) {
    Button(
        colors = ButtonDefaults.buttonColors(
            backgroundColor = MaterialTheme.colors.secondary
        ),
        onClick = onClick,
        modifier = modifier,
    ) {
        Text(text)
    }
}

class CallToActionViewButton @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyle: Int = 0
) : AbstractComposeView(context, attrs, defStyle) {

    var text by mutableStateOf<String>("")
    var onClick by mutableStateOf<() -> Unit>({})

    @Composable
    override fun Content() {
        YourAppTheme {
            CallToActionButton(text, onClick)
        }
    }
}

Xin lưu ý rằng các thông số có thể biến đổi sẽ trở thành biến có thể thay đổi bên trong chế độ xem tùy chỉnh. Điều này làm cho chế độ xem CallToActionViewButton tùy chỉnh có thể thổi phồng và sử dụng được, ví dụ: Liên kết chế độ xem, như chế độ xem truyền thống. Xem ví dụ bên dưới:

class ExampleActivity : Activity() {

    private lateinit var binding: ActivityExampleBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityExampleBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.callToAction.apply {
            text = getString(R.string.something)
            onClick = { /* Do something */ }
        }
    }
}

Nếu thành phần tùy chỉnh chứa trạng thái có thể thay đổi, hãy xem Nguồn thực tế.

Giao diện

Sau khi sử dụng Material Design, bạn có thể sử dụng thư viện Thành phần thiết kế Material Design cho Android (MDC) để tạo giao diện cho các ứng dụng Android. Như đã đề cập trong tài liệu Soạn chủ đề, ứng dụng Compose sẽ triển khai các khái niệm này bằng MaterialTheme có thể kết hợp.

Khi tạo màn hình mới trong ứng dụng Compose, bạn nên đảm bảo rằng bạn áp dụng MaterialTheme trước khi bất kỳ phần mềm tổng hợp nào phát ra giao diện người dùng từ thư viện thành phần tài liệu. Các thành phần quan trọng (Button, Text, v.v.) phụ thuộc vào việc có sẵn một MaterialTheme và hành vi của các thành phần đó chưa được xác định nếu không có.

Tất cả các mẫu Soạn thư Jetpack đều sử dụng giao diện Soạn thư tùy chỉnh dựa trên MaterialTheme.

Nhiều nguồn đáng tin cậy

Một ứng dụng hiện có thể cần phải điều chỉnh và tạo kiểu cho nhiều lượt xem. Khi giới thiệu tính năng Soạn thư trong một ứng dụng hiện có, bạn cần di chuyển giao diện để sử dụng MaterialTheme cho mọi màn hình Compose. Điều này có nghĩa là chủ đề của ứng dụng sẽ có 2 nguồn thực tế: giao diện dựa trên Chế độ xem và giao diện Compose. Mọi thay đổi đối với kiểu của bạn sẽ cần được thực hiện ở nhiều nơi.

Nếu kế hoạch của bạn là di chuyển hoàn toàn ứng dụng sang Soạn thư, thì cuối cùng, bạn sẽ cần tạo phiên bản Compose của giao diện hiện có. Vấn đề là bạn càng tạo sớm giao diện của mình trong quá trình phát triển, thì bạn càng phải bảo trì nhiều hơn nữa trong quá trình phát triển.

Bộ chuyển đổi Giao diện soạn thảo MDC

Nếu bạn đang sử dụng thư viện MDC trong ứng dụng Android, thư viện Bộ điều hợp giao diện MDC sẽ cho phép bạn dễ dàng sử dụng lại màu sắc, kiểu chữtạo hình các chủ đề dựa trên các chế độ xem dựa trên Chế độ xem hiện có:

import com.google.android.material.composethemeadapter.MdcTheme

class ExampleActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            // Use MdcTheme instead of MaterialTheme
            // Colors, typography, and shape have been read from the
            // View-based theme used in this Activity
            MdcTheme {
                ExampleComposable(/*...*/)
            }
        }
    }
}

Xem tài liệu về thư viện MDC để biết thêm thông tin.

Bộ chuyển đổi Giao diện soạn thảo ứng dụng

Thư viện Bộ chuyển đổi giao diện AppCompat cho phép bạn dễ dàng sử dụng lại các giao diện XML của AppCompat để kết hợp trong ứng dụng Jetpack Compose. Công cụ này tạo MaterialTheme bằng các giá trị color [màu_sắc]typtype trong giao diện của ngữ cảnh.

class ExampleActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            AppCompatTheme {
                // Colors, typography, and shape have been read from the
                // View-based theme used in this Activity
                ExampleComposable(/*...*/)
            }
        }
    }
}

Kiểu thành phần mặc định

Cả thư viện MDC và bộ chuyển đổi giao diện Soạn thảo AppCompat đều không đọc bất kỳ kiểu tiện ích con mặc định nào theo giao diện. Nguyên nhân là do tính năng Soạn thư không có khái niệm về tính năng có thể kết hợp mặc định.

Hãy đọc thêm về kiểu thành phầnhệ thống thiết kế tùy chỉnh trong tài liệu về Chủ đề.

Lớp phủ giao diện trong ứng dụng Compose

Khi di chuyển các màn hình dựa trên Chế độ xem sang ứng dụng Compose, hãy lưu ý đến cách sử dụng thuộc tính android:theme. Có thể bạn cần một MaterialTheme mới trong phần đó của cây Giao diện người dùng Compose.

Đọc thêm về chủ đề này trong Hướng dẫn theo chủ đề.

WindowInsets và Ảnh động IME

Từ tính năng Soạn thư phiên bản 1.2.0 trở đi, bạn có thể xử lý WindowInsets bằng cách sử dụng công cụ sửa đổi để xử lý các bố cục đó. Ảnh động IME cũng được hỗ trợ.

class ExampleActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        WindowCompat.setDecorFitsSystemWindows(window, false)

        setContent {
            MaterialTheme {
              MyScreen()
            }
        }
    }
}

@Composable
fun MyScreen() {
    Box {
        LazyColumn(
            modifier = Modifier
                .fillMaxSize() // fill the entire window
                .imePadding() // padding for the bottom for the IME
                .imeNestedScroll(), // scroll IME at the bottom
            content = { }
        )
        FloatingActionButton(
            modifier = Modifier
                .align(Alignment.BottomEnd)
                .padding(16.dp) // normal 16dp of padding for FABs
                .navigationBarsPadding() // padding for navigation bar
                .imePadding(), // padding for when IME appears
            onClick = { }
        ) {
            Icon( /* ... */)
        }
    }
}

Ảnh động hiển thị phần tử giao diện người dùng cuộn lên và xuống để nhường chỗ cho bàn phím

Hình 2. Ảnh động IME

Vui lòng xem tài liệu về thư viện nội dung để biết thêm thông tin chi tiết.

Ưu tiên phân chia trạng thái khỏi bản trình bày

Theo truyền thống, View là một trạng thái. View quản lý các trường mô tả nội dung để hiển thị, ngoài cách hiển thị nội dung đó. Khi bạn chuyển đổi một View thành Soạn thư, hãy tìm cách tách riêng dữ liệu đang được hiển thị sang luồng dữ liệu một chiều, như đã giải thích ở phần cái tra cứu trạng thái.

Ví dụ: View có thuộc tính visibility mô tả liệu thuộc tính này đang hiển thị, ẩn mặt hay đã biến mất. Đây là một thuộc tính vốn có của View. Mặc dù các đoạn mã khác có thể làm thay đổi chế độ hiển thị của View, nhưng bản thân View chỉ thực sự biết chế độ hiển thị hiện tại của mã đó. Logic để đảm bảo rằng một View có thể hiển thị là có thể dễ gặp lỗi và thường liên quan đến chính View.

Ngược lại, tính năng Soạn thư giúp bạn dễ dàng hiển thị các thành phần có thể kết hợp hoàn toàn khác nhau bằng cách sử dụng logic có điều kiện trong Kotlin:

if (showCautionIcon) {
    CautionIcon(/* ... */)
}

Theo thiết kế,CautionIcon không cần phải biết hoặc quan tâm đến lý do tại sao nội dung đó được hiển thị và không có khái niệm vềvisibility: nội dung đó nằm trong Bản sáng tác hoặc không.

Bằng cách tách biệt hoạt động quản lý trạng thái và logic trình bày, bạn có thể tự do thay đổi cách hiển thị nội dung dưới dạng lượt chuyển đổi trạng thái thành giao diện người dùng. Việc có thể hiển thị trạng thái lên hàng khi cần cũng làm cho Chức năng có thể sử dụng lại dễ dàng hơn, vì quyền sở hữu trạng thái linh hoạt hơn.

Quảng cáo các thành phần được đóng gói và có thể sử dụng lại

Các phần tử View thường có ý tưởng về vị trí chúng tồn tại: bên trong một Activity, một Dialog, một Fragment hoặc một nơi nào đó bên trong một hệ thống phân cấp View khác. Do các tệp này thường bị thổi phồng từ các tệp bố cục tĩnh, nên cấu trúc tổng thể của View có xu hướng rất cứng nhắc. Điều này dẫn đến việc ghép nối chặt chẽ hơn và khiếnView khó thay đổi hoặc sử dụng lại hơn.

Ví dụ: View tùy chỉnh có thể giả định rằng chế độ xem này có chế độ xem con thuộc một loại nhất định với một mã nhận dạng nhất định, và trực tiếp thay đổi các thuộc tính của loại đó để phản hồi một số hành động. Cách này giúp kết hợp chặt chẽ những yếu tố đóView các thành phần cùng nhau: Phiên bảnView có thể gặp sự cố hoặc bị hỏng nếu không tìm thấy con và có thể không sử dụng lại được nếu không có View mẹ.

Thao tác này ít gặp vấn đề hơn khi sử dụng tính năng Compose có thể sử dụng lại. Phần tử mẹ có thể dễ dàng chỉ định trạng thái và lệnh gọi lại, nhờ đó, bạn có thể viết Nội dung linh hoạt có thể sử dụng lại mà không cần phải biết chính xác vị trí sẽ sử dụng.

var isEnabled by rememberSaveable { mutableStateOf(false) }

Column {
    ImageWithEnabledOverlay(isEnabled)
    ControlPanelWithToggle(
        isEnabled = isEnabled,
        onEnabledChanged = { isEnabled = it }
    )
}

Trong ví dụ trên, cả ba phần đều được gói gọn và kết hợp ít hơn:

  • ImageWithEnabledOverlay chỉ cần biết trạng thái hiện tại của isEnabled. Bạn không cần phải biết là ControlPanelWithToggle có tồn tại hay không, hoặc thậm chí là làm thế nào để kiểm soát được.

  • ControlPanelWithToggle không biết rằng ImageWithEnabledOverlay tồn tại. Có thể không có, một hoặc nhiều cách để isEnabled hiển thị và ControlPanelWithToggle sẽ không phải thay đổi.

  • Đối với cấp độ gốc, việc ImageWithEnabledOverlay hoặc ControlPanelWithToggle lồng ghép sâu có hiệu lực hay không là không quan trọng. Những con đó có thể đang tạo hoạt động cho các thay đổi, hoán đổi nội dung hoặc chuyển nội dung cho các con khác.

Mẫu này còn được gọi là đảo ngược quyền kiểm soát mà bạn có thể đọc thêm trong tài liệu về CompositionLocal.

Xử lý các thay đổi về kích thước màn hình

Việc có các tài nguyên khác nhau cho các kích thước cửa sổ khác nhau là một trong những cách chính để tạo các bố cục View thích ứng. Mặc dù các tài nguyên đủ điều kiện vẫn là tùy chọn cho các quyết định về bố cục cấp màn hình, nhưng tính năng Soạn thư giúp bạn dễ dàng thay đổi bố cục hoàn toàn về mã với logic có điều kiện thông thường. Bạn có thể đưa ra quyết định dựa trên các công cụ như BoxWithConstraints, dựa trên không gian có sẵn cho từng phần tử, điều này không thể áp dụng được với các tài nguyên đủ điều kiện sau:

@Composable
fun MyComposable() {
    BoxWithConstraints {
        if (minWidth < 480.dp) {
            /* Show grid with 4 columns */
        } else if (minWidth < 720.dp) {
            /* Show grid with 8 columns */
        } else {
            /* Show grid with 12 columns */
        }
    }
}

Hãy đọc bài viết tạo bố cục thích ứng để tìm hiểu về các kỹ thuật Compose cung cấp để tạo giao diện người dùng thích ứng.

Cuộn lồng bằng các Chế độ xem

Để biết thêm thông tin về cách bật tương tác cuộn lồng giữa các phần tử Chế độ xem có thể cuộn và thành phần kết hợp có thể cuộn, được lồng vào cả hai hướng, hãy đọc qua bài viết Tương tác cuộn lồng.

Compose trong RecyclerView

Các thành phần kết hợp trong RecyclerView hoạt động hiệu quả kể từ phiên bản RecyclerView 1.3.0-alpha02. Hãy đảm bảo bạn đang sử dụng ít nhất là phiên bản 1.3.0-alpha02 của RecyclerView để xem các lợi ích đó.