From bcbb8535580788a489bace4aa98707d8d4f84393 Mon Sep 17 00:00:00 2001 From: 3dtours Date: Sun, 5 Jul 2026 18:16:39 +0700 Subject: [PATCH] Feat: Complete 14 sliders integration, remove pen button and implement custom 3-second long press (10_FIX_SLIDER.md) --- 10_FIX_SLIDER.md | 221 ++++++++++++++++++ .../java/com/photobooth/app/MainActivity.kt | 195 ++++++++++++++-- .../java/com/photobooth/app/PresetAdapter.kt | 69 +++--- .../app/src/main/res/layout/activity_main.xml | 145 ++++++++---- .../app/src/main/res/layout/item_preset.xml | 12 - 5 files changed, 530 insertions(+), 112 deletions(-) create mode 100644 10_FIX_SLIDER.md diff --git a/10_FIX_SLIDER.md b/10_FIX_SLIDER.md new file mode 100644 index 0000000..9d000ef --- /dev/null +++ b/10_FIX_SLIDER.md @@ -0,0 +1,221 @@ +Dưới đây là kế hoạch hoàn thiện toàn bộ danh sách các thanh kéo (Sliders) trong bảng Floating Card mờ cho tệp cấu hình JSON đầy đủ của bạn. Chúng ta sẽ đưa tất cả 14 thông số chia theo đúng 4 nhóm cấu trúc của tệp cấu hình vào trong giao diện cuộn dọc của `ScrollView`. + +--- + +## 📐 1. Cấu Trúc Đầy Đủ Cho Layout XML (`activity_main.xml`) + +Thay thế phần nội dung bên trong `LinearLayout` của bảng Floating Card (nằm dưới tiêu đề) để bổ sung đầy đủ tất cả các thanh kéo, phân chia theo từng nhóm trực quan giúp người dùng dễ thao tác. + +```xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +--- + +## 💻 2. Logic Khởi Tạo & Đồng Bộ Giá Trị (Kotlin trong `MainActivity.kt`) + +Khi người dùng mở bảng chỉnh sửa cho preset, hàm `openAdvancedSliders` sẽ thực hiện gán các giá trị ban đầu từ tệp JSON lên các thanh Slider phần cứng đồng thời thiết lập lắng nghe cử chỉ: + +```kotlin +fun openAdvancedSliders(selectedPreset: ColorPreset) { + editingPreset = selectedPreset.deepCopy() // Nhân bản dữ liệu để tránh ghi đè bản gốc + + binding.tvEditPresetName.text = "Chỉnh sửa: ${editingPreset!!.name}" + + // 1. Nạp dữ liệu Nhóm Basic + binding.sliderBrightness.value = editingPreset!!.basic.brightness + binding.sliderContrast.value = editingPreset!!.basic.contrast + binding.sliderSaturation.value = editingPreset!!.basic.saturation + binding.sliderVibrance.value = editingPreset!!.basic.vibrance + binding.sliderTemperature.value = editingPreset!!.basic.temperature + binding.sliderTint.value = editingPreset!!.basic.tint + + // 2. Nạp dữ liệu Nhóm Advanced + binding.sliderClarity.value = editingPreset!!.advanced.clarity + binding.sliderDehaze.value = editingPreset!!.advanced.dehaze + binding.sliderVignetteAmount.value = editingPreset!!.advanced.vignetteAmount + + // 3. Nạp dữ liệu Nhóm Tone Curve Emulation + binding.sliderFadedBlack.value = editingPreset!!.toneCurve.fadedBlackLevel + binding.sliderShadowsTintR.value = editingPreset!!.toneCurve.shadowsTintR + binding.sliderShadowsTintG.value = editingPreset!!.toneCurve.shadowsTintG + binding.sliderShadowsTintB.value = editingPreset!!.toneCurve.shadowsTintB + + // 4. Nạp dữ liệu Nhóm Film Grain + binding.sliderGrainAmount.value = editingPreset!!.grain.grainAmount + binding.sliderGrainSize.value = editingPreset!!.grain.grainSize + + // Gọi hàm thiết lập sự kiện lắng nghe chuyển động cho toàn bộ Sliders + setupSliderListeners() + + // Hiển thị Card mờ floating lọt lòng màn hình camera như ảnh image_2be399.jpg + binding.cardAdvancedSliders.visibility = View.VISIBLE +} + +``` + +--- + +## 🔄 3. Cơ Chế Cập Nhật Giá Trị Thời Gian Thực (Real-time Pipeline) + +Viết một hàm tập trung gán sự kiện thay đổi (`addOnChangeListener`) cho tất cả các thanh kéo. Khi con trỏ dịch chuyển, chữ số nhãn text sẽ đổi ngay lập tức và đẩy cấu trúc Object mới sang GPU xử lý trực tiếp trên kính ngắm. + +```kotlin +private fun setupSliderListeners() { + + // Ví dụ gán cho Brightness + binding.sliderBrightness.addOnChangeListener { _, value, _ -> + binding.tvLabelBrightness.text = String.format("Brightness (Độ sáng): %.2f", value) + editingPreset?.basic?.brightness = value + applyLiveFilterToCamera(editingPreset) // Đẩy ma trận màu sang GPU render + } + + // Ví dụ gán cho Vibrance + binding.sliderVibrance.addOnChangeListener { _, value, _ -> + binding.tvLabelVibrance.text = String.format("Vibrance (Bão hòa thông minh): %.2f", value) + editingPreset?.basic?.vibrance = value + applyLiveFilterToCamera(editingPreset) + } + + // Ví dụ gán cho Clarity (Nhóm Advanced) + binding.sliderClarity.addOnChangeListener { _, value, _ -> + binding.tvLabelClarity.text = String.format("Clarity (Độ rõ nét vùng trung tính): %.2f", value) + editingPreset?.advanced?.clarity = value + applyLiveFilterToCamera(editingPreset) + } + + // Ví dụ gán cho Faded Black Level (Nhóm Tone Curve) + binding.sliderFadedBlack.addOnChangeListener { _, value, _ -> + binding.tvLabelFadedBlack.text = String.format("Faded Black Level (Độ mờ vùng đen): %.2f", value) + editingPreset?.toneCurve?.fadedBlackLevel = value + applyLiveFilterToCamera(editingPreset) + } + + // Ví dụ gán cho Grain Amount (Nhóm Film Grain) + binding.sliderGrainAmount.addOnChangeListener { _, value, _ -> + binding.tvLabelGrainAmount.text = String.format("Grain Amount (Mật độ hạt): %.2f", value) + editingPreset?.grain?.grainAmount = value + applyLiveFilterToCamera(editingPreset) + } + + // Thực hiện viết gán tương tự cho toàn bộ các thanh Sliders còn lại trong danh sách... +} + +``` + +Bảng cấu trúc 14 thông số này sẽ tạo ra khả năng tùy biến bộ lọc ảnh mạnh mẽ không thua kém gì các bộ công cụ chỉnh ảnh phim chuyên nghiệp như VSCO hay Lightroom Mobile. \ No newline at end of file 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 99520c3..16f43bd 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 @@ -136,19 +136,30 @@ class MainActivity : AppCompatActivity() { val contrast = extractJsonDoubleField(content, "contrast").toFloat() val saturation = extractJsonDoubleField(content, "saturation").toFloat() val vibrance = extractJsonDoubleField(content, "vibrance").toFloat() + val temperature = extractJsonDoubleField(content, "temperature").toFloat() + val tint = extractJsonDoubleField(content, "tint").toFloat() val clarity = extractJsonDoubleField(content, "clarity").toFloat() val dehaze = extractJsonDoubleField(content, "dehaze").toFloat() + val vignetteAmount = extractJsonDoubleField(content, "vignette_amount").toFloat() + + val fadedBlackLevel = extractJsonDoubleField(content, "faded_black_level").toFloat() + val shadowsTintR = extractJsonDoubleField(content, "shadows_tint_r").toFloat() + val shadowsTintG = extractJsonDoubleField(content, "shadows_tint_g").toFloat() + val shadowsTintB = extractJsonDoubleField(content, "shadows_tint_b").toFloat() + + val grainAmount = extractJsonDoubleField(content, "grain_amount").toFloat() + val grainSize = extractJsonDoubleField(content, "grain_size").toFloat() val preset = ColorPreset( id = id, name = name, themeCategory = category, isEditable = true, - basic = BasicAdjustments(brightness, contrast, saturation, vibrance, 0f, 0f), - advanced = AdvancedEffects(clarity, dehaze, 0f), - toneCurve = ToneCurveEmulation(0f, 0f, 0f, 0f), - grain = FilmGrain(0f, 0f) + basic = BasicAdjustments(brightness, contrast, saturation, vibrance, temperature, tint), + advanced = AdvancedEffects(clarity, dehaze, vignetteAmount), + toneCurve = ToneCurveEmulation(fadedBlackLevel, shadowsTintR, shadowsTintG, shadowsTintB), + grain = FilmGrain(grainAmount, grainSize) ) customList.add(preset) } catch (e: Exception) { @@ -213,11 +224,22 @@ class MainActivity : AppCompatActivity() { "instax_bw_cool" -> android.graphics.Color.argb(80, 128, 128, 128) "instax_cool_summer" -> android.graphics.Color.argb(45, 0, 188, 212) else -> { - // Map user configured RGB overlay incorporating Vibrance, Clarity, and Dehaze - val alpha = ((preset.basic.vibrance + 1.0f) * 45 + 15).toInt().coerceIn(10, 110) - val r = ((preset.advanced.clarity + 1.0f) * 115 + 20).toInt().coerceIn(0, 255) - val g = ((preset.basic.contrast + 1.0f) * 100 + 20).toInt().coerceIn(0, 255) - val b = ((preset.advanced.dehaze + 1.0f) * 95 + 15).toInt().coerceIn(0, 255) + // Sophisticated ARGB mapping to simulate film filters using adjustment parameters + // Vibrance and Saturation increase opacity of the filter color + val alpha = (((preset.basic.vibrance + preset.basic.saturation) / 2f + 1.0f) * 40 + 20).toInt().coerceIn(10, 120) + + // Temperature (+ is warm/red-yellow), Brightness, Clarity, and ShadowsTintR affect red + val tempWarmth = if (preset.basic.temperature > 0) preset.basic.temperature * 50f else 0f + val r = (((preset.advanced.clarity + preset.basic.brightness) / 2f + 1.0f) * 100 + 30 + tempWarmth + (preset.toneCurve.shadowsTintR * 30)).toInt().coerceIn(0, 255) + + // Contrast, Tint (- is green), and ShadowsTintG affect green + val tintGreen = if (preset.basic.tint < 0) -preset.basic.tint * 50f else 0f + val g = ((preset.basic.contrast + 1.0f) * 90 + 20 + tintGreen + (preset.toneCurve.shadowsTintG * 30)).toInt().coerceIn(0, 255) + + // Dehaze, cool Temperature (- is blue), and ShadowsTintB affect blue + val tempCoolness = if (preset.basic.temperature < 0) -preset.basic.temperature * 60f else 0f + val b = (((preset.advanced.dehaze + 1.0f) * 80) + 20 + tempCoolness + (preset.toneCurve.shadowsTintB * 30)).toInt().coerceIn(0, 255) + android.graphics.Color.argb(alpha, r, g, b) } } @@ -308,6 +330,27 @@ class MainActivity : AppCompatActivity() { } // Advanced Sliders listeners + binding.sliderBrightness.addOnChangeListener { _, value, _ -> + editingPreset?.let { preset -> + preset.basic.brightness = value + binding.tvLabelBrightness.text = "Brightness (Độ sáng): ${String.format("%.2f", value)}" + applyPresetFilter(preset) + } + } + binding.sliderContrast.addOnChangeListener { _, value, _ -> + editingPreset?.let { preset -> + preset.basic.contrast = value + binding.tvLabelContrast.text = "Contrast (Tương phản): ${String.format("%.2f", value)}" + applyPresetFilter(preset) + } + } + binding.sliderSaturation.addOnChangeListener { _, value, _ -> + editingPreset?.let { preset -> + preset.basic.saturation = value + binding.tvLabelSaturation.text = "Saturation (Bão hòa màu): ${String.format("%.2f", value)}" + applyPresetFilter(preset) + } + } binding.sliderVibrance.addOnChangeListener { _, value, _ -> editingPreset?.let { preset -> preset.basic.vibrance = value @@ -315,6 +358,20 @@ class MainActivity : AppCompatActivity() { applyPresetFilter(preset) } } + binding.sliderTemperature.addOnChangeListener { _, value, _ -> + editingPreset?.let { preset -> + preset.basic.temperature = value + binding.tvLabelTemperature.text = "Temperature (Nhiệt màu): ${String.format("%.2f", value)}" + applyPresetFilter(preset) + } + } + binding.sliderTint.addOnChangeListener { _, value, _ -> + editingPreset?.let { preset -> + preset.basic.tint = value + binding.tvLabelTint.text = "Tint (Sắc độ hồng/xanh): ${String.format("%.2f", value)}" + applyPresetFilter(preset) + } + } binding.sliderClarity.addOnChangeListener { _, value, _ -> editingPreset?.let { preset -> preset.advanced.clarity = value @@ -329,6 +386,55 @@ class MainActivity : AppCompatActivity() { applyPresetFilter(preset) } } + binding.sliderVignetteAmount.addOnChangeListener { _, value, _ -> + editingPreset?.let { preset -> + preset.advanced.vignetteAmount = value + binding.tvLabelVignetteAmount.text = "Vignette Amount (Bo tối góc): ${String.format("%.2f", value)}" + applyPresetFilter(preset) + } + } + binding.sliderFadedBlack.addOnChangeListener { _, value, _ -> + editingPreset?.let { preset -> + preset.toneCurve.fadedBlackLevel = value + binding.tvLabelFadedBlack.text = "Faded Black Level (Độ mờ vùng đen): ${String.format("%.2f", value)}" + applyPresetFilter(preset) + } + } + binding.sliderShadowsTintR.addOnChangeListener { _, value, _ -> + editingPreset?.let { preset -> + preset.toneCurve.shadowsTintR = value + binding.tvLabelShadowsTintR.text = "Shadows Tint R (Sắc Đỏ vùng tối): ${String.format("%.2f", value)}" + applyPresetFilter(preset) + } + } + binding.sliderShadowsTintG.addOnChangeListener { _, value, _ -> + editingPreset?.let { preset -> + preset.toneCurve.shadowsTintG = value + binding.tvLabelShadowsTintG.text = "Shadows Tint G (Sắc Lục vùng tối): ${String.format("%.2f", value)}" + applyPresetFilter(preset) + } + } + binding.sliderShadowsTintB.addOnChangeListener { _, value, _ -> + editingPreset?.let { preset -> + preset.toneCurve.shadowsTintB = value + binding.tvLabelShadowsTintB.text = "Shadows Tint B (Sắc Lam vùng tối): ${String.format("%.2f", value)}" + applyPresetFilter(preset) + } + } + binding.sliderGrainAmount.addOnChangeListener { _, value, _ -> + editingPreset?.let { preset -> + preset.grain.grainAmount = value + binding.tvLabelGrainAmount.text = "Grain Amount (Mật độ hạt): ${String.format("%.2f", value)}" + applyPresetFilter(preset) + } + } + binding.sliderGrainSize.addOnChangeListener { _, value, _ -> + editingPreset?.let { preset -> + preset.grain.grainSize = value + binding.tvLabelGrainSize.text = "Grain Size (Kích thước hạt): ${String.format("%.2f", value)}" + applyPresetFilter(preset) + } + } binding.btnCancelEdit.setOnClickListener { closeAdvancedSliders() @@ -433,14 +539,38 @@ class MainActivity : AppCompatActivity() { binding.tvEditPresetName.text = "Chỉnh sửa: ${selectedPreset.name}" // Update Sliders visual values + binding.sliderBrightness.value = editingPreset!!.basic.brightness + binding.sliderContrast.value = editingPreset!!.basic.contrast + binding.sliderSaturation.value = editingPreset!!.basic.saturation binding.sliderVibrance.value = editingPreset!!.basic.vibrance + binding.sliderTemperature.value = editingPreset!!.basic.temperature + binding.sliderTint.value = editingPreset!!.basic.tint binding.sliderClarity.value = editingPreset!!.advanced.clarity binding.sliderDehaze.value = editingPreset!!.advanced.dehaze + binding.sliderVignetteAmount.value = editingPreset!!.advanced.vignetteAmount + binding.sliderFadedBlack.value = editingPreset!!.toneCurve.fadedBlackLevel + binding.sliderShadowsTintR.value = editingPreset!!.toneCurve.shadowsTintR + binding.sliderShadowsTintG.value = editingPreset!!.toneCurve.shadowsTintG + binding.sliderShadowsTintB.value = editingPreset!!.toneCurve.shadowsTintB + binding.sliderGrainAmount.value = editingPreset!!.grain.grainAmount + binding.sliderGrainSize.value = editingPreset!!.grain.grainSize // Update text labels + binding.tvLabelBrightness.text = "Brightness (Độ sáng): ${String.format("%.2f", editingPreset!!.basic.brightness)}" + binding.tvLabelContrast.text = "Contrast (Tương phản): ${String.format("%.2f", editingPreset!!.basic.contrast)}" + binding.tvLabelSaturation.text = "Saturation (Bão hòa màu): ${String.format("%.2f", editingPreset!!.basic.saturation)}" binding.tvLabelVibrance.text = "Vibrance (Bão hòa thông minh): ${String.format("%.2f", editingPreset!!.basic.vibrance)}" + binding.tvLabelTemperature.text = "Temperature (Nhiệt màu): ${String.format("%.2f", editingPreset!!.basic.temperature)}" + binding.tvLabelTint.text = "Tint (Sắc độ hồng/xanh): ${String.format("%.2f", editingPreset!!.basic.tint)}" 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)}" + binding.tvLabelVignetteAmount.text = "Vignette Amount (Bo tối góc): ${String.format("%.2f", editingPreset!!.advanced.vignetteAmount)}" + binding.tvLabelFadedBlack.text = "Faded Black Level (Độ mờ vùng đen): ${String.format("%.2f", editingPreset!!.toneCurve.fadedBlackLevel)}" + binding.tvLabelShadowsTintR.text = "Shadows Tint R (Sắc Đỏ vùng tối): ${String.format("%.2f", editingPreset!!.toneCurve.shadowsTintR)}" + binding.tvLabelShadowsTintG.text = "Shadows Tint G (Sắc Lục vùng tối): ${String.format("%.2f", editingPreset!!.toneCurve.shadowsTintG)}" + binding.tvLabelShadowsTintB.text = "Shadows Tint B (Sắc Lam vùng tối): ${String.format("%.2f", editingPreset!!.toneCurve.shadowsTintB)}" + binding.tvLabelGrainAmount.text = "Grain Amount (Mật độ hạt): ${String.format("%.2f", editingPreset!!.grain.grainAmount)}" + binding.tvLabelGrainSize.text = "Grain Size (Kích thước hạt): ${String.format("%.2f", editingPreset!!.grain.grainSize)}" // Scale animation from center binding.cardAdvancedSliders.apply { @@ -503,13 +633,32 @@ class MainActivity : AppCompatActivity() { name = newName, themeCategory = "Custom", isEditable = true, - basic = BasicAdjustments(tempPreset.basic.brightness, tempPreset.basic.contrast, tempPreset.basic.saturation, tempPreset.basic.vibrance, 0f, 0f), - advanced = AdvancedEffects(tempPreset.advanced.clarity, tempPreset.advanced.dehaze, 0f), - toneCurve = ToneCurveEmulation(0f, 0f, 0f, 0f), - grain = FilmGrain(0f, 0f) + basic = BasicAdjustments( + tempPreset.basic.brightness, + tempPreset.basic.contrast, + tempPreset.basic.saturation, + tempPreset.basic.vibrance, + tempPreset.basic.temperature, + tempPreset.basic.tint + ), + advanced = AdvancedEffects( + tempPreset.advanced.clarity, + tempPreset.advanced.dehaze, + tempPreset.advanced.vignetteAmount + ), + toneCurve = ToneCurveEmulation( + tempPreset.toneCurve.fadedBlackLevel, + tempPreset.toneCurve.shadowsTintR, + tempPreset.toneCurve.shadowsTintG, + tempPreset.toneCurve.shadowsTintB + ), + grain = FilmGrain( + tempPreset.grain.grainAmount, + tempPreset.grain.grainSize + ) ) - // Serialize to JSON format manually + // Serialize to JSON format manually (incorporating all 14 parameters) val jsonString = """ { "id": "$customId", @@ -521,23 +670,23 @@ class MainActivity : AppCompatActivity() { "contrast": ${customPreset.basic.contrast}, "saturation": ${customPreset.basic.saturation}, "vibrance": ${customPreset.basic.vibrance}, - "temperature": 0.0, - "tint": 0.0 + "temperature": ${customPreset.basic.temperature}, + "tint": ${customPreset.basic.tint} }, "advanced_effects": { "clarity": ${customPreset.advanced.clarity}, "dehaze": ${customPreset.advanced.dehaze}, - "vignette_amount": 0.0 + "vignette_amount": ${customPreset.advanced.vignetteAmount} }, "tone_curve_emulation": { - "faded_black_level": 0.0, - "shadows_tint_r": 0.0, - "shadows_tint_g": 0.0, - "shadows_tint_b": 0.0 + "faded_black_level": ${customPreset.toneCurve.fadedBlackLevel}, + "shadows_tint_r": ${customPreset.toneCurve.shadowsTintR}, + "shadows_tint_g": ${customPreset.toneCurve.shadowsTintG}, + "shadows_tint_b": ${customPreset.toneCurve.shadowsTintB} }, "film_grain": { - "grain_amount": 0.0, - "grain_size": 0.0 + "grain_amount": ${customPreset.grain.grainAmount}, + "grain_size": ${customPreset.grain.grainSize} } } """.trimIndent() diff --git a/android-project/app/src/main/java/com/photobooth/app/PresetAdapter.kt b/android-project/app/src/main/java/com/photobooth/app/PresetAdapter.kt index 345c1ad..eac6146 100644 --- a/android-project/app/src/main/java/com/photobooth/app/PresetAdapter.kt +++ b/android-project/app/src/main/java/com/photobooth/app/PresetAdapter.kt @@ -18,13 +18,11 @@ class PresetAdapter( ) : RecyclerView.Adapter() { private var selectedPosition = 0 - private var editingPosition = -1 inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val cardPreset: MaterialCardView = view.findViewById(R.id.cardPreset) val viewPresetColor: View = view.findViewById(R.id.viewPresetColor) val txtPresetName: TextView = view.findViewById(R.id.txtPresetName) - val btnSmallEdit: ImageButton = view.findViewById(R.id.btnSmallEdit) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { @@ -55,13 +53,6 @@ class PresetAdapter( holder.viewPresetColor.background = circleDrawable } - // Show edit button on long press - if (position == editingPosition && preset.isEditable) { - holder.btnSmallEdit.visibility = View.VISIBLE - } else { - holder.btnSmallEdit.visibility = View.GONE - } - // Highlight selection val strokeWidthPx = (3 * holder.itemView.context.resources.displayMetrics.density).toInt() if (position == selectedPosition) { @@ -72,30 +63,53 @@ class PresetAdapter( holder.cardPreset.strokeWidth = 0 } - holder.itemView.setOnClickListener { - val previousSelected = selectedPosition - selectedPosition = position - notifyItemChanged(previousSelected) - notifyItemChanged(selectedPosition) - onPresetSelected(preset) + // Setup custom 3-second long press detector with touch slop cancellation + val handler = android.os.Handler(android.os.Looper.getMainLooper()) + var isLongPressTriggered = false + var startX = 0f + var startY = 0f + val touchSlop = 15f // pixels + + val longPressRunnable = Runnable { + if (preset.isEditable) { + isLongPressTriggered = true + holder.itemView.performHapticFeedback(android.view.HapticFeedbackConstants.LONG_PRESS) + onEditClicked(preset) + } } - holder.itemView.setOnLongClickListener { - if (preset.isEditable) { - val prevEditing = editingPosition - editingPosition = if (editingPosition == position) -1 else position - notifyItemChanged(prevEditing) - if (editingPosition != -1) { - notifyItemChanged(editingPosition) + holder.itemView.setOnTouchListener { view, event -> + when (event.action) { + android.view.MotionEvent.ACTION_DOWN -> { + isLongPressTriggered = false + startX = event.x + startY = event.y + handler.postDelayed(longPressRunnable, 3000) + } + android.view.MotionEvent.ACTION_MOVE -> { + if (Math.abs(event.x - startX) > touchSlop || Math.abs(event.y - startY) > touchSlop) { + handler.removeCallbacks(longPressRunnable) + } + } + android.view.MotionEvent.ACTION_UP -> { + handler.removeCallbacks(longPressRunnable) + if (!isLongPressTriggered) { + val previousSelected = selectedPosition + selectedPosition = holder.adapterPosition + if (selectedPosition != RecyclerView.NO_POSITION) { + notifyItemChanged(previousSelected) + notifyItemChanged(selectedPosition) + onPresetSelected(preset) + view.performClick() + } + } + } + android.view.MotionEvent.ACTION_CANCEL -> { + handler.removeCallbacks(longPressRunnable) } - holder.itemView.performHapticFeedback(android.view.HapticFeedbackConstants.LONG_PRESS) } true } - - holder.btnSmallEdit.setOnClickListener { - onEditClicked(preset) - } } override fun getItemCount(): Int = presets.size @@ -108,7 +122,6 @@ class PresetAdapter( fun resetSelection() { val previousSelected = selectedPosition selectedPosition = 0 - editingPosition = -1 notifyItemChanged(previousSelected) notifyItemChanged(selectedPosition) } 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 4a7245a..dc4f7f1 100644 --- a/android-project/app/src/main/res/layout/activity_main.xml +++ b/android-project/app/src/main/res/layout/activity_main.xml @@ -151,65 +151,112 @@ android:contentDescription="Cancel Edit" /> - - + - + android:text="ĐIỀU CHỈNH CƠ BẢN" + android:textColor="#FF9800" + android:textStyle="bold" + android:textSize="11sp" + android:layout_marginTop="8dp" + android:layout_marginBottom="4dp"/> - - - + + + - + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + android:text="GIẢ LẬP TONE CURVE & MÀU VÙNG TỐI" + android:textColor="#FF9800" + android:textStyle="bold" + android:textSize="11sp" + android:layout_marginTop="12dp" + android:layout_marginBottom="4dp"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + +