feat(chat-character): 신규 캐릭터 전체보기 화면 및 API 연동 추가
This commit is contained in:
@@ -191,6 +191,7 @@
|
|||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity android:name=".chat.character.newcharacters.NewCharactersAllActivity" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"
|
android:name="com.google.android.gms.oss.licenses.OssLicensesMenuActivity"
|
||||||
|
|||||||
@@ -43,6 +43,14 @@ interface CharacterApi {
|
|||||||
@Query("size") size: Int
|
@Query("size") size: Int
|
||||||
): Single<ApiResponse<CharacterImageListResponse>>
|
): Single<ApiResponse<CharacterImageListResponse>>
|
||||||
|
|
||||||
|
// 신규 캐릭터 전체보기
|
||||||
|
@GET("/api/chat/character/recent")
|
||||||
|
fun getRecentCharacters(
|
||||||
|
@Header("Authorization") authHeader: String,
|
||||||
|
@Query("page") page: Int,
|
||||||
|
@Query("size") size: Int
|
||||||
|
): Single<ApiResponse<kr.co.vividnext.sodalive.chat.character.newcharacters.RecentCharactersResponse>>
|
||||||
|
|
||||||
@POST("/api/chat/character/image/purchase")
|
@POST("/api/chat/character/image/purchase")
|
||||||
fun purchaseCharacterImage(
|
fun purchaseCharacterImage(
|
||||||
@Header("Authorization") authHeader: String,
|
@Header("Authorization") authHeader: String,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import kr.co.vividnext.sodalive.base.SodaDialog
|
|||||||
import kr.co.vividnext.sodalive.chat.character.curation.CurationSectionAdapter
|
import kr.co.vividnext.sodalive.chat.character.curation.CurationSectionAdapter
|
||||||
import kr.co.vividnext.sodalive.chat.character.detail.CharacterDetailActivity
|
import kr.co.vividnext.sodalive.chat.character.detail.CharacterDetailActivity
|
||||||
import kr.co.vividnext.sodalive.chat.character.detail.CharacterDetailActivity.Companion.EXTRA_CHARACTER_ID
|
import kr.co.vividnext.sodalive.chat.character.detail.CharacterDetailActivity.Companion.EXTRA_CHARACTER_ID
|
||||||
|
import kr.co.vividnext.sodalive.chat.character.newcharacters.NewCharactersAllActivity
|
||||||
import kr.co.vividnext.sodalive.chat.character.recent.RecentCharacterAdapter
|
import kr.co.vividnext.sodalive.chat.character.recent.RecentCharacterAdapter
|
||||||
import kr.co.vividnext.sodalive.common.LoadingDialog
|
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
@@ -289,6 +290,14 @@ class CharacterTabFragment : BaseFragment<FragmentCharacterTabBinding>(
|
|||||||
recyclerView.adapter = newCharacterAdapter
|
recyclerView.adapter = newCharacterAdapter
|
||||||
|
|
||||||
binding.tvNewCharacterAll.setOnClickListener {
|
binding.tvNewCharacterAll.setOnClickListener {
|
||||||
|
ensureLoginAndAuth {
|
||||||
|
startActivity(
|
||||||
|
Intent(
|
||||||
|
requireContext(),
|
||||||
|
NewCharactersAllActivity::class.java
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 신규 캐릭터 LiveData 구독
|
// 신규 캐릭터 LiveData 구독
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package kr.co.vividnext.sodalive.chat.character.newcharacters
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import kr.co.vividnext.sodalive.base.BaseActivity
|
||||||
|
import kr.co.vividnext.sodalive.chat.character.detail.CharacterDetailActivity
|
||||||
|
import kr.co.vividnext.sodalive.chat.character.detail.CharacterDetailActivity.Companion.EXTRA_CHARACTER_ID
|
||||||
|
import kr.co.vividnext.sodalive.common.GridSpacingItemDecoration
|
||||||
|
import kr.co.vividnext.sodalive.common.LoadingDialog
|
||||||
|
import kr.co.vividnext.sodalive.databinding.ActivityNewCharactersAllBinding
|
||||||
|
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||||
|
import org.koin.android.ext.android.inject
|
||||||
|
|
||||||
|
class NewCharactersAllActivity : BaseActivity<ActivityNewCharactersAllBinding>(
|
||||||
|
ActivityNewCharactersAllBinding::inflate
|
||||||
|
) {
|
||||||
|
private val viewModel: NewCharactersAllViewModel by inject()
|
||||||
|
|
||||||
|
private lateinit var adapter: NewCharactersAllAdapter
|
||||||
|
private lateinit var loadingDialog: LoadingDialog
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setupView()
|
||||||
|
bindData()
|
||||||
|
viewModel.loadMore()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setupView() {
|
||||||
|
loadingDialog = LoadingDialog(this, layoutInflater)
|
||||||
|
binding.toolbar.tvBack.text = "신규 캐릭터 전체보기"
|
||||||
|
binding.toolbar.tvBack.setOnClickListener { finish() }
|
||||||
|
|
||||||
|
val spanCount = 3
|
||||||
|
val spacingPx = 8f.dpToPx().toInt()
|
||||||
|
|
||||||
|
adapter = NewCharactersAllAdapter { characterId ->
|
||||||
|
startActivity(
|
||||||
|
Intent(this, CharacterDetailActivity::class.java).apply {
|
||||||
|
putExtra(EXTRA_CHARACTER_ID, characterId)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.rvCharacters.layoutManager = GridLayoutManager(this, spanCount)
|
||||||
|
binding.rvCharacters.addItemDecoration(
|
||||||
|
GridSpacingItemDecoration(
|
||||||
|
spanCount,
|
||||||
|
spacingPx,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
binding.rvCharacters.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||||
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
|
super.onScrolled(recyclerView, dx, dy)
|
||||||
|
val lastVisibleItemPosition = (recyclerView.layoutManager as LinearLayoutManager)
|
||||||
|
.findLastVisibleItemPosition()
|
||||||
|
val totalItemCount = recyclerView.adapter?.itemCount ?: 0
|
||||||
|
if (
|
||||||
|
!recyclerView.canScrollVertically(1) &&
|
||||||
|
lastVisibleItemPosition >= totalItemCount - 1
|
||||||
|
) {
|
||||||
|
viewModel.loadMore()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
binding.rvCharacters.adapter = adapter
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindData() {
|
||||||
|
viewModel.isLoading.observe(this) { isLoading ->
|
||||||
|
if (isLoading) loadingDialog.show(screenWidth) else loadingDialog.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.totalCount.observe(this) { count ->
|
||||||
|
binding.tvTotalCount.text = "$count"
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.items.observe(this) { list ->
|
||||||
|
adapter.addItems(list.drop(adapter.itemCount))
|
||||||
|
binding.rvCharacters.visibility = if (list.isEmpty()) View.GONE else View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.toastLiveData.observe(this) { message ->
|
||||||
|
message?.let { showToast(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package kr.co.vividnext.sodalive.chat.character.newcharacters
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import coil.load
|
||||||
|
import coil.transform.RoundedCornersTransformation
|
||||||
|
import kr.co.vividnext.sodalive.R
|
||||||
|
import kr.co.vividnext.sodalive.chat.character.Character
|
||||||
|
import kr.co.vividnext.sodalive.databinding.ItemNewCharacterAllBinding
|
||||||
|
import kr.co.vividnext.sodalive.extensions.dpToPx
|
||||||
|
|
||||||
|
class NewCharactersAllAdapter(
|
||||||
|
private val onClick: (Long) -> Unit
|
||||||
|
) : RecyclerView.Adapter<NewCharactersAllAdapter.VH>() {
|
||||||
|
|
||||||
|
private val items = mutableListOf<Character>()
|
||||||
|
|
||||||
|
inner class VH(val binding: ItemNewCharacterAllBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||||
|
fun bind(item: Character) {
|
||||||
|
binding.tvCharacterName.text = item.name
|
||||||
|
binding.tvCharacterDescription.text = item.description
|
||||||
|
binding.ivCharacter.load(item.imageUrl) {
|
||||||
|
crossfade(true)
|
||||||
|
placeholder(R.drawable.ic_logo_service_center)
|
||||||
|
transformations(RoundedCornersTransformation(16f.dpToPx()))
|
||||||
|
}
|
||||||
|
binding.root.setOnClickListener { onClick(item.characterId) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
|
||||||
|
val binding = ItemNewCharacterAllBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||||
|
return VH(binding)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount(): Int = items.size
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: VH, position: Int) {
|
||||||
|
holder.bind(items[position])
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addItems(newItems: List<Character>) {
|
||||||
|
val start = items.size
|
||||||
|
items.addAll(newItems)
|
||||||
|
notifyItemRangeInserted(start, newItems.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
|
fun clear() {
|
||||||
|
items.clear()
|
||||||
|
notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package kr.co.vividnext.sodalive.chat.character.newcharacters
|
||||||
|
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import com.orhanobut.logger.Logger
|
||||||
|
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||||
|
import kr.co.vividnext.sodalive.base.BaseViewModel
|
||||||
|
import kr.co.vividnext.sodalive.chat.character.Character
|
||||||
|
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
|
||||||
|
|
||||||
|
class NewCharactersAllViewModel(
|
||||||
|
private val repository: NewCharactersRepository
|
||||||
|
) : BaseViewModel() {
|
||||||
|
|
||||||
|
private val _toastLiveData = MutableLiveData<String?>()
|
||||||
|
val toastLiveData: LiveData<String?> get() = _toastLiveData
|
||||||
|
|
||||||
|
private val _isLoading = MutableLiveData(false)
|
||||||
|
val isLoading: LiveData<Boolean> get() = _isLoading
|
||||||
|
|
||||||
|
private val _totalCount = MutableLiveData<Long>(0)
|
||||||
|
val totalCount: LiveData<Long> get() = _totalCount
|
||||||
|
|
||||||
|
private val _items = MutableLiveData<List<Character>>(emptyList())
|
||||||
|
val items: LiveData<List<Character>> get() = _items
|
||||||
|
|
||||||
|
private var page = 0
|
||||||
|
private val size = 20
|
||||||
|
private var isLast = false
|
||||||
|
|
||||||
|
fun loadMore() {
|
||||||
|
if (_isLoading.value == true || isLast) return
|
||||||
|
_isLoading.value = true
|
||||||
|
|
||||||
|
compositeDisposable.add(
|
||||||
|
repository.getRecentCharacters(
|
||||||
|
token = "Bearer ${SharedPreferenceManager.token}",
|
||||||
|
page = page,
|
||||||
|
size = size
|
||||||
|
)
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
.subscribe({ response ->
|
||||||
|
val data = response.data
|
||||||
|
if (response.success && data != null) {
|
||||||
|
val current = _items.value ?: emptyList()
|
||||||
|
val next = current + data.content
|
||||||
|
_items.value = next
|
||||||
|
_totalCount.value = data.totalCount
|
||||||
|
if (data.content.isNotEmpty()) {
|
||||||
|
page += 1
|
||||||
|
} else {
|
||||||
|
isLast = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_toastLiveData.value = response.message
|
||||||
|
?: "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||||
|
}
|
||||||
|
_isLoading.value = false
|
||||||
|
}, { e ->
|
||||||
|
_isLoading.value = false
|
||||||
|
e.message?.let { Logger.e(it) }
|
||||||
|
_toastLiveData.value = "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package kr.co.vividnext.sodalive.chat.character.newcharacters
|
||||||
|
|
||||||
|
import io.reactivex.rxjava3.core.Single
|
||||||
|
import kr.co.vividnext.sodalive.chat.character.CharacterApi
|
||||||
|
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||||
|
|
||||||
|
class NewCharactersRepository(
|
||||||
|
private val api: CharacterApi
|
||||||
|
) {
|
||||||
|
fun getRecentCharacters(
|
||||||
|
token: String,
|
||||||
|
page: Int,
|
||||||
|
size: Int
|
||||||
|
): Single<ApiResponse<RecentCharactersResponse>> {
|
||||||
|
return api.getRecentCharacters(
|
||||||
|
authHeader = token,
|
||||||
|
page = page,
|
||||||
|
size = size
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
package kr.co.vividnext.sodalive.chat.character.newcharacters
|
||||||
|
|
||||||
|
import androidx.annotation.Keep
|
||||||
|
import com.google.gson.annotations.SerializedName
|
||||||
|
import kr.co.vividnext.sodalive.chat.character.Character
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
data class RecentCharactersResponse(
|
||||||
|
@SerializedName("totalCount") val totalCount: Long,
|
||||||
|
@SerializedName("content") val content: List<Character>
|
||||||
|
)
|
||||||
@@ -67,6 +67,8 @@ import kr.co.vividnext.sodalive.audition.role.AuditionRoleDetailViewModel
|
|||||||
import kr.co.vividnext.sodalive.chat.character.CharacterApi
|
import kr.co.vividnext.sodalive.chat.character.CharacterApi
|
||||||
import kr.co.vividnext.sodalive.chat.character.CharacterTabRepository
|
import kr.co.vividnext.sodalive.chat.character.CharacterTabRepository
|
||||||
import kr.co.vividnext.sodalive.chat.character.CharacterTabViewModel
|
import kr.co.vividnext.sodalive.chat.character.CharacterTabViewModel
|
||||||
|
import kr.co.vividnext.sodalive.chat.character.newcharacters.NewCharactersAllViewModel
|
||||||
|
import kr.co.vividnext.sodalive.chat.character.newcharacters.NewCharactersRepository
|
||||||
import kr.co.vividnext.sodalive.chat.character.comment.CharacterCommentApi
|
import kr.co.vividnext.sodalive.chat.character.comment.CharacterCommentApi
|
||||||
import kr.co.vividnext.sodalive.chat.character.comment.CharacterCommentRepository
|
import kr.co.vividnext.sodalive.chat.character.comment.CharacterCommentRepository
|
||||||
import kr.co.vividnext.sodalive.chat.character.detail.detail.CharacterDetailRepository
|
import kr.co.vividnext.sodalive.chat.character.detail.detail.CharacterDetailRepository
|
||||||
@@ -364,6 +366,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
|
|||||||
viewModel { TalkTabViewModel(get()) }
|
viewModel { TalkTabViewModel(get()) }
|
||||||
viewModel { kr.co.vividnext.sodalive.chat.character.comment.CharacterCommentListViewModel(get()) }
|
viewModel { kr.co.vividnext.sodalive.chat.character.comment.CharacterCommentListViewModel(get()) }
|
||||||
viewModel { kr.co.vividnext.sodalive.chat.character.comment.CharacterCommentReplyViewModel(get()) }
|
viewModel { kr.co.vividnext.sodalive.chat.character.comment.CharacterCommentReplyViewModel(get()) }
|
||||||
|
viewModel { NewCharactersAllViewModel(get()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private val repositoryModule = module {
|
private val repositoryModule = module {
|
||||||
@@ -413,6 +416,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
|
|||||||
factory { CharacterGalleryRepository(get()) }
|
factory { CharacterGalleryRepository(get()) }
|
||||||
factory { TalkTabRepository(get()) }
|
factory { TalkTabRepository(get()) }
|
||||||
factory { CharacterCommentRepository(get()) }
|
factory { CharacterCommentRepository(get()) }
|
||||||
|
factory { NewCharactersRepository(get()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
61
app/src/main/res/layout/activity_new_characters_all.xml
Normal file
61
app/src/main/res/layout/activity_new_characters_all.xml
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="@color/black"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<include
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
layout="@layout/detail_toolbar" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_vertical"
|
||||||
|
android:layout_marginTop="13.3dp"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingHorizontal="24dp"
|
||||||
|
android:paddingVertical="6.7dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="@font/gmarket_sans_medium"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="전체"
|
||||||
|
android:textColor="@color/color_e2e2e2"
|
||||||
|
android:textSize="13.3sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_total_count"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:fontFamily="@font/gmarket_sans_medium"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="0"
|
||||||
|
android:textColor="@color/color_ff5c49"
|
||||||
|
android:textSize="13.3sp" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="2dp"
|
||||||
|
android:fontFamily="@font/gmarket_sans_medium"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="개"
|
||||||
|
android:textColor="@color/color_e2e2e2"
|
||||||
|
android:textSize="13.3sp" />
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/rv_characters"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:padding="12dp"
|
||||||
|
tools:listitem="@layout/item_new_character_all" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -160,8 +160,7 @@
|
|||||||
android:fontFamily="@font/pretendard_regular"
|
android:fontFamily="@font/pretendard_regular"
|
||||||
android:text="전체보기"
|
android:text="전체보기"
|
||||||
android:textColor="#90A4AE"
|
android:textColor="#90A4AE"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp" />
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
|||||||
52
app/src/main/res/layout/item_new_character_all.xml
Normal file
52
app/src/main/res/layout/item_new_character_all.xml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<?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="0dp"
|
||||||
|
android:paddingEnd="0dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/iv_character"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:scaleType="centerCrop"
|
||||||
|
android:contentDescription="@null"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintDimensionRatio="1:1"
|
||||||
|
tools:src="@drawable/ic_logo_service_center" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_character_name"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="@color/color_b0bec5"
|
||||||
|
android:textSize="18sp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/iv_character"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
tools:text="캐릭터 이름" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tv_character_description"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="6dp"
|
||||||
|
android:layout_marginTop="4dp"
|
||||||
|
android:ellipsize="end"
|
||||||
|
android:maxLines="1"
|
||||||
|
android:textColor="#78909C"
|
||||||
|
android:textSize="14sp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/tv_character_name"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
tools:text="설명 텍스트" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
Reference in New Issue
Block a user