Files
photobooth-app/11_FIX_PRESET_APPLY.md
T

11 KiB

🛠️ KẾ HOẠCH FIX BUG: CHUẨN HÓA LOGIC SLIDERS & FIX LỖI TRÀN KHUNG HIỂN THỊ

Mục tiêu:

  1. Khắc phục triệt để lỗi gán nhầm thông số xử lý ảnh (ví dụ: kéo Brightness lại đổi Temperature).
  2. Chuyển đổi dải giá trị từ số thập phân nhỏ sang thang đo nguyên trực quan từ -100 đến +100 để người dùng thấy rõ sự thay đổi.
  3. Cô lập nội dung cuộn bên trong phạm vi bo góc của MaterialCardView để không bị tràn đè lên khung viền của bảng.

📐 1. Khắc Phục Lỗi Giao Diện Tràn Khung Biển (activity_main.xml)

Nguyên nhân lỗi: Do tệp cấu hình cũ đặt thuộc tính padding trực tiếp lên thẻ cha hoặc thẻ LinearLayout bên trong mà không giới hạn vùng hiển thị đồ họa của thẻ cuộn ScrollView. Khi vuốt, các phần tử chữ và thanh kéo sẽ trượt đè qua các góc bo tròn của hộp Floating Card.

Giải pháp: Di chuyển toàn bộ cấu hình padding vào bên trong thẻ ScrollView và ép cấu trúc cắt vùng đồ họa thừa bằng thuộc tính android:clipToPadding="true".

<!-- LỚP 3: BẢNG SLIDERS FLOATING (Nằm gọn bên trong ảnh cameraContainer) -->
<com.google.android.material.card.MaterialCardView
    android:id="@+id/cardAdvancedSliders"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="24dp"
    app:cardCornerRadius="12dp"
    app:cardElevation="8dp"
    android:visibility="gone"
    app:strokeWidth="0dp"
    android:backgroundTint="#A6000000">

    <!-- FIX LỖI ĐÈ KHUNG: Đưa padding và clipToPadding vào ScrollView -->
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true"
        android:scrollbars="vertical"
        android:padding="16dp"
        android:clipToPadding="true"> <!-- Ép nội dung cuộn phải biến mất khi chạm tới viền bo góc -->

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <!-- Tiêu đề & Nút X (Đóng) -->
            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="16dp">
                <TextView
                    android:id="@+id/tvEditPresetName"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="#FFFFFF"
                    android:textStyle="bold"
                    android:textSize="15sp" />
                <ImageButton
                    android:id="@+id/btnCancelEdit"
                    android:layout_width="32dp"
                    android:layout_height="32dp"
                    android:layout_alignParentEnd="true"
                    android:background="@null"
                    android:src="@drawable/ic_close_white" />
            </RelativeLayout>

            <!-- ================= CẬP NHẬT DẢI GIÁ TRỊ TOÀN BỘ SLIDERS (-100 đến 100) ================= -->
            <!-- Brightness -->
            <TextView android:id="@+id/tvLabelBrightness" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp"/>
            <com.google.android.material.slider.Slider 
                android:id="@+id/sliderBrightness" 
                android:layout_width="match_parent" 
                android:layout_height="wrap_content" 
                android:valueFrom="-100.0" 
                android:valueTo="100.0" 
                android:value="0.0"
                app:thumbColor="#FF9800" 
                app:trackColorActive="#FF9800"/>

            <!-- Temperature -->
            <TextView android:id="@+id/tvLabelTemperature" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp"/>
            <com.google.android.material.slider.Slider 
                android:id="@+id/sliderTemperature" 
                android:layout_width="match_parent" 
                android:layout_height="wrap_content" 
                android:valueFrom="-100.0" 
                android:valueTo="100.0" 
                android:value="0.0"
                app:thumbColor="#FF9800" 
                app:trackColorActive="#FF9800"/>

            <!-- Áp dụng tương tự cho tất cả 12 thanh Sliders còn lại cấu hình valueFrom="-100.0" và valueTo="100.0" -->
            <!-- Đối với các thuộc tính đi từ 0 (Vignette, Grain, Faded Black) thì cấu hình valueFrom="0.0" và valueTo="100.0" -->

            <!-- NÚT LƯU THÀNH PRESET MỚI -->
            <Button
                android:id="@+id/btnSaveCustomPreset"
                android:layout_width="match_parent"
                android:layout_height="48dp"
                android:layout_marginTop="20dp"
                android:text="LƯU THÀNH PRESET MỚI"
                android:textColor="#FFFFFF"
                android:backgroundTint="#FF9800"
                android:textStyle="bold" />

        </LinearLayout>
    </ScrollView>
</com.google.android.material.card.MaterialCardView>


💻 2. Chuẩn Hóa Logic Gán Hàm Độc Lập & Quy Đổi Tỷ Lệ Giá Trị (Kotlin)

Giải pháp sửa lỗi gán sai thông số: Quy hoạch lại chính xác từng sự kiện lắng nghe để tránh việc copy-paste mã lệnh bị trùng lặp biến.

Giải pháp dải giá trị lớn: Người dùng nhìn thấy và kéo trượt trên thang đo nguyên trực quan từ -100 đến +100 (để thấy tác động lớn, rõ rệt trên UI). Tuy nhiên, trước khi gán dữ liệu vào hàm applyLiveFilterToCamera để GPU xử lý, ta phải chia cho 100 để đưa về dải số thực chuẩn hóa (-1.0 đến 1.0) giúp lõi phần cứng đồ họa không bị lỗi tính toán (vì Shader/CoreImage nhận giá trị số thực từ -1 đến 1).

private fun setupSliderListeners() {

    // FIX 1: Chuẩn hóa gán đúng hàm cho Brightness
    binding.sliderBrightness.addOnChangeListener { _, value, _ ->
        // Hiển thị số nguyên (-100 đến 100) lên nhãn văn bản cho người dùng thấy
        binding.tvLabelBrightness.text = String.format("Brightness (Độ sáng): %.0f", value)
        
        // Quy đổi tỉ lệ về dải số thực (-1.0 đến 1.0) để gán cho Object cấu hình
        val normalizedValue = value / 100f
        editingPreset?.basic?.brightness = normalizedValue
        
        // Đẩy giá trị ĐÚNG của Brightness sang bộ lọc GPU
        applyLiveFilterToCamera(editingPreset)
    }

    // FIX 2: Chuẩn hóa gán đúng hàm cho Temperature (Không bị nhầm với Brightness nữa)
    binding.sliderTemperature.addOnChangeListener { _, value, _ ->
        binding.tvLabelTemperature.text = String.format("Temperature (Nhiệt màu): %.0f", value)
        
        val normalizedValue = value / 100f
        editingPreset?.basic?.temperature = normalizedValue // Gán chuẩn xác cho biến temperature
        
        applyLiveFilterToCamera(editingPreset)
    }

    // FIX 3: Chuẩn hóa gán đúng hàm cho Vibrance
    binding.sliderVibrance.addOnChangeListener { _, value, _ ->
        binding.tvLabelVibrance.text = String.format("Vibrance (Bão hòa thông minh): %.0f", value)
        
        val normalizedValue = value / 100f
        editingPreset?.basic?.vibrance = normalizedValue // Gán chuẩn xác cho biến vibrance
        
        applyLiveFilterToCamera(editingPreset)
    }

    // FIX 4: Chuẩn hóa cho hiệu ứng nhiễu hạt Film Grain (Dải giá trị từ 0 đến 100)
    binding.sliderGrainAmount.addOnChangeListener { _, value, _ ->
        binding.tvLabelGrainAmount.text = String.format("Grain Amount (Mật độ hạt): %.0f%%", value)
        
        val normalizedValue = value / 100f
        editingPreset?.grain?.grainAmount = normalizedValue // Gán chuẩn xác cho biến grainAmount
        
        applyLiveFilterToCamera(editingPreset)
    }
    
    // Tiến hành rà soát lại tất cả các khối listener còn lại đảm bảo đúng biến nào - hàm đó!
}


🔄 3. Cơ Chế Nạp Lại Giá Trị Gốc Lên Sliders (Quy Đổi Ngược Nhân 100)

Khi người dùng mở bảng chỉnh sửa, chúng ta phải làm một phép tính ngược lại: Lấy giá trị số thực từ tệp JSON, nhân với 100 để hiển thị đúng vị trí con trỏ trên thanh kéo -100 -> +100.

fun openAdvancedSliders(selectedPreset: ColorPreset) {
    editingPreset = selectedPreset.deepCopy()

    binding.tvEditPresetName.text = "Chỉnh sửa: ${editingPreset!!.name}"

    // Đọc giá trị số thực từ JSON, nhân 100 để hiển thị dải số nguyên rõ rệt trên thanh trượt
    binding.sliderBrightness.value = editingPreset!!.basic.brightness * 100f
    binding.sliderTemperature.value = editingPreset!!.basic.temperature * 100f
    binding.sliderVibrance.value = editingPreset!!.basic.vibrance * 100f
    binding.sliderClarity.value = editingPreset!!.advanced.clarity * 100f
    binding.sliderDehaze.value = editingPreset!!.advanced.dehaze * 100f
    
    binding.sliderGrainAmount.value = editingPreset!!.grain.grainAmount * 100f
    binding.sliderGrainSize.value = editingPreset!!.grain.grainSize * 100f

    // Cập nhật text nhãn ban đầu
    binding.tvLabelBrightness.text = String.format("Brightness (Độ sáng): %.0f", binding.sliderBrightness.value)
    binding.tvLabelTemperature.text = String.format("Temperature (Nhiệt màu): %.0f", binding.sliderTemperature.value)

    setupSliderListeners()
    binding.cardAdvancedSliders.visibility = View.VISIBLE
}


📅 4. Các Đầu Việc Cần Hoàn Thành (Checklist)

  • Thực hiện thay đổi cấu trúc ScrollView trong tệp XML để cô lập hoàn toàn nội dung cuộn, giải quyết triệt để lỗi tràn khung hiển thị.
  • Tiến hành cập nhật lại toàn bộ 14 hàm lắng nghe sự kiện addOnChangeListener, rà soát kỹ lưỡng từng dòng lệnh để đảm bảo tính độc lập tuyệt đối giữa các thông số.
  • Chạy thử nghiệm kéo trượt mạnh về hai cực đầu mút (-100100) trên thiết bị thật để kiểm tra độ trễ phản hồi hình ảnh và biên độ thay đổi của màu ảnh xem đã đủ rõ rệt chưa.