feat(chat-room-ui): 사용자 메시지, AI 메시지 아이템 레이아웃, 타이핑 인디케이터 아이템 레이아웃 및 애니메이션 추가
item_chat_user_message.xml - 오른쪽 정렬된 메시지 버블 구현 - 버블 왼쪽에 시간 텍스트(tv_time) 배치 - bg_chat_user_message 배경 및 패딩 적용 - 텍스트 접근성과 가독성 향상을 위한 속성 설정 item_chat_ai_message.xml - 왼쪽 정렬된 메시지, 프로필 이미지와 이름, 오른쪽 시간 표시 구조 구현 - 그룹화 대응을 위한 조건부 표시(View visibility) 구조 마련 - bg_chat_ai_message 배경과 가독성 개선 속성 적용 item_chat_typing_indicator.xml, typing_dots_animation.xml - AI 메시지와 동일한 좌측 정렬 구조에 3개 점 애니메이션 영역 구현 - 600ms alpha 애니메이션 반복으로 로딩 상태 시각화 - 추후 ViewHolder에서 점별 startOffset 설정을 통해 순차 반짝임 완성 예정
This commit is contained in:
98
app/src/main/res/layout/item_chat_ai_message.xml
Normal file
98
app/src/main/res/layout/item_chat_ai_message.xml
Normal file
@@ -0,0 +1,98 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
AI 메시지 아이템 레이아웃 (3.3)
|
||||
- 왼쪽 정렬된 메시지 버블
|
||||
- 프로필 이미지, 이름(조건부), 메시지, 시간(오른쪽, 조건부)
|
||||
- 배경: @drawable/bg_chat_ai_message
|
||||
- 그룹화 지원: iv_profile, tv_name, tv_time의 가시성을 어댑터에서 제어
|
||||
-->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingBottom="6dp">
|
||||
|
||||
<!-- 프로필 이미지 (그룹 첫 메시지에서만 보임) -->
|
||||
<ImageView
|
||||
android:id="@+id/iv_profile"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_placeholder_profile"
|
||||
android:contentDescription="AI 프로필 이미지"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<!-- 메시지 그룹: 이름 + 메시지 버블 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/message_group"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_profile"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/tv_time"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
android:layout_marginEnd="8dp">
|
||||
|
||||
<!-- 보낸이 이름 (그룹의 첫 메시지에서만 보임) -->
|
||||
<TextView
|
||||
android:id="@+id/tv_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="12sp"
|
||||
android:includeFontPadding="false"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end" />
|
||||
|
||||
<!-- 메시지 버블 컨테이너 -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/message_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_chat_ai_message"
|
||||
android:paddingStart="14dp"
|
||||
android:paddingEnd="14dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_message"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="15sp"
|
||||
android:lineSpacingExtra="2dp"
|
||||
android:includeFontPadding="false"
|
||||
android:hyphenationFrequency="normal"
|
||||
android:breakStrategy="balanced"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1000"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<!-- 전송 시간: 버블의 오른쪽에 표시 (그룹의 마지막 메시지에서만 보임) -->
|
||||
<TextView
|
||||
android:id="@+id/tv_time"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/color_777777"
|
||||
android:textSize="12sp"
|
||||
android:includeFontPadding="false"
|
||||
app:layout_constraintBottom_toBottomOf="@id/message_group"
|
||||
app:layout_constraintStart_toEndOf="@id/message_group"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginStart="8dp" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
110
app/src/main/res/layout/item_chat_typing_indicator.xml
Normal file
110
app/src/main/res/layout/item_chat_typing_indicator.xml
Normal file
@@ -0,0 +1,110 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
타이핑 인디케이터 아이템 레이아웃 (3.4)
|
||||
- AI 메시지 레이아웃과 동일한 좌측 정렬 구조
|
||||
- 메시지 영역 대신 3개 점(•••) 애니메이션 표시
|
||||
- 애니메이션: @anim/typing_dots_animation (순차 반짝임은 추후 ViewHolder에서 startOffset 제어 가능)
|
||||
-->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingBottom="6dp">
|
||||
|
||||
<!-- 프로필 이미지 (AI) -->
|
||||
<ImageView
|
||||
android:id="@+id/iv_profile"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_placeholder_profile"
|
||||
android:contentDescription="AI 프로필 이미지"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<!-- 이름 + 타이핑 버블 그룹 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/message_group"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_profile"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginEnd="56dp">
|
||||
|
||||
<!-- 보낸이 이름 (필요 시 표시) -->
|
||||
<TextView
|
||||
android:id="@+id/tv_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="12sp"
|
||||
android:includeFontPadding="false"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:maxLines="1"
|
||||
android:ellipsize="end" />
|
||||
|
||||
<!-- 타이핑 표시 컨테이너 (AI 버블 배경 재사용 가능) -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/typing_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_chat_ai_message"
|
||||
android:paddingStart="14dp"
|
||||
android:paddingEnd="14dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp">
|
||||
|
||||
<!-- 3개 점 애니메이션 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/ll_typing_dots"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dot1"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="\u2022"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@android:color/white"
|
||||
android:includeFontPadding="false"
|
||||
android:animation="@anim/typing_dots_animation" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dot2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:text="\u2022"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@android:color/white"
|
||||
android:includeFontPadding="false"
|
||||
android:animation="@anim/typing_dots_animation" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dot3"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="4dp"
|
||||
android:text="\u2022"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@android:color/white"
|
||||
android:includeFontPadding="false"
|
||||
android:animation="@anim/typing_dots_animation" />
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
63
app/src/main/res/layout/item_chat_user_message.xml
Normal file
63
app/src/main/res/layout/item_chat_user_message.xml
Normal file
@@ -0,0 +1,63 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
사용자 메시지 아이템 레이아웃
|
||||
- 오른쪽 정렬된 메시지 버블
|
||||
- 버블의 왼쪽에 시간 표시
|
||||
- 배경: @drawable/bg_chat_user_message
|
||||
-->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingTop="6dp"
|
||||
android:paddingBottom="6dp">
|
||||
|
||||
<!-- 전송 시간: 버블의 왼쪽에 표시 -->
|
||||
<TextView
|
||||
android:id="@+id/tv_time"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/color_777777"
|
||||
android:textSize="12sp"
|
||||
android:includeFontPadding="false"
|
||||
app:layout_constraintBottom_toBottomOf="@id/message_container"
|
||||
app:layout_constraintEnd_toStartOf="@id/message_container"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:layout_marginEnd="8dp"/>
|
||||
|
||||
<!-- 메시지 버블 컨테이너: 오른쪽 정렬 -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/message_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/bg_chat_user_message"
|
||||
android:paddingStart="14dp"
|
||||
android:paddingEnd="14dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="10dp"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/tv_time">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_message"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="15sp"
|
||||
android:lineSpacingExtra="2dp"
|
||||
android:includeFontPadding="false"
|
||||
android:hyphenationFrequency="normal"
|
||||
android:breakStrategy="balanced"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1000"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
Reference in New Issue
Block a user