Kotlin TIL(19) [AppleMarket 과제]

jxxn_a·2023년 9월 1일
0

TIL

목록 보기
19/28

🍎 AppleMarket 🍎


[필수 과제]

1) 사과마켓 메인 페이지
2) 판매상품 상세 페이지

📂 메인페이지 필수 구현 📂

  • 디자인 및 화면 구성을 최대한 동일하게 해주세요. (사이즈 및 여백도 최대한 맞춰주세요.) ✨
  • 상품 데이터는 아래 dummy data 를 사용합니다. (더미 데이터는 자유롭게 추가 및 수정 가능)
  • 데미데이터 : [이미지 링크], [상품 리스트 링크]
  • RecyclerViewer를 이용해 리스트 화면을 만들어주세요.
  • 상단 툴바를 제거하고 풀스크린 화면으로 세팅해주세요. (statusbar는 남기고)
  • 상품 이미지는 모서리를 라운드 처리해주세요.
  • 상품 이름은 최대 두 줄이고, 그래도 넘어가면 뒷 부분에 …으로 처리해주세요.
  • 뒤로가기(BACK)버튼 클릭시 종료하시겠습니까? [확인][취소] 다이얼로그를 띄워주세요.
  • 상단 종모양 아이콘을 누르면 Notification을 생성해 주세요.
  • 상품 가격은 1000단위로 콤마(,) 처리해주세요.
  • 상품 아이템들 사이에 회색 라인을 추가해서 구분해주세요.
  • 상품 선택시 아래 상품 상세 페이지로 이동합니다.
  • 상품 상세페이지 이동시 intent로 객체를 전달합니다. (Parcelize 사용)

📂 상세페이지 필수 구현

  • 디자인 및 화면 구성을 최대한 동일하게 해주세요.
    (사이즈 및 여백도 최대한 맞춰주세요.) ✨
  • 메인화면에서 전달받은 데이터로 판매자, 주소, 아이템, 글내용, 가격등을 화면에 표시합니다.
  • 하단 가격표시 레이아웃을 제외하고 전체화면은 스크롤이 되어야합니다.
  • 상단 < 버튼을 누르면 상세 화면은 종료되고 메인화면으로 돌아갑니다.

📢 잊지말고 Gradle에 viewBinding 추가하기 📢

Gradle Scripts -> build.gradle(Module :app) -> android { 열려있는 곳 마지막에 아래 코드 넣기 -> 상단 snyk 누르기

    buildFeatures {
        viewBinding = true
    }

📢 Parcelize를 사용하기 위해 추가적으로 해야할 것 📢

Gradle Scripts -> build.gradle(Module :app) -> plugins { 열린 부분에 아래 코드 넣기 -> 상단 snyk 누르기

id 'kotlin-parcelize'

💫 Feature 💫

📑 MainActivity 📑

class MainActivity : AppCompatActivity() {

    private lateinit var binding:ActivityMainBinding
    private val dataList = mutableListOf<MarketInfo>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val appleList=Constants.getAppleMarketData()

        val adapter = Adapter(appleList)
        binding.recyclerview.adapter = adapter
        binding.recyclerview.layoutManager = LinearLayoutManager(this)
        val intent = Intent(this,DetailPage::class.java)
        adapter.setOnClickListener(object: com.example.applemarket.Adapter.OnClickListener {
            override fun onClick(position: Int, model: MarketInfo) {
                intent.putExtra("MarketInfo", model)
                startActivity(intent)
            }
        })

        binding.bellImage.setOnClickListener {
            notification()
        }
    }

    fun notification() {
        val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
        val builder: NotificationCompat.Builder
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channelId = "one-channel"
            val channelName = "My Channel"
            val channel = NotificationChannel(
                channelId,
                channelName,
                NotificationManager.IMPORTANCE_DEFAULT
            )
            manager.createNotificationChannel(channel)
            builder = NotificationCompat.Builder(this, channelId)
        } else {
            builder = NotificationCompat.Builder(this)
        }

        builder.run {
            setSmallIcon(R.drawable.notification_icon)
            setContentTitle("키워드 알림")
            setContentText("설정한 키워드에 대한 알림이 도착했습니다!!")
        }
        manager.notify(1, builder.build())
    }

    override fun onBackPressed() {
        val alertDialog = AlertDialog.Builder(this)
        alertDialog.setTitle("종료")
        alertDialog.setMessage("정말 종료하시겠습니까?")
        alertDialog.setIcon(R.drawable.chat)
        alertDialog.setPositiveButton("확인") { dialog, which ->
            finish()
        }
        alertDialog.setNegativeButton("취소", null)
        alertDialog.show()
    }
}

[MainActivity를 구현하면서 어려웠던 부분들]

val appleList=Constants.getAppleMarketData()
// Constants 파일에서 Data 가져오기

val intent = Intent(this,DetailPage::class.java)
adapter.setOnClickListener(object: com.example.applemarket.Adapter.OnClickListener{
    override fun onClick(position: Int, model: MarketInfo) {
        intent.putExtra("MarketInfo", model)
        startActivity(intent)
    }
// 메인페이지 중 하나를 눌렀을 때 DetailPage로 정보 intent하기
// override fun onClick에 Intent 할 정보 입력하기
// MarketInfo는 Constants 파일에 있음

📑 DetailActivity 📑

class DetailPage : AppCompatActivity() {

    private lateinit var binding: ActivityDetailBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityDetailBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val getItem = intent.getParcelableExtra<MarketInfo>("MarketInfo")
        if (getItem != null) {
            binding.picture.setImageResource(getItem.itemImage)
            binding.nickname.text = getItem.nickName
            binding.address.text = getItem.address
            binding.title.text = getItem.itemTitle
            binding.nextTitle.text = getItem.itemInfo
            binding.price.text = (String.format("%,d원",getItem.priceInfo))
        }

        val back : ImageButton = binding.backButton
        back.setOnClickListener {
            val intent : Intent = Intent(this, MainActivity::class.java)
            startActivity(intent)
        }

    }
}

[DetailActivity를 구현하면서 어려웠던 부분들]

val get Item = intent.getParcedlableExtra<MarketInfo>("MarketInfo")
// MainActivity에서 putExtra된 정보를 intent받는 과정

if (getItem != null) {
    binding.price.text = (String.format("%,d원",getItem.priceInfo))
    
// price는 MarketInfo에서 Int이지만 binding 받아야 하는 건 String값이기 때문에 String.format과정을 통해 불러와준다.

📑 Adapter 📑

class Adapter(val items: MutableList<MarketInfo>) : RecyclerView.Adapter<Adapter.Holder>() {
    private var onClickListener : OnClickListener? = null

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Adapter.Holder {
        return Holder(
            ItemViewBinding.inflate(
                LayoutInflater.from(parent.context), parent, false
            )
        )
    }

    override fun onBindViewHolder(holder: Holder, position: Int) {
        val item = items[position]
        holder.imageView.setImageResource(items[position].itemImage)
        holder.itemTitle.text = items[position].itemTitle
        holder.address.text = items[position].address
        holder.priceInfo.text = (String.format("%,d원",items[position].priceInfo))
        holder.chat.text = items[position].chat.toString()
        holder.heart.text = items[position].heart.toString()
        holder.itemView.setOnClickListener {
            if(onClickListener != null) {
                onClickListener!!.onClick(position, item)
            }
        }
    }

    override fun getItemCount(): Int {
        return items.size
    }

    fun setOnClickListener(onClickListner : OnClickListener) {
        this.onClickListener = onClickListner
    }

    interface OnClickListener {
        fun onClick(position: Int, model: MarketInfo)
    }

    class Holder(val binding: ItemViewBinding) : RecyclerView.ViewHolder(binding.root) {
        val imageView = binding.imageView
        val itemTitle = binding.titleText
        val address = binding.locationText
        val priceInfo = binding.price
        val chat = binding.chatNumber
        val heart = binding.heartNumber
    }
}

[Adapter를 구현하면서 어려웠던 부분들]

val item = items[position]
holder.priceInfo.text = (String.format("%,d원",items[position].priceInfo))
// price는 MarketInfo에서 Int이지만 binding 받아야 하는 건 String값이기 때문에 String.format과정을 통해 불러와준다.

📑 Constants 📑

object Constants {
    fun getAppleMarketData(): MutableList<MarketInfo> {

        val appleList = mutableListOf<MarketInfo>()
        val data1 = MarketInfo(
            R.drawable.sample1,
            "산지 한달된 선풍기 팝니다",
            "이사가서 필요가 없어졌어요 급하게 내놓습니다",
            "대현동",
            1000,
            "서울 서대문구 창천동",
            13,
            25
        )
        appleList.add(data1)
        val data2 = MarketInfo(
            R.drawable.sample2,
            "김치냉장고",
            "이사로인해 내놔요",
            "안마담",
            20000,
            "인천 계양구 귤현동",
            8,
            28
        )
        appleList.add(data2)
        val data3 = MarketInfo(
            R.drawable.sample3,
            "샤넬 카드지갑",
            "고퀄지갑이구요\n사용감이 있어서 싸게 내어둡니다",
            "코코유",
            10000,
            "수성구 범어동",
            23,
            5
        )
        appleList.add(data3)
        val data4 = MarketInfo(
            R.drawable.sample4,
            "금고",
            "금고\n떼서 가져가야함\n대우월드마크센텀\n미국이주관계로 싸게 팝니다",
            "Nicole",
            10000,
            "해운대구 우제2동",
            14,
            17
        )
        appleList.add(data4)
        val data5 = MarketInfo(
            R.drawable.sample5,
            "갤럭시Z플립3 팝니다",
            "갤럭시 Z플립3 그린 팝니다\n항시 케이스 씌워서 썻고 필름 한장챙겨드립니다\n화면에 살짝 스크래치난거 말고 크게 이상은없습니다!",
            "절명",
            150000,
            "연제구 연산제8동",
            22,
            9
        )
        appleList.add(data5)
        val data6 = MarketInfo(
            R.drawable.sample6,
            "프라다 복조리백",
            "까임 오염없고 상태 깨끗합니다\n정품여부모름",
            "미니멀하게",
            50000,
            "수원시 영통구 원천동",
            25,
            16
        )
        appleList.add(data6)
        val data7 = MarketInfo(
            R.drawable.sample7,
            "울산 동해오션뷰 60평 복층 펜트하우스 \n1일 숙박권 펜션 힐링 숙소 별장",
            "울산 동해바다뷰 60평 복층 펜트하우스 1일 숙박권\n(에어컨이 없기에 낮은 가격으로 변경했으며 8월 초 가장 더운날 다녀가신 분 경우 시원했다고 잘 지내다 가셨습니다)\n1. 인원: 6명 기준입니다. 1인 10,000원 추가요금\n2. 장소: 북구 블루마시티, 32-33층\n3. 취사도구, 침구류, 세면도구, 드라이기 2개, 선풍기 4대 구비\n4. 예약방법: 예약금 50,000원 하시면 저희는 명함을 드리며 입실 오전 잔금 입금하시면 저희는 동.호수를 알려드리며 고객님은 예약자분 신분증 앞면 주민번호 뒷자리 가리시거나 지우시고 문자로 보내주시면 저희는 카드키를 우편함에 놓아 둡니다.\n5. 33층 옥상 야외 테라스 있음, 가스버너 있음\n6. 고기 굽기 가능\n7. 입실 오후 3시, 오전 11시 퇴실, 정리, 정돈 , 밸브 잠금 부탁드립니다.\n8. 층간소음 주의 부탁드립니다.\n9. 방3개, 화장실3개, 비데 3개\n10. 저희 집안이 쓰는 별장입니다.",
            "굿리치",
            150000,
            "남구 옥동",
            142,
            54
        )
        appleList.add(data7)
        val data8 = MarketInfo(
            R.drawable.sample8,
            "샤넬 탑핸들 가방",
            "샤넬 트랜디 CC 탑핸들 스몰 램스킨 블랙 금장 플랩백 !\n \n색상 : 블랙\n사이즈 : 25.5cm * 17.5cm * 8cm\n구성 : 본품더스트\n\n급하게 돈이 필요해서 팝니다 ㅠ ㅠ",
            "난쉽",
            180000,
            "동래구 온천제2동",
            31,
            7
        )
        appleList.add(data8)
        val data9 = MarketInfo(
            R.drawable.sample9,
            "4행정 엔진분무기 판매합니다.",
            "3년전에 사서 한번 사용하고 그대로 둔 상태입니다. 요즘 사용은 안해봤습니다. 그래서 저렴하게 내 놓습니다. 중고라 반품은 어렵습니다.\n",
            "알뜰한",
            30000,
            "원주시 명륜2동",
            7,
            28
        )
        appleList.add(data9)
        val data10 = MarketInfo(
            R.drawable.sample10,
            "셀린느 버킷 가방",
            "22년 신세계 대전 구매입니당\n셀린느 버킷백\n구매해서 몇번사용했어요\n까짐 스크래치 없습니다.\n타지역에서 보내는거라 택배로 진행합니당!",
            "똑태현",
            190000,
            "중구 동화동",
            40,
            6
        )
        appleList.add(data10)

        return appleList

    }
}

📑 MarketInfo 📑

@Parcelize
data class MarketInfo(
    val itemImage : Int,
    val itemTitle : String,
    val itemInfo : String,
    val nickName : String,
    val priceInfo : Int,
    val address : String,
    val chat : Int,
    val heart : Int
) : Parcelable

💫 UI 💫

🎨 activity_main 🎨

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/toolBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="내배캠동"
        android:textStyle="bold"
        android:textSize="16sp"
        android:paddingVertical="20dp"
        android:paddingLeft="20dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/low_image"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:src="@drawable/low"
        android:layout_marginVertical="17dp"
        app:layout_constraintStart_toEndOf="@id/toolBar"
        app:layout_constraintTop_toTopOf="parent"
        />

    <ImageButton
        android:id="@+id/bell_image"
        android:layout_width="25dp"
        android:layout_height="25dp"
        android:adjustViewBounds="true"
        android:background="@android:color/transparent"
        android:scaleType="fitCenter"
        android:src="@drawable/bell"
        android:layout_marginRight="20dp"
        android:layout_marginVertical="20dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintTop_toBottomOf="@id/toolBar"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        >

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/recyclerview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            />
    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

🎨 activity_detail 🎨

<?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="match_parent">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/linear"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent">

            <ImageView
                android:id="@+id/picture"
                android:layout_width="match_parent"
                android:layout_height="350dp"
                android:scaleType="fitXY"
                android:src="@drawable/sample1"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <ImageButton
                android:id="@+id/back_button"
                android:layout_width="35dp"
                android:layout_height="35dp"
                android:src="@drawable/back"
                android:scaleType="fitXY"
                android:background="@android:color/transparent"
                android:layout_marginTop="20dp"
                android:layout_marginStart="15dp"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                />

            <View
                android:id="@+id/Infoview"
                android:layout_width="match_parent"
                android:layout_height="90dp"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintHorizontal_bias="1.0"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/picture" />

            <TextView
                android:id="@+id/temperature"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginEnd="80dp"
                android:layout_marginBottom="20dp"
                android:text="39.3°C"
                android:textColor="#059487"
                android:textSize="18sp"
                android:textStyle="bold"
                app:layout_constraintBottom_toBottomOf="@id/Infoview"
                app:layout_constraintEnd_toEndOf="@id/Infoview"
                app:layout_constraintTop_toTopOf="@id/Infoview" />

            <ImageView
                android:id="@+id/imoticon"
                android:layout_width="35dp"
                android:layout_height="35dp"
                android:layout_marginEnd="30dp"
                android:layout_marginTop="15dp"
                android:src="@drawable/imoticon"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintTop_toBottomOf="@id/picture"
                />

            <TextView
                android:id="@+id/mannerText"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginEnd="30dp"
                android:layout_marginTop="25dp"
                android:text="@string/manner_text"
                android:textColor="#AEACAC"
                app:layout_constraintTop_toTopOf="@id/imoticon"
                app:layout_constraintBottom_toBottomOf="@id/line"
                app:layout_constraintEnd_toEndOf="parent" />

            <View
                android:id="@+id/line"
                android:layout_width="match_parent"
                android:layout_height="1dp"
                android:background="#D9D9D9"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/Infoview" />

            <ImageView
                android:id="@+id/circle"
                android:layout_width="60dp"
                android:layout_height="60dp"
                android:layout_marginStart="20dp"
                android:src="@drawable/circle"
                app:layout_constraintBottom_toBottomOf="@id/Infoview"
                app:layout_constraintStart_toStartOf="@+id/Infoview"
                app:layout_constraintTop_toTopOf="@id/Infoview" />

            <TextView
                android:id="@+id/nickname"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="10dp"
                android:layout_marginBottom="20dp"
                android:text="닉네임"
                android:textSize="18sp"
                android:textStyle="bold"
                app:layout_constraintBottom_toBottomOf="@id/Infoview"
                app:layout_constraintStart_toEndOf="@id/circle"
                app:layout_constraintTop_toTopOf="@id/Infoview" />

            <TextView
                android:id="@+id/address"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="10dp"
                android:layout_marginTop="8dp"
                android:layout_marginBottom="30dp"
                android:text="서울특별시 송파구"
                android:textSize="15sp"
                android:textStyle="bold"
                app:layout_constraintBottom_toBottomOf="@id/Infoview"
                app:layout_constraintStart_toEndOf="@id/circle"
                app:layout_constraintTop_toBottomOf="@id/nickname" />

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginBottom="50dp"
                android:orientation="vertical"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintTop_toBottomOf="@id/Infoview">

                <TextView
                    android:id="@+id/title"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="20dp"
                    android:layout_marginTop="15dp"
                    android:text="제목"
                    android:textSize="20sp"
                    android:textStyle="bold" />

                <TextView
                    android:id="@+id/nextTitle"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="20dp"
                    android:layout_marginTop="20dp"
                    android:layout_marginEnd="20dp"
                    android:text="상품 상세 설명"
                    android:textSize="16sp"
                    android:textStyle="bold" />

            </LinearLayout>

        </androidx.constraintlayout.widget.ConstraintLayout>

    </ScrollView>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/linear"
        android:layout_width="match_parent"
        android:layout_height="70dp"
        android:orientation="horizontal"
        app:layout_constraintBottom_toBottomOf="parent"
        android:backgroundTint="@color/white"
        >

        <ImageView
            android:id="@+id/heart"
            android:layout_width="30dp"
            android:layout_height="30dp"
            android:src="@drawable/heart"
            android:layout_marginStart="20dp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            />

        <View
            android:id="@+id/vertical_grey"
            android:layout_width="1dp"
            android:layout_height="45dp"
            android:layout_marginStart="20dp"
            android:background="#D9D9D9"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toEndOf="@id/heart"
            />

        <TextView
            android:id="@+id/price"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="20dp"
            android:text="100,000"
            android:textSize="20sp"
            android:textStyle="bold"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toEndOf="@id/vertical_grey"
            />

        <TextView
            android:id="@+id/chat_button"
            android:layout_width="100dp"
            android:layout_height="40dp"
            android:background="@drawable/orange_background"
            android:gravity="center"
            android:text="채팅하기"
            android:textColor="@color/white"
            android:textSize="16sp"
            android:textStyle="bold"
            android:layout_marginEnd="20dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            />

    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

🎨 item_view 🎨

<?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">

    <androidx.cardview.widget.CardView
        android:id="@+id/cardView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_margin="10dp"
        app:cardCornerRadius="10dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <ImageView
            android:id="@+id/image_view"
            android:layout_width="135dp"
            android:layout_height="135dp"
            android:scaleType="fitEnd"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.cardview.widget.CardView>

    <TextView
        android:id="@+id/title_text"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintWidth_percent="0.6"
        android:layout_marginVertical="18dp"
        android:layout_marginLeft="10dp"
        android:text="Title"
        android:maxLines="2"
        android:ellipsize="end"
        android:textSize="14sp"
        app:layout_constraintStart_toEndOf="@id/cardView"
        app:layout_constraintTop_toTopOf="parent"
        />

    <TextView
        android:id="@+id/location_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginVertical="7dp"
        android:layout_marginLeft="10dp"
        android:text="Location"
        android:textColor="#474444"
        android:textSize="12sp"
        app:layout_constraintStart_toEndOf="@id/cardView"
        app:layout_constraintTop_toBottomOf="@id/title_text" />

    <TextView
        android:id="@+id/price"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginVertical="7dp"
        android:layout_marginLeft="10dp"
        android:text="price"
        android:textSize="14sp"
        android:textStyle="bold"
        app:layout_constraintStart_toEndOf="@id/cardView"
        app:layout_constraintTop_toBottomOf="@id/location_text" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:layout_marginEnd="20dp"
        android:layout_marginBottom="10dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        >

        <ImageView
            android:id="@+id/chat_image"
            android:layout_width="14dp"
            android:layout_height="14dp"
            android:src="@drawable/chat"
            android:layout_marginRight="5dp"
            android:layout_marginTop="3dp"
            />

        <TextView
            android:id="@+id/chatNumber"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="00"
            android:layout_marginRight="5dp"
            />

        <ImageView
            android:id="@+id/heart_image"
            android:layout_width="15dp"
            android:layout_height="15dp"
            android:src="@drawable/heart"
            android:layout_marginRight="5dp"
            android:layout_marginTop="3dp"
            />

        <TextView
            android:id="@+id/heartNumber"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="00"
            />

    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="2dp"
        android:background="#E1DCDC"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

🌱 글씨에 밑줄을 넣는 방법 🌱

<resources>
    <string name="app_name">AppleMarket</string>
    <string name="manner_text"><u>매너온도</u></string>
</resources>

💗 개인과제 후기 💗

개인과제를 진행하면서 상세페이지로 데이터를 넘기는 과정이 너무 어려웠다...
너무 어려워서 다른 분들께 도움을 많이 받아서 겨우 완성하고 제출했다.
다음 팀 프로젝트 괜찮을까?

8개의 댓글

comment-user-thumbnail
2023년 9월 1일

3줄이상 읽기 힘든 병에 걸렸는데, 끝까지 다 봣네요! 굳!

1개의 답글
comment-user-thumbnail
2023년 9월 1일

오.. 1주일마다 올라온다는 이 귀한걸.. 따끈따끈할때 보다니 ㄷㄷㄷ

1개의 답글
comment-user-thumbnail
2023년 9월 1일

ㅋㅋㅋㅋㅋㅋㅋ흠 코드 복붙같습니다만....킁

1개의 답글
comment-user-thumbnail
2023년 9월 3일

제목에 TIL이라고 잘못 써있네요 WIL로 수정 바랍니다.

1개의 답글