feat(character-comment): 답글 페이지 UI 및 페이징 스텁 구현
- 댓글 리스트 아이템 터치 시 답글 페이지로 전환 연결 - 상단 뒤로 가기/닫기, 입력 폼, divider, 원본 댓글, 들여 쓰기된 답글 목록 구성 - RecyclerView 최하단 도달 시 더미 데이터 추가 로드(무한 스크롤 스텁) - 답글 등록/수정/삭제 동작 스텁 처리 - 추가 파일 - layout: fragment_character_comment_reply.xml, item_character_comment_reply.xml - 코드: CharacterCommentReplyFragment, CharacterCommentReplyAdapter - 변경 파일 - CharacterCommentListBottomSheet: openReply() 추가 - CharacterCommentListFragment: 아이템 클릭 시 답글 페이지 진입
This commit is contained in:
@@ -52,4 +52,13 @@ class CharacterCommentListBottomSheet(
|
|||||||
.replace(R.id.fl_container, fragment, tag)
|
.replace(R.id.fl_container, fragment, tag)
|
||||||
.commit()
|
.commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun openReply(original: CharacterCommentResponse) {
|
||||||
|
val tag = "CHARACTER_COMMENT_REPLY"
|
||||||
|
val fragment = CharacterCommentReplyFragment.newInstance(characterId, original)
|
||||||
|
childFragmentManager.beginTransaction()
|
||||||
|
.add(R.id.fl_container, fragment, tag)
|
||||||
|
.addToBackStack(tag)
|
||||||
|
.commit()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,7 +103,9 @@ class CharacterCommentListFragment : BaseFragment<FragmentCharacterCommentListBi
|
|||||||
}
|
}
|
||||||
}.show(childFragmentManager, "comment_more")
|
}.show(childFragmentManager, "comment_more")
|
||||||
},
|
},
|
||||||
onClickItem = { /* 답글 보기로 이동 예정 (stub) */ }
|
onClickItem = { item ->
|
||||||
|
(parentFragment as? CharacterCommentListBottomSheet)?.openReply(item)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
val recyclerView = binding.rvComment
|
val recyclerView = binding.rvComment
|
||||||
|
|||||||
@@ -0,0 +1,141 @@
|
|||||||
|
package kr.co.vividnext.sodalive.chat.character.comment
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.PopupMenu
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import androidx.viewbinding.ViewBinding
|
||||||
|
import coil.load
|
||||||
|
import coil.transform.CircleCropTransformation
|
||||||
|
import kr.co.vividnext.sodalive.R
|
||||||
|
import kr.co.vividnext.sodalive.databinding.ItemCharacterCommentBinding
|
||||||
|
import kr.co.vividnext.sodalive.databinding.ItemCharacterCommentReplyBinding
|
||||||
|
|
||||||
|
class CharacterCommentReplyAdapter(
|
||||||
|
private val currentUserId: Long,
|
||||||
|
private val onModify: (id: Long, newText: String, isReply: Boolean) -> Unit,
|
||||||
|
private val onDelete: (id: Long, isReply: Boolean) -> Unit
|
||||||
|
) : RecyclerView.Adapter<CharacterReplyVH>() {
|
||||||
|
|
||||||
|
// 첫 번째 아이템은 항상 원본 댓글
|
||||||
|
val items = mutableListOf<Any>() // [CharacterCommentResponse] + List<CharacterReplyResponse>
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int): Int = if (position == 0) 0 else 1
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CharacterReplyVH {
|
||||||
|
return if (viewType == 0) {
|
||||||
|
CharacterReplyHeaderVH(
|
||||||
|
ItemCharacterCommentBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
CharacterReplyItemVH(
|
||||||
|
context = parent.context,
|
||||||
|
binding = ItemCharacterCommentReplyBinding.inflate(LayoutInflater.from(parent.context), parent, false),
|
||||||
|
currentUserId = currentUserId,
|
||||||
|
onModify = onModify,
|
||||||
|
onDelete = onDelete
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: CharacterReplyVH, position: Int) {
|
||||||
|
val item = items[position]
|
||||||
|
holder.bind(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = items.size
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class CharacterReplyVH(binding: ViewBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||||
|
abstract fun bind(item: Any)
|
||||||
|
}
|
||||||
|
|
||||||
|
class CharacterReplyHeaderVH(
|
||||||
|
private val binding: ItemCharacterCommentBinding
|
||||||
|
) : CharacterReplyVH(binding) {
|
||||||
|
override fun bind(item: Any) {
|
||||||
|
val data = item as CharacterCommentResponse
|
||||||
|
if (data.memberProfileImage.isNotBlank()) {
|
||||||
|
binding.ivCommentProfile.load(data.memberProfileImage) {
|
||||||
|
crossfade(true)
|
||||||
|
placeholder(R.drawable.ic_placeholder_profile)
|
||||||
|
error(R.drawable.ic_placeholder_profile)
|
||||||
|
transformations(CircleCropTransformation())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
binding.ivCommentProfile.setImageResource(R.drawable.ic_placeholder_profile)
|
||||||
|
}
|
||||||
|
binding.tvCommentNickname.text = data.memberNickname
|
||||||
|
binding.tvCommentDate.text = timeAgo(data.createdAt)
|
||||||
|
binding.tvComment.text = data.comment
|
||||||
|
binding.tvWriteReply.visibility = View.GONE
|
||||||
|
binding.ivMenu.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CharacterReplyItemVH(
|
||||||
|
private val context: Context,
|
||||||
|
private val binding: ItemCharacterCommentReplyBinding,
|
||||||
|
private val currentUserId: Long,
|
||||||
|
private val onModify: (id: Long, newText: String, isReply: Boolean) -> Unit,
|
||||||
|
private val onDelete: (id: Long, isReply: Boolean) -> Unit
|
||||||
|
) : CharacterReplyVH(binding) {
|
||||||
|
|
||||||
|
override fun bind(item: Any) {
|
||||||
|
val data = item as CharacterReplyResponse
|
||||||
|
if (data.memberProfileImage.isNotBlank()) {
|
||||||
|
binding.ivCommentProfile.load(data.memberProfileImage) {
|
||||||
|
crossfade(true)
|
||||||
|
placeholder(R.drawable.ic_placeholder_profile)
|
||||||
|
error(R.drawable.ic_placeholder_profile)
|
||||||
|
transformations(CircleCropTransformation())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
binding.ivCommentProfile.setImageResource(R.drawable.ic_placeholder_profile)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.tvCommentNickname.text = data.memberNickname
|
||||||
|
binding.tvCommentDate.text = timeAgo(data.createdAt)
|
||||||
|
binding.tvComment.text = data.comment
|
||||||
|
|
||||||
|
val isOwner = data.memberId == currentUserId
|
||||||
|
binding.ivMenu.visibility = View.VISIBLE
|
||||||
|
binding.ivMenu.setOnClickListener {
|
||||||
|
val popup = PopupMenu(context, it)
|
||||||
|
if (isOwner) {
|
||||||
|
popup.menuInflater.inflate(R.menu.content_comment_option_menu, popup.menu)
|
||||||
|
} else {
|
||||||
|
popup.menuInflater.inflate(R.menu.content_comment_option_menu2, popup.menu)
|
||||||
|
}
|
||||||
|
popup.setOnMenuItemClickListener { mi ->
|
||||||
|
when (mi.itemId) {
|
||||||
|
R.id.menu_review_modify -> {
|
||||||
|
// 간단화: 수정은 현재 텍스트 그대로 콜백 (실서비스는 인라인 에디트 UI 구성)
|
||||||
|
onModify(data.replyId, data.comment, true)
|
||||||
|
}
|
||||||
|
R.id.menu_review_delete -> {
|
||||||
|
onDelete(data.replyId, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
popup.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun timeAgo(createdAtMillis: Long): String {
|
||||||
|
val now = System.currentTimeMillis()
|
||||||
|
val diff = (now - createdAtMillis).coerceAtLeast(0)
|
||||||
|
val minutes = diff / 60_000
|
||||||
|
if (minutes < 1) return "방금전"
|
||||||
|
if (minutes < 60) return "${minutes}분전"
|
||||||
|
val hours = minutes / 60
|
||||||
|
if (hours < 24) return "${hours}시간전"
|
||||||
|
val days = hours / 24
|
||||||
|
if (days < 365) return "${days}일전"
|
||||||
|
val years = days / 365
|
||||||
|
return "${years}년전"
|
||||||
|
}
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
package kr.co.vividnext.sodalive.chat.character.comment
|
||||||
|
|
||||||
|
import android.app.Service
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import coil.load
|
||||||
|
import coil.transform.CircleCropTransformation
|
||||||
|
import kr.co.vividnext.sodalive.R
|
||||||
|
import kr.co.vividnext.sodalive.base.BaseFragment
|
||||||
|
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||||
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
|
import kr.co.vividnext.sodalive.databinding.FragmentCharacterCommentReplyBinding
|
||||||
|
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 캐릭터 댓글 답글 페이지 (Stub)
|
||||||
|
* - 상단: 뒤로가기(텍스트 + ic_back), 닫기(X)
|
||||||
|
* - 입력 폼, divider, 원본 댓글, 답글 목록(들여쓰기)
|
||||||
|
* - 스크롤 하단 도달 시 더미 데이터 추가 로드
|
||||||
|
* - API 연동은 추후 (현재 스텁)
|
||||||
|
*/
|
||||||
|
class CharacterCommentReplyFragment : BaseFragment<FragmentCharacterCommentReplyBinding>(
|
||||||
|
FragmentCharacterCommentReplyBinding::inflate
|
||||||
|
) {
|
||||||
|
|
||||||
|
private lateinit var imm: InputMethodManager
|
||||||
|
private lateinit var loadingDialog: LoadingDialog
|
||||||
|
private lateinit var adapter: CharacterCommentReplyAdapter
|
||||||
|
|
||||||
|
private var original: CharacterCommentResponse? = null
|
||||||
|
private var characterId: Long = 0
|
||||||
|
|
||||||
|
private var page: Int = 1
|
||||||
|
|
||||||
|
override fun onCreateView(
|
||||||
|
inflater: LayoutInflater,
|
||||||
|
container: ViewGroup?,
|
||||||
|
savedInstanceState: Bundle?
|
||||||
|
): View? {
|
||||||
|
characterId = arguments?.getLong(EXTRA_CHARACTER_ID) ?: 0
|
||||||
|
original = arguments?.let {
|
||||||
|
val cid = it.getLong(EXTRA_ORIGINAL_COMMENT_ID, -1)
|
||||||
|
if (cid == -1L) null else CharacterCommentResponse(
|
||||||
|
commentId = cid,
|
||||||
|
memberId = it.getLong(EXTRA_ORIGINAL_MEMBER_ID),
|
||||||
|
memberProfileImage = it.getString(EXTRA_ORIGINAL_MEMBER_PROFILE_IMAGE) ?: "",
|
||||||
|
memberNickname = it.getString(EXTRA_ORIGINAL_MEMBER_NICKNAME) ?: "",
|
||||||
|
createdAt = it.getLong(EXTRA_ORIGINAL_CREATED_AT),
|
||||||
|
replyCount = it.getInt(EXTRA_ORIGINAL_REPLY_COUNT),
|
||||||
|
comment = it.getString(EXTRA_ORIGINAL_COMMENT_TEXT) ?: ""
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return super.onCreateView(inflater, container, savedInstanceState)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
if (original == null) {
|
||||||
|
parentFragmentManager.popBackStack()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loadingDialog = LoadingDialog(requireActivity(), layoutInflater)
|
||||||
|
imm = requireContext().getSystemService(Service.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
|
||||||
|
setupView()
|
||||||
|
loadMore() // 초기 로드 (스텁)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupView() {
|
||||||
|
binding.tvBack.setOnClickListener { parentFragmentManager.popBackStack() }
|
||||||
|
binding.ivClose.setOnClickListener { (parentFragment as? CharacterCommentListBottomSheet)?.dismiss() }
|
||||||
|
|
||||||
|
binding.ivCommentProfile.load(SharedPreferenceManager.profileImage) {
|
||||||
|
crossfade(true)
|
||||||
|
placeholder(R.drawable.ic_placeholder_profile)
|
||||||
|
error(R.drawable.ic_placeholder_profile)
|
||||||
|
transformations(CircleCropTransformation())
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.ivCommentSend.setOnClickListener {
|
||||||
|
hideKeyboard()
|
||||||
|
val text = binding.etComment.text.toString()
|
||||||
|
if (text.isBlank()) return@setOnClickListener
|
||||||
|
// 스텁: 로컬에 즉시 추가
|
||||||
|
val me = CharacterReplyResponse(
|
||||||
|
replyId = System.currentTimeMillis(),
|
||||||
|
memberId = SharedPreferenceManager.userId,
|
||||||
|
memberProfileImage = SharedPreferenceManager.profileImage,
|
||||||
|
memberNickname = SharedPreferenceManager.nickname,
|
||||||
|
createdAt = System.currentTimeMillis(),
|
||||||
|
comment = text
|
||||||
|
)
|
||||||
|
val insertAt = adapter.items.size
|
||||||
|
adapter.items.add(me)
|
||||||
|
adapter.notifyItemInserted(insertAt)
|
||||||
|
binding.rvCommentReply.scrollToPosition(adapter.items.size - 1)
|
||||||
|
binding.etComment.setText("")
|
||||||
|
Toast.makeText(requireContext(), "등록되었습니다 (stub)", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
adapter = CharacterCommentReplyAdapter(
|
||||||
|
currentUserId = SharedPreferenceManager.userId,
|
||||||
|
onModify = { id, newText, isReply ->
|
||||||
|
Toast.makeText(requireContext(), "수정 스텁: $id", Toast.LENGTH_SHORT).show()
|
||||||
|
},
|
||||||
|
onDelete = { id, isReply ->
|
||||||
|
val index = adapter.items.indexOfFirst {
|
||||||
|
when (it) {
|
||||||
|
is CharacterReplyResponse -> it.replyId == id
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (index > 0) { // 0은 원본 댓글이므로 제외
|
||||||
|
adapter.items.removeAt(index)
|
||||||
|
adapter.notifyItemRemoved(index)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
).apply {
|
||||||
|
items.clear()
|
||||||
|
items.add(original!!) // 헤더: 원본 댓글
|
||||||
|
}
|
||||||
|
|
||||||
|
val recyclerView = binding.rvCommentReply
|
||||||
|
recyclerView.setHasFixedSize(true)
|
||||||
|
recyclerView.layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false)
|
||||||
|
recyclerView.addItemDecoration(object : RecyclerView.ItemDecoration() {
|
||||||
|
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
|
||||||
|
super.getItemOffsets(outRect, view, parent, state)
|
||||||
|
outRect.left = 13.3f.dpToPx().toInt()
|
||||||
|
outRect.right = 13.3f.dpToPx().toInt()
|
||||||
|
when (parent.getChildAdapterPosition(view)) {
|
||||||
|
0 -> { outRect.top = 13.3f.dpToPx().toInt(); outRect.bottom = 12f.dpToPx().toInt() }
|
||||||
|
adapter.itemCount - 1 -> { outRect.top = 12f.dpToPx().toInt(); outRect.bottom = 13.3f.dpToPx().toInt() }
|
||||||
|
else -> { outRect.top = 12f.dpToPx().toInt(); outRect.bottom = 12f.dpToPx().toInt() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
|
super.onScrolled(recyclerView, dx, dy)
|
||||||
|
val lm = recyclerView.layoutManager as LinearLayoutManager
|
||||||
|
val last = lm.findLastCompletelyVisibleItemPosition()
|
||||||
|
val total = (recyclerView.adapter?.itemCount ?: 1) - 1
|
||||||
|
if (!recyclerView.canScrollVertically(1) && last == total) {
|
||||||
|
loadMore()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
recyclerView.adapter = adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun hideKeyboard() {
|
||||||
|
imm.hideSoftInputFromWindow(view?.windowToken, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 스텁 페이징: 더미 데이터 생성
|
||||||
|
private fun loadMore() {
|
||||||
|
// 원본 댓글이 있어야 함
|
||||||
|
val originalId = original?.commentId ?: return
|
||||||
|
val newItems = (1..10).map { idx ->
|
||||||
|
val id = page * 10_000L + idx
|
||||||
|
val writerId = if (idx % 5 == 0) SharedPreferenceManager.userId else -idx.toLong()
|
||||||
|
CharacterReplyResponse(
|
||||||
|
replyId = id,
|
||||||
|
memberId = writerId,
|
||||||
|
memberProfileImage = "",
|
||||||
|
memberNickname = "게스트$id",
|
||||||
|
createdAt = System.currentTimeMillis() - (idx * 60_000L * page),
|
||||||
|
comment = "캐릭터 답글 예시 텍스트 $originalId-$id"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val start = adapter.items.size
|
||||||
|
adapter.items.addAll(newItems)
|
||||||
|
adapter.notifyItemRangeInserted(start, newItems.size)
|
||||||
|
page++
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val EXTRA_CHARACTER_ID = "extra_character_id"
|
||||||
|
private const val EXTRA_ORIGINAL_COMMENT_ID = "extra_original_comment_id"
|
||||||
|
private const val EXTRA_ORIGINAL_MEMBER_ID = "extra_original_member_id"
|
||||||
|
private const val EXTRA_ORIGINAL_MEMBER_PROFILE_IMAGE = "extra_original_member_profile_image"
|
||||||
|
private const val EXTRA_ORIGINAL_MEMBER_NICKNAME = "extra_original_member_nickname"
|
||||||
|
private const val EXTRA_ORIGINAL_CREATED_AT = "extra_original_created_at"
|
||||||
|
private const val EXTRA_ORIGINAL_REPLY_COUNT = "extra_original_reply_count"
|
||||||
|
private const val EXTRA_ORIGINAL_COMMENT_TEXT = "extra_original_comment_text"
|
||||||
|
fun newInstance(characterId: Long, original: CharacterCommentResponse): CharacterCommentReplyFragment {
|
||||||
|
val args = Bundle().apply {
|
||||||
|
putLong(EXTRA_CHARACTER_ID, characterId)
|
||||||
|
putLong(EXTRA_ORIGINAL_COMMENT_ID, original.commentId)
|
||||||
|
putLong(EXTRA_ORIGINAL_MEMBER_ID, original.memberId)
|
||||||
|
putString(EXTRA_ORIGINAL_MEMBER_PROFILE_IMAGE, original.memberProfileImage)
|
||||||
|
putString(EXTRA_ORIGINAL_MEMBER_NICKNAME, original.memberNickname)
|
||||||
|
putLong(EXTRA_ORIGINAL_CREATED_AT, original.createdAt)
|
||||||
|
putInt(EXTRA_ORIGINAL_REPLY_COUNT, original.replyCount)
|
||||||
|
putString(EXTRA_ORIGINAL_COMMENT_TEXT, original.comment)
|
||||||
|
}
|
||||||
|
return CharacterCommentReplyFragment().apply { arguments = args }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
124
app/src/main/res/layout/fragment_character_comment_reply.xml
Normal file
124
app/src/main/res/layout/fragment_character_comment_reply.xml
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@drawable/bg_top_round_corner_16_7_222222">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/rl_toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_back"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="13.3dp"
|
||||||
|
android:drawablePadding="6.7dp"
|
||||||
|
android:fontFamily="@font/gmarket_sans_medium"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:text="답글"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="14.7sp"
|
||||||
|
app:drawableStartCompat="@drawable/ic_back"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@+id/iv_close"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/iv_close" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/iv_close"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:contentDescription="@null"
|
||||||
|
android:paddingHorizontal="13.3dp"
|
||||||
|
android:paddingTop="12dp"
|
||||||
|
android:paddingBottom="12dp"
|
||||||
|
android:src="@drawable/ic_circle_x_white"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/divider"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_below="@+id/rl_toolbar"
|
||||||
|
android:layout_marginHorizontal="13.3dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:background="#78909C" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/ll_comment_input"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_below="@+id/divider"
|
||||||
|
android:background="@drawable/bg_top_round_corner_8_222222"
|
||||||
|
android:elevation="6dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="13.3dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/iv_comment_profile"
|
||||||
|
android:layout_width="33.3dp"
|
||||||
|
android:layout_height="33.3dp"
|
||||||
|
android:contentDescription="@null"
|
||||||
|
tools:src="@drawable/ic_placeholder_profile" />
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/rl_input_comment"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/et_comment"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_round_corner_10_232323_3bb9f1"
|
||||||
|
android:hint="답글을 입력해 보세요"
|
||||||
|
android:importantForAutofill="no"
|
||||||
|
android:inputType="text|textMultiLine"
|
||||||
|
android:maxLines="5"
|
||||||
|
android:paddingVertical="13.3dp"
|
||||||
|
android:paddingStart="13.3dp"
|
||||||
|
android:paddingEnd="50.7dp"
|
||||||
|
android:textColor="@color/color_eeeeee"
|
||||||
|
android:textColorHint="@color/color_777777"
|
||||||
|
android:textCursorDrawable="@drawable/edit_text_cursor"
|
||||||
|
android:textSize="13.3sp"
|
||||||
|
tools:ignore="LabelFor" />
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/iv_comment_send"
|
||||||
|
android:layout_width="33.3dp"
|
||||||
|
android:layout_height="33.3dp"
|
||||||
|
android:layout_alignParentEnd="true"
|
||||||
|
android:layout_centerVertical="true"
|
||||||
|
android:layout_marginEnd="6dp"
|
||||||
|
android:contentDescription="@null"
|
||||||
|
android:src="@drawable/btn_message_send" />
|
||||||
|
</RelativeLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:id="@+id/divider2"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_below="@+id/ll_comment_input"
|
||||||
|
android:layout_marginHorizontal="13.3dp"
|
||||||
|
android:background="#78909C" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rv_comment_reply"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_below="@+id/divider2"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:padding="13.3dp" />
|
||||||
|
</RelativeLayout>
|
||||||
82
app/src/main/res/layout/item_character_comment_reply.xml
Normal file
82
app/src/main/res/layout/item_character_comment_reply.xml
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingStart="24dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/iv_comment_profile"
|
||||||
|
android:layout_width="30dp"
|
||||||
|
android:layout_height="30dp"
|
||||||
|
android:contentDescription="@null"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
tools:src="@drawable/ic_placeholder_profile" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/ll_comment_nickname"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
app:layout_constraintEnd_toStartOf="@+id/iv_menu"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/iv_comment_profile"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/iv_comment_profile">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_comment_nickname"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="@font/pretendard_bold"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="14sp"
|
||||||
|
tools:text="닉네임" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/iv_menu"
|
||||||
|
android:layout_width="20dp"
|
||||||
|
android:layout_height="20dp"
|
||||||
|
android:contentDescription="@null"
|
||||||
|
android:src="@drawable/ic_seemore_vertical_white"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/iv_comment_profile" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_comment_date"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="2dp"
|
||||||
|
android:fontFamily="@font/pretendard_regular"
|
||||||
|
android:textColor="@color/color_b0bec5"
|
||||||
|
android:textSize="12sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/ll_comment_nickname"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/ll_comment_nickname"
|
||||||
|
tools:text="2시간전" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_comment"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:fontFamily="@font/pretendard_regular"
|
||||||
|
android:lineSpacingExtra="4dp"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="@+id/tv_comment_date"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/tv_comment_date"
|
||||||
|
tools:text="답글 내용이 표시됩니다." />
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="1dp"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:background="#78909C"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@+id/tv_comment" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
Reference in New Issue
Block a user