1. Trước khi bắt đầu
Trong lớp học lập trình này, bạn sẽ tìm hiểu cách hài hoà các màu sắc tuỳ chỉnh với những màu do giao diện động tạo ra.
Điều kiện tiên quyết
Nhà phát triển nên
- Quen thuộc với các khái niệm cơ bản về việc tạo giao diện trong Android
- Thành thạo khi làm việc với các khung hiển thị tiện ích Android và thuộc tính của chúng
Kiến thức bạn sẽ học được
- Cách sử dụng tính năng hài hoà màu sắc trong ứng dụng bằng nhiều phương pháp
- Cách hoạt động của tính năng hài hoà và cách tính năng này thay đổi màu sắc
Bạn cần có
- Máy tính đã cài đặt Android nếu bạn muốn làm theo.
2. Tổng quan về ứng dụng
Voyaĝi là một ứng dụng giao thông công cộng đã sử dụng một giao diện động. Đối với nhiều hệ thống giao thông công cộng, màu sắc là một chỉ báo quan trọng về tàu hoả, xe buýt hoặc xe điện và những chỉ báo này không thể thay thế bằng bất kỳ màu chính, màu phụ hoặc màu thứ ba nào có sẵn. Chúng ta sẽ tập trung vào RecyclerView của thẻ đi phương tiện công cộng có màu.

3. Tạo giao diện
Bạn nên sử dụng công cụ Material Theme Builder của chúng tôi làm điểm dừng chân đầu tiên để tạo một giao diện Material3. Trên thẻ tuỳ chỉnh, giờ đây bạn có thể thêm nhiều màu sắc hơn vào giao diện của mình. Ở bên phải, bạn sẽ thấy các vai trò màu sắc và bảng màu sắc thái cho những màu đó.
Trong phần màu mở rộng, bạn có thể xoá hoặc đổi tên màu.

Trình đơn xuất sẽ hiển thị một số lựa chọn xuất có thể có. Tại thời điểm viết bài này, cách xử lý đặc biệt của Material Theme Builder đối với chế độ cài đặt hài hoà chỉ có trong Android Views

Tìm hiểu các giá trị xuất mới
Để bạn có thể sử dụng những màu này và các vai trò màu liên quan trong giao diện của mình, bất kể bạn có chọn hài hoà hay không, tệp tải xuống được xuất hiện nay bao gồm một tệp attrs.xml chứa tên vai trò màu cho từng màu tuỳ chỉnh.
<resources>
<attr name="colorCustom1" format="color" />
<attr name="colorOnCustom1" format="color" />
<attr name="colorCustom1Container" format="color" />
<attr name="colorOnCustom1Container" format="color" />
<attr name="harmonizeCustom1" format="boolean" />
<attr name="colorCustom2" format="color" />
<attr name="colorOnCustom2" format="color" />
<attr name="colorCustom2Container" format="color" />
<attr name="colorOnCustom2Container" format="color" />
<attr name="harmonizeCustom2" format="boolean" />
</resources>
Trong themes.xml, chúng ta đã tạo 4 vai trò màu cho mỗi màu tuỳ chỉnh (color<name>, colorOn<name>, color<name>Container, and colorOn<nameContainer>). Các thuộc tính harmonize<name> phản ánh việc nhà phát triển có chọn lựa chọn này trong Material Theme Builder hay không. Thao tác này sẽ không thay đổi màu trong giao diện cốt lõi.
<resources>
<style name="AppTheme" parent="Theme.Material3.Light.NoActionBar">
<!--- Normal theme attributes ... -->
<item name="colorCustom1">#006876</item>
<item name="colorOnCustom1">#ffffff</item>
<item name="colorCustom1Container">#97f0ff</item>
<item name="colorOnCustom1Container">#001f24</item>
<item name="harmonizeCustom1">false</item>
<item name="colorCustom2">#016e00</item>
<item name="colorOnCustom2">#ffffff</item>
<item name="colorCustom2Container">#78ff57</item>
<item name="colorOnCustom2Container">#002200</item>
<item name="harmonizeCustom2">false</item>
</style>
</resources>
Trong tệp colors.xml, các màu gốc dùng để tạo các vai trò màu nêu trên được chỉ định cùng với các giá trị boolean cho biết bảng màu có được chuyển hay không.
<resources>
<!-- other colors used in theme -->
<color name="custom1">#1AC9E0</color>
<color name="custom2">#32D312</color>
</resources>
4. Khám phá màu tuỳ chỉnh
Khi phóng to bảng điều khiển bên của Material Theme Builder, chúng ta có thể thấy rằng việc thêm một màu tuỳ chỉnh sẽ hiển thị một bảng điều khiển với 4 vai trò màu chính trong bảng màu sáng và tối.

Trong Khung hiển thị Android, chúng tôi xuất những màu này cho bạn nhưng ở chế độ nền, chúng có thể được biểu thị bằng một thực thể của đối tượng ColorRoles.
Lớp ColorRoles có 4 thuộc tính: accent, onAccent, accentContainer và onAccentContainer. Đây là các thuộc tính biểu thị số nguyên của 4 màu thập lục phân.
public final class ColorRoles {
private final int accent;
private final int onAccent;
private final int accentContainer;
private final int onAccentContainer;
// truncated code
}
Bạn có thể truy xuất 4 vai trò màu chính từ một màu tuỳ ý tại thời gian chạy bằng cách sử dụng getColorRoles trong lớp MaterialColors có tên là getColorRoles. Lớp này cho phép bạn tạo bộ gồm 4 vai trò màu đó tại thời gian chạy khi có một màu gốc cụ thể.
public static ColorRoles getColorRoles(
@NonNull Context context,
@ColorInt int color
) { /* implementation */ }
Tương tự, các giá trị đầu ra là giá trị màu thực tế, KHÔNG phải con trỏ đến các giá trị đó.**
5. Tính năng hài hoà màu sắc là gì?
Hệ thống màu mới của Material được thiết kế theo thuật toán, tạo ra màu chính, màu phụ, màu thứ ba và màu trung tính từ một màu gốc nhất định. Một điểm đáng lo ngại mà chúng tôi nhận được rất nhiều khi trao đổi với các đối tác nội bộ và bên ngoài là cách áp dụng màu động trong khi vẫn kiểm soát được một số màu.
Những màu sắc này thường mang một ý nghĩa hoặc bối cảnh cụ thể trong ứng dụng. Nếu bị thay thế bằng một màu ngẫu nhiên, ý nghĩa hoặc bối cảnh đó sẽ bị mất. Nếu không, những màu này có thể trông khó chịu hoặc không phù hợp.
Màu sắc trong Material You được mô tả bằng sắc độ, độ bão hoà và tông màu. Sắc độ của một màu liên quan đến nhận thức của một người về màu đó như một thành viên của một dải màu so với một dải màu khác. Sắc độ mô tả độ sáng hoặc độ tối của màu sắc, còn độ bão hoà là cường độ của màu sắc. Nhận thức về sắc độ có thể bị ảnh hưởng bởi các yếu tố văn hoá và ngôn ngữ, chẳng hạn như việc thiếu từ chỉ màu xanh dương trong các nền văn hoá cổ đại, thay vào đó, màu xanh dương được coi là cùng họ với màu xanh lục.
Một sắc độ cụ thể có thể được coi là ấm hoặc lạnh tuỳ thuộc vào vị trí của sắc độ đó trên phổ sắc độ. Việc chuyển sang tông màu đỏ, cam hoặc vàng thường được coi là làm cho màu sắc ấm hơn, còn việc chuyển sang tông màu xanh dương, xanh lục hoặc tím được cho là làm cho màu sắc lạnh hơn. Ngay cả trong các màu ấm hoặc lạnh, bạn sẽ có các tông màu ấm và lạnh. Ở bên dưới, màu vàng "ấm" có sắc cam hơn, trong khi màu vàng "lạnh" có sắc xanh lục hơn. 
Thuật toán hài hoà màu sắc sẽ kiểm tra sắc độ của màu không được dịch chuyển và màu mà màu đó cần hài hoà để xác định một sắc độ hài hoà nhưng không làm thay đổi các đặc tính màu cơ bản. Trong hình ảnh đầu tiên, có ít sắc độ hài hoà hơn của màu xanh lục, vàng và cam được vẽ trên một quang phổ. Trong hình ảnh tiếp theo, màu xanh lục và màu cam đã được hài hoà với sắc vàng. Màu xanh lục mới ấm hơn và màu cam mới lạnh hơn.
Sắc độ đã thay đổi trên màu cam và màu xanh lục nhưng bạn vẫn có thể nhận biết được màu cam và màu xanh lục.

Nếu bạn muốn tìm hiểu thêm về một số quyết định thiết kế, khám phá và cân nhắc, thì các đồng nghiệp của tôi là Ayan Daniels và Andrew Lu đã viết một bài đăng trên blog đi sâu hơn một chút so với phần này.
6. Điều chỉnh màu sắc theo cách thủ công
Để hài hoà một tông màu, có 2 hàm trong MaterialColors, harmonize và harmonizeWithPrimary.
harmonizeWithPrimary sử dụng Context làm phương tiện để truy cập vào giao diện hiện tại và sau đó là màu chính của giao diện đó.
@ColorInt
public static int harmonizeWithPrimary(@NonNull Context context, @ColorInt int colorToHarmonize) {
return harmonize(
colorToHarmonize,
getColor(context, R.attr.colorPrimary, MaterialColors.class.getCanonicalName()));
}
@ColorInt
public static int harmonize(@ColorInt int colorToHarmonize, @ColorInt int colorToHarmonizeWith) {
return Blend.harmonize(colorToHarmonize, colorToHarmonizeWith);
}
Để truy xuất bộ gồm 4 âm, chúng ta cần làm thêm một chút.
Vì đã có màu nguồn, chúng ta cần:
- xác định xem có nên hài hoà hay không,
- xác định xem chúng ta có đang ở chế độ tối hay không và
- trả về đối tượng
ColorRolesđã được điều chỉnh hoặc chưa được điều chỉnh.
Xác định xem có nên điều chỉnh cho phù hợp hay không
Trong giao diện được xuất từ Material Theme Builder, chúng tôi đã thêm các thuộc tính boolean bằng cách sử dụng quy tắc đặt tên harmonize<Color>. Dưới đây là một hàm tiện ích để truy cập vào giá trị đó.
Nếu tìm thấy, hàm này sẽ trả về giá trị của màu đó; nếu không, hàm này sẽ xác định rằng không nên hài hoà màu.
// Looks for associated harmonization attribute based on the color id
// custom1 ===> harmonizeCustom1
fun shouldHarmonize(context: Context, colorId: Int): Boolean {
val root = context.resources.getResourceEntryName(colorId)
val harmonizedId = "harmonize" + root.replaceFirstChar { it.uppercaseChar() }
val identifier = context.resources.getIdentifier(
harmonizedId, "bool", context.packageName)
return if (identifier != 0) context.resources.getBoolean(identifier) else false
}
Tạo đối tượng ColorRoles được điều chỉnh
retrieveHarmonizedColorRoles là một hàm tiện lợi khác kết hợp tất cả các bước nêu trên: truy xuất giá trị màu cho một tài nguyên được đặt tên, cố gắng phân giải một thuộc tính boolean để xác định sự hài hoà và trả về một đối tượng ColorRoles bắt nguồn từ màu gốc hoặc màu pha trộn (cho trước bảng phối màu sáng hoặc tối).
fun retrieveHarmonizedColorRoles(
view: View,
customId: Int,
isLight: Boolean
): ColorRoles {
val context = view.context
val custom = context.getColor(customId);
val shouldHarmonize = shouldHarmonize(context, customId)
if (shouldHarmonize) {
val blended = MaterialColors.harmonizeWithPrimary(context, custom)
return MaterialColors.getColorRoles(blended, isLight)
} else return MaterialColors.getColorRoles(custom, isLight)
}
7. Đang điền thông tin thẻ đi phương tiện công cộng
Như đã đề cập trước đó, chúng ta sẽ sử dụng RecyclerView và bộ chuyển đổi để điền và tô màu bộ sưu tập thẻ phương tiện công cộng.

Lưu trữ dữ liệu phương tiện công cộng
Để lưu trữ dữ liệu văn bản và thông tin màu sắc cho thẻ phương tiện công cộng, chúng tôi đang sử dụng một lớp dữ liệu lưu trữ tên, đích đến và mã nhận dạng tài nguyên màu.
data class TransitInfo(val name: String, val destination: String, val colorId: Int)
/* truncated code */
val transitItems = listOf(
TransitInfo("53", "Irvine", R.color.custom1),
TransitInfo("153", "Brea", R.color.custom1),
TransitInfo("Orange County Line", "Oceanside", R.color.custom2),
TransitInfo("Pacific Surfliner", "San Diego", R.color.custom2)
)
Chúng ta sẽ sử dụng màu này để tạo ra các tông màu cần thiết theo thời gian thực.
Bạn có thể điều chỉnh cho phù hợp trong thời gian chạy bằng hàm onBindViewHolder sau đây.
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val transitInfo = list.get(position)
val color = transitInfo.colorId
if (!colorRolesMap.containsKey(color)) {
val roles = retrieveHarmonizedColorRoles(
holder.itemView, color,
!isNightMode(holder.itemView.context)
)
colorRolesMap.put(color, roles)
}
val card = holder.card
holder.transitName.text = transitInfo.name
holder.transitDestination.text = transitInfo.destination
val colorRoles = colorRolesMap.get(color)
if (colorRoles != null) {
holder.card.setCardBackgroundColor(colorRoles.accentContainer)
holder.transitName.setTextColor(colorRoles.onAccentContainer)
holder.transitDestination.setTextColor(colorRoles.onAccentContainer)
}
}
8. Tự động phối màu
Thay vì tự xử lý việc điều chỉnh, bạn có thể để chúng tôi xử lý cho bạn. HarmonizedColorOptions là một lớp trình tạo cho phép bạn chỉ định nhiều nội dung mà chúng ta đã thực hiện theo cách thủ công cho đến nay.
Sau khi truy xuất ngữ cảnh hiện tại để có quyền truy cập vào bảng phối màu động hiện tại, bạn cần chỉ định các màu cơ bản mà bạn muốn hài hoà và tạo một ngữ cảnh mới dựa trên đối tượng HarmonizedColorOptions đó và ngữ cảnh đã bật DynamicColors.
Nếu bạn không muốn hài hoà một màu, chỉ cần không đưa màu đó vào harmonizedOptions.
val newContext = DynamicColors.wrapContextIfAvailable(requireContext())
val harmonizedOptions = HarmonizedColorsOptions.Builder()
.setColorResourceIds(intArrayOf(R.color.custom1, R.color.custom2))
.build();
harmonizedContext =
HarmonizedColors.wrapContextIfAvailable(dynamicColorsContext, harmonizedOptions)
Khi đã xử lý màu cơ bản hài hoà, bạn có thể cập nhật onBindViewHolder để chỉ cần gọi MaterialColors.getColorRoles và chỉ định xem các vai trò được trả về sẽ là sáng hay tối.
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
/*...*/
val color = transitInfo.colorId
if (!colorRolesMap.containsKey(color)) {
val roles = MaterialColors.getColorRoles(context.getColor(color), !isNightMode(context))
)
colorRolesMap.put(color, roles)
}
val card = holder.card
holder.transitName.text = transitInfo.name
holder.transitDestination.text = transitInfo.destination
val colorRoles = colorRolesMap.get(color)
if (colorRoles != null) {
holder.card.setCardBackgroundColor(colorRoles.accentContainer)
holder.transitName.setTextColor(colorRoles.onAccentContainer)
holder.transitDestination.setTextColor(colorRoles.onAccentContainer)
}
}
9. Tự động điều chỉnh các thuộc tính giao diện cho phù hợp
Các phương thức được trình bày cho đến nay đều dựa vào việc truy xuất vai trò màu từ một màu riêng lẻ. Điều này rất phù hợp để cho thấy rằng một âm điệu thực đang được tạo ra nhưng không thực tế đối với hầu hết các ứng dụng hiện có. Bạn có thể sẽ không lấy trực tiếp một màu mà thay vào đó sử dụng một thuộc tính giao diện hiện có.
Trước đó trong lớp học lập trình này, chúng ta đã nói về việc xuất các thuộc tính giao diện.
<resources>
<style name="AppTheme" parent="Theme.Material3.Light.NoActionBar">
<!--- Normal theme attributes ... -->
<item name="colorCustom1">#006876</item>
<item name="colorOnCustom1">#ffffff</item>
<item name="colorCustom1Container">#97f0ff</item>
<item name="colorOnCustom1Container">#001f24</item>
<item name="harmonizeCustom1">false</item>
<item name="colorCustom2">#016e00</item>
<item name="colorOnCustom2">#ffffff</item>
<item name="colorCustom2Container">#78ff57</item>
<item name="colorOnCustom2Container">#002200</item>
<item name="harmonizeCustom2">false</item>
</style>
</resources>
Tương tự như phương thức tự động đầu tiên, chúng ta có thể cung cấp các giá trị cho HarmonizedColorOptions và sử dụng HarmonizedColors để truy xuất một Ngữ cảnh có các màu hài hoà. Có một điểm khác biệt chính giữa hai phương thức này. Ngoài ra, chúng ta cần cung cấp một lớp phủ giao diện chứa các trường cần được điều chỉnh.
val dynamicColorsContext = DynamicColors.wrapContextIfAvailable(requireContext())
// Harmonizing individual attributes
val harmonizedColorAttributes = HarmonizedColorAttributes.create(
intArrayOf(
R.attr.colorCustom1,
R.attr.colorOnCustom1,
R.attr.colorCustom1Container,
R.attr.colorOnCustom1Container,
R.attr.colorCustom2,
R.attr.colorOnCustom2,
R.attr.colorCustom2Container,
R.attr.colorOnCustom2Container
), R.style.AppTheme_Overlay
)
val harmonizedOptions =
HarmonizedColorsOptions.Builder().setColorAttributes(harmonizedColorAttributes).build()
val harmonizedContext =
HarmonizedColors.wrapContextIfAvailable(dynamicColorsContext, harmonizedOptions)
Bộ chuyển đổi của bạn sẽ sử dụng ngữ cảnh hài hoà. Các giá trị trong lớp phủ giao diện phải tham chiếu đến biến thể sáng hoặc tối không hài hoà.
<style name="AppTheme.Overlay" parent="AppTheme">
<item name="colorCustom1">@color/harmonized_colorCustom1</item>
<item name="colorOnCustom1">@color/harmonized_colorOnCustom1</item>
<item name="colorCustom1Container">@color/harmonized_colorCustom1Container</item>
<item name="colorOnCustom1Container">@color/harmonized_colorOnCustom1Container</item>
<item name="colorCustom2">@color/harmonized_colorCustom2</item>
<item name="colorOnCustom2">@color/harmonized_colorOnCustom2</item>
<item name="colorCustom2Container">@color/harmonized_colorCustom2Container</item>
<item name="colorOnCustom2Container">@color/harmonized_colorOnCustom2Container</item>
</style>
Trong tệp bố cục XML, chúng ta có thể sử dụng các thuộc tính hài hoà đó như bình thường.
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
style="?attr/materialCardViewFilledStyle"
android:id="@+id/card"
android:layout_width="80dp"
android:layout_height="100dp"
android:layout_marginStart="8dp"
app:cardBackgroundColor="?attr/colorCustom1Container"
>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp">
<TextView
android:id="@+id/transitName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="28sp"
android:textStyle="bold"
android:textColor="?attr/colorOnCustom1Container"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/transitDestination"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:textColor="?attr/colorOnCustom1Container"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
10. Mã nguồn
package com.example.voyagi.harmonization.ui.dashboard
import android.content.Context
import android.content.res.Configuration
import android.graphics.Typeface
import android.os.Bundle
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.voyagi.harmonization.R
import com.example.voyagi.harmonization.databinding.FragmentDashboardBinding
import com.example.voyagi.harmonization.ui.home.TransitCardAdapter
import com.example.voyagi.harmonization.ui.home.TransitInfo
import com.google.android.material.card.MaterialCardView
import com.google.android.material.color.ColorRoles
import com.google.android.material.color.DynamicColors
import com.google.android.material.color.HarmonizedColorAttributes
import com.google.android.material.color.HarmonizedColors
import com.google.android.material.color.HarmonizedColorsOptions
import com.google.android.material.color.MaterialColors
class DashboardFragment : Fragment() {
enum class TransitMode { BUS, TRAIN }
data class TransitInfo2(val name: String, val destination: String, val mode: TransitMode)
private lateinit var dashboardViewModel: DashboardViewModel
private var _binding: FragmentDashboardBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
dashboardViewModel =
ViewModelProvider(this).get(DashboardViewModel::class.java)
_binding = FragmentDashboardBinding.inflate(inflater, container, false)
val root: View = binding.root
val recyclerView = binding.recyclerView
val transitItems = listOf(
TransitInfo2("53", "Irvine", TransitMode.BUS),
TransitInfo2("153", "Brea", TransitMode.BUS),
TransitInfo2("Orange County Line", "Oceanside", TransitMode.TRAIN),
TransitInfo2("Pacific Surfliner", "San Diego", TransitMode.TRAIN)
)
val dynamicColorsContext = DynamicColors.wrapContextIfAvailable(requireContext())
// Harmonizing individual attributes
val harmonizedColorAttributes = HarmonizedColorAttributes.create(
intArrayOf(
R.attr.colorCustom1,
R.attr.colorOnCustom1,
R.attr.colorCustom1Container,
R.attr.colorOnCustom1Container,
R.attr.colorCustom2,
R.attr.colorOnCustom2,
R.attr.colorCustom2Container,
R.attr.colorOnCustom2Container
), R.style.AppTheme_Overlay
)
val harmonizedOptions =
HarmonizedColorsOptions.Builder().setColorAttributes(harmonizedColorAttributes).build()
val harmonizedContext =
HarmonizedColors.wrapContextIfAvailable(dynamicColorsContext, harmonizedOptions)
val adapter = TransitCardAdapterAttr(transitItems, harmonizedContext)
recyclerView.adapter = adapter
recyclerView.layoutManager =
LinearLayoutManager(harmonizedContext, RecyclerView.HORIZONTAL, false)
return root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
class TransitCardAdapterAttr(val list: List<DashboardFragment.TransitInfo2>, context: Context) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
val colorRolesMap = mutableMapOf<Int, ColorRoles>()
private var harmonizedContext: Context? = context
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): RecyclerView.ViewHolder {
return if (viewType == DashboardFragment.TransitMode.BUS.ordinal) {
BusViewHolder(LayoutInflater.from(harmonizedContext).inflate(R.layout.transit_item_bus, parent, false))
} else TrainViewHolder(LayoutInflater.from(harmonizedContext).inflate(R.layout.transit_item_train, parent, false))
}
override fun getItemCount(): Int {
return list.size
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val item = list[position]
if (item.mode.ordinal == DashboardFragment.TransitMode.BUS.ordinal) {
(holder as BusViewHolder).bind(item)
(holder as TransitBindable).adjustNameLength()
} else {
(holder as TrainViewHolder).bind(item)
(holder as TransitBindable).adjustNameLength()
}
}
override fun getItemViewType(position: Int): Int {
return list[position].mode.ordinal
}
interface TransitBindable {
val card: MaterialCardView
var transitName: TextView
var transitDestination: TextView
fun bind(item: DashboardFragment.TransitInfo2) {
transitName.text = item.name
transitDestination.text = item.destination
}
fun Float.toDp(context: Context) =
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
this,
context.resources.displayMetrics
)
fun adjustNameLength(){
if (transitName.length() > 4) {
val layoutParams = card.layoutParams
layoutParams.width = 100f.toDp(card.context).toInt()
card.layoutParams = layoutParams
transitName.setTypeface(Typeface.DEFAULT_BOLD);
transitName.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16.0f)
}
}
}
inner class BusViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), TransitBindable {
override val card: MaterialCardView = itemView.findViewById(R.id.card)
override var transitName: TextView = itemView.findViewById(R.id.transitName)
override var transitDestination: TextView = itemView.findViewById(R.id.transitDestination)
}
inner class TrainViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), TransitBindable {
override val card: MaterialCardView = itemView.findViewById(R.id.card)
override var transitName: TextView = itemView.findViewById(R.id.transitName)
override var transitDestination: TextView = itemView.findViewById(R.id.transitDestination)
}
}
11. Ví dụ về giao diện người dùng
Chủ đề mặc định và màu tuỳ chỉnh không có sự hài hoà

Màu tuỳ chỉnh hài hoà


12. Tóm tắt
Trong lớp học lập trình này, bạn đã tìm hiểu:
- Kiến thức cơ bản về thuật toán hài hoà màu sắc của chúng tôi
- Cách tạo vai trò màu từ một màu đã thấy.
- Cách điều chỉnh màu sắc một cách chọn lọc trong giao diện người dùng.
- Cách điều chỉnh một nhóm thuộc tính trong một giao diện.
Nếu bạn có thắc mắc, cứ thoải mái hỏi chúng tôi bất cứ lúc nào qua @MaterialDesign trên X.
Hãy theo dõi để biết xem nội dung và hướng dẫn về thiết kế trên youtube.com/MaterialDesign