diff --git a/9_FEAT_UI.md b/9_FEAT_UI.md
new file mode 100644
index 0000000..4c4620a
--- /dev/null
+++ b/9_FEAT_UI.md
@@ -0,0 +1,214 @@
+# 🎨 KẾ HOẠCH ĐIỀU CHỈNH: BẢNG SLIDERS FLOATING & HIỂN THỊ TRANSPARENT FRAME
+
+Mục tiêu: Đưa bảng chỉnh sửa thành một khối mờ floating nằm gọn bên trong khung hình camera, cho phép cuộn dọc nội dung sliders. Đồng thời, cấu trúc lại hệ thống hiển thị để phần trong suốt (Transparent) của file Frame PNG lọt lòng đúng luồng camera.
+
+---
+
+## 📐 1. Cấu Trúc Lại Phân Tầng Giao Diện (`activity_main.xml`)
+
+Để bảng điều khiển nằm lọt vào bên trong phần hình ảnh, chúng ta phải đưa nó vào làm con trực tiếp của `FrameLayout` quản lý camera (`cameraContainer`). Thứ tự khai báo trong XML sẽ quyết định tầng hiển thị (Z-index):
+
+```
+[FrameLayout: cameraContainer] (Tỉ lệ 3:4)
+ ├── Lớp 1 (Đáy): PreviewView (Luồng camera gốc từ phần cứng)
+ ├── Lớp 2 (Giữa): ImageView (Khung ảnh PNG trong suốt)
+ └── Lớp 3 (Trên cùng): CardView / ScrollView (Bảng Sliders mờ floating)
+
+```
+
+### 💻 Đoạn mã XML chỉnh sửa chi tiết:
+
+```xml
+
+
+
+
+
+
+
+
+
+
+
+ app:cardCornerRadius="12dp"
+ app:cardElevation="8dp"
+ android:visibility="gone"
+ app:strokeWidth="0dp"
+ android:backgroundTint="#A6000000">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+---
+
+## 💻 2. Kịch Bản Xử Lý Kỹ Thuật (Kotlin)
+
+### 🔹 2.1. Hiện thực hóa việc hiện thị Frame suốt (Transparent Layer)
+
+Để luồng camera hiện lên ở phần ruột trong suốt của khung hình:
+
+1. Các file asset khung ảnh (ví dụ: `instaxframe.png`) thiết kế trên phần mềm đồ họa phải được đục lỗ rỗng (Alpha channel = 0) ở khu vực hiển thị ảnh chụp.
+2. Trong Android, khi người dùng chọn mẫu từ Timeline, ta nạp trực tiếp bằng luồng luân chuyển nhẹ:
+```kotlin
+val bitmap = BitmapFactory.decodeStream(assets.open("frames/instaxframe.png"))
+binding.imgFrameOverlay.setImageBitmap(bitmap)
+
+```
+
+
+Do cơ chế xếp chồng tầng của `FrameLayout`, luồng CameraX hoạt động ở `viewFinder` (Lớp 1) sẽ tự động xuyên thấu và hiển thị mượt mà qua các vùng trống của `imgFrameOverlay` (Lớp 2).
+
+### 🔹 2.2. Logic cập nhật Text hiển thị chỉ số thời gian thực (Real-time Value Indicator)
+
+Bám sát thiết kế hiển thị số ở nhãn text (ví dụ: `Vibrance (...): -0.23`), ta cập nhật chuỗi text liên tục khi con trỏ di chuyển:
+
+```kotlin
+binding.sliderVibrance.addOnChangeListener { slider, value, fromUser ->
+ // 1. Cập nhật chữ hiển thị giá trị số thực tế
+ binding.tvLabelVibrance.text = String.format("Vibrance (Bão hòa thông minh): %.2f", value)
+
+ // 2. Cập nhật Object tạm thời
+ editingPreset?.basic?.vibrance = value
+
+ // 3. Đẩy sang GPU render lập tức lên ViewFinder
+ applyLiveFilterToCamera(editingPreset)
+}
+
+```
+
+---
+
+## 📅 3. Các Đầu Việc Cần Triển Khai (Checklist)
+
+* [ ] **Kiểm tra độ trong suốt Asset:** Đảm bảo tất cả các file khung ảnh trong thư mục `assets/frames/` là định dạng **PNG-24** hỗ trợ Transparent hoàn toàn.
+* [ ] **Cập nhật hiệu ứng hiển thị Card:** Khi mở bảng sliders, áp dụng hiệu ứng phóng to nhẹ từ tâm (`ScaleAnimation`) thay vì trượt từ dưới lên để phù hợp hơn với kiểu giao diện hộp Floating Card cố định.
+* [ ] **Tối ưu hóa ScrollView:** Cài đặt thuộc tính ngăn chặn xung đột vuốt (`Touch Interception`) giữa thanh cuộn của `ScrollView` và các con trỏ trượt của `Slider Mechanism` của Material Component, giúp việc kéo thông số không bị trượt nhầm làm cuộn cả bảng.
diff --git a/android-project/app/src/main/java/com/photobooth/app/MainActivity.kt b/android-project/app/src/main/java/com/photobooth/app/MainActivity.kt
index ab2a7ee..99520c3 100644
--- a/android-project/app/src/main/java/com/photobooth/app/MainActivity.kt
+++ b/android-project/app/src/main/java/com/photobooth/app/MainActivity.kt
@@ -311,21 +311,21 @@ class MainActivity : AppCompatActivity() {
binding.sliderVibrance.addOnChangeListener { _, value, _ ->
editingPreset?.let { preset ->
preset.basic.vibrance = value
- binding.lblVibrance.text = "Vibrance (Bão hòa thông minh): ${String.format("%.2f", value)}"
+ binding.tvLabelVibrance.text = "Vibrance (Bão hòa thông minh): ${String.format("%.2f", value)}"
applyPresetFilter(preset)
}
}
binding.sliderClarity.addOnChangeListener { _, value, _ ->
editingPreset?.let { preset ->
preset.advanced.clarity = value
- binding.lblClarity.text = "Clarity (Độ rõ nét vùng trung tính): ${String.format("%.2f", value)}"
+ binding.tvLabelClarity.text = "Clarity (Độ rõ nét vùng trung tính): ${String.format("%.2f", value)}"
applyPresetFilter(preset)
}
}
binding.sliderDehaze.addOnChangeListener { _, value, _ ->
editingPreset?.let { preset ->
preset.advanced.dehaze = value
- binding.lblDehaze.text = "Dehaze (Độ trong suốt / Sương mù): ${String.format("%.2f", value)}"
+ binding.tvLabelDehaze.text = "Dehaze (Độ trong suốt / Sương mù): ${String.format("%.2f", value)}"
applyPresetFilter(preset)
}
}
@@ -438,30 +438,34 @@ class MainActivity : AppCompatActivity() {
binding.sliderDehaze.value = editingPreset!!.advanced.dehaze
// Update text labels
- binding.lblVibrance.text = "Vibrance (Bão hòa thông minh): ${String.format("%.2f", editingPreset!!.basic.vibrance)}"
- binding.lblClarity.text = "Clarity (Độ rõ nét vùng trung tính): ${String.format("%.2f", editingPreset!!.advanced.clarity)}"
- binding.lblDehaze.text = "Dehaze (Độ trong suốt / Sương mù): ${String.format("%.2f", editingPreset!!.advanced.dehaze)}"
+ binding.tvLabelVibrance.text = "Vibrance (Bão hòa thông minh): ${String.format("%.2f", editingPreset!!.basic.vibrance)}"
+ binding.tvLabelClarity.text = "Clarity (Độ rõ nét vùng trung tính): ${String.format("%.2f", editingPreset!!.advanced.clarity)}"
+ binding.tvLabelDehaze.text = "Dehaze (Độ trong suốt / Sương mù): ${String.format("%.2f", editingPreset!!.advanced.dehaze)}"
- // Slide up overlay panel animation
- binding.layoutAdvancedSliders.apply {
+ // Scale animation from center
+ binding.cardAdvancedSliders.apply {
visibility = android.view.View.VISIBLE
alpha = 0f
- translationY = 200f
+ scaleX = 0.8f
+ scaleY = 0.8f
animate()
.alpha(1f)
- .translationY(0f)
- .setDuration(300)
+ .scaleX(1f)
+ .scaleY(1f)
+ .setDuration(250)
+ .setInterpolator(android.view.animation.OvershootInterpolator(1.2f))
.start()
}
}
private fun closeAdvancedSliders() {
- binding.layoutAdvancedSliders.animate()
+ binding.cardAdvancedSliders.animate()
.alpha(0f)
- .translationY(200f)
- .setDuration(250)
+ .scaleX(0.8f)
+ .scaleY(0.8f)
+ .setDuration(200)
.withEndAction {
- binding.layoutAdvancedSliders.visibility = android.view.View.GONE
+ binding.cardAdvancedSliders.visibility = android.view.View.GONE
currentSelectedPreset?.let { applyPresetFilter(it) }
}
.start()
diff --git a/android-project/app/src/main/res/layout/activity_main.xml b/android-project/app/src/main/res/layout/activity_main.xml
index 402eab8..4a7245a 100644
--- a/android-project/app/src/main/res/layout/activity_main.xml
+++ b/android-project/app/src/main/res/layout/activity_main.xml
@@ -101,117 +101,135 @@
android:shadowRadius="10"
android:visibility="gone"
android:text="3" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-