fix: chỉnh sửa button cho phần chỉnh presets
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
package com.photobooth.app
|
||||
|
||||
import android.graphics.Color
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
data class AttributeNode(
|
||||
val type: EditAttribute,
|
||||
val displayName: String,
|
||||
val iconResId: Int
|
||||
)
|
||||
|
||||
class AttributeAdapter(
|
||||
private val items: List<AttributeNode>,
|
||||
private val onItemSelected: (AttributeNode) -> Unit
|
||||
) : RecyclerView.Adapter<AttributeAdapter.ViewHolder>() {
|
||||
|
||||
var selectedPosition = 0
|
||||
set(value) {
|
||||
val old = field
|
||||
field = value
|
||||
notifyItemChanged(old)
|
||||
notifyItemChanged(value)
|
||||
}
|
||||
|
||||
fun updateSelection(selectedType: EditAttribute) {
|
||||
val index = items.indexOfFirst { it.type == selectedType }
|
||||
if (index != -1) {
|
||||
selectedPosition = index
|
||||
}
|
||||
}
|
||||
|
||||
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
|
||||
val imgIcon: ImageView = view.findViewById(R.id.imgAttributeIcon)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val view = LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.item_attribute_icon, parent, false)
|
||||
return ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val item = items[position]
|
||||
holder.imgIcon.setImageResource(item.iconResId)
|
||||
|
||||
// If selected, color is orange, else white
|
||||
if (position == selectedPosition) {
|
||||
holder.imgIcon.setColorFilter(Color.parseColor("#FF9800"))
|
||||
} else {
|
||||
// Keep original vector coloring if it is colored droplet, else white tint
|
||||
if (item.type == EditAttribute.SHADOWS_TINT_R ||
|
||||
item.type == EditAttribute.SHADOWS_TINT_G ||
|
||||
item.type == EditAttribute.SHADOWS_TINT_B) {
|
||||
holder.imgIcon.clearColorFilter()
|
||||
} else {
|
||||
holder.imgIcon.setColorFilter(Color.parseColor("#FFFFFF"))
|
||||
}
|
||||
}
|
||||
|
||||
holder.itemView.setOnClickListener {
|
||||
selectedPosition = holder.adapterPosition
|
||||
onItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = items.size
|
||||
}
|
||||
@@ -45,6 +45,11 @@ class MainActivity : AppCompatActivity() {
|
||||
private var currentSelectedPreset: ColorPreset? = null
|
||||
private var editingPreset: ColorPreset? = null
|
||||
|
||||
private var currentAttribute: EditAttribute = EditAttribute.BRIGHTNESS
|
||||
private lateinit var attributeAdapter: AttributeAdapter
|
||||
private var startX = 0f
|
||||
private var startVal = 0
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
@@ -194,6 +199,38 @@ class MainActivity : AppCompatActivity() {
|
||||
}, { preset ->
|
||||
openAdvancedSliders(preset)
|
||||
})
|
||||
|
||||
// Setup Attributes List and Ruler View for the editor panel
|
||||
val attributeItems = 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)
|
||||
)
|
||||
attributeAdapter = AttributeAdapter(attributeItems) { node ->
|
||||
onAttributeSelected(node)
|
||||
}
|
||||
binding.rvAttributeIcons.layoutManager = LinearLayoutManager(
|
||||
this, LinearLayoutManager.HORIZONTAL, false
|
||||
)
|
||||
binding.rvAttributeIcons.adapter = attributeAdapter
|
||||
|
||||
binding.rulerView.onValueChangeListener = { valInt ->
|
||||
binding.tvRulerValue.text = "$valInt"
|
||||
setCurrentAttributeValue(valInt / 100f)
|
||||
editingPreset?.let { applyPresetFilter(it) }
|
||||
}
|
||||
}
|
||||
|
||||
private fun setFrameOverlay(fileName: String) {
|
||||
@@ -217,48 +254,142 @@ class MainActivity : AppCompatActivity() {
|
||||
currentSelectedPreset = preset
|
||||
if (preset.id == "DEFAULT") {
|
||||
binding.imgFilterOverlay.visibility = android.view.View.GONE
|
||||
binding.viewFinder.setLayerType(android.view.View.LAYER_TYPE_NONE, null)
|
||||
} else {
|
||||
binding.imgFilterOverlay.visibility = android.view.View.VISIBLE
|
||||
val isBeingEdited = editingPreset != null
|
||||
val filterColor = if (isBeingEdited) {
|
||||
// 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)
|
||||
val useCalculation = isBeingEdited || (preset.id != "instax_faded_warm_01" && preset.id != "instax_bw_cool" && preset.id != "instax_cool_summer")
|
||||
val filterColor = if (useCalculation) {
|
||||
// 1. ALPHA (Độ đậm/Mờ của lớp filter): Được quyết định bởi Saturation, Vibrance và hiệu ứng nâng cao
|
||||
// Quy đổi dải từ -1.0 -> 1.0 sang mức độ trong suốt hợp lý (từ 15 đến 140 trong hệ 255)
|
||||
val baseAlpha = 60f
|
||||
val saturationImpact = (preset.basic.saturation + preset.basic.vibrance) * 25f
|
||||
val contrastImpact = preset.basic.contrast * 15f
|
||||
val alpha = (baseAlpha + saturationImpact + contrastImpact).toInt().coerceIn(15, 140)
|
||||
|
||||
// 2. BASE COLOR (Màu nền mặc định ban đầu trước khi tinh chỉnh)
|
||||
// Bắt đầu từ một màu trung tính hoặc lấy màu gốc của Theme nếu có
|
||||
var baseR = 140f
|
||||
var baseG = 130f
|
||||
var baseB = 120f
|
||||
|
||||
// 3. BRIGHTNESS (Độ sáng): Bắt buộc phải cộng/trừ ĐỀU vào cả 3 kênh R, G, B để dịch chuyển Exposure
|
||||
val brightnessOffset = preset.basic.brightness * 80f // Nhân hệ số để biên độ thay đổi từ -100 đến +100 rõ rệt
|
||||
baseR += brightnessOffset
|
||||
baseG += brightnessOffset
|
||||
baseB += brightnessOffset
|
||||
|
||||
// 4. TEMPERATURE (Nhiệt màu): + là Ấm (Tăng Đỏ, Giảm Lam), - là Lạnh (Giảm Đỏ, Tăng Lam)
|
||||
val tempOffset = preset.basic.temperature * 50f
|
||||
baseR += tempOffset
|
||||
baseB -= tempOffset
|
||||
|
||||
// 5. TINT (Sắc độ): + là Ám hồng (Tăng Đỏ & Lam, Giảm Lục), - là Ám xanh (Giảm Đỏ & Lam, Tăng Lục)
|
||||
val tintOffset = preset.basic.tint * 40f
|
||||
baseG -= tintOffset
|
||||
baseR += (tintOffset * 0.5f)
|
||||
baseB += (tintOffset * 0.5f)
|
||||
|
||||
// 6. TONE CURVE & SHADOWS TINT (Ám màu vùng tối đặc trưng của Instax)
|
||||
baseR += (preset.toneCurve.shadowsTintR * 35f)
|
||||
baseG += (preset.toneCurve.shadowsTintG * 35f)
|
||||
baseB += (preset.toneCurve.shadowsTintB * 35f)
|
||||
|
||||
// Nâng đáy màu đen (Faded Black) tạo độ sờn ảnh
|
||||
val fadeOffset = preset.toneCurve.fadedBlackLevel * 30f
|
||||
baseR += fadeOffset
|
||||
baseG += fadeOffset
|
||||
baseB += fadeOffset
|
||||
|
||||
// 7. CLARITY & DEHAZE (Điều tiết thêm độ tương phản cục bộ của màu)
|
||||
val advancedOffset = (preset.advanced.clarity - preset.advanced.dehaze) * 20f
|
||||
baseR += advancedOffset
|
||||
baseG += advancedOffset
|
||||
baseB += advancedOffset
|
||||
|
||||
// 8. ÉP BIÊN ĐỘ (COERCE) để đảm bảo giá trị RGB luôn nằm trong giới hạn vật lý 0 -> 255
|
||||
val r = baseR.toInt().coerceIn(0, 255)
|
||||
val g = baseG.toInt().coerceIn(0, 255)
|
||||
val b = baseB.toInt().coerceIn(0, 255)
|
||||
|
||||
android.graphics.Color.argb(alpha, r, g, b)
|
||||
} else {
|
||||
// Khối xử lý cho các mẫu mặc định cố định không sửa đổi
|
||||
when (preset.id) {
|
||||
"instax_faded_warm_01" -> android.graphics.Color.argb(55, 255, 152, 0)
|
||||
"instax_bw_cool" -> android.graphics.Color.argb(80, 128, 128, 128)
|
||||
"instax_cool_summer" -> android.graphics.Color.argb(45, 0, 188, 212)
|
||||
else -> {
|
||||
val alpha = (((preset.basic.vibrance + preset.basic.saturation) / 2f + 1.0f) * 40 + 20).toInt().coerceIn(10, 120)
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
}
|
||||
else -> android.graphics.Color.argb(0, 0, 0, 0)
|
||||
}
|
||||
}
|
||||
binding.imgFilterOverlay.setBackgroundColor(filterColor)
|
||||
applyColorMatrixToViewFinder(preset)
|
||||
}
|
||||
}
|
||||
|
||||
private fun applyColorMatrixToViewFinder(preset: ColorPreset) {
|
||||
val finalMatrix = android.graphics.ColorMatrix()
|
||||
|
||||
// 1. Saturation & Vibrance (Sắc độ bão hòa màu thực tế)
|
||||
val sat = (preset.basic.saturation + preset.basic.vibrance * 0.5f) + 1f
|
||||
val satMatrix = android.graphics.ColorMatrix().apply {
|
||||
setSaturation(sat.coerceIn(0f, 3f))
|
||||
}
|
||||
finalMatrix.postConcat(satMatrix)
|
||||
|
||||
// 2. Contrast (Tương phản thực tế)
|
||||
val scale = preset.basic.contrast + 1f
|
||||
val translate = (-0.5f * scale + 0.5f) * 255f
|
||||
val contrastMatrix = android.graphics.ColorMatrix(floatArrayOf(
|
||||
scale, 0f, 0f, 0f, translate,
|
||||
0f, scale, 0f, 0f, translate,
|
||||
0f, 0f, scale, 0f, translate,
|
||||
0f, 0f, 0f, 1f, 0f
|
||||
))
|
||||
finalMatrix.postConcat(contrastMatrix)
|
||||
|
||||
// 3. Brightness (Độ sáng Exposure thực tế)
|
||||
val brightnessOffset = preset.basic.brightness * 60f
|
||||
val brightnessMatrix = android.graphics.ColorMatrix(floatArrayOf(
|
||||
1f, 0f, 0f, 0f, brightnessOffset,
|
||||
0f, 1f, 0f, 0f, brightnessOffset,
|
||||
0f, 0f, 1f, 0f, brightnessOffset,
|
||||
0f, 0f, 0f, 1f, 0f
|
||||
))
|
||||
finalMatrix.postConcat(brightnessMatrix)
|
||||
|
||||
// 4. Temperature (Nhiệt màu thực tế: Vàng ấm / Xanh lạnh)
|
||||
val temp = preset.basic.temperature
|
||||
val rScale = 1f + temp * 0.12f
|
||||
val gScale = 1f + temp * 0.04f
|
||||
val bScale = 1f - temp * 0.12f
|
||||
val tempMatrix = android.graphics.ColorMatrix(floatArrayOf(
|
||||
rScale, 0f, 0f, 0f, 0f,
|
||||
0f, gScale, 0f, 0f, 0f,
|
||||
0f, 0f, bScale, 0f, 0f,
|
||||
0f, 0f, 0f, 1f, 0f
|
||||
))
|
||||
finalMatrix.postConcat(tempMatrix)
|
||||
|
||||
// 5. Tint (Sắc độ: Hồng dâu / Xanh lá)
|
||||
val tint = preset.basic.tint
|
||||
val rScaleTint = 1f + tint * 0.06f
|
||||
val gScaleTint = 1f - tint * 0.12f
|
||||
val bScaleTint = 1f + tint * 0.06f
|
||||
val tintMatrix = android.graphics.ColorMatrix(floatArrayOf(
|
||||
rScaleTint, 0f, 0f, 0f, 0f,
|
||||
0f, gScaleTint, 0f, 0f, 0f,
|
||||
0f, 0f, bScaleTint, 0f, 0f,
|
||||
0f, 0f, 0f, 1f, 0f
|
||||
))
|
||||
finalMatrix.postConcat(tintMatrix)
|
||||
|
||||
val paint = android.graphics.Paint().apply {
|
||||
colorFilter = android.graphics.ColorMatrixColorFilter(finalMatrix)
|
||||
}
|
||||
binding.viewFinder.setLayerType(android.view.View.LAYER_TYPE_HARDWARE, paint)
|
||||
}
|
||||
|
||||
private fun updateFrameModeIcon(frame: FrameItem) {
|
||||
if (frame.id == "DEFAULT" || frame.imageFileName.isEmpty()) {
|
||||
binding.btnMainFrame.setImageResource(R.drawable.ic_default_frame_thumbnail)
|
||||
@@ -341,118 +472,31 @@ class MainActivity : AppCompatActivity() {
|
||||
onCaptureClick()
|
||||
}
|
||||
|
||||
// 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)
|
||||
// Camera viewfinder container swipe gesture listener to adjust parameters
|
||||
binding.cameraContainer.setOnTouchListener { _, event ->
|
||||
if (binding.layoutTimelineEditor.visibility != android.view.View.VISIBLE) {
|
||||
return@setOnTouchListener false
|
||||
}
|
||||
}
|
||||
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
|
||||
binding.tvLabelVibrance.text = "Vibrance (Bão hòa thông minh): ${String.format("%.2f", value)}"
|
||||
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
|
||||
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.tvLabelDehaze.text = "Dehaze (Độ trong suốt / Sương mù): ${String.format("%.2f", value)}"
|
||||
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)
|
||||
when (event.action) {
|
||||
android.view.MotionEvent.ACTION_DOWN -> {
|
||||
startX = event.x
|
||||
startVal = binding.rulerView.value
|
||||
}
|
||||
android.view.MotionEvent.ACTION_MOVE -> {
|
||||
val deltaX = event.x - startX
|
||||
val sensitivity = 5f // pixels per unit value change
|
||||
val deltaVal = (deltaX / sensitivity).toInt()
|
||||
binding.rulerView.value = startVal + deltaVal
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
binding.btnCancelEdit.setOnClickListener {
|
||||
binding.btnEditorClose.setOnClickListener {
|
||||
closeAdvancedSliders()
|
||||
}
|
||||
|
||||
binding.btnSaveCustomPreset.setOnClickListener {
|
||||
binding.btnEditorCheck.setOnClickListener {
|
||||
editingPreset?.let { preset ->
|
||||
showSavePresetDialog(preset)
|
||||
}
|
||||
@@ -548,72 +592,105 @@ class MainActivity : AppCompatActivity() {
|
||||
grain = selectedPreset.grain.copy()
|
||||
)
|
||||
|
||||
binding.tvEditPresetName.text = "Chỉnh sửa: ${selectedPreset.name}"
|
||||
binding.layoutEditorTitleBar.visibility = android.view.View.VISIBLE
|
||||
|
||||
// Setup initial selected attribute: Brightness
|
||||
val initialNode = AttributeNode(EditAttribute.BRIGHTNESS, "Brightness (Độ sáng)", R.drawable.ic_attr_brightness)
|
||||
onAttributeSelected(initialNode)
|
||||
|
||||
// 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
|
||||
binding.panelSelectionContainer.visibility = android.view.View.GONE
|
||||
binding.bottomControlPanel.visibility = android.view.View.GONE
|
||||
|
||||
// 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 {
|
||||
binding.layoutTimelineEditor.apply {
|
||||
visibility = android.view.View.VISIBLE
|
||||
alpha = 0f
|
||||
scaleX = 0.8f
|
||||
scaleY = 0.8f
|
||||
animate()
|
||||
.alpha(1f)
|
||||
.scaleX(1f)
|
||||
.scaleY(1f)
|
||||
.setDuration(250)
|
||||
.setInterpolator(android.view.animation.OvershootInterpolator(1.2f))
|
||||
.start()
|
||||
}
|
||||
}
|
||||
|
||||
private fun closeAdvancedSliders() {
|
||||
binding.cardAdvancedSliders.animate()
|
||||
binding.layoutTimelineEditor.animate()
|
||||
.alpha(0f)
|
||||
.scaleX(0.8f)
|
||||
.scaleY(0.8f)
|
||||
.setDuration(200)
|
||||
.withEndAction {
|
||||
binding.cardAdvancedSliders.visibility = android.view.View.GONE
|
||||
binding.layoutTimelineEditor.visibility = android.view.View.GONE
|
||||
binding.layoutEditorTitleBar.visibility = android.view.View.GONE
|
||||
binding.panelSelectionContainer.visibility = android.view.View.VISIBLE
|
||||
binding.bottomControlPanel.visibility = android.view.View.VISIBLE
|
||||
editingPreset = null
|
||||
currentSelectedPreset?.let { applyPresetFilter(it) }
|
||||
}
|
||||
.start()
|
||||
}
|
||||
|
||||
private fun onAttributeSelected(selectedNode: AttributeNode) {
|
||||
currentAttribute = selectedNode.type
|
||||
binding.tvCurrentAttributeName.text = selectedNode.displayName
|
||||
|
||||
// Adjust Ruler limits based on attribute range
|
||||
val isNonNegative = selectedNode.type == EditAttribute.VIGNETTE_AMOUNT ||
|
||||
selectedNode.type == EditAttribute.FADED_BLACK_LEVEL ||
|
||||
selectedNode.type == EditAttribute.SHADOWS_TINT_R ||
|
||||
selectedNode.type == EditAttribute.SHADOWS_TINT_G ||
|
||||
selectedNode.type == EditAttribute.SHADOWS_TINT_B ||
|
||||
selectedNode.type == EditAttribute.GRAIN_AMOUNT ||
|
||||
selectedNode.type == EditAttribute.GRAIN_SIZE
|
||||
|
||||
binding.rulerView.minVal = if (isNonNegative) 0 else -100
|
||||
binding.rulerView.maxVal = 100
|
||||
|
||||
val currentVal = (getCurrentAttributeValue() * 100f).toInt()
|
||||
binding.rulerView.value = currentVal
|
||||
binding.tvRulerValue.text = "$currentVal"
|
||||
|
||||
attributeAdapter.updateSelection(selectedNode.type)
|
||||
}
|
||||
|
||||
private fun getCurrentAttributeValue(): Float {
|
||||
val preset = editingPreset ?: return 0f
|
||||
return when (currentAttribute) {
|
||||
EditAttribute.BRIGHTNESS -> preset.basic.brightness
|
||||
EditAttribute.CONTRAST -> preset.basic.contrast
|
||||
EditAttribute.SATURATION -> preset.basic.saturation
|
||||
EditAttribute.VIBRANCE -> preset.basic.vibrance
|
||||
EditAttribute.TEMPERATURE -> preset.basic.temperature
|
||||
EditAttribute.TINT -> preset.basic.tint
|
||||
EditAttribute.CLARITY -> preset.advanced.clarity
|
||||
EditAttribute.DEHAZE -> preset.advanced.dehaze
|
||||
EditAttribute.VIGNETTE_AMOUNT -> preset.advanced.vignetteAmount
|
||||
EditAttribute.FADED_BLACK_LEVEL -> preset.toneCurve.fadedBlackLevel
|
||||
EditAttribute.SHADOWS_TINT_R -> preset.toneCurve.shadowsTintR
|
||||
EditAttribute.SHADOWS_TINT_G -> preset.toneCurve.shadowsTintG
|
||||
EditAttribute.SHADOWS_TINT_B -> preset.toneCurve.shadowsTintB
|
||||
EditAttribute.GRAIN_AMOUNT -> preset.grain.grainAmount
|
||||
EditAttribute.GRAIN_SIZE -> preset.grain.grainSize
|
||||
}
|
||||
}
|
||||
|
||||
private fun setCurrentAttributeValue(value: Float) {
|
||||
val preset = editingPreset ?: return
|
||||
when (currentAttribute) {
|
||||
EditAttribute.BRIGHTNESS -> preset.basic.brightness = value
|
||||
EditAttribute.CONTRAST -> preset.basic.contrast = value
|
||||
EditAttribute.SATURATION -> preset.basic.saturation = value
|
||||
EditAttribute.VIBRANCE -> preset.basic.vibrance = value
|
||||
EditAttribute.TEMPERATURE -> preset.basic.temperature = value
|
||||
EditAttribute.TINT -> preset.basic.tint = value
|
||||
EditAttribute.CLARITY -> preset.advanced.clarity = value
|
||||
EditAttribute.DEHAZE -> preset.advanced.dehaze = value
|
||||
EditAttribute.VIGNETTE_AMOUNT -> preset.advanced.vignetteAmount = value.coerceIn(0f, 1f)
|
||||
EditAttribute.FADED_BLACK_LEVEL -> preset.toneCurve.fadedBlackLevel = value.coerceIn(0f, 1f)
|
||||
EditAttribute.SHADOWS_TINT_R -> preset.toneCurve.shadowsTintR = value.coerceIn(0f, 1f)
|
||||
EditAttribute.SHADOWS_TINT_G -> preset.toneCurve.shadowsTintG = value.coerceIn(0f, 1f)
|
||||
EditAttribute.SHADOWS_TINT_B -> preset.toneCurve.shadowsTintB = value.coerceIn(0f, 1f)
|
||||
EditAttribute.GRAIN_AMOUNT -> preset.grain.grainAmount = value.coerceIn(0f, 1f)
|
||||
EditAttribute.GRAIN_SIZE -> preset.grain.grainSize = value.coerceIn(0f, 1f)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSavePresetDialog(tempPreset: ColorPreset) {
|
||||
val input = android.widget.EditText(this).apply {
|
||||
hint = "Nhập tên Preset mới"
|
||||
|
||||
@@ -63,7 +63,7 @@ class PresetAdapter(
|
||||
holder.cardPreset.strokeWidth = 0
|
||||
}
|
||||
|
||||
// Setup custom 3-second long press detector with touch slop cancellation
|
||||
// Setup custom 1-second long press detector with touch slop cancellation
|
||||
val handler = android.os.Handler(android.os.Looper.getMainLooper())
|
||||
var isLongPressTriggered = false
|
||||
var startX = 0f
|
||||
@@ -84,7 +84,7 @@ class PresetAdapter(
|
||||
isLongPressTriggered = false
|
||||
startX = event.x
|
||||
startY = event.y
|
||||
handler.postDelayed(longPressRunnable, 3000)
|
||||
handler.postDelayed(longPressRunnable, 800)
|
||||
}
|
||||
android.view.MotionEvent.ACTION_MOVE -> {
|
||||
if (Math.abs(event.x - startX) > touchSlop || Math.abs(event.y - startY) > touchSlop) {
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.photobooth.app
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
|
||||
class RulerView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : View(context, attrs, defStyleAttr) {
|
||||
|
||||
var value: Int = 0
|
||||
set(newValue) {
|
||||
val clamped = newValue.coerceIn(minVal, maxVal)
|
||||
if (field != clamped) {
|
||||
field = clamped
|
||||
invalidate()
|
||||
onValueChangeListener?.invoke(clamped)
|
||||
}
|
||||
}
|
||||
|
||||
var minVal: Int = -100
|
||||
set(newValue) {
|
||||
field = newValue
|
||||
invalidate()
|
||||
}
|
||||
|
||||
var maxVal: Int = 100
|
||||
set(newValue) {
|
||||
field = newValue
|
||||
invalidate()
|
||||
}
|
||||
|
||||
var onValueChangeListener: ((Int) -> Unit)? = null
|
||||
|
||||
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
|
||||
private var lastX = 0f
|
||||
private var isDragging = false
|
||||
|
||||
private val density = resources.displayMetrics.density
|
||||
private fun dp(value: Float) = value * density
|
||||
|
||||
override fun onDraw(canvas: Canvas) {
|
||||
super.onDraw(canvas)
|
||||
val w = width.toFloat()
|
||||
val h = height.toFloat()
|
||||
val cx = w / 2f
|
||||
|
||||
// Draw ticks
|
||||
// One tick mark every tickInterval value units.
|
||||
// The spacing in pixels between tickInterval units is tickSpacingPx.
|
||||
val tickInterval = 2
|
||||
val tickSpacingPx = dp(8f)
|
||||
|
||||
// Draw ticks within visible area
|
||||
val unitsPerPixel = tickInterval / tickSpacingPx
|
||||
val minVisibleUnit = (value - cx * unitsPerPixel).toInt() - 5
|
||||
val maxVisibleUnit = (value + cx * unitsPerPixel).toInt() + 5
|
||||
|
||||
for (u in minVisibleUnit..maxVisibleUnit) {
|
||||
if (u < minVal || u > maxVal) continue
|
||||
if (u % tickInterval != 0) continue
|
||||
|
||||
val x = cx + (u - value) * (tickSpacingPx / tickInterval)
|
||||
if (x < 0 || x > w) continue
|
||||
|
||||
val isMajor = u % 10 == 0
|
||||
val isMedium = u % 5 == 0 && !isMajor
|
||||
|
||||
val tickHeight = when {
|
||||
isMajor -> dp(20f)
|
||||
isMedium -> dp(14f)
|
||||
else -> dp(8f)
|
||||
}
|
||||
|
||||
paint.color = if (isMajor) Color.WHITE else Color.GRAY
|
||||
paint.strokeWidth = if (isMajor) dp(1.5f) else dp(1.0f)
|
||||
|
||||
// Draw line from bottom upwards
|
||||
canvas.drawLine(x, h, x, h - tickHeight, paint)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTouchEvent(event: MotionEvent): Boolean {
|
||||
parent.requestDisallowInterceptTouchEvent(true)
|
||||
when (event.action) {
|
||||
MotionEvent.ACTION_DOWN -> {
|
||||
lastX = event.x
|
||||
isDragging = true
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
if (isDragging) {
|
||||
val deltaX = event.x - lastX
|
||||
lastX = event.x
|
||||
// Move the value proportionally to translation
|
||||
val tickSpacingPx = dp(8f)
|
||||
val tickInterval = 2
|
||||
val pixelsPerUnit = tickSpacingPx / tickInterval
|
||||
val deltaVal = -(deltaX / pixelsPerUnit)
|
||||
value = (value + deltaVal).toInt().coerceIn(minVal, maxVal)
|
||||
}
|
||||
}
|
||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||
isDragging = false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF9800"
|
||||
android:pathData="M12,17l-6,-6h12z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69L23.31,12 20,8.69zM12,18c-3.31,0-6-2.69-6-6s2.69-6 6-6 6,2.69 6,6-2.69,6-6,6z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10-4.48 10-10S17.52,2 12,2zm0,18c-4.41,0-8-3.59-8-8s3.59-8 8-8 8,3.59 8,8-3.59,8-8,8zm0-13c-2.76,0-5,2.24-5,5s2.24,5 5,5 5-2.24 5-5-2.24-5-5-5zm0,8c-1.66,0-3-1.34-3-3s1.34-3 3-3 3,1.34 3,3-1.34,3-3,3z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M12,22c5.52,0 10-4.48 10-10S17.52,2 12,2 2,6.48 2,12s4.48,10 10,10zM12,4c4.42,0 8,3.58 8,8s-3.58,8-8,8V4z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M2,4h20v2H2zm0,5h20v2H2zm0,5h20v2H2zm0,5h20v2H2z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2-0.9 2-2V5C21,3.9 20.1,3 19,3zm-2,10H7v-2h10v2z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M3,5h2v2H3zm4,0h2v2H7zm4,0h2v2H11zm4,0h2v2H15zm4,0h2v2H19zM3,11h2v2H3zm4,0h2v2H7zm4,0h2v2H11zm4,0h2v2H15zm4,0h2v2H19zM3,17h2v2H3zm4,0h2v2H7zm4,0h2v2H11zm4,0h2v2H15zm4,0h2v2H19z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M6,12a2,2 0 1,0 4,0a2,2 0 1,0 -4,0M14,12a4,4 0 1,0 8,0a4,4 0 1,0 -8,0" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10-4.48 10-10S17.52,2 12,2zm0,18c-4.41,0-8-3.59-8-8s3.59-8 8-8 8,3.59 8,8-3.59,8-8,8z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M15,13V5c0-1.66-1.34-3-3-3S9,3.34 9,5v8c-1.21,0.91-2,2.37-2,4 0,2.76 2.24,5 5,5s5-2.24 5-5c0-1.63-0.79-3.09-2-4zM12,20c-1.66,0-3-1.34-3-3 0-1.09 0.58-2.05 1.5-2.58V5c0-0.28 0.22-0.5 0.5-0.5s0.5,0.22 0.5,0.5v9.42c0.92,0.53 1.5,1.49 1.5,2.58 0,1.66-1.34,3-3,3z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M12,2.69C12,2.69 5,10 5,14c0,3.86 3.14,7 7,7s7-3.14 7-7c0-4-7-11.31-7-11.31zM12,19c-2.76,0-5-2.24-5-5 0-2.5 3.57-7.44 5-9.25 1.43,1.8 5,6.73 5,9.25 0,2.76-2.24,5-5,5z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#40C4FF"
|
||||
android:pathData="M12,2.69C12,2.69 5,10 5,14c0,3.86 3.14,7 7,7s7-3.14 7-7c0-4-7-11.31-7-11.31zM12,19c-2.76,0-5-2.24-5-5 0-2.5 3.57-7.44 5-9.25 1.43,1.8 5,6.73 5,9.25 0,2.76-2.24,5-5,5z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#69F0AE"
|
||||
android:pathData="M12,2.69C12,2.69 5,10 5,14c0,3.86 3.14,7 7,7s7-3.14 7-7c0-4-7-11.31-7-11.31zM12,19c-2.76,0-5-2.24-5-5 0-2.5 3.57-7.44 5-9.25 1.43,1.8 5,6.73 5,9.25 0,2.76-2.24,5-5,5z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FF5252"
|
||||
android:pathData="M12,2.69C12,2.69 5,10 5,14c0,3.86 3.14,7 7,7s7-3.14 7-7c0-4-7-11.31-7-11.31zM12,19c-2.76,0-5-2.24-5-5 0-2.5 3.57-7.44 5-9.25 1.43,1.8 5,6.73 5,9.25 0,2.76-2.24,5-5,5z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M12,7.5L14,12l4.5,2-4.5,2-2,4.5-2-4.5-4.5-2 4.5-2 2-4.5zM19,3l1,2.2L22.2,6l-2.2,1L19,9.2 18,7l-2.2-1 2.2-1L19,3z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M21,3H3C1.9,3 1,3.9 1,5v14c0,1.1 0.9,2 2,2h18c1.1,0 2-0.9 2-2V5C23,3.9 22.1,3 21,3zM12,18c-3.31,0-6-2.69-6-6s2.69-6 6-6s6,2.69 6,6S15.31,18 12,18z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69L23.31,12 20,8.69zM12,18c-3.31,0-6-2.69-6-6s2.69-6 6-6 6,2.69 6,6-2.69,6-6,6z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41-1.41z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M12,22c5.52,0 10-4.48 10-10S17.52,2 12,2 2,6.48 2,12s4.48,10 10,10zM12,4c4.42,0 8,3.58 8,8s-3.58,8-8,8V4z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10-4.48 10-10S17.52,2 12,2zm0,18c-4.41,0-8-3.59-8-8s3.59-8 8-8 8,3.59 8,8-3.59,8-8,8z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M15,13V5c0-1.66-1.34-3-3-3S9,3.34 9,5v8c-1.21,0.91-2,2.37-2,4 0,2.76 2.24,5 5,5s5-2.24 5-5c0-1.63-0.79-3.09-2-4zM12,20c-1.66,0-3-1.34-3-3 0-1.09 0.58-2.05 1.5-2.58V5c0-0.28 0.22-0.5 0.5-0.5s0.5,0.22 0.5,0.5v9.42c0.92,0.53 1.5,1.49 1.5,2.58 0,1.66-1.34,3-3,3z" />
|
||||
</vector>
|
||||
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24.0"
|
||||
android:viewportHeight="24.0">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M12,7.5L14,12l4.5,2-4.5,2-2,4.5-2-4.5-4.5-2 4.5-2 2-4.5zM19,3l1,2.2L22.2,6l-2.2,1L19,9.2 18,7l-2.2-1 2.2-1L19,3z" />
|
||||
</vector>
|
||||
@@ -54,6 +54,29 @@
|
||||
android:contentDescription="Switch Camera" />
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 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"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintTop_toBottomOf="@id/topBar"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<!-- 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)"
|
||||
android:textColor="#FF9800"
|
||||
android:textSize="14sp"
|
||||
android:textStyle="bold" />
|
||||
</RelativeLayout>
|
||||
|
||||
<!-- ================= 2. KHUNG FRAME CHÍNH (CAMERA + FILTER + FRAME OVERLAY) ================= -->
|
||||
<FrameLayout
|
||||
android:id="@+id/cameraContainer"
|
||||
@@ -102,179 +125,6 @@
|
||||
android:visibility="gone"
|
||||
android:text="3" />
|
||||
|
||||
<!-- LỚP 4: BẢNG SLIDERS FLOATING (Nằm lọt vào giữa hình ảnh camera) -->
|
||||
<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">
|
||||
|
||||
<!-- ScrollView cho phép cuộn lên xuống tự do để chỉnh tất cả các thông số -->
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
android:scrollbars="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- Tiêu đề & Nút X (Đóng) -->
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="12dp">
|
||||
<TextView
|
||||
android:id="@+id/tvEditPresetName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Chỉnh sửa: Instax Nắng Chiều"
|
||||
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="@android:color/transparent"
|
||||
android:src="@drawable/ic_close_white"
|
||||
app:tint="#FFFFFF"
|
||||
android:contentDescription="Cancel Edit" />
|
||||
</RelativeLayout>
|
||||
|
||||
<!-- ================= NHÓM 1: CƠ BẢN (BASIC ADJUSTMENTS) ================= -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
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"/>
|
||||
|
||||
<!-- Brightness -->
|
||||
<TextView android:id="@+id/tvLabelBrightness" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Brightness (Độ sáng): 0.00"/>
|
||||
<com.google.android.material.slider.Slider android:id="@+id/sliderBrightness" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
|
||||
|
||||
<!-- Contrast -->
|
||||
<TextView android:id="@+id/tvLabelContrast" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Contrast (Tương phản): 0.00"/>
|
||||
<com.google.android.material.slider.Slider android:id="@+id/sliderContrast" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
|
||||
|
||||
<!-- Saturation -->
|
||||
<TextView android:id="@+id/tvLabelSaturation" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Saturation (Bão hòa màu): 0.00"/>
|
||||
<com.google.android.material.slider.Slider android:id="@+id/sliderSaturation" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
|
||||
|
||||
<!-- Vibrance -->
|
||||
<TextView android:id="@+id/tvLabelVibrance" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Vibrance (Bão hòa thông minh): 0.00"/>
|
||||
<com.google.android.material.slider.Slider android:id="@+id/sliderVibrance" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
|
||||
|
||||
<!-- Temperature -->
|
||||
<TextView android:id="@+id/tvLabelTemperature" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Temperature (Nhiệt màu): 0.00"/>
|
||||
<com.google.android.material.slider.Slider android:id="@+id/sliderTemperature" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
|
||||
|
||||
<!-- Tint -->
|
||||
<TextView android:id="@+id/tvLabelTint" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Tint (Sắc độ hồng/xanh): 0.00"/>
|
||||
<com.google.android.material.slider.Slider android:id="@+id/sliderTint" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
|
||||
|
||||
|
||||
<!-- ================= NHÓM 2: NÂNG CAO (ADVANCED EFFECTS) ================= -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="HIỆU ỨNG NÂNG CAO"
|
||||
android:textColor="#FF9800"
|
||||
android:textStyle="bold"
|
||||
android:textSize="11sp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<!-- Clarity -->
|
||||
<TextView android:id="@+id/tvLabelClarity" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Clarity (Độ rõ nét vùng trung tính): 0.00"/>
|
||||
<com.google.android.material.slider.Slider android:id="@+id/sliderClarity" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
|
||||
|
||||
<!-- Dehaze -->
|
||||
<TextView android:id="@+id/tvLabelDehaze" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Dehaze (Độ trong suốt / Sương mù): 0.00"/>
|
||||
<com.google.android.material.slider.Slider android:id="@+id/sliderDehaze" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
|
||||
|
||||
<!-- Vignette Amount -->
|
||||
<TextView android:id="@+id/tvLabelVignetteAmount" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Vignette Amount (Bo tối góc): 0.00"/>
|
||||
<com.google.android.material.slider.Slider android:id="@+id/sliderVignetteAmount" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="0.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
|
||||
|
||||
|
||||
<!-- ================= NHÓM 3: GIẢ LẬP ĐƯỜNG CONG MÀU (TONE CURVE EMULATION) ================= -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
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"/>
|
||||
|
||||
<!-- Faded Black Level -->
|
||||
<TextView android:id="@+id/tvLabelFadedBlack" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Faded Black Level (Độ mờ vùng đen): 0.00"/>
|
||||
<com.google.android.material.slider.Slider android:id="@+id/sliderFadedBlack" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="0.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
|
||||
|
||||
<!-- Shadows Tint R -->
|
||||
<TextView android:id="@+id/tvLabelShadowsTintR" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Shadows Tint R (Sắc Đỏ vùng tối): 0.00"/>
|
||||
<com.google.android.material.slider.Slider android:id="@+id/sliderShadowsTintR" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="0.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
|
||||
|
||||
<!-- Shadows Tint G -->
|
||||
<TextView android:id="@+id/tvLabelShadowsTintG" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Shadows Tint G (Sắc Lục vùng tối): 0.00"/>
|
||||
<com.google.android.material.slider.Slider android:id="@+id/sliderShadowsTintG" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="0.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
|
||||
|
||||
<!-- Shadows Tint B -->
|
||||
<TextView android:id="@+id/tvLabelShadowsTintB" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Shadows Tint B (Sắc Lam vùng tối): 0.00"/>
|
||||
<com.google.android.material.slider.Slider android:id="@+id/sliderShadowsTintB" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="0.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
|
||||
|
||||
|
||||
<!-- ================= NHÓM 4: HẠT NHIỄU PHIM (FILM GRAIN) ================= -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="HẠT NHIỄU PHIM"
|
||||
android:textColor="#FF9800"
|
||||
android:textStyle="bold"
|
||||
android:textSize="11sp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginBottom="4dp"/>
|
||||
|
||||
<!-- Grain Amount -->
|
||||
<TextView android:id="@+id/tvLabelGrainAmount" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Grain Amount (Mật độ hạt): 0.00"/>
|
||||
<com.google.android.material.slider.Slider android:id="@+id/sliderGrainAmount" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="0.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
|
||||
|
||||
<!-- Grain Size -->
|
||||
<TextView android:id="@+id/tvLabelGrainSize" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Grain Size (Kích thước hạt): 0.00"/>
|
||||
<com.google.android.material.slider.Slider android:id="@+id/sliderGrainSize" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="0.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
|
||||
|
||||
<!-- 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="16dp"
|
||||
android:text="LƯU THÀNH PRESET MỚI"
|
||||
android:textColor="#FFFFFF"
|
||||
android:backgroundTint="#FF9800"
|
||||
android:textStyle="bold"
|
||||
android:insetTop="0dp"
|
||||
android:insetBottom="0dp"
|
||||
app:cornerRadius="4dp" />
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
</FrameLayout>
|
||||
|
||||
<!-- ================= KHỐI 1: TIMELINE FRAMES & PRESETS ================= -->
|
||||
@@ -415,4 +265,96 @@
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<!-- KHU VỰC ĐIỀU KHIỂN CHỈNH SỬA ẢNH (Thay thế Control Panel khi nhấn giữ preset) -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/layoutTimelineEditor"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="180dp"
|
||||
android:background="#000000"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<!-- TẦNG 1: THANH TRƯỢT THƯỚC ĐO ĐỘC ĐÁO (RULER SLIDER) -->
|
||||
<FrameLayout
|
||||
android:id="@+id/rulerContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.photobooth.app.RulerView
|
||||
android:id="@+id/rulerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<!-- Kim chỉ số 0 màu cam chính giữa cố định -->
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_gravity="center">
|
||||
<TextView
|
||||
android:id="@+id/tvRulerValue"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="0"
|
||||
android:textColor="#FF9800"
|
||||
android:textSize="12sp"
|
||||
android:textStyle="bold"
|
||||
android:layout_gravity="center_horizontal"/>
|
||||
<ImageView
|
||||
android:layout_width="12dp"
|
||||
android:layout_height="12dp"
|
||||
android:src="@drawable/ic_arrow_down_orange"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:contentDescription="Indicator" />
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
||||
<!-- TẦNG 2: PHẦN HIỂN THỊ ICON CÁC THUỘC TÍNH (HORIZONTAL LIST) -->
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rvAttributeIcons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:clipToPadding="false"
|
||||
app:layout_constraintTop_toBottomOf="@id/rulerContainer"
|
||||
app:layout_constraintBottom_toTopOf="@id/layoutEditorActionButtons" />
|
||||
|
||||
<!-- TẦNG 3: THANH THAO TÁC HOÀN THÀNH (HỦY / ĐỒNG Ý) -->
|
||||
<RelativeLayout
|
||||
android:id="@+id/layoutEditorActionButtons"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:paddingStart="20dp"
|
||||
android:paddingEnd="20dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<!-- Nút X (Hủy bỏ các thông số vừa chỉnh) -->
|
||||
<ImageButton
|
||||
android:id="@+id/btnEditorClose"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:background="@null"
|
||||
android:src="@drawable/ic_close_white"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:contentDescription="Close" />
|
||||
|
||||
<!-- Nút Dấu Tích (Đồng ý để lưu preset) -->
|
||||
<ImageButton
|
||||
android:id="@+id/btnEditorCheck"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:background="@null"
|
||||
android:src="@drawable/ic_check_white"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:contentDescription="Save" />
|
||||
</RelativeLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="50dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imgAttributeIcon"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:contentDescription="Attribute Icon" />
|
||||
</FrameLayout>
|
||||
Reference in New Issue
Block a user