Computer >> 컴퓨터 >  >> 체계 >> Android

Android 앱 빌드 시작 방법:목업, UI 및 XML 레이아웃 만들기

Kriptofolio 앱 시리즈 - 파트 2

그렇다면 실제로 새 앱 빌드를 시작하는 방법은 무엇입니까? 가장 먼저 해야 할 일은 무엇입니까? Android Studio를 실행하고 코드로 바로 이동해야 한다고 생각한다면 다시 생각해 보세요. 그것이 득보다 실이 더 많을 수 있기 때문에 하지 말라고 권하고 싶은 바로 그 일입니다. 하지만 가능한 한 빨리 첫 번째 코드 줄을 작성하기 시작하고 싶은 마음이 큽니다.

대신 UI 모형으로 현명한 계획을 세우는 데 집중하는 것이 좋습니다. 모든 좋은 새 앱 프로젝트는 그것으로 시작해야 한다는 것을 기억하십시오. 이 접근 방식을 사용하면 많은 시간을 허비하지 않고 처음부터 고품질 제품을 구축할 수 있습니다.

그래서 시리즈의 이 부분에서는 "Kriptofolio"(이전의 "My Crypto Coins") 앱 목업을 제시하고 제작 방법에 대해 논의하겠습니다. 또한 모든 UI 레이아웃을 빌드할 것입니다. 이러한 레이아웃은 코딩 대상을 명확하게 나타내는 견고한 기반이 될 것입니다. 마지막으로 앱을 다양한 언어로 현지화하고 오른쪽에서 왼쪽으로 작성된 언어를 처리하는 방법을 배웁니다.

시리즈 콘텐츠

  • 소개:2018–2019년 최신 Android 앱을 구축하기 위한 로드맵
  • 1부:SOLID 원칙 소개
  • 2부:Android 앱 빌드 시작 방법:목업, UI 및 XML 레이아웃 만들기(현재 위치)
  • 3부:아키텍처에 관한 모든 것:다양한 아키텍처 패턴 탐색 및 앱에서 이를 사용하는 방법
  • 4부:Dagger 2를 사용하여 앱에서 종속성 주입을 구현하는 방법
  • 5부:Retrofit, OkHttp, Gson, Glide 및 Coroutine을 사용하여 RESTful 웹 서비스 처리

모형

프로젝트 모형을 만들 수 있는 다양한 방법이 있습니다. 가장 간단한 방법은 연필과 종이 한 장을 가지고 그림을 그리는 것입니다. 가장 좋은 점은 이 방법을 사용하면 비용이 들지 않고 즉시 시작할 수 있다는 것입니다. 아 그리고 거의 잊어버렸습니다. 실행 취소 기능이 없기 때문에 지우개도 준비해야 합니다. ?

나처럼 더 많은 기능이 필요하다고 생각되면 세부 모형을 만들기 위해 특수 소프트웨어를 사용하는 것이 좋습니다. 나는 그것을 구입하는 데 돈을 투자하고 사용법을 배우는 데 시간을 투자해야 할지라도 연필과 종이 대신 소프트웨어를 사용하는 것을 선호합니다.

시장에는 선택할 수 있는 다양한 소프트웨어 옵션이 있습니다. 귀하의 모든 요구 사항에 가장 적합한 조사를 직접 수행해야 합니다. 내 모든 프로젝트 목업에 대해 지금 데스크탑 앱용 Balsamiq 목업을 사용하고 있습니다. Balsamiq는 빠르고 효과적이며 사용하기 매우 쉬운 와이어 프레임 소프트웨어입니다. 나름 만족스럽기 때문에 안드로이드 앱 제작에 추천하니 부담없이 사용해 보세요.

Android 앱 빌드 시작 방법:목업, UI 및 XML 레이아웃 만들기

나는 잘 생각하고 매우 상세한 모형을 만드는 것으로 My Crypto Coins 앱 프로젝트를 시작했습니다. 모든 것을 아주 세세하게 만들면 실수를 피할 수 있을 거라 생각했습니다. 또한 개발 과정에서 갑자기 기능이 변경되는 데 시간을 낭비하지 않을 것입니다. 좋은 목업을 만들기 위해 많은 노력을 기울이면 약간의 상상력으로 최종 제품을보고 느낄 수 있습니다.

내 목표는 최종 제품에서 보여야 하는 것처럼 목업에서 모든 것을 정의하는 것이었습니다. 그러기 위해 서두르지 않고 필요한 만큼 시간을 보내려고 노력했습니다. 다음은 My Crypto Coins 앱의 최종 모형입니다.

Android 앱 빌드 시작 방법:목업, UI 및 XML 레이아웃 만들기
Balsamiq 모형 소프트웨어로 만든 My Crypto Coins 앱 모형

스톡 디자인 — 소재

이야기해야 할 또 다른 중요한 사항은 앱의 시각적 디자인입니다. 이 글을 쓰는 시점에서 Material Design은 모든 Android 앱에 대해 Google에서 권장하는 기본 시각적 디자인입니다. 그리고 생각해 보세요. 스톡 디자인은 잘못될 수 없습니다.

My Crypto Coins 앱의 경우 Material Design을 사용할 것입니다. 앱은 자세한 온라인 지침에 정의된 모범 사례를 따릅니다.

Android 앱 빌드 시작 방법:목업, UI 및 XML 레이아웃 만들기

Material.io/guidelines — 이 프로젝트의 디자인 가이드라인이 될 공식 머티리얼 디자인 생활 사양 문서입니다.

레이아웃

이제 앱의 와이어프레임이 이미 준비되었으면 실제 UI를 빌드할 때입니다. 다시 코드 작성에 뛰어들 수 있지만, 왜 서두를까요? 대신 모든 XML 레이아웃을 구축하는 데 집중하는 것이 좋습니다. 여기서 제 조언은 XML에 더 많이 넣을수록 더 적은 코드를 작성해야 한다는 것입니다.

Android 팀은 XML 레이아웃에 새로운 기능을 추가하여 UI를 구축하는 방법을 지속적으로 개선하고 있습니다. 때로는 원하는 모양에 도달하기 위해 몇 줄의 코드를 작성하고 싶을 때가 있습니다. 그러나 코드 없이도 가능한지 알아보기 위해 주제를 더 깊이 조사하는 것이 좋습니다. 기억하십시오:코드가 적을수록 프로젝트가 더 깔끔해 보입니다. 또한 다른 개발자가 더 쉽게 이해할 수 있고 유지 관리하기 쉽습니다.

마지막으로 모든 XML 레이아웃을 만들면 앱이 만들어진 것처럼 느껴질 것입니다. 당신은 그것을 실행하고 최종 사용자에게 어떻게 느끼는지 볼 수 있습니다. 일부 가짜 데이터를 표시하고 아무 것도 하지 않는 것은 중요하지 않습니다. 이제 과감한 변경을 할 수 있는 마지막 기회가 있습니다.

다른 사람을 위한 앱을 만들고 있다면 지금이 발표하기에 좋은 시기입니다. 마지막 순간에 놀라운 변경을 요청받을 수도 있습니다. 그렇게 하면 절대 사용되지 않을 기능에 대한 코드 작성을 피할 수 있습니다. 많은 사람들이 충분한 상상력을 가지고 있지 않으며 그들이 원하는 것이 무엇인지 결정하기 위해 먼저 보고 만질 필요가 있습니다. 따라서 작업 프로세스의 마지막 단계를 위해 레이아웃을 남겨두지 마십시오.

이 앱의 경우 모든 최신 Android 앱에서 공통적으로 사용되는 다양한 구성요소를 사용할 것입니다.

  • CoordinatorLayout — 강력한 FrameLayout의 주요 매력은 애니메이션과 내부 보기의 전환을 조정하는 기능입니다.
  • AppBarLayout — 머티리얼 디자인의 앱 바 개념의 많은 기능, 즉 스크롤 제스처를 구현하는 수직 LinearLayout입니다.
  • 도구 모음 — 애플리케이션 레이아웃 내에서 사용하기 위한 작업 모음의 일반화
  • CollapsingToolbarLayout — 축소 앱 바를 구현하는 툴바용 래퍼. AppBarLayout의 직계 자식으로 사용하도록 설계되었습니다.
  • ConstraintLayout — 위젯을 유연한 방식으로 배치하고 크기를 조정할 수 있는 ViewGroup입니다. 스테로이드의 RelativeLayout을 상상해 보세요.
  • SwipeRefreshLayout — 스와이프하여 새로고침 사용자 인터페이스 패턴을 완전히 구현할 수 있게 해주는 위젯입니다. 수직 스와이프를 감지하고 고유한 진행률 표시줄을 표시하며 앱에서 콜백 메서드를 트리거합니다.
  • FloatingActionButton — 앱 UI에서 기본 작업을 트리거하는 원형 버튼입니다.

메인 화면의 경우 이러한 모든 구성 요소의 조합을 사용하여 멋진 UI/UX를 만듭니다. 사용자는 레이아웃의 상단 부분을 확장 및 축소하여 모든 암호화폐 포트폴리오의 총 가치를 찾을 수 있습니다. 그들은 지난 24시간 동안 가치의 변화를 확인하고 선택한 법정 화폐를 변경할 수 있습니다.

Android 앱 빌드 시작 방법:목업, UI 및 XML 레이아웃 만들기
메인 화면 레이아웃 생성

물론 이 9가지 외에도 다른 구성 요소가 있습니다. 특정 경우에 어떤 것을 사용해야 하는지 알기 위해 전체 구성 요소 팔레트를 마스터하는 것이 매우 중요합니다. 수시로 변하는 모바일 트렌드에 따라 팔레트도 확장될 것입니다.

모든 구성 요소에 대해 이야기하지는 않겠지만 제 조언은 해당 구성 요소를 직접 조사하는 것입니다. 나를 이해하는 가장 좋은 방법은 Android Studio에서 자동 드래그 앤 드롭 도구를 사용하는 대신 XML 레이아웃을 수동으로 빌드하는 것입니다.

스타일, 색상, 치수, 문자열, 아이콘

얼핏 보면 이 섹션 제목에 언급된 내용이 중요하지 않은 것처럼 보일 수 있습니다. 그러나 현대적이고 독점적으로 만들고 싶다면 어떤 앱에서든 만들 때 노력을 기울이길 바랍니다. 이 블로그 게시물 시리즈는 MODERN Android 앱을 빌드하는 방법임을 기억하십시오! 그것이 얼마나 잘못되었든 사람들은 일반적으로 기능이 아니라 외관으로 앱을 판단합니다.

따라서 여기가 처음부터 신규 사용자의 사랑을 받을 수 있는 절호의 기회입니다. 다음은 제 팁입니다.

  • XML 레이아웃 파일을 작성할 때 보기에 대한 몇 가지 공통 속성을 반복하는 위치를 인식해야 합니다. 별도의 XML 스타일로 정의되도록 이동합니다. 그렇게 하면 더 짧은 XML 레이아웃 파일을 갖게 됩니다. 별도의 장소에서 모든 앱 스타일링 측면을 제어할 수 있습니다. 그것이 당신에게 어떤 혜택을 줄지 상상해보십시오. 예를 들어 사용자가 앱 스킨(밝게, 어둡게 등)을 선택하도록 허용할 수 있습니다.
  • 모든 앱 색상도 별도의 파일에 정의하고 즉시 변경하여 모양을 실험해 볼 수 있습니다. 때로는 동일한 제품에 새로운 생명을 부여하고 단순히 새로운 색상으로 상쾌하게 하여 사용자를 다시 참여시킬 수 있습니다. 멋진 색상을 선택하는 데 도움이 될 수 있는 웹사이트가 꽤 많이 있지만 제가 가장 좋아하는 웹사이트는 MaterialPalette.com이므로 한 번 살펴보세요.
  • 앱 차원을 별도의 파일에 정의합니다. 이렇게 하면 다양한 크기의 화면에서 잘 보이도록 앱을 조정할 수 있습니다.
  • 모든 문자열을 하드 코딩하면 안 되며 잊어버린 경우 Android Studio에서 알려줍니다. 그것을 무시하지 마십시오. 문자열을 분리할 때 가장 좋은 점은 앱을 다른 언어로 번역할 수 있다는 것입니다.
  • 앱에 아이콘을 사용할 때는 항상 XML 벡터 드로어블 형식을 선호하세요. 이것은 새로운 권장 표준이며 픽셀화를 방지하는 현명한 방법입니다. 커뮤니티에서 전문적으로 제작된 많은 머티리얼 디자인 스타일 아이콘을 찾으려면 MaterialDesignIcons.com을 확인하십시오. 이 웹사이트를 사용하여 My Crypto Coins 앱의 아이콘을 얻었습니다.

리사이클러 보기

My Crypto Coins 앱의 메인 화면은 사용자가 보유하고 있는 암호화폐 목록으로 구성됩니다. 이를 위해 RecyclerView 위젯이 가장 적합합니다. 큰 데이터 세트(또는 자주 변경되는 데이터)를 기반으로 요소의 스크롤 목록을 표시하는 위젯입니다.

장점으로 인해 RecyclerView는 목록 화면을 만드는 데 권장되는 구성 요소입니다. 더 간단한 ListView 구성 요소의 더 고급이고 유연한 버전입니다. 우리는 그것에 대해서도 나중에 이야기할 것입니다. 이 부분에서는 레이아웃만 생성합니다. 우리는 코딩에 집중하지 않습니다.

앱을 시각적으로 보려면 코드를 작성하여 RecyclerView를 구현해야 합니다. RecyclerView를 구현하는 단계는 다음과 같습니다.

1. RecyclerView 구성 요소를 추가합니다.

우리의 MainActivity 레이아웃은 activity_main.xml입니다. . 이 레이아웃에는 content_main.xml가 포함됩니다. 조각인 레이아웃. 이 MainActivityListFragment 레이아웃 팽창 fragment_main_list.xml . 따라서 여기에 RecyclerView 컴포넌트를 추가해야 합니다.

...
<android.support.v7.widget.RecyclerView
    android:id="@+id/recyclerview_fragment_main_list"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/colorForMainListBackground"
    android:clipToPadding="false"
    android:paddingBottom="72dp"
    android:paddingTop="5dp"
    android:scrollbarStyle="outsideOverlay"
    android:scrollbars="vertical" />
...

보시다시피 하단에 패딩 공간을 남기도록 RecyclerView를 구성합니다. FloatingActionButton으로 마지막 목록 항목을 덮는 것을 피하기 위해 이 작업을 수행합니다. 또한 사용할 수 있도록 세로 스크롤 막대를 켭니다.

2. RecyclerView 행 레이아웃을 만듭니다.

초기 목적을 위해 각 행에 대한 항목 이름만 설정합니다. 단순화된 레이아웃은 다음과 같아야 합니다.

<android.support.v7.widget.CardView xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginBottom="@dimen/main_cardview_list_item_outer_top_bottom_margin"
    android:layout_marginEnd="@dimen/main_cardview_list_item_outer_start_end_margin"
    android:layout_marginStart="@dimen/main_cardview_list_item_outer_start_end_margin"
    android:layout_marginTop="@dimen/main_cardview_list_item_outer_top_bottom_margin"
    android:foreground="?android:attr/selectableItemBackground"
    android:clickable="true"
    android:focusable="true"
    app:cardBackgroundColor="@color/colorForMainListItemBackground">

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="@dimen/main_cardview_list_item_inner_margin">
        ...
        <android.support.v7.widget.AppCompatTextView
            android:id="@+id/item_name"
            style="@style/MainListItemPrimeText"
            android:layout_marginEnd="@dimen/main_cardview_list_item_text_between_margin"
            android:layout_marginStart="@dimen/main_cardview_list_item_inner_margin"
            app:layout_constraintBottom_toTopOf="@+id/item_amount_symbol"
            app:layout_constraintEnd_toStartOf="@+id/guideline1_percent"
            app:layout_constraintStart_toEndOf="@+id/item_image_icon"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_chainStyle="spread"
            tools:text="@string/sample_text_item_name" />
        ...
    </android.support.constraint.ConstraintLayout>

</android.support.v7.widget.CardView>

3. 데이터 어댑터 클래스를 만듭니다.

현재 어댑터는 문자열 데이터를 허용합니다. 나중에 별도의 클래스 데이터 모델을 만들어야 합니다. 하나의 문자열보다 더 많은 정보를 전달해야 합니다.

class MainRecyclerViewAdapter(val dataList: ArrayList<String>) : RecyclerView.Adapter<MainRecyclerViewAdapter.CustomViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomViewHolder {
        val v = LayoutInflater.from(parent.context).inflate(R.layout.fragment_main_list_item, parent, false)
        return CustomViewHolder(v)
    }

    override fun onBindViewHolder(holder: CustomViewHolder, position: Int) {
        holder.txtName?.text = dataList[position]
    }

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


    inner class CustomViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

        val txtName = itemView.findViewById<TextView>(R.id.item_name)
    }
}

4. RecyclerView를 사용자 지정 어댑터에 연결합니다.

class MainActivityListFragment : Fragment() {

    private lateinit var recyclerView: RecyclerView
    private lateinit var recyclerAdapter: MainRecyclerViewAdapter

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        val v: View = inflater.inflate(R.layout.fragment_main_list, container, false)

        recyclerView = v.findViewById(R.id.recyclerview_fragment_main_list)

        return v
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {

        super.onActivityCreated(savedInstanceState)

        setupList()
    }

    private fun setupList() {

        val data = ArrayList<String>()
        data.add("Bitcoin")
        data.add("Etherium")
        data.add("Ripple")
        data.add("Bitcoin Cash")
        data.add("Litecoin")
        data.add("NEO")
        data.add("Stellar")
        data.add("EOS")
        data.add("Cardano")
        data.add("Stellar")
        data.add("IOTA")
        data.add("Dash")
        data.add("Monero")
        data.add("TRON")
        data.add("NEM")
        data.add("ICON")
        data.add("Bitcoin Gold")
        data.add("Zcash")
        data.add("Verge")

        recyclerView.layoutManager = LinearLayoutManager(activity)
        recyclerAdapter = MainRecyclerViewAdapter(data)
        recyclerView.adapter = recyclerAdapter
    }
}

완료! 이제 기본 화면은 다음과 같습니다.

Android 앱 빌드 시작 방법:목업, UI 및 XML 레이아웃 만들기
메인 화면

목록 보기

이 프로젝트에서는 이미 보유하고 있는 암호화폐를 추가할 수 있는 화면에 ListView를 사용합니다. 최근 몇 년 동안의 단점으로 인해 더 이상 사용되지 않습니다.

그래서 지금 많은 분들이 RecyclerView로 동일한 기능을 쉽게 만들 수 있는데 왜 My Crypto Coins 앱에서 이 앱을 사용하기로 결정했는지 궁금해 하실 것입니다.

그러나 이 프로젝트는 먼저 교육 목적으로 만들어졌습니다. ListView와 작동 방식에 대한 통찰력을 얻는 것이 도움이 될 것이라고 생각했습니다. 모든 개발자는 레거시 코드에서 ListView를 실행할 수 있으며 작업 방법을 아는 것이 가장 좋습니다. 게다가 우리가 만들 목록은 너무 간단해서 ListView의 기술적인 한계로 인해 문제가 발생하지 않습니다.

ListView를 구현하는 데 필요한 매우 유사한 단계를 따르십시오.

1. ListView 구성 요소를 추가합니다.

가장 먼저 할 일은 AddSearchActivity에 ListView를 추가하는 것입니다. . 액티비티 레이아웃 파일 activity_add_search.xml 열기 content_add_search.xml이 포함되어 있음을 알 수 있습니다. . 거기에 ListView 컴포넌트를 추가할 것입니다.

...
<ListView
    android:id="@+id/listview_activity_add_search"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:scrollbars="vertical" />
...

2. ListView 행 레이아웃을 만듭니다.

이전과 마찬가지로 초기 목적으로 각 행에 대한 항목 이름만 설정합니다. 단순화된 레이아웃은 다음과 같습니다.

<android.support.constraint.ConstraintLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="@dimen/add_search_list_item_inner_margin">
    ...
    <android.support.v7.widget.AppCompatTextView
        android:id="@+id/item_name"
        style="@style/AddSearchListItemPrimeText"
        android:layout_marginEnd="@dimen/add_search_list_item_text_between_margin_2x"
        android:layout_marginStart="@dimen/add_search_list_item_text_between_margin"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toStartOf="@+id/item_symbol"
        app:layout_constraintStart_toEndOf="@+id/item_image_icon"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="@string/sample_text_item_name" />
    ...
</android.support.constraint.ConstraintLayout>

3. 데이터 어댑터 클래스를 만듭니다.

RecyclerView와 마찬가지로 현재 ListView 어댑터는 항목 이름을 가져오고 화면에 표시하기 위해 문자열 데이터만 허용합니다. 나중에 우리는 별도의 클래스 데이터 모델을 사용할 것입니다. 이 부분은 암호화폐 제목만 표시하는 아주 간단한 목록을 만들고 싶습니다. 사용자 지정 어댑터를 만드는 대신 기본값인 ArrayAdapter를 사용할 수 있습니다.

class AddSearchListAdapter(context: Context, private val dataSource: ArrayList<String>) : BaseAdapter() {

    private val inflater: LayoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater

    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        val view: View
        val holder: CustomViewHolder

        if (convertView == null) {

            view = inflater.inflate(R.layout.activity_add_search_list_item, parent, false)

            holder = CustomViewHolder()
            holder.nameTextView = view.findViewById(R.id.item_name)

            view.tag = holder

        } else {

            view = convertView
            holder = convertView.tag as CustomViewHolder
        }

        val nameTextView = holder.nameTextView

        nameTextView.text = getItem(position) as String

        return view
    }

    override fun getItem(position: Int): Any {
        return dataSource[position]
    }

    override fun getItemId(position: Int): Long {
        return position.toLong();
    }

    override fun getCount(): Int {
        return dataSource.size
    }


    inner class CustomViewHolder {
        lateinit var nameTextView: AppCompatTextView
    }

}

어댑터 코드에서 볼 수 있듯이 CustomViewHolder을 생성하여 ViewHolder 패턴을 사용합니다. 목록 행 보기 참조를 저장합니다. findViewById() 호출 메서드는 두 번만 발생합니다. 이를 통해 목록 스크롤을 원활하고 효율적으로 수행할 수 있습니다.

ListView는 ViewHolder 패턴을 사용할 필요가 없습니다. RecyclerView의 어댑터는 기본적으로 이러한 종류의 보호 기능을 제공하므로 강제로 사용해야 합니다.

4. ListView를 사용자 지정 어댑터에 연결합니다.

기본 ArrayAdapter를 사용하면 val adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, data)처럼 보일 수 있습니다. . 이것이 ListView 구성 요소의 아름다움입니다. 원하는 경우 고유한 어댑터나 행 레이아웃을 구축하지 않고도 간단한 목록을 매우 빠르게 만들 수 있습니다(2단계와 3단계 건너뛰기).

class AddSearchActivity : AppCompatActivity() {

    private lateinit var listView: ListView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_add_search)
        setSupportActionBar(toolbar2)
        supportActionBar?.setDisplayHomeAsUpEnabled(true)

        val data = ArrayList<String>()
        data.add("Bitcoin")
        data.add("Etherium")
        data.add("Ripple")
        data.add("Bitcoin Cash")
        data.add("Litecoin")
        data.add("NEO")
        data.add("Stellar")
        data.add("EOS")
        data.add("Cardano")
        data.add("Stellar")
        data.add("IOTA")
        data.add("Dash")
        data.add("Monero")
        data.add("TRON")
        data.add("NEM")
        data.add("ICON")
        data.add("Bitcoin Gold")
        data.add("Zcash")
        data.add("Verge")

        val adapter = AddSearchListAdapter(this, data)

        listView = findViewById(R.id.listview_activity_add_search)
        listView.adapter = adapter

    }
    ...
}

ListView 설정이 준비되었습니다!

Android 앱 빌드 시작 방법:목업, UI 및 XML 레이아웃 만들기
암호화폐 추가 화면

검색 보기

모든 암호화폐가 나열된 동일한 화면에서 SearchView도 추가해야 합니다. 검색은 이름을 입력하여 특정 암호화폐를 찾고자 하는 모든 사용자에게 유용한 기능이 될 것입니다. 이 부분에서는 기능을 완전히 구축하지 않고 시각적 부분만 구현합니다. 프로젝트에 SearchView를 추가하려면 다음 단계를 따르세요.

1. XML에서 검색 가능한 구성을 선언합니다.

검색 가능한 구성 파일은 xml이라는 res 디렉토리에 추가되어야 합니다. 여기에서 작동 방식을 정의하는 SearchView 구성 요소의 속성을 지정할 수 있습니다.

<searchable
    xmlns:android="https://schemas.android.com/apk/res/android"
    android:label="@string/app_name"
    android:hint="@string/search_hint">
</searchable>

2. 검색 가능한 활동이 될 새 활동을 만드십시오.

AppCompatActivity()를 확장하는 새로운 빈 활동을 만들 것입니다. . 이름을 AddSearchActivity으로 지정하겠습니다. .

3. Android 매니페스트 파일에서 새로 생성된 활동을 검색 가능하도록 지정합니다.

<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.baruckis.mycryptocoins">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        ...
        <activity
            android:name=".AddSearchList.AddSearchActivity"
            android:launchMode="singleTop"
            android:parentActivityName=".MainList.MainActivity"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.SEARCH" />
            </intent-filter>
            <meta-data
                android:name="android.app.searchable"
                android:resource="@xml/searchable" />
        </activity>
        ...
    </application>

</manifest>

Android 시스템이 검색 프로세스를 처리하도록 할 것입니다. 이것이 AddSearchActivity의 액티비티 요소에 인텐트 액션 검색 및 메타데이터를 추가하는 이유입니다. . 메타 데이터에는 res/xml 폴더에 있는 검색 가능한 구성 파일에 연결된 이름과 리소스가 있습니다.

4. 검색 메뉴를 만듭니다.

res/menu 폴더 안에 메뉴 리소스 파일을 만들 것입니다.

<menu xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/search"
        android:icon="@drawable/ic_search"
        android:title="@string/action_search"
        app:actionViewClass="android.support.v7.widget.SearchView"
        app:showAsAction="ifRoom|collapseActionView" />
</menu>

5. 활동에 검색 메뉴를 추가합니다.

Android 검색 위젯을 메뉴 작업 보기로 추가합니다.

class AddSearchActivity : AppCompatActivity() {
    ...
    override fun onCreateOptionsMenu(menu: Menu?): Boolean {

        menuInflater.inflate(R.menu.menu_search, menu)

        val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
        val searchView = menu?.findItem(R.id.search)?.actionView as SearchView
        searchView.setSearchableInfo(searchManager.getSearchableInfo(componentName))
        searchView.maxWidth = Integer.MAX_VALUE

        return true
    }
}

이제 SearchView가 활동에 추가됩니다. 여전히 검색 기능이 작동하지 않습니다. 하지만 이 부분은 우리가 원하는 대로 구현했습니다.

Android 앱 빌드 시작 방법:목업, UI 및 XML 레이아웃 만들기
암호화폐 추가 화면 - 검색 기능

설정

최신 Android 앱을 만들고 싶다면 설정 화면을 포함하고 사용자에게 앱 설정에 대한 액세스 권한을 부여하는 것이 좋습니다. 앱에 설정을 포함하면 사용자가 앱의 일부 기능을 제어할 수 있어 사용자를 더 행복하게 만듭니다. 이제 앱이 작동하는 방식을 제어할 수 있습니다. 그래서 마이 크립토 코인 앱의 설정 화면도 만들어 보겠습니다.

Android 앱 빌드 시작 방법:목업, UI 및 XML 레이아웃 만들기
갤러리 템플릿의 새 설정 활동

Android Studio 템플릿으로 설정 화면을 만들 수 있으며 필요한 모든 코드가 생성됩니다. 기본적으로 이 블로그 게시물을 작성할 때 기본 설정 헤더와 함께 설정 활동이 생성됩니다. 처음에 몇 가지 설정만 가질 계획인 작은 앱에서는 이것이 원하는 것이 아닙니다.

Android 앱 빌드 시작 방법:목업, UI 및 XML 레이아웃 만들기
기본 설정 헤더가 있는 기본 설정 템플릿

따라서 우리는 모든 것을 수동으로 구축할 것입니다. 설정 화면은 XML과 유사한 레이아웃으로 설계되었습니다. 이 블로그 게시물의 시각적 부분만 다시 만들기 위해 단계별로 이동해 보겠습니다.

1. 환경 설정 화면 XML 파일을 만듭니다.

res/xml 디렉토리에 위치해야 하는 기본 설정 화면 XML 파일을 만들 것입니다.

<PreferenceScreen xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools">
    <PreferenceCategory android:title="@string/pref_general_category_title">
        <ListPreference
            android:defaultValue="@string/pref_default_language_value"
            android:entries="@array/pref_language_list_entries"
            android:entryValues="@array/pref_language_list_values"
            android:icon="@drawable/ic_translate"
            android:key="language_list"
            android:summary="@string/pref_default_language_entry"
            android:title="@string/pref_language_title"
            tools:summary="@string/pref_default_language_entry" />

        <ListPreference
            android:defaultValue="@string/pref_default_fiat_currency_value"
            android:entries="@array/pref_fiat_currency_list_entries"
            android:entryValues="@array/pref_fiat_currency_list_values"
            android:icon="@drawable/ic_cash"
            android:key="fiat_currency_list"
            android:summary="@string/pref_default_fiat_currency_entry"
            android:title="@string/pref_fiat_currency_title"
            tools:summary="@string/pref_default_fiat_currency_entry" />

        <ListPreference
            android:defaultValue="@string/pref_default_date_format_value"
            android:entries="@array/pref_date_format_list_entries"
            android:entryValues="@array/pref_date_format_list_values"
            android:icon="@drawable/ic_date_range"
            android:key="date_format_list"
            android:summary="@string/pref_default_date_format_entry"
            android:title="@string/pref_date_format_title"
            tools:summary="@string/pref_default_date_format_entry" />

        <SwitchPreference
            android:defaultValue="true"
            android:icon="@drawable/ic_calendar_clock"
            android:key="24h_switch"
            android:summary="@string/pref_24h_switch_summary"
            android:title="@string/pref_24h_switch_title" />

    </PreferenceCategory>

    <PreferenceCategory android:title="@string/pref_support_category_title">

        <Preference
            android:icon="@drawable/ic_star"
            android:title="@string/pref_rate_app_title" />

        <Preference
            android:icon="@drawable/ic_share"
            android:title="@string/pref_share_app_title" />

        <Preference
            android:icon="@drawable/ic_attach_money"
            android:summary="@string/pref_donate_view_summary"
            android:title="@string/pref_donate_view_title" />

        <Preference
            android:icon="@drawable/ic_currency_btc"
            android:summary="@string/pref_donate_crypto_summary"
            android:title="@string/pref_donate_crypto_title" />

    </PreferenceCategory>

    <PreferenceCategory android:title="@string/pref_support_about_title">

        <Preference
            android:icon="@drawable/ic_web"
            android:summary="@string/pref_website_summary"
            android:title="@string/pref_website_title" />

        <Preference
            android:icon="@drawable/ic_human_greeting"
            android:summary="@string/pref_author_summary"
            android:title="@string/pref_author_title" />

        <Preference
            android:icon="@drawable/ic_github_circle"
            android:summary="@string/pref_source_summary"
            android:title="@string/pref_source_title" />

        <Preference
            android:icon="@drawable/ic_file_multiple"
            android:title="@string/pref_open_source_title" />

        <Preference
            android:icon="@drawable/ic_copyright"
            android:summary="@string/pref_license_summary"
            android:title="@string/pref_license_title" />

        <Preference
            android:icon="@drawable/ic_info_outline"
            android:summary="@string/pref_app_summary"
            android:title="@string/pref_app_title" />

    </PreferenceCategory>

</PreferenceScreen>

2. 환경 설정 조각을 만듭니다.

그런 다음 간단한 빈 조각(SettingsFragment)을 만들어야 합니다. , PreferenceFragment()를 확장해야 합니다. . 이 조각은 우리가 만든 XML 리소스에서 기본 설정을 만듭니다. 앞으로 이 조각에는 XML 설정을 확장하는 데 필요한 모든 메서드가 포함될 것입니다. 또한 설정이 변경되면 콜백을 제공합니다.

class SettingsFragment : PreferenceFragment() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        addPreferencesFromResource(R.xml.pref_main);
    }
}

3. 환경 설정 활동을 만듭니다.

설정 프래그먼트가 준비되면 새 활동을 만들어 보겠습니다. AppCompatPreferenceActivity , PreferenceActivity() 확장 . 이 클래스는 모든 장치 및 버전에서 호환성을 제공합니다.

abstract class AppCompatPreferenceActivity : PreferenceActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        delegate.installViewFactory()
        delegate.onCreate(savedInstanceState)
        super.onCreate(savedInstanceState)
    }

    override fun onPostCreate(savedInstanceState: Bundle?) {
        super.onPostCreate(savedInstanceState)
        delegate.onPostCreate(savedInstanceState)
    }

    val supportActionBar: ActionBar?
        get() = delegate.supportActionBar

    fun setSupportActionBar(toolbar: Toolbar?) {
        delegate.setSupportActionBar(toolbar)
    }

    override fun getMenuInflater(): MenuInflater {
        return delegate.menuInflater
    }

    override fun setContentView(@LayoutRes layoutResID: Int) {
        delegate.setContentView(layoutResID)
    }

    override fun setContentView(view: View) {
        delegate.setContentView(view)
    }

    override fun setContentView(view: View, params: ViewGroup.LayoutParams) {
        delegate.setContentView(view, params)
    }

    override fun addContentView(view: View, params: ViewGroup.LayoutParams) {
        delegate.addContentView(view, params)
    }

    override fun onPostResume() {
        super.onPostResume()
        delegate.onPostResume()
    }

    override fun onTitleChanged(title: CharSequence, color: Int) {
        super.onTitleChanged(title, color)
        delegate.setTitle(title)
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        delegate.onConfigurationChanged(newConfig)
    }

    override fun onStop() {
        super.onStop()
        delegate.onStop()
    }

    override fun onDestroy() {
        super.onDestroy()
        delegate.onDestroy()
    }

    override fun invalidateOptionsMenu() {
        delegate.invalidateOptionsMenu()
    }

    private val delegate: AppCompatDelegate by lazy {
        AppCompatDelegate.create(this, null)
    }
}

4. 설정 활동을 만듭니다.

class SettingsActivity : AppCompatPreferenceActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setupActionBar()

        fragmentManager.beginTransaction().replace(android.R.id.content, SettingsFragment()).commit()
    }

    private fun setupActionBar() {
        supportActionBar?.setDisplayHomeAsUpEnabled(true)
    }

    override fun onMenuItemSelected(featureId: Int, item: MenuItem): Boolean {
        val id = item.itemId
        if (id == android.R.id.home) {
            if (!super.onMenuItemSelected(featureId, item)) {
                NavUtils.navigateUpFromSameTask(this)
            }
            return true
        }
        return super.onMenuItemSelected(featureId, item)
    }
}

5. 기본 메뉴에 설정 항목을 추가합니다.

<menu xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    tools:context="com.baruckis.mycryptocoins.MainList.MainActivity">
    <item
        android:id="@+id/action_settings"
        android:orderInCategory="100"
        android:title="@string/action_settings"
        app:showAsAction="never" />
</menu>

6. 더보기 메뉴에서 설정을 선택하면 새로 생성된 설정 활동을 시작합니다.

class MainActivity : AppCompatActivity() {
    ...
    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        // Inflate the menu; this adds items to the action bar if it is present.
        menuInflater.inflate(R.menu.menu_main, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return when (item.itemId) {
            R.id.action_settings -> {
                startActivity(Intent(this@MainActivity, SettingsActivity::class.java));
                return true
            }
            else -> super.onOptionsItemSelected(item)
        }
    }
}

7. Android 매니페스트 파일에 새로 생성된 설정 활동을 지정합니다.

<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.baruckis.mycryptocoins">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        ...
        <activity
            android:name=".Settings.SettingsActivity"
            android:label="@string/title_activity_settings"
            android:parentActivityName=".MainList.MainActivity">
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="com.baruckis.mycryptocoins.MainList.MainActivity" />
        </activity>
    </application>

</manifest>

축하합니다. 마침내 도구 모음의 메뉴 항목에서 설정을 실행할 수 있습니다.

Android 앱 빌드 시작 방법:목업, UI 및 XML 레이아웃 만들기
설정 화면

타사 라이브러리 사용 — Gmail 스타일 플립 서클 버튼 보기

좋습니다. 타사 라이브러리 사용에 대한 다양한 의견이 있습니다. 개발 플랫폼의 원래 공급업체가 아닌 개발자가 만든 코드입니다. 얼마나 오래, 얼마나 잘 지원될지 확실하지 않기 때문에 어떤 사람들은 이를 기피합니다. 한편 다른 사람들은 개발 프로세스를 가속화할 수 있기 때문에 이를 사용합니다.

사용 결정은 특정 경우에 따라 달라야 합니다. 라이브러리를 직접 조사하고 모든 요구 사항을 충족할 수 있을 만큼 유연한지 결정해야 합니다.

My Crypto Coins 앱의 경우 원형 이미지 보기를 만들고 싶은 상황이 있습니다. 이 이미지는 원 모양 안에 특정 암호화폐 아이콘을 표시해야 합니다. 하지만 아이콘이 없으면 암호 화폐 코드의 처음 세 글자를 보여주고 싶습니다.

그 외에도 이미지를 클릭하여 선택할 수 있기를 바랍니다. 선택 항목은 짧은 플립 보기 애니메이션으로 표시되어야 합니다.

내가 설명한 이 모든 UX는 원래 내가 발명한 것이 아닙니다. Gmail 앱에서 유사한 동작을 찾을 수 있으며 정말 좋아 보입니다.

Android 앱 빌드 시작 방법:목업, UI 및 XML 레이아웃 만들기
Gmail 앱 플립 서클 보기 애니메이션

하지만 어떻게 만들까요? 사실 원형 이미지 보기는 Android Studio 구성 요소 팔레트에서 찾을 수 있는 기본 구성 요소도 아닙니다.

어려운 길을 가서 모든 것을 스스로 만들려고 할 수도 있지만, 이는 시간과 에너지를 낭비할 수 있습니다. 대신, 이를 위해 특별히 제작된 많은 오픈 소스 라이브러리 중 하나를 구현하도록 선택할 수 있습니다.

내 UI/UX 요구 사항에 대해 GitHub davideas/FlipView에서 라이브러리를 찾았습니다. 약간의 조사 후에 나는 그것이 정말로 전문적으로 만들어 졌다고 결정했습니다. 구현하기 쉽고 시도해 볼 수 있을 만큼 충분히 오래 지원됩니다. 사용을 시작하려면 다음과 같은 간단한 단계를 따르십시오.

1. 종속성을 추가합니다.

라이브러리를 프로젝트로 가져옵니다.

dependencies {
  implementation 'eu.davidea:flipview:1.1.3'
}

2. 그것을 사용하십시오.

Android의 일반 및 사용자 정의 앱 속성으로 FlipView를 구성합니다. 쉽지 않나요?

<android.support.v7.widget.CardView xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    ...
        <eu.davidea.flipview.FlipView
            android:id="@+id/item_image_icon"
            style="@style/FlipView"
            android:clickable="true"
            android:focusable="true"
            app:layout_constraintBottom_toTopOf="@+id/item_ranking"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    ...
</android.support.v7.widget.CardView>

따라서 바퀴를 재발명하지 마십시오. 결과를 더 빨리 전달하려면 항상 일부 라이브러리를 먼저 사용하는 것이 좋습니다. 그리고 프로젝트가 지저분해지면 오래된 라이브러리를 탓하는 것이 아니라 코드를 얼마나 잘 구성하느냐가 문제입니다. 프로젝트가 충분히 모듈화되어 있다면 문제가 없을 것입니다. 그러나 그것은 별도의 주제이며 이후 블로그 게시물에서 프로젝트 아키텍처에 대해 이야기하겠습니다.

Android 앱 빌드 시작 방법:목업, UI 및 XML 레이아웃 만들기
기본 화면 목록 내에서 사용되는 Flip View

RTL 지원을 통한 현지화

이 앱을 만들기로 마음먹었을 때 기본 영어 UI로 만드는 것뿐만 아니라 개발 초기부터 모국어인 리투아니아어로 번역하는 것을 목표로 삼았습니다.

이 목표를 통해 Android의 현지화에 대해 배우게 되었습니다. 내가 찾은 것은 여러 언어에 대한 지원을 추가하는 것이 매우 쉽다는 것입니다. 앞서 언급했듯이 먼저 모든 문자열을 strings.xml 파일로 분리해야 합니다. 그런 다음 Android Studio 내에서 번역 편집기 도구를 실행할 수 있습니다. 이렇게 하면 새로운 언어 지원을 추가할 수 있습니다.

Android 앱 빌드 시작 방법:목업, UI 및 XML 레이아웃 만들기

인터페이스가 매우 직관적임을 알 수 있습니다. 이제 strings.xml 파일의 각 문자열을 새 언어로 번역해야 합니다. 새 파일은 별도의 디렉토리에 IDE에 의해 생성됩니다. 예를 들어, 리투아니아어의 경우 values-lt/strings.xml입니다. . 그게 다야! ?

Android 기기 시스템 언어를 방금 번역한 언어로 전환한 다음 앱을 실행하면 모든 UI가 자동으로 번역으로 업데이트됩니다. 또한 My Crypto Coins 앱의 경우 나중에 런타임에 언어를 전환하는 기능을 추가할 계획입니다. 그래서 설정 화면에서 언어 전환기를 볼 수 있습니다.

Android 앱 빌드 시작 방법:목업, UI 및 XML 레이아웃 만들기
설정 화면 - 언어 리투아니아어

앱을 다른 언어로 번역하면 확실히 청중이 넓어집니다. 아랍어, 히브리어 또는 페르시아어와 같이 오른쪽에서 왼쪽(RTL)으로 쓰여지는 몇 가지 특수 언어에 대한 지원을 추가하여 더 나아가지 않으시겠습니까?

Android 앱 빌드 시작 방법:목업, UI 및 XML 레이아웃 만들기

실제로 RTL 지원을 추가하는 것은 전혀 어렵지 않습니다. 매니페스트 파일에 지원 속성을 추가하기만 하면 됩니다.

<manifest xmlns:android="https://schemas.android.com/apk/res/android"
    package="com.baruckis.mycryptocoins">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        ...
    </application>

</manifest>

축하합니다. 이제 앱이 RTL을 지원합니다. 그러나 모든 것이 즉시 올바르게 작동할 것이라고 맹목적으로 신뢰할 수는 없습니다. 얼마나 잘 지원되는지 직접 확인해야 합니다. 그렇게 하려면 RTL 언어 중 하나를 기본 장치 언어로 선택하십시오.

RTL에 대한 몇 가지 팁:

  • 모든 레이아웃에서 모든 Left을 대체해야 합니다. 및 Right Start이 있는 레이아웃 속성 및 End 동등한. 예:android:paddingLeft android:paddingStart로 대체되어야 합니다. .
  • RTL용 특수 드로어블이 없는 경우 특수 autoMirrored을 사용하여 현재 드로어블을 미러링할 수 있습니다. 속성.
<vector xmlns:android="https://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:autoMirrored="true"
    android:tint="@color/colorForPreferenceIcon"
    android:viewportHeight="24.0"
    android:viewportWidth="24.0">
    <path
        android:fillColor="#FF000000"
        android:pathData="M1.5,4V5.5C1.5,9.65 3.71,13.28 7,15.3V20H22V18C22,15.34 16.67,14 14,14C14,14 13.83,14 13.75,14C9,14 5,10 5,5.5V4M14,4A4,4 0 0,0 10,8A4,4 0 0,0 14,12A4,4 0 0,0 18,8A4,4 0 0,0 14,4Z" />
</vector>
  • RTL 대신 LTR이 필요한 특정 장소가 있다면 그렇게 할 수도 있습니다. 예를 들어 레이아웃을 LTR에 강제 적용하려면 android:layoutDirection="ltr"를 추가하기만 하면 됩니다. 그 보기에. android:textDirection="ltr"를 사용하는 것보다 텍스트 보기 내에서 텍스트 방향을 강제해야 하는 경우 .
Android 앱 빌드 시작 방법:목업, UI 및 XML 레이아웃 만들기

그건 그렇고 번역 편집기를 사용할 때 전문 번역 서비스를 주문할 때 매우 유용한 기능이 있습니다. 개인적으로 히브리어에 대해 아는 것이 없더라도 My Crypto Coins 프로젝트의 예제 RTL 언어로 히브리어에 대한 지원을 추가하는 것이 목표입니다. 그래서 이 기능은 정말 좋은 아이디어인 것 같습니다. 몇 번의 클릭만으로 IDE에서 직접 영어에서 히브리어로 번역을 성공적으로 주문했습니다.

Android 앱 빌드 시작 방법:목업, UI 및 XML 레이아웃 만들기
번역 서비스 주문

최종 생각

이 시리즈의 두 번째 부분에서는 상세한 모형을 기반으로 하는 일부 초기 코드로 모든 UI 레이아웃을 만들었습니다. 나는 당신을 혼란스럽게하지 않기 위해 각 주제에 대해 너무 자세히 설명하고 싶지 않았습니다. 제 목표는 큰 그림을 보는 방법, 작은 세부 사항에 먼저 초점을 맞추는 방법, 나중에 값비싼 실수를 피하는 방법을 보여주는 것이었습니다.

충분히 다루지 않은 것을 발견하면 이를 시작점으로 사용하여 자신의 연구를 수행하십시오. 웹에는 항상 배울 수 있는 좋은 리소스가 많이 있습니다. ?

저장소

여기에서 이 부분에 대해 생성된 모든 XML 레이아웃과 코드를 찾을 수 있습니다.

GitHub에서 소스 보기

아츄! 읽어 주셔서 감사합니다! 저는 원래 2018년 5월 14일 개인 블로그 www.baruckis.com에 이 게시물을 게시했습니다.