Files
photobooth-app/14_FIX_PRESET_BUTTON.md
T

152 lines
8.3 KiB
Markdown

# 🎨 KẾ HOẠCH BỔ SUNG THUỘC TÍNH EDITOR & HIỂN THỊ TÊN ĐỘNG TRÊN TOP BAR
Mục tiêu: Đưa toàn bộ 14 thông số cấu hình vào `rvAttributeIcons` dạng danh sách cuộn ngang không giới hạn. Đồng bộ hóa việc chọn icon để hiển thị tên tiếng Việt tương ứng lên vùng văn bản phía trên ảnh và gán chính xác sự kiện vuốt trên ảnh cho thuộc tính đó.
---
## 📐 1. Cấu Trúc Lại XML Layout Để Thêm Dải Hiển Thị Tên (`activity_main.xml`)
Bám sát ảnh `image.png`, chúng ta cần thêm một `TextView` nằm ở dải đen phía trên bức ảnh để làm nhiệm vụ hiển thị tên nút đang chọn.
```xml
<!-- DẢI ĐEN PHÍA TRÊN ẢNH CAMERA (Như mô tả trong image.png) -->
<RelativeLayout
android:id="@+id/layoutEditorTitleBar"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="#000000"
app:layout_bottom_toTopOf="@id/cameraContainer"
app:layout_constraintTop_toBottomOf="@id/topBar">
<!-- Văn bản hiển thị động tên nút đang được chọn -->
<TextView
android:id="@+id/tvCurrentAttributeName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Brightness (Độ sáng)" <!-- Tên động hiển thị đây -->
android:textColor="#FF9800" <!-- Màu cam nổi bật hoặc trắng tùy chọn -->
android:textSize="14sp"
android:textStyle="bold" />
</RelativeLayout>
```
---
## 📁 2. Khai Báo Toàn Bộ Bản Đồ Thuộc Tính & Icons (Kotlin Model)
Để quản lý 14 nút thuộc tính một cách khoa học, chúng ta tạo một danh sách Model bao gồm: ID thuộc tính, Tên hiển thị (Tiếng Việt), và File định dạng Icon tương ứng trong `res/drawable`.
### 🔹 2.1. Định nghĩa Data Class cho nút thuộc tính
```kotlin
data class AttributeNode(
val type: EditAttribute,
val displayName: String,
val iconResId: Int
)
enum class EditAttribute {
BRIGHTNESS, CONTRAST, SATURATION, VIBRANCE, TEMPERATURE, TINT, // Nhóm 1: Cơ bản
CLARITY, DEHAZE, VIGNETTE_AMOUNT, // Nhóm 2: Nâng cao
FADED_BLACK_LEVEL, SHADOWS_TINT_R, SHADOWS_TINT_G, SHADOWS_TINT_B, // Nhóm 3: Tone Curve
GRAIN_AMOUNT, GRAIN_SIZE // Nhóm 4: Nhiễu hạt
}
```
### 🔹 2.2. Khởi tạo mảng dữ liệu 14 nút đầy đủ
```kotlin
val editorAttributes = listOf(
AttributeNode(EditAttribute.BRIGHTNESS, "Brightness (Độ sáng)", R.drawable.ic_attr_brightness),
AttributeNode(EditAttribute.CONTRAST, "Contrast (Tương phản)", R.drawable.ic_attr_contrast),
AttributeNode(EditAttribute.SATURATION, "Saturation (Bão hòa màu)", R.drawable.ic_attr_saturation),
AttributeNode(EditAttribute.VIBRANCE, "Vibrance (Bão hòa thông minh)", R.drawable.ic_attr_vibrance),
AttributeNode(EditAttribute.TEMPERATURE, "Temperature (Nhiệt màu)", R.drawable.ic_attr_temperature),
AttributeNode(EditAttribute.TINT, "Tint (Sắc độ hồng/xanh)", R.drawable.ic_attr_tint),
AttributeNode(EditAttribute.CLARITY, "Clarity (Độ rõ nét)", R.drawable.ic_attr_clarity),
AttributeNode(EditAttribute.DEHAZE, "Dehaze (Độ trong suốt / Sương mù)", R.drawable.ic_attr_dehaze),
AttributeNode(EditAttribute.VIGNETTE_AMOUNT, "Vignette Amount (Bo tối góc)", R.drawable.ic_attr_vignette),
AttributeNode(EditAttribute.FADED_BLACK_LEVEL, "Faded Black Level (Độ mờ vùng đen)", R.drawable.ic_attr_fade),
AttributeNode(EditAttribute.SHADOWS_TINT_R, "Shadows Tint R (Sắc Đỏ vùng tối)", R.drawable.ic_attr_tint_r),
AttributeNode(EditAttribute.SHADOWS_TINT_G, "Shadows Tint G (Sắc Lục vùng tối)", R.drawable.ic_attr_tint_g),
AttributeNode(EditAttribute.SHADOWS_TINT_B, "Shadows Tint B (Sắc Lam vùng tối)", R.drawable.ic_attr_tint_b),
AttributeNode(EditAttribute.GRAIN_AMOUNT, "Grain Amount (Mật độ hạt)", R.drawable.ic_attr_grain_amount),
AttributeNode(EditAttribute.GRAIN_SIZE, "Grain Size (Kích thước hạt)", R.drawable.ic_attr_grain_size)
)
```
---
## 💻 3. Xử Lý Logic Tương Tác & Đồng Bộ Trạng Thái Động
Khi gán danh sách này vào `rvAttributeIcons` dưới dạng LayoutManager cuộn ngang (`LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)`), người dùng có thể thoải mái vuốt lướt sang phải để tìm các nút bị ẩn như mũi tên chỉ dẫn trong ảnh `image.png`.
### 🔹 3.1. Sự kiện Click đổi màu và đồng bộ Tên lên Top Bar
Trong sự kiện bấm chọn một Icon thuộc tính bên trong Adapter của RecyclerView:
```kotlin
fun onAttributeSelected(selectedNode: AttributeNode) {
// 1. Lưu lại thuộc tính đang chỉnh sửa hiện tại
currentAttribute = selectedNode.type
// 2. Cập nhật văn bản lên dải đen phía trên ảnh đúng theo thiết kế image_44ee6b.jpg
binding.tvCurrentAttributeName.text = selectedNode.displayName
// 3. Đọc giá trị hiện tại của thuộc tính này trong file JSON tạm thời để nạp lên thước đo
val currentValue = getCurrentAttributeValue(selectedNode.type)
binding.tvRulerValue.text = String.format("%.0f", currentValue * 100f)
// 4. Ra lệnh cho Adapter vẽ lại (Notify) để đổi màu Icon được chọn thành MÀU CAM
attributeAdapter.updateSelection(selectedNode.type)
}
```
### 🔹 3.2. Cập nhật hàm điều phối Vuốt trên ảnh (Swipe Logic Matrix)
Hàm tiếp nhận giá trị `deltaX` khi người dùng vuốt trên màn hình ảnh giờ đây sẽ phân nhánh chính xác sang cả 14 thông số, không còn tình trạng gán nhầm hàm:
```kotlin
private fun updateAttributeBySwipe(newValue: Float) {
// newValue có dải giá trị từ -1.0f đến 1.0f sau khi đã chia cho 100
when (currentAttribute) {
EditAttribute.BRIGHTNESS -> editingPreset?.basic?.brightness = newValue
EditAttribute.CONTRAST -> editingPreset?.basic?.contrast = newValue
EditAttribute.SATURATION -> editingPreset?.basic?.saturation = newValue
EditAttribute.VIBRANCE -> editingPreset?.basic?.vibrance = newValue
EditAttribute.TEMPERATURE -> editingPreset?.basic?.temperature = newValue
EditAttribute.TINT -> editingPreset?.basic?.tint = newValue
EditAttribute.CLARITY -> editingPreset?.advanced?.clarity = newValue
EditAttribute.DEHAZE -> editingPreset?.advanced?.dehaze = newValue
EditAttribute.VIGNETTE_AMOUNT -> editingPreset?.advanced?.vignetteAmount = newValue.coerceIn(0f, 1f)
EditAttribute.FADED_BLACK_LEVEL -> editingPreset?.toneCurve?.fadedBlackLevel = newValue.coerceIn(0f, 1f)
EditAttribute.SHADOWS_TINT_R -> editingPreset?.toneCurve?.shadowsTintR = newValue.coerceIn(0f, 1f)
EditAttribute.SHADOWS_TINT_G -> editingPreset?.toneCurve?.shadowsTintG = newValue.coerceIn(0f, 1f)
EditAttribute.SHADOWS_TINT_B -> editingPreset?.toneCurve?.shadowsTintB = newValue.coerceIn(0f, 1f)
EditAttribute.GRAIN_AMOUNT -> editingPreset?.grain?.grainAmount = newValue.coerceIn(0f, 1f)
EditAttribute.GRAIN_SIZE -> editingPreset?.grain?.grainSize = newValue.coerceIn(0f, 1f)
}
// Luôn luôn gọi render cập nhật đồ họa thời gian thực lên Camera ngay lập tức
applyLiveFilterToCamera(editingPreset)
}
```
---
## 📅 4. Các Đầu Việc Cần Triển Khai (Checklist)
* [ ] **Chuẩn bị 14 cặp Icons tương ứng:** Tạo hoặc import các asset dạng vector đơn sắc trắng vào thư mục dự án trên Linux.
* [ ] **Cấu hình `clipToPadding="false"` cho RecyclerView:** Thiết lập thuộc tính này cho thanh icon để khi cuộn, các icon ở hai đầu rìa có khoảng không trượt mượt mà, không bị cắt cụt đột ngột.
* [ ] **Kiểm thử liên kết:** Đảm bảo khi mở màn hình Editor lên, mặc định app sẽ tự động giả lập click vào nút đầu tiên (Brightness), icon sáng cam và dải chữ phía trên hiển thị đúng chữ `"Brightness (Độ sáng)"` như ảnh `image.png`.