[Udemy] 안드로이드 앱 개발 부트캠프 섹션 4 - 상품 상세 페이지 만들기.

Delight Yoon·2022년 10월 30일
0

웅진X유데미 STARTERS

목록 보기
4/16

안드로이드 앱 개발 부트캠프 섹션4 수강을 마치고, 중요하다고 싶은 내용들을 적어보았다..
어떻게 적어야될 지 잘 모르겠어서, 일단 작성한 코드를 모두 올리고, 중요하다 싶은 내용들을 적어보겠다.
(레이아웃만 작성하는 것만해도 어렵다 ㅠ_ㅠ Material Design을 사용하는 것이 처음이라 그런가 ..? )

📌 activity_product_detail.xml

<?xml version="1.0" encoding="utf-8"?>

<androidx.coordinatorlayout.widget.CoordinatorLayout 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="match_parent">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/shoppi_white">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar_product_detail"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_scrollFlags="scroll|enterAlways"
            app:navigationIcon="@drawable/ic_gnb_back" />	<!-- AppBar의 뒤로 가는 내비게이션을 지정한다. -->

    </com.google.android.material.appbar.AppBarLayout>

    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"> <!-- 스크롤 뷰의 시작점이 appBar의 하단으로 이동.-->

        <!-- NestedScrollView는 자식 뷰로 추가되는 뷰들을 감싸주는 컨테이너가 필요하다 -->
        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <ImageView
                android:id="@+id/iv_product_thumbnail"
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:layout_marginStart="16dp"
                android:layout_marginEnd="16dp"
                android:contentDescription="@string/description_product_thumbnail"
                android:src="@drawable/img_pdp_top_01"
                app:layout_constraintDimensionRatio="w, 1:1"      width를 기준으로 height와 1:1 비율로 하겠다는 
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/tv_product_brand_name"
                style="@style/TextSubtitle3.Grey03"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="16dp"
                android:layout_marginTop="24dp"
                android:layout_marginEnd="16dp"
                android:text="twg. official"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/iv_product_thumbnail" />

            <TextView
                android:id="@+id/tv_product_label"
                style="@style/TextMedium"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="16dp"
                android:layout_marginTop="12dp"
                android:layout_marginEnd="16dp"
                android:text="[19차 리오더] 페이크 레더 숏 테일러 자켓 블랙(1 color)"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/tv_product_brand_name" />

            <TextView
                android:id="@+id/tv_product_discount_rate"
                style="@style/TextMedium.Purple.Bold"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="16dp"
                android:layout_marginTop="20dp"
                android:layout_marginEnd="16dp"
                android:text="10%"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/tv_product_label" />

            <TextView
                android:id="@+id/tv_product_discount_price"
                style="@style/TextMedium"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="10dp"
                android:text="99,800원"
                app:layout_constraintBottom_toBottomOf="@id/tv_product_discount_rate"
                app:layout_constraintStart_toEndOf="@+id/tv_product_discount_rate"
                app:layout_constraintTop_toTopOf="@id/tv_product_discount_rate" />

            <TextView
                android:id="@+id/tv_product_price"
                style="@style/TextSubtitle2.Grey05"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="6dp"
                android:text="110,000원"
                app:layout_constraintBaseline_toBaselineOf="@id/tv_product_discount_price"
                app:layout_constraintStart_toEndOf="@id/tv_product_discount_price" />

            <View
                android:id="@+id/view_divider_product"
                android:layout_width="0dp"
                android:layout_height="1dp"
                android:layout_marginStart="16dp"
                android:layout_marginTop="24dp"
                android:background="#eeeff1"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/tv_product_price" />

            <TextView
                android:id="@+id/tv_product_detail_image_title"
                style="@style/TextSubtitle3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="16dp"
                android:layout_marginTop="24dp"
                android:text="@string/title_product_detail_image"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/view_divider_product" />


            <ImageView
                android:id="@+id/iv_product_detail_image"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginTop="24dp"
                android:layout_marginBottom="50dp"
                android:contentDescription="@string/description_product_detail_image"
                android:scaleType="centerCrop"
                android:src="@drawable/img_pdp_top_01"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/tv_product_detail_image_title" />

        </androidx.constraintlayout.widget.ConstraintLayout>


    </androidx.core.widget.NestedScrollView>


</androidx.coordinatorlayout.widget.CoordinatorLayout>

ImageViewscaleType , constraintDimensionRatio , contentDescription 정도 알아가면 좋을 것 같다.

  • scaleType - 속성 값에 따라, ImageView에 나타나는 이미지의 비율이 달라진다. ( 참고 : https://sharp57dev.tistory.com/23 )
  • contentDimensionsRatio - width or height 를 기준으로, 둘의 비율을 정하는 속성.
  • contentDescription - 내용 설명을 하는 속성.

그리고 여기서 중요한 것은 Material Design에서 추구하는 AppBar가 스크롤 되었을 때, Scrolling Behavior의 가이드라인에 맞추는 방법이다.

Scrolling Behavior 를 구현하기 위한 예제

<androidx.coordinatorlayout.widget.CoordinatorLayout
    ...
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <com.google.android.material.appbar.MaterialToolbar
            android:id="@+id/topAppBar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:title="@string/page_title"
            app:menu="@menu/top_app_bar"
            app:navigationIcon="@drawable/ic_menu_24dp"
            style="@style/Widget.MaterialComponents.Toolbar.Primary"
            />

    </com.google.android.material.appbar.AppBarLayout>

    <!-- Note: A RecyclerView can also be used -->
    <androidx.core.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <!-- Scrollable content -->

    </androidx.core.widget.NestedScrollView>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

AppBarScrolling Behavior를 구현하기 위해서는 모두를 CoordinatorLayout으로 감싸고, ToolbarAppBarLayout으로 감싸는 형태가 되어야 하며, NestedScrollView를 사용하여야 한다.
NestedScrollView에서 app:layout_behavior="@string/appbar_scrolling_view_behavior" 다음과 같이 지정해주어야 한다.

AppBar의 밑에서부터 ScrollView 를 시작하겠다는 뜻.

참고하면 좋을 사항들

  • NestedScrollView의 자식 뷰들을 감싸는 컨테이너인 Layout이 필요하다.

  • ToolBarscrollFlagsToolbar를 감싸는 레이아웃이 얼만큼 보일 지 가릴 지를 정하는 속성이다.

    • scroll 부분은 -> scroll에 반응하라는 표시이고, scroll | ?를 변경하면 된다.

    • exitUntilCollapsed

      • 위로 스크롤 : Toolbar만 남기고 다 올림.
      • 아래로 스크롤 : 최상단까지 가면, AppBarLayout 전체가 내려오기 시작.
    • enterAlways

      • 위로 스크롤 : Toolbar도 같이 다 올림.
      • 아래로 스크롤 : 스크롤 즉시, AppBarLayout 전체가 내려오기 시작.
    • enterAlwaysCollapsed

      • 위로 스크롤 : Toolbar도 같이 다 올림.
      • 아래로 스크롤 : 최상단까지 가면, AppBarLayout 전체가 내려오기 시작.
  • ToolbarnavigationIconToolbarnaviagation 의 아이콘을 지정하는 속성이고, 클릭 되었을 때, 액션을 설정하는 방법은 나중에 더 자세히 알아보자.

📌 MainActivity

package com.youngsun.shoppi.app

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_product_detail)
    }
}

activity_product_detail.xml 레이아웃 파일을 MainActivity의 화면으로 지정한다.

📌 strings.xml

<resources>
    <string name="app_name">shoppi-android</string>
    <string name="description_product_thumbnail">상품 상세 대표 이미지</string>
    <string name="title_product_detail_image">상품설명</string>
    <string name="description_product_detail_image">상품 상세 이미지</string>
</resources>

자주 바뀔 것 같은 문자열 값들은 valuesstrings에 지정해두는 것이 좋다.

📌 colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="shoppi_purple">#7d22ff</color>
    <color name="shoppi_pink_01">#66e062ff</color>
    <color name="shoppi_pink_02">#11e062ff</color>
    <color name="shoppi_gradient_purple">#6b19ff</color>
    <color name="shoppi_gradient_pink">#fd62ff</color>
    <color name="shoppi_black_01">#000000</color>
    <color name="shoppi_black_02">#111111</color>
    <color name="shoppi_grey_01">#5f5f69</color>
    <color name="shoppi_grey_02">#52514d</color>
    <color name="shoppi_grey_03">#555555</color>
    <color name="shoppi_grey_04">#888888</color>
    <color name="shoppi_grey_05">#939393</color>
    <color name="shoppi_grey_06">#d1d1d1</color>
    <color name="shoppi_grey_07">#d6d6d6</color>
    <color name="shoppi_grey_08">#e9e9e9</color>
    <color name="shoppi_grey_09">#eeeff1</color>
    <color name="shoppi_bluegrey">#f2f4f6</color>
    <color name="shoppi_white">#ffffff</color>
    <color name="shoppi_brown">#967a6d</color>

</resources>

📌 styles.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--  ImageView Style  -->
    <style name="Circle">
        <item name="cornerSize">50%</item>
    </style>

    <style name="RoundedBig">
        <item name="cornerSize">12dp</item>
    </style>

    <style name="RoundedMedium">
        <item name="cornerSize">8dp</item>
    </style>

    <!--  Text Style  -->
    <style name="TextHeadline32" >
        <item name="android:textSize">32sp</item>
        <item name="android:textColor">@color/shoppi_black_01</item>
    </style>

    <style name="TextHeadline32.White" >
        <item name="android:textColor">@color/shoppi_white</item>
    </style>

    <style name="TextHeadline32.White.Bold" >
        <item name="android:textStyle">bold</item>
    </style>

    <style name="TextHeadline5.Black02" parent="TextAppearance.MaterialComponents.Headline5">
        <item name="android:textColor">@color/shoppi_black_02</item>
    </style>

    <style name="TextHeadline5.Black02.Bold" >
        <item name="android:textStyle">bold</item>
    </style>

    <style name="TextMedium" parent="TextAppearance.AppCompat.Medium">
        <item name="android:textColor">@color/shoppi_black_01</item>
    </style>

    <style name="TextMedium.Bold">
        <item name="android:textStyle">bold</item>
    </style>

    <style name="TextMedium.Purple">
        <item name="android:textColor">@color/shoppi_purple</item>
    </style>

    <style name="TextMedium.Purple.Bold">
        <item name="android:textStyle">bold</item>
    </style>

    <style name="TextSubtitle1" parent="TextAppearance.MaterialComponents.Subtitle1">
        <item name="android:textColor">@color/shoppi_black_01</item>
    </style>

    <style name="TextSubtitle1.Bold" >
        <item name="android:textStyle">bold</item>
    </style>

    <style name="TextSubtitle1.White">
        <item name="android:textColor">@color/shoppi_white</item>
    </style>

    <style name="TextSubtitle1.White.Bold">
        <item name="android:textStyle">bold</item>
        <item name="android:textColor">@color/shoppi_white</item>
    </style>

    <style name="TextSubtitle2" parent="TextAppearance.MaterialComponents.Subtitle2">
        <item name="android:textColor">@color/shoppi_black_01</item>
    </style>

    <style name="TextSubtitle2.Black02" >
        <item name="android:textColor">@color/shoppi_black_02</item>
    </style>

    <style name="TextSubtitle2.Black02.Bold" >
        <item name="android:textStyle">bold</item>
    </style>

    <style name="TextSubtitle2.Purple" >
        <item name="android:textColor">@color/shoppi_purple</item>
    </style>

    <style name="TextSubtitle2.Purple.Bold" >
        <item name="android:textStyle">bold</item>
    </style>

    <style name="TextSubtitle2.Grey05" >
        <item name="android:textColor">@color/shoppi_grey_05</item>
    </style>

    <style name="TextSubtitle3">
        <item name="android:textSize">13sp</item>
        <item name="android:textColor">@color/shoppi_black_01</item>
    </style>

    <style name="TextSubtitle3.Bold">
        <item name="android:textStyle">bold</item>
    </style>

    <style name="TextSubtitle3.Grey03">
        <item name="android:textColor">@color/shoppi_grey_03</item>
    </style>

    <style name="TextCaption1" parent="TextAppearance.AppCompat.Caption">
        <item name="android:textColor">@color/shoppi_black_01</item>
    </style>

    <style name="TextCaption1.White" >
        <item name="android:textColor">@color/shoppi_white</item>
    </style>

    <style name="TextCaption1.Grey01" >
        <item name="android:textColor">@color/shoppi_grey_01</item>
    </style>

    <style name="TextCaption1.Grey03" >
        <item name="android:textColor">@color/shoppi_grey_03</item>
    </style>

    <style name="TextCaption1.Grey05" >
        <item name="android:textColor">@color/shoppi_grey_05</item>
    </style>

    <style name="TextCaption2" parent="TextAppearance.AppCompat.Caption">
        <item name="android:textSize">11sp</item>
        <item name="android:textColor">@color/shoppi_black_01</item>
    </style>

    <style name="TextCaption2.White" >
        <item name="android:textColor">@color/shoppi_white</item>
    </style>

    <style name="TextCaption2.White.Bold" >
        <item name="android:textStyle">bold</item>
    </style>

    <style name="TextCaption2.Grey04" >
        <item name="android:textColor">@color/shoppi_grey_04</item>
    </style>
</resources>

📌 themes.xml

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Theme.Shoppiandroid" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/shoppi_purple</item>
        <item name="colorPrimaryVariant">@color/shoppi_gradient_purple</item>
        <item name="colorOnPrimary">@color/shoppi_white</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/shoppi_pink_01</item>
        <item name="colorSecondaryVariant">@color/shoppi_pink_02</item>
        <item name="colorOnSecondary">@color/shoppi_black_01</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
        <!-- Customize your theme here. -->
    </style>
</resources>

NoActionBar 를 지정하여, 화면 위에 있는 디폴트 ActionBar를 없애준다.

Material Guidelines - Implementing your theme 을 보면 colors 의 정보를 알 수 있다.

colorPrimary - AppBar의 배경 색
colorPrimaryVariant - statusBar의 배경 색.

on 이 붙은 color 들은 text 또는 icon의 색을 나타낸다.

colorOnPrimary - AppBaricontext 색.

secondary 는 버튼또는 위젯의 배경 색과 text의 색을 나타낸다.

마찬가지로 on이 붙었을 경우, 버튼의 texticon의 색을 나타낸다고 생각하면 된다.

📌 themes.xml(night)

<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Theme.Shoppiandroid" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/shoppi_purple</item>
        <item name="colorPrimaryVariant">@color/shoppi_gradient_purple</item>
        <item name="colorOnPrimary">@color/shoppi_black_01</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/shoppi_pink_01</item>
        <item name="colorSecondaryVariant">@color/shoppi_pink_02</item>
        <item name="colorOnSecondary">@color/shoppi_black_01</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor">?attr/colorPrimaryVariant</item>
        <!-- Customize your theme here. -->
    </style>
</resources>

다크모드를 지정할 시, 이 themes.xml 에 지정된 값으로 theme이 적용된다.
NoActionBar 를 지정하여, 화면 위에 있는 디폴트 ActionBar를 없애준다.

📌 참고

profile
Yoon's Dev Blog

0개의 댓글