Implement 4_ANDROID_UI: Two-tier Frame Selector UI (RecyclerViews, Adapters, Real-time Overlay) with standardized resources (strings, colors, assets configurations)

This commit is contained in:
2026-07-05 16:18:50 +07:00
parent 9f9ec87d54
commit 53926a3f95
14 changed files with 721 additions and 82 deletions
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#40FFFFFF">
<item>
<shape android:shape="oval">
<solid android:color="@color/theme_white" />
<stroke android:width="4dp" android:color="@color/theme_primary" />
<size android:width="64dp" android:height="64dp" />
</shape>
</item>
</ripple>
@@ -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="@color/theme_white"
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,-8 0,-1.85 0.63,-3.55 1.69,-4.9L16.9,18.31C15.55,19.37 13.85,20 12,20zm6.31,-3.1L6.9,5.69C8.25,4.63 9.95,4 12,4c4.41,0 8,3.59 8,8 0,1.85 -0.63,3.55 -1.69,4.9z" />
</vector>
@@ -0,0 +1,12 @@
<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="@color/theme_white"
android:pathData="M12,12m-3.2,0a3.2,3.2 0,1 1,6.4 0a3.2,3.2 0,1 1,-6.4 0" />
<path
android:fillColor="@color/theme_white"
android:pathData="M9,2L7.17,4H4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V6c0,-1.1 -0.9,-2 -2,-2h-3.17L15,2H9zm3,15c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5z" />
</vector>
@@ -3,99 +3,136 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#121212">
android:background="@color/theme_bg_dark">
<!-- Màn hình xem trước Camera -->
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="3:4"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/controlPanel" />
<!-- Thanh công cụ trên đầu (Translucent Black Top Bar) -->
<!-- ================= 1. TOP BAR CONTROL ================= -->
<LinearLayout
android:id="@+id/topBar"
android:layout_width="match_parent"
android:layout_height="60dp"
android:background="#80000000"
android:background="@color/theme_bar_bg"
android:orientation="horizontal"
android:gravity="center"
android:paddingHorizontal="16dp"
app:layout_constraintTop_toTopOf="parent">
<!-- Nút Flash -->
<Button
android:id="@+id/btnFlash"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginHorizontal="4dp"
android:backgroundTint="#2D2D2D"
android:textColor="#FFFFFF"
android:text="⚡ Off"
android:backgroundTint="@color/theme_item_card_bg"
android:textColor="@color/theme_white"
android:text="@string/flash_off"
android:textSize="12sp"
android:insetTop="0dp"
android:insetBottom="0dp" />
<!-- Nút Hẹn giờ -->
<Button
android:id="@+id/btnTimer"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginHorizontal="4dp"
android:backgroundTint="#2D2D2D"
android:textColor="#FFFFFF"
android:text="⏱️ Off"
android:backgroundTint="@color/theme_item_card_bg"
android:textColor="@color/theme_white"
android:text="@string/timer_off"
android:textSize="12sp"
android:insetTop="0dp"
android:insetBottom="0dp" />
<!-- Nút Đổi Camera -->
<Button
android:id="@+id/btnSwitchCamera"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_marginHorizontal="4dp"
android:backgroundTint="#2D2D2D"
android:textColor="#FFFFFF"
android:text="🔄 Front"
android:backgroundTint="@color/theme_item_card_bg"
android:textColor="@color/theme_white"
android:text="@string/camera_front"
android:textSize="12sp"
android:insetTop="0dp"
android:insetBottom="0dp" />
</LinearLayout>
<!-- Số đếm ngược khi hẹn giờ chụp -->
<TextView
android:id="@+id/txtTimerCountdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FFFFFF"
android:textSize="100sp"
android:textStyle="bold"
android:shadowColor="#80000000"
android:shadowDx="4"
android:shadowDy="4"
android:shadowRadius="10"
android:visibility="gone"
android:text="3"
app:layout_constraintTop_toTopOf="@id/viewFinder"
app:layout_constraintBottom_toBottomOf="@id/viewFinder"
app:layout_constraintLeft_toLeftOf="@id/viewFinder"
app:layout_constraintRight_toRightOf="@id/viewFinder" />
<!-- ================= 2. KHUNG FRAME CHÍNH (CAMERA + OVERLAY) ================= -->
<FrameLayout
android:id="@+id/cameraContainer"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="3:4"
app:layout_constraintTop_toBottomOf="@id/topBar"
app:layout_constraintBottom_toTopOf="@id/timelineContainer">
<!-- Thanh chọn Zoom ngay trên bảng điều khiển -->
<!-- Lớp đáy: Kính ngắm CameraX -->
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- Lớp đè (Overlay): Hiển thị Khung hình PNG do người dùng chọn -->
<ImageView
android:id="@+id/imgFrameOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter"
android:contentDescription="Frame Overlay" />
<!-- Số đếm ngược khi hẹn giờ chụp -->
<TextView
android:id="@+id/txtTimerCountdown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textColor="@color/theme_white"
android:textSize="100sp"
android:textStyle="bold"
android:shadowColor="@color/theme_panel_dark"
android:shadowDx="4"
android:shadowDy="4"
android:shadowRadius="10"
android:visibility="gone"
android:text="3" />
</FrameLayout>
<!-- ================= 3. TIMELINE DANH SÁCH STACK FRAMES ================= -->
<LinearLayout
android:id="@+id/zoomBar"
android:id="@+id/timelineContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@id/cameraContainer"
app:layout_constraintBottom_toTopOf="@id/zoomControls">
<!-- TẦNG BỔ SUNG: RecyclerView hiển thị các mẫu khung con bên trong Stack -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvSubFrames"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="6dp"
android:background="@color/theme_sub_bar_bg"
android:visibility="gone" />
<!-- TẦNG GỐC: Danh sách các Stack chủ đề lớn -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvFrameStacks"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp" />
</LinearLayout>
<!-- ================= 4. CỤM NÚT ĐIỀU KHIỂN ZOOM ================= -->
<LinearLayout
android:id="@+id/zoomControls"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="12dp"
android:background="#80000000"
android:orientation="horizontal"
android:background="@color/theme_sub_bar_bg"
android:padding="4dp"
app:layout_constraintTop_toBottomOf="@id/timelineContainer"
app:layout_constraintBottom_toTopOf="@id/controlPanel"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent">
@@ -105,9 +142,9 @@
android:layout_width="50dp"
android:layout_height="36dp"
android:layout_marginHorizontal="2dp"
android:backgroundTint="#2D2D2D"
android:textColor="#FFFFFF"
android:text="0.5x"
android:backgroundTint="@color/theme_item_card_bg"
android:textColor="@color/theme_white"
android:text="@string/zoom_05x"
android:textSize="10sp"
android:padding="0dp"
android:insetTop="0dp"
@@ -118,9 +155,9 @@
android:layout_width="50dp"
android:layout_height="36dp"
android:layout_marginHorizontal="2dp"
android:backgroundTint="#FF9800"
android:textColor="#FFFFFF"
android:text="1x"
android:backgroundTint="@color/theme_primary"
android:textColor="@color/theme_white"
android:text="@string/zoom_1x"
android:textSize="10sp"
android:padding="0dp"
android:insetTop="0dp"
@@ -131,32 +168,32 @@
android:layout_width="50dp"
android:layout_height="36dp"
android:layout_marginHorizontal="2dp"
android:backgroundTint="#2D2D2D"
android:textColor="#FFFFFF"
android:text="3x"
android:backgroundTint="@color/theme_item_card_bg"
android:textColor="@color/theme_white"
android:text="@string/zoom_3x"
android:textSize="10sp"
android:padding="0dp"
android:insetTop="0dp"
android:insetBottom="0dp" />
</LinearLayout>
<!-- Bảng điều khiển phía dưới -->
<!-- ================= 5. CONTROL PANEL CHỤP ẢNH ================= -->
<RelativeLayout
android:id="@+id/controlPanel"
android:layout_width="match_parent"
android:layout_height="120dp"
android:background="#000000"
app:layout_constraintBottom_toBottomOf="parent">
android:layout_height="0dp"
android:background="@color/theme_panel_dark"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/zoomControls">
<!-- Nút chụp hình -->
<ImageButton
android:id="@+id/btnCapture"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_centerInParent="true"
android:background="@android:color/transparent"
android:src="@android:drawable/ic_menu_camera"
android:contentDescription="Capture Button" />
android:background="@drawable/bg_capture_button"
android:src="@drawable/ic_camera_white"
android:contentDescription="@string/btn_capture_desc" />
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/cardStack"
android:layout_width="120dp"
android:layout_height="50dp"
android:layout_margin="6dp"
app:cardCornerRadius="8dp"
app:cardElevation="2dp"
app:strokeWidth="0dp"
app:cardBackgroundColor="@color/theme_item_card_bg">
<TextView
android:id="@+id/txtStackName"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textColor="@color/theme_white"
android:textSize="14sp"
android:textStyle="bold"
android:padding="8dp"
android:text="Stack 1" />
</com.google.android.material.card.MaterialCardView>
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/cardSubFrame"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_margin="4dp"
app:cardCornerRadius="6dp"
app:cardElevation="1dp"
app:strokeWidth="0dp"
app:cardBackgroundColor="@color/theme_item_card_bg">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="4dp">
<ImageView
android:id="@+id/imgFrameThumb"
android:layout_width="48dp"
android:layout_height="48dp"
android:scaleType="fitCenter"
android:contentDescription="Frame Thumb" />
<TextView
android:id="@+id/txtFrameName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="@color/theme_white"
android:textSize="10sp"
android:singleLine="true"
android:ellipsize="end"
android:text="Frame" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="theme_bg_dark">#121212</color>
<color name="theme_panel_dark">#000000</color>
<color name="theme_bar_bg">#80000000</color>
<color name="theme_sub_bar_bg">#1E1E1E</color>
<color name="theme_item_card_bg">#2D2D2D</color>
<color name="theme_primary">#FF9800</color>
<color name="theme_white">#FFFFFF</color>
<color name="theme_red_stroke">#D32F2F</color>
<color name="theme_transparent">#00000000</color>
</resources>
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Retro Photobooth</string>
<string name="btn_capture_desc">Capture Button</string>
<!-- Camera controls translation -->
<string name="flash_off">⚡ Off</string>
<string name="flash_on">⚡ On</string>
<string name="flash_auto">⚡ Auto</string>
<string name="timer_off">⏱️ Off</string>
<string name="timer_seconds">⏱️ %1$ds</string>
<string name="camera_front">🔄 Front</string>
<string name="camera_back">🔄 Back</string>
<string name="default_frame">Mặc định</string>
<string name="zoom_05x">0.5x</string>
<string name="zoom_1x">1x</string>
<string name="zoom_3x">3x</string>
<!-- User feedback toasts -->
<string name="toast_camera_start_error">Không thể khởi động Camera</string>
<string name="toast_camera_permission_denied">Quyền Camera bị từ chối.</string>
<string name="toast_zoom_unsupported">Thiết bị không hỗ trợ mức zoom %1$.1fx</string>
<string name="toast_capture_success">📸 Tách! Chụp ảnh thành công!</string>
<string name="toast_frame_load_error">Không thể tải khung hình: %1$s</string>
</resources>