코틀린에서는 함수의 파라미터에 디폴트 값을 정할 수 있습니다. 이런 기능은 자바에서는 지원하지 않았던 기능입니다.

그래서 코틀린에서 만든 메소드를 자바에서 사용하게 될 때 문제가 발생할 수 있는데, @JvmOverloads라는 어노테이션을 이용하여 해결할 수 있습니다. 

 

@JvmOverloads라는 어노테이션을 적용하면 모든 경우의 메소드를 만들어주게 됩니다.

 

'Android' 카테고리의 다른 글

@JvmOverloads  (0) 2020.12.15
[Android] 키패드에 따른 화면변경 설정하기  (0) 2020.04.20
[Android] 테스트  (0) 2020.04.11
[Android] Gradle  (0) 2020.04.10
[Android] ContentProvider, Service, BroadcastReceiver  (0) 2020.04.03

코틀린에서는 함수의 파라미터에 디폴트 값을 정할 수 있습니다. 이런 기능은 자바에서는 지원하지 않았던 기능입니다.

그래서 코틀린에서 만든 메소드를 자바에서 사용하게 될 때 문제가 발생할 수 있는데, @JvmOverloads라는 어노테이션을 이용하여 해결할 수 있습니다. 

 

@JvmOverloads라는 어노테이션을 적용하면 모든 경우의 메소드를 만들어주게 됩니다.

 

'Android' 카테고리의 다른 글

@JvmOverloads  (0) 2020.12.15
[Android] 키패드에 따른 화면변경 설정하기  (0) 2020.04.20
[Android] 테스트  (0) 2020.04.11
[Android] Gradle  (0) 2020.04.10
[Android] ContentProvider, Service, BroadcastReceiver  (0) 2020.04.03

MotionLayout 은 앱에서 모션과 위젯 애니메이션을 관리하는 데 사용할 수 있는 레이아웃 유형입니다.

MotionLayout 은 ConstraintLayout 의 서브클래스이며 ConstraintLayout 의 다양한 레이아웃 기능을 기초로합니다.

ConstraintLayout 라이브러리의 일부인 MotionLayout 은 지원 라이브러리로 사용 가능하며, API 레벨 14와 호환됩니다.

그림 1. 기본터치 컨트롤 모션입니다.

 

MotionLayout 은 레이아웃 전환과 복잡한 모션 처리를 연결하며 속성 애니메이션 프레임워크, TransitionManager 및 CoordinatorLayout 의 혼합된 기능을 제공합니다. 

 

레이아웃 사이의 전환 외에도 MotionLayout 을 통해 레이아웃 속성에 애니메이션을 적용할 수 있습니다.

한  기본적으로 검색 가능 전환(seekable transitions)을 지원합니다. 이 말은 터치 입력과 같은 어떤 조건을 기준으로 전환 내의 어떤 포인트를 즉시 보여줄 수 있다는 것을 의미합니다. 또한 MotionLayout에서는 키프레임도 지원하므로 사용자의 필요에 맞게 완전히 맞춤설정된 전환을 사용할 수 있습니다.

 

MotionLayout 은 완전히 선언적인 것으로, 복잡도에 상관없이 XML로 모든 전환을 설명할 수 있습니다.

 ☆참고 : MotionLayout은 직접 하위 요소와만 호환됩니다. 중첩 레이아웃 계층 구조 또는 액티비티 전환은 지원하지 않습니다. 

디자인 고려 사항 

MotionLayout 은 버튼 및 제목 표시줄과 같이 사용자가 상호작용하는 UI 요소를 이동, 크기 조절 및 애니메이션화하기 위한 것입니다. 앱의 모션은 단지 애플리케이션의 불필요한 특수 효과가 되어서는 안 됩니다. 사용자가 애플리케이션 기능을 이해하는 데 도움이 되어야 합니다. 모션을 사용하여 앱을 디자인하는데 관한 자세한 내용은 모션 이해의 머티리얼 디자인 섹션을 참조하세요. 

 시작하기

 프로젝트에서 MotionLayout 사용을 시작하려면 다음 단계를 따르세요.

1. ConstraintLayout 종속 항목 추가

프로젝트에서 MotionLayout 을 사용하려면 ConstraintLayout 2.0 종속 항목을 앱의 build.gradle 파일에 추가합니다.

AndroidX를 사용중이라면 다음 종속 항목을 추가하세요. 

    dependencies {
        implementation
'androidx.constraintlayout:constraintlayout:2.0.0-beta1'
   
}   

 AndroidX를 사용하지 않으면 다음 지원 라이브러리 종속성을 추가하세요.

    dependencies {
        implementation
'com.android.support.constraint:constraint-layout:2.0.0-beta1'
   
}

2. MotionLayout 파일 만들기

MotionLayout은  ConstraintLayout 의 서브클래스이므로 다음 예에서와 같이 레이아웃 리소스 파일에서 클래스 이름을 바꿔 기존 ConstraintLayout을 MotionLayout 으로 전환할 수 있습니다.

    <!-- before: ConstraintLayout -->
   
<androidx.constraintlayout.widget.ConstraintLayout .../>
   
<!-- after: MotionLayout -->
   
<androidx.constraintlayout.motion.widget.MotionLayout .../> 

다음은 그림 1에서 모션을 생성하는 데 사용할 수 있는 MotionLayout 파일의 전체 예입니다.

    <?xml version="1.0" encoding="utf-8"?>
    <!-- activity_main.xml -->
    <androidx.constraintlayout.motion.widget.MotionLayout
        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:id="@+id/motionLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutDescription="@xml/scene_01"
        tools:showPaths="true">

        <View
            android:id="@+id/button"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:background="@color/colorAccent"
            android:text="Button" />

    </androidx.constraintlayout.motion.widget.MotionLayout>
            

3. MotionScene 만들기

이전 MotionLayout 예에서 app:layoutDescription 속성은 MotionScene 을 참조합니다.

MotionScene 은 해당 레이아웃의 모든 모션 설명을 포함하는 XML 리소스 파일입니다.

레이아웃 정보를 모션 설명과 별도로 유지하기 위해 각 MotionLayout 은 별도의 MotionScene을 참조합니다.

MotionScene의 정의는 MotionLayout 의 유사한 정의보다 우선한다는 점에 유의합니다.

    <?xml version="1.0" encoding="utf-8"?>
    <MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:motion="http://schemas.android.com/apk/res-auto">

        <Transition
            motion:constraintSetStart="@+id/start"
            motion:constraintSetEnd="@+id/end"
            motion:duration="1000">
            <OnSwipe
                motion:touchAnchorId="@+id/button"
                motion:touchAnchorSide="right"
                motion:dragDirection="dragRight" />
        </Transition>

        <ConstraintSet android:id="@+id/start">
            <Constraint
                android:id="@+id/button"
                android:layout_width="64dp"
                android:layout_height="64dp"
                android:layout_marginStart="8dp"
                motion:layout_constraintBottom_toBottomOf="parent"
                motion:layout_constraintStart_toStartOf="parent"
                motion:layout_constraintTop_toTopOf="parent" />
        </ConstraintSet>

        <ConstraintSet android:id="@+id/end">
            <Constraint
                android:id="@+id/button"
                android:layout_width="64dp"
                android:layout_height="64dp"
                android:layout_marginEnd="8dp"
                motion:layout_constraintBottom_toBottomOf="parent"
                motion:layout_constraintEnd_toEndOf="parent"
                motion:layout_constraintTop_toTopOf="parent" />
        </ConstraintSet>

    </MotionScene>
        

<Transition> 에는 모션의 기본 정의가 포함되어 있습니다.

motion:constraintSetStart 및 motion:constraintSetEnd 는 모션의 엔드포인트 참조입니다. 
이러한 엔드포인트는 나중에 <MotionScene>의 <ConstraintSet> 요소에 정의됩니다.

motion:duration 은 모션이 완료되는 데 걸리는 시간을 밀리초 단위로 지정합니다.

 

<OnSwipe> 를 사용하면 터치를 통해 모션을 제어할 수 있습니다.

 motion:touchAnchorId 는 스와이프하고 드래그할 수 있는 뷰를 나타냅니다.

motion:touchAnchorSide 는 오른쪽으로 뷰를 드래그하는 것을 나타냅니다.

motion:dragDirection 은 드래그의 진행방향을 나타냅니다. 예를 들어 motion:dragDirection="dragRight" 는 오른쪽으로 드래그하면 진행률이 증가한다는 의미입니다.

 

<ConstraintSet> 를 통해서는 모션을 설명하는 다양한 제약조건을 정의합니다.

이 예에서는 모션의 엔드포인트마다 한 개씩 ConstraintSet 를 정의합니다.

이 엔드포인트는 세로로는 가운데에 위치합니다. (app:layout_constraintTop_toTopof="parent" 및 app:layout_constraintBottom_toBottomOf="parent" 사용). 가로로는 화면의 맨 왼쪽과 오른쪽에 엔드포인트가 있습니다.

 Interpolated Attributes

 MotionScene 파일에서 COnstraintSet 요소에는 전환하는 동안 추가되는 추가 속성이 포함될 수 있습니다. 위치 및 경계 이외에 다음 속성은 MotionLayout을 통해 추가됩니다.

  • alpha

  • visibility

  • rotation, rotationX, rotationY

  • translationZ, translationY, translationX

  • scaleX, scaleY

Custom attributes

<Constraint> 에서 <CustomAttribute> 요소를 사용하여 위치 또는 View 속성과 관련되지 않은 속성의 전환을 지정할 수 있습니다. 

    <Constraint
        android:id="@+id/button" ...>
        <CustomAttribute
            motion:attributeName="backgroundColor"
            motion:customColorValue="#D81B60"/>
    </Constraint>

<CustomAttribute> 에는 다음과 같은 두 가지 고유 속성이 있습니다.

  • motion:attributeName 은 필수이며 getter 및 setter 메서드가 있는 객체와 일치해야 합니다.
    getter와 setter는 특정 패턴과 일치해야 합니다. 예를 들어, Google의 View에는 기본적으로 getBackgroundColor() 및 setBackgroundColor() 메서드가 있으므로 backgroundColor 가 지원됩니다.

  • 입력해야하는 다른 속성은 값 유형을 기반으로 합니다. 지원되는 다음 유형 중에서 선택하세요.

    • motion:customColorValue - 색상
    • motion:customIntegerValue - 정수
    • motion:customFloatValue - 부동수
    • motion:custromStringValue - 문자열
    • motion:customDimension - 측정기준
    • motion:customBoolean - 부울

 맞춤 속성을 지정할 때 시작 및 종료 <ConstraintSet> 요소 모두에 엔드포인트 값을 정의해야합니다.

 예: 배경색 변경

 앞의 예를 기반으로 그림 2에 표시된 대로 보기에서 모션의 일부로 색상을 변경해 보겠습니다.

그림2. 뷰가 이동하면 배경색이 변경됩니다.

 아래와 같이 <CustomAttribute> 요소를 각 <ConstraintSet> 요소에 추가합니다.

    <ConstraintSet android:id="@+id/start">
        <Constraint
            android:id="@+id/button"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:layout_marginStart="8dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintStart_toStartOf="parent"
            motion:layout_constraintTop_toTopOf="parent">
            <CustomAttribute
                motion:attributeName="backgroundColor"
                motion:customColorValue="#D81B60" />
        </Constraint>
    </ConstraintSet>

    <ConstraintSet android:id="@+id/end">
        <Constraint
            android:id="@+id/button"
            android:layout_width="64dp"
            android:layout_height="64dp"
            android:layout_marginEnd="8dp"
            motion:layout_constraintBottom_toBottomOf="parent"
            motion:layout_constraintEnd_toEndOf="parent"
            motion:layout_constraintTop_toTopOf="parent">
            <CustomAttribute
                motion:attributeName="backgroundColor"
                motion:customColorValue="#9999FF" />
        </Constraint>
    </ConstraintSet>
    

 추가 MotionLayout 속성

 위의 예에 있는 속성 외에도 MotionLayout 에는 다음과 같이 지정할 수 있는 다른 속성이 있습니다.

  • app:applyMotionScene="boolean" 은 MotionScene 적용 여부를 나타냅니다.
    이 속성의 기본값은 true 입니다.

  • app:showPaths="boolean" 은 모션이 실행 중일 때 모션 경로를 표시할지 나타냅니다.
    이 속성의 기본값은 false 입니다.

  • app:progress="float" 를 사용하면 전환 진행 상황을 명시적으로 지정할 수 있습니다.
    0(전환시작)부터 1(전환종료)까지 부동 소수점 값을 사용할 수 있습니다.

  • app:currentState="reference" 를 사용하면 특정 ConstraintSet 를 지정할 수 있습니다.
  • app:motionDebug 를 사용하면 모션에 관한 추가 디버그 정보를 표시할 수 있습니다.
    가능한 값은 'SHOW_PROGRESS', 'SHOW_PATH' 또는 'SHOW_ALL' 입니다.

SUMMARY

MotionLayout 은 앱에서 모션과 위젯 애니메이션을 관리하는 데 사용할 수 있는 레이아웃 유형입니다.

기본적으로 검색 가능 전환(seekable transitions)을 지원합니다. 이 말은 터치 입력과 같은 어떤 조건을 기준으로 전환 내의 어떤 포인트를 즉시 보여줄 수 있다는 것을 의미합니다. 또한 키프레임도 지원하므로 사용자의 필요에 맞게 전환을 사용할 수 있습니다.

 

MotionScene은 해당 레이아웃의 모든 모션 설명을 포함하는 XML 리소스이며, 레이아웃 정보를 모션 설명과 별도로 유지하기 위해 MotionLayout은 별도의 MotionScene을 참조합니다. MotionScene의 정의는 MotionLayout 의 유사한 정의보다 우선합니다.

 

<Transition> 에는 모션의 기본 정의가 포함되어 있습니다.

motion:constraintSetStart 및 motion:constraintSetEnd 는 모션의 엔드포인트 참조입니다. 
이러한 엔드포인트는 나중에 <MotionScene>의 <ConstraintSet> 요소에 정의됩니다.

 

<OnSwipe> 를 사용하면 터치를 통해 모션을 제어할 수 있습니다.

<ConstraintSet> 를 통해서는 모션을 설명하는 다양한 제약조건을 정의합니다.

 

<CustomAttribute> 에는 다음과 같은 두 가지 고유 속성이 있습니다.

  • motion:attributeName 은 필수이며 getter 및 setter 메서드가 있는 객체와 일치해야 합니다.
    getter와 setter는 특정 패턴과 일치해야 합니다. 

  • 입력해야하는 다른 속성은 값 유형을 기반으로 합니다. 

맞춤 속성을 지정할 때 시작 및 종료 <ConstraintSet> 요소 모두에 엔드포인트 값을 정의해야합니다.

Reference

MotionLayout : developer.android.com/training/constraint-layout/motionlayout?hl=ko#androidx

'Android > Android Developer' 카테고리의 다른 글

Layout 레이아웃  (0) 2020.12.01
ConstraintLaytout  (0) 2020.11.02

Layout

레이아웃은 앱에서 사용자 인터페이스를 위한 구조(예: 액티비티)를 정의합니다.

레이아웃의 모든 요소는 ViewViewGroup 객체의 계층을 사용하여 빌드됩니다. 

일반적으로 View는 사용자가 보고 상호작용할 수 있는 것을 그립니다. 

View Group은 그림 1과 같이 View와 ViewGroup 객체의 레이아웃 구조를 정의하는 투명 컨테이너입니다. 

 

그림 1. 레이아웃을 정의하는 뷰 계층.

일반적으로 View 객체는 '위젯' 이라고 하고 여러 하위 클래스 중 하나가 될 수 있습니다.

(예 : Button 또는 TextView)

 

ViewGroup 객체는 대개 '레이아웃' 이라고 부르고 다양한 레이아웃 구조를 제공하는 여러 유형 중 하나일 수 있습니다. (예: LinearLayout 또는 ConstraintLayout)

 

레이아웃을 정의하는 방법

  • UI 요소를 XML로 선언.

  • 런타임에 레이아웃 요소 인스턴스화.

XML에서 UI를 선언하면 동작을 제어하는 코드와 앱을 표현하는 것을 분리할 수 있습니다.

또한 XML 파일을 사용하면 다양한 화면 크기와 방향에 여러 가지 레이아웃을 쉽게 제공할 수 있습니다.

 

Android 프레임워크는 위 방법 중 하나 또는 두 가지 모두를 사용하여 앱의 UI를 빌드하는 유연한 수단을 제공합니다.

예를 들어 앱의 기본 레이아웃을 XML에서 선언한 다음, 런타임에서 레이아웃을 수정할 수 있습니다.

XML 쓰기

Android의 XML 어휘를 사용하면 UI 레이아웃과 그 안에 들어있는 화면 요소를 HTML에서 웹 페이지를 디자인할 떄와 같은 방식으로 신속하게 디자인할 수 있습니다. 

 

각 레이아웃 파일에는 반드시 딱 하나의 루트 요소만 있어야 하며, 이는 View 또는 ViewGroup 객체여야 합니다.

루트 요소를 정의한 후에 더 많은 레이아웃 객체 또는 위젯을 하위 요소로 추가하여 계층적으로 레이아웃을 정의하는 뷰 계층을 빌드할 수 있습니다. 

 

레이아웃을 XML로 선언하고 나면 그 파일을 Android 프로젝트의 res/layout/ 폴더 내에 .xml 확장자로 저장하여 적절하게 컴파일되도록 합니다.

 

레이아웃 리소스

레이아웃 리소스는 Activity의 UI 또는 UI 구성요소의 아키텍처를 정의합니다.

 

파일 위치

  • res/layout/filename.xml

  • 파일 이름이 리소스 ID로 사용됩니다.

컴파일된 리소스 데이터 유형

  • View 리소스를 가리키는 리소스 포인터입니다.

리소스 참조

  • 자바 : R.layout.filename

  • XML : @[package:]layout/filename

더보기

요소

<ViewGoup>

다른 View 요소의 컨테이너. 다양한 ViewGroup 객체가 있으며 각 객체를 사용하여 여러 방식으로 하위 요소의 레이아웃을 지정할 수 있습니다. VIewGroup 객체이는 LinearLayout, RelativeLayout, FrameLayout 등이 있습니다.

 

ViewGroup의 모든 파생 객체가 중첩된 View를 허용한다고 가정해서는 안됩니다. 일부 ViewGroup은 AdapterVIew 클래스를 구현한 것이며 이 클래스는 Adapter 하위에서만 하위 요소를 걸정합니다.

 

속성

android:id

  • 리소스 ID. 요소의 고유한 리소스 이름이며 이를 사용하여 애플리케이션에서 ViewGroup의 참조를 가져올 수 있습니다.

android:layout_height   및   android:layout_width

  • 크기 또는 키워드. 필수사항. 크기 값(또는 크기 리소스) 또는 키워드("match_parent" 또는 "wrap_content")

더 많은 속성이 ViewGroup 기본 클래스에서 지원되며 사용가능한 모든 속성의 참조는 ViewGroup 클래스의 대응하는 문서를 확인하세요.

 

<View>

일반적으로 '위젯'이라고 하는 개별 UI 구성요소입니다. VIew 객체에는 TextVIew, Button, CheckBox 등이 있습니다.

 

속성

android:id

  • 리소스 ID. 요소의 고유한 리소스 이름이며 이를 사용하여 애플리케이션에서 ViewGroup의 참조를 가져올 수 있습니다.

android:layout_height   및   android:layout_width

  • 크기 또는 키워드. 필수사항. 크기 값(또는 크기 리소스) 또는 키워드("match_parent" 또는 "wrap_content")

더 많은 속성이 View 기본 클래스에서 지원되며 사용가능한 모든 속성의 참조는 대응하는 참조 문서를 확인하세요.

 

<requestFocus>

View 객체를 나타내는 모든 요소는 빈 요소를 포함할 수 있고 이 빈 요소는 화면에서 상위 요소에 초기 포커스를 줍니다. 이러한 요소는 파일당 하나만 포함할 수 있습니다.

 

<include>

레이아웃에 레이아웃 파일을 포함합니다.

속성

layout

  • 레이아웃 리소스. 필수사항. 레이아웃 리소스를 가리키는 참조입니다.

android:id

  • 리소스 ID. 요소의 고유한 리소스 이름이며 이를 사용하여 애플리케이션에서 ViewGroup의 참조를 가져올 수 있습니다.

android:layout_height   및  android:layout_width

  • 크기 또는 키워드. 필수사항.  크기 값(또는 크기 리소스) 또는 키워드("match_parent" 또는 "wrap_content"

<include> 내에 포함된 레이아웃에서 루트요소에 의해 지원되는 다른 레이아웃 속성을 포함할 수 있고 그러한 속성은 루트 요소에 정의된 속성을 재정의합니다.

주의 : <include> 태그를 사용하여 레이아웃 속성을 재정의하면 다른 레이아웃 속성을 적용하기 위해 andorid:layout_height, andorid:layout_width를 모두 재정의해야 합니다.

 

레이아웃을 포함하는 또 다른 방법은 ViewStub을 사용하는 것입니다. ViewStub은 가벼운 View로서, 명시적으로 펼칠 때까지 레이아웃 공간을 소비하지 않으며 이 시점에 android:layout 속성에 의해 재정의된 레이아웃 파일을 포함합니다. ViewStub을 사용하는 방법에 관한 자세한 내용은 주문형 뷰 로드를 참조합니다.

 

<merge>

레이아웃 계층 구조에 그려지지 않은 대체 루트 요소입니다. 루트 요소로 이 요소를 사용하면 이 레이아웃이 이미 적절한 상위 뷰를 포함하여 <merge> 요소의 하위 요소를 포함하는 레이아웃에 배치되는 경우에 유용합니다.

<include>를 사용하여 이 레이아웃을 다른 레이아웃 파일에 포함하고 이 레이아웃이 다른 ViewGroup 컨테이너가 필요하지 않을 때에 특히 유용합니다. 레이아웃 병합에 관한 자세한 내용은 <include/>로 레이아웃 재사용을 참조하세요.

 

android:id값 

ID 값의 경우 일반적으로 "@+id/name" 구문 형식을 사용해야 합니다. 더하기 기호(+)는 새 리소스 ID를 나타내며 리소스 정수가 아직 존재하지 않는다면 aapt 도구는 R.java 클래스에 새 리소스 정수를 만듭니다.

 

android:layout_height 및 android:layout_width의 값

높이 및 너비 값은 Android에서 지원하는 모든 크기 단위(px, dp, sp, pt, in, mm) 또는 다음의 키워드를 사용하여 표현할 수 있습니다.

  • match_parent : 상위 요소의 크기와 일치하도록 크기를 설정합니다. fill_parent 지원을 중단하기 위해 API 레벨 8에서 추가 되었습니다.

  • wrap_content

XML 리소스 로드

앱을 컴파일하는 경우, 각 XML 레이아웃 파일이 View 리소스 안에 컴파일 됩니다.

Activity.onCreate() 콜백 구현에서 앱 코드로부터 레이아웃 리소스를 로드해야 합니다.

setContentView() 를 호출하고 R.layout.layout_file_name의 형태로 레이아웃 리소스에 대한 참조로 전달합니다.

예를 들어 XML 레이아웃이 main_layout.xml로 저장된다면 다음과 같이 액티비티에 대해 로드합니다.

fun onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main_layout)
}

 액티비티 내의 onCreate() 콜백 메서드는 액티비티가 시작될 때 Android 프레임워크에 의해 호출됩니다.

 

특성

모든 View와 ViewGroup 객체는 고유한 여러가지 XML 특성을 지원합니다.

어떤 특성은 View 객체에만 적용되지만(예: TextView는 textSize 특성 지원), 이 특성은 이 클래스를 확장할 수 있는 View 객체로부터 상속받은 것입니다. 어떤 특성은 루트 View 클래스에서 상속되기 때문에(예: id 특성) 모든 View 객체에 공통적으로 적용됩니다.

 

그리고 나머지 특성은 "레이아웃 매개변수"로 간주됩니다. 이들은 View 객체의 특정한 레이아웃 방향을 설명하는 것으로, 이는 해당 객체의 상위 ViewGroup 객체에서 정의된 바에 따릅니다.

 

ID

View 객체는 트리 내에서 뷰를 고유하게 식별할 수 있는 ID가 연결될 수 있습니다. 

앱을 컴파일할 때 이 ID는 정수로 참조되지만, 일반적으로 레이아웃 XML 파일의 id 특성에서 문자열로 할당됩니다.

이는 모든 View 객체에 공통적인 XML 특성이며(View 클래스에서 정의), 매우 자주 사용하게 될 것입니다.

 

문자열 시작 부분에 있는 앳 기호(@)는 XML 파서가 ID 문자열의 나머지를 파싱하고 확장하여 ID 리소스로 식별해야 한다는 것을 나타냅니다. 더하기 기호(+)는 이것이 새 리소스 이름이며, 이것을 반드시 생성하여 리소스에 추가해야 한다는 것을 뜻합니다. 

 

Android 리소스를 참조할 때에는 더하기 기호는 필요하지 않지만 android 패키지 네임스페이스를 다음과 같이 반드시 추가해야 합니다.

android:id="@android:id/empty"

android 패키지 네임스페이스가 들어가면 이제 로컬 리소스 클래스에서가 아니라 android.R 리소스 클래스에서 ID를 참조하게 됩니다.  

 레이아웃 매개변수

layout_something  이라는 XML 레이아웃 특성이 뷰가 상주하는 ViewGoup에 대해 적절한 뷰의 레이아웃 매개변수를 정의합니다. 

 

모든 ViewGroup 클래스가 중첩된 클래스를 하나씩 구현하며 이것이 ViewGroup.LayoutParams 를 확장합니다.

이 하위 클래스에는 각 하위 뷰의 크기와 위치를 뷰 그룹에 적절한 방식으로 정의하는 속성 유형이 들어있습니다.

그림 2에서 볼 수 있듯이, 상위 뷰 그룹이 각 하위 뷰의 레이아웃 매개변수를 정의합니다.

 

그림2. 각 뷰와 연관된 레이아웃 매개변수가 있는 뷰 계층을 시각화한 것.

모든 LayoutParams 하위 클래스에는 설정 값에 대한 각기 자신만의 구문이 있다는 점을 참고하세요.

각 하위 요소는 자신의 상위에 적합한 LayoutParams를 정의해야 합니다. 다만 이것은 자신의 하위에 대해 각기 다른 LayoutParams도 정의할 수 있습니다.

 

모든 ViewGroup에는 너비와 높이(layout_width, layout_height)가 포함되며, 각 뷰는 이들을 반드시 정의해야 합니다. 선택사항으로 여백과 테두리도 포함하는 LayoutParams도 많습니다.

 

너비와 높이는 정확한 치수로 지정할 수 있습니다. 다만 이것은 자주 하지 않는 것이 좋습니다.

그보다는 다음과 같은 상수 중 하나를 사용하여 너비 또는 높이를 설정하는 경우가 더 많습니다.

  • wrap_content는 콘텐츠에 필요한 치수대로 자동으로 크기를 조정하도록 뷰에 지시합니다.

  • match_parent는 상위 뷰 그룹이 허용하는 한 최대한으로 커지도록 뷰에 지시합니다.

 일반적으로 픽셀과 같이 절대적인 단위를 사용하여 레이아웃 너비와 높이를 지정하는 것은 권장하지 않습니다.

그 대신, 밀도 독립적인 픽셀 단위(dp), wrap_content 또는 match_parent와 같이 상대적인 측정치를 사용하는 것이 더 낫습니다. 이렇게하면 앱이 다양한 기기 화면 크기에 걸쳐서도 적절하게 표시되도록 보장하는데 도움이 되기 때문입니다.

 

레이아웃 위치

뷰의 모양은 직사각형입니다. 뷰에는 위치가 있으며, 이는 한 쌍의 왼쪽 및 상단좌표, 그리고 두 개의 치수가 너비와 높이를 나타내는 형식으로 표현됩니다. 위치와 치수의 단위는 픽셀입니다.

 

뷰의 위치를 검색할 수 있습니다. getLeft() 및 getTop() 메서드를 호출하면 됩니다.

getLeft()는 뷰를 나타내는 직사각형의 왼쪽, 즉 X좌표를 반환합니다.

getTop()은 뷰를 나타내는 직사각형의 상단, 즉 Y좌표를 반환합니다.

이들 메서드는 둘 다 해당 뷰의 상위에 상대적인 뷰의 위치를 반환합니다.

예를들어 getLeft() 가 20을 반환하는 경우 이는 해당 뷰가 그 뷰의 바로 상위의 왼쪽 가장자리에서 오른쪽으로 20픽셀 떨어진 곳에 있다는 뜻입니다.

 

그 외에도 불필요한 계산을 피하기 위해 여러가지 편의 메서드가 제공됩니다. 

getRight() 및 getBottom()을 들 수 있습니다. 이 메서드는 해당 뷰를 나타내는 직사각형의 오른쪽과 하단가장자리의 좌표를 반환합니다.

 

예를 들어 getRight()를 호출하는 것은 getLeft() + getWidth() 계산과 비슷합니다.

크기, 패딩 및 여백

뷰의 크기는 너비와 높이로 표현됩니다. 사실 하나의 뷰는 두 쌍의 너비 및 높이 값을 소유합니다.

 

첫 번째 쌍을 측정된 너비 및 측정된 높이라고 합니다. 이들 치수는 뷰가 상위 내에서 얼마나 커지고자 하는지를 정의합니다.

측정된 치수를 가져오려면 getMeasuredWidth() 및 getMeasuredHeidght() 를 호출합니다. 

 

두 번째 쌍은 단순히 너비 및 높이라고 일컬으며, 때로는 그리기 너비(drawing width) 및 그리기 높이(drawing height)로 부를 때도 있습니다. 이러한 치수는 그리는 시간과 배치 후에 뷰가 화면에 표시되는 실제 크기를 정의합니다. 이들 값은 측정된 너비및 높이와 달라도 되지만 꼭 달라야 하는 것은 아닙니다. 너비와 높이를 가져오려면 getWidth() 및 getHeight()을 호출하면 됩니다.

 

뷰의 치수를 측정하기 위해 뷰는 자신의 패딩을 감안합니다. 패딩은 뷰의 왼쪽, 상단, 오른쪽 및 하단 부분에 대해 픽셀로 표시됩니다. 패딩은 정해진 픽셀 수를 사용하여 뷰의 콘텐츠를 오프셋하는 데 쓰일 수도 있습니다. 

예를 들어 왼쪽 패딩을 2로 설정하면 해당 뷰의 콘텐츠를 왼쪽 가장자리에서 오른쪽으로 2픽셀 밀어냅니다.

패딩을 설정할 때는 setPadding(int, int, int, int) 메서드를 사용하면 되고,

이를 쿼리하려면 getPaddingLeft(), getPaddingTop(), getPaddingRight(), getPaddingBottom()을 사용하면 됩니다.

 

뷰는 패딩을 정의할 수 있지만, 여백(margin)에 대한 지원은 전혀 제공하지 않습니다. 다만 뷰 그룹이 그와 같은 지원을 제공합니다.

일반 레이아웃

ViewGroup 클래스의 각 하위 클래스는 각기 고유한 방식으로 자신 안에 중첩된 뷰를 표시합니다.

아래는 Android 플랫폼에서 기본 제공되는, 보다 보편적인 레이아웃 유형을 몇 가지 나타낸 것입니다.

 하나 이상의 레이아웃을 또 다른 레이아웃에 중첩하여 UI 디자인을 이룰 수도 있지만, 레이아웃 계층을 가능한 한 얕게 유지하도록 애써야 합니다. 중첩된 레이아웃이 적을수록 레이아웃이 더욱 빠르게 그려집니다.
(가로로 넓은 뷰 계층이 깊은 뷰 계층보다 낫습니다)

LinaearLayout

여러 하위 요소를 하나의 가로 또는 세로 방향 행으로 정리하는 레이아웃.

이 레이아웃은 창의 길이가 화면 길이를 웃도는 경우 스크롤바를 생성합니다.

RelativeLayout

여러 하위 객체의 위치를 서로 상대적으로 나타내거나, 상위와 상대적으로 나타낼 수 있도록 해줍니다.

WebView

웹 페이지를 표시합니다.

 

SUMMARY

레이아웃은 앱에서 사용자 인터페이스를 위한 구조를 정의합니다.

레이아웃의 모든 요소는 View 와 ViewGroup 객체의 계층을 사용하여 빌드됩니다.

 

레이아웃을 XML로 선언할 시 동작을 제어하는 코드와 앱을 표현하는 것을 분리할 수 있습니다. 또한 XML 파일을 사용하면 다양한 화면 크기와 방향에 여러 가지 레이아웃을 쉽게 제공할 수 있습니다. 

 

View 객체는 트리 내에서 뷰를 고유하게 식별할 수 있는 ID가 연결될 수 있으며, 앱을 컴파일할 때 이 ID는 정수로 참조되지만, 일반적으로 레이아웃 XML 파일의 id 특성에서 문자열로 할당됩니다.

 

View 의 모양은 직사각형입니다. 뷰에는 위치가 있으며, 이는 한 쌍의 왼쪽 및 상단좌표, 그리고 두 개의 치수가 너비와 높이를 나타내는 형식으로 표현됩니다. 위치와 치수의 단위는 픽셀입니다.

 

Reference

레이아웃 : developer.android.com/guide/topics/ui/declaring-layout?hl=ko

레이아웃 리소스 : developer.android.com/guide/topics/resources/layout-resource?hl=ko

'Android > Android Developer' 카테고리의 다른 글

MotionLayout  (0) 2020.12.02
ConstraintLaytout  (0) 2020.11.02

ConstraintLayout을 사용하면 플랫 뷰 계층 구조(중첩 뷰 그룹이 없음)로 크고 복잡한 레이아웃을 만들 수 있습니다.

 

 ConstraintLayout에서 뷰의 위치를 정의하려면 보기의 가로 및 세로 제약조건을 각각 하나 이상 추가해야 합니다. 

각 제약조건은 다른 뷰, 상위 레이아웃 또는 보이지 않는 안내선을 기준으로 한 정렬 또는 연결을 나타냅니다. 

각 제약조건은 세로 또는 가로 축을 따라 뷰의 위치를 정의하므로, 각 뷰에는 축마다 하나 이상의 제약조건이 있어야 합니다.

 

뷰를 Layout Editor에 놓으면 제약조건이 없어도 둔 위치에 그래돌 남습니다. 이는 단지 더 쉽게 편집하도록 한 것이며, 기기에서 레이아웃을 실행할 때 뷰에 제약조건이 없으면 [0,0](맨 위 왼쪽 모서리) 위치에 그립니다.

 

제약조건이 누락되어도 컴파일 오류가 발생하지는 않지만, Layout Editor에서는 누락된 제약조건을 툴바에 오류로 표시합니다. 오류 및 기타 경고를 보려면 Show Warnings and Errors를 클릭하세요. 제약조건이 누락되지 않도록 Layout Editor에서는 자동 연결 및 제약조건 추론 기능을 사용하여 제약조건을 자동으로 추가합니다.

 

ConstraintLayout을 프로젝트에 추가 

프로젝트에서 ConstraintLayout을 사용하려면 다음을 진행합니다.

1. 모듈 수준 build.gradle 파일에 선언된 maven.google.com 저장소가 있는지 확인합니다.

    repositories {
        google
()
   
}

2. 다음 예와 같이 동일한 build.gradle 파일에 종속 항목으로 라이브러리를 추가합니다. 최신버전은 다음 예에 표시된 버전과 다를 수 있습니다.

  dependencies {
    implementation
"androidx.constraintlayout:constraintlayout:2.0.0"
}

3. 툴바 또는 동기화 알림에서 Sync Project with Gradle Files를 클릭합니다.

 

 제약조건 추가 또는 삭제

상위 요소 포지셔닝

그림7. 상위 요소의 가로제약조건

 

뷰의측면을 레이아웃의 대응하는 가장자리로 제한합니다.

 

그림에서 뷰의 왼쪽은 상위 레이아웃의 왼쪽 가장자리에 연결되어 있습니다. 여백을 사용하여 가장자리로부터의 거리를 정의할 수 있습니다.

 

 

 

위치 순서 지정

 

 가로 또는 세로로 두 뷰가 표시되는 순서를 정의합니다.

 

그림에서 B는 항상 A의 오른쪽에 있도록 제한되고 C는 A 아래 있도록 제한됩니다. 그러나 이 제약조건은 정렬을 의미하지 않으므로, B는 여전히 위 아래로 이동할 수 있습니다.

 

 

  

정렬

 

뷰의 가장자리를 다른 뷰의 가장자리에 맞게 정렬합니다.

 

위의 그림에서 B의 왼쪽은 A의 왼쪽에 맞게 정렬됩니다.

뷰의 중심으로 정렬하려면 양쪽에 제약조건을 만듭니다.

 

 

 

 

 

제약조건에서 안쪽으로 보기를 드래그하여 기준에서 벗어나 정렬할 수 있습니다.   

예를 들어 아래 그림에서는 24dp 벗어나 정렬된 B를 보여줍니다.

이와 같은 오프셋은 제한된 뷰의 여백으로 정의됩니다. 

 

정렬할 뷰를 모두 선택하나 후 툴바에서 Align을 클릭하여 정렬 유형을 선택할 수도 있습니다.

 

 

기준선 정렬 

  

뷰의 텍스트 기준선을 다른 뷰의 텍스트 기준선에 맞춥니다.

 

그림에서 B의 첫 번째 줄은 A의 텍스트에 맞게 정렬됩니다.

 

기준선 제약조건을 만들려면 제한할 텍스트 뷰를 마우스 오른쪽 버튼으로 클릭한 다음 Show Baseline을 클릭합니다. 그런다음 텍스트 기준선을 클릭하고 선을 다른 기준선으로 드래그 합니다. 

  

안내선으로 제한 

 

뷰를 제한할 수 있는 세로 또는 가로 안내선을 추가할 수 있습니다. 안내선은 앱 사용자에게 표시되지 않습니다. 레이아웃의 가장자리를 기준으로 dp 단위 또는 백분율을 기반으로 레이아웃에 안내선을 배치할 수 있습니다. 

  

안내선을 만들려면 툴바에서 Guidelines를 클릭한 다음 Add Vertical Gduieline 또는 Add Horizontal Guideline을 클릭합니다.

 

점선을 드래그하여 위치를 변경하고 안내선 가장자리의 원을 클릭하여 측정 모드를 전환합니다.

 

경계선으로 제한 

  

안내선과 비슷하게 경계선은 뷰를 제한하는 데 사용할 수 있는 표시되지 않는 선입니다. 단, 경계선은 자체 위치를 정의하지 않으며, 경계선 내로 제한된 뷰 위치를 기반으로 경계선 위치가 이동됩니다. 뷰를 특정한 하나의 뷰가 아니라 뷰 집합으로 제한하려고 할 때 유용합니다.

 

예를 들어 그림에서는 C가 경계선의 오른쪽으로 제한된다는 것을 보여줍니다. 경계는 A와 B의 끝으로 설정됩니다. 따라서 A 또는 B의 오른쪽 중 맨 오른쪽에 있는 것에 따라 경계선이 이동합니다. 

 

경계선을 만들려면 다음 단계를 따르세요.

 1. 툴바에서 Guidelines를 클릭한 후 Add VerticalBarrier 또는 Add Horizontal Barrier를 클릭합니다.

 

 2. Component Tree 창에서 경계선 내의 원하는 뷰를 선택하여 경계선 구성요소로 드래그합니다.

 

 3. Component Tree에서 경계선을 선택하고 Attributes 창을 연 후 barrierDirection을 설정합니다.

 

이제 다른 뷰에서 경계선으로의 제약조건을 만들 수 있습니다. 

 

제약조건 편향 조정 

뷰의 양쪽에 제약 조건을 추가하면 두 제약 조건 사이의 중심에 뷰가 위치하며 기본적으로 편향은 50%입니다. 

 

뷰 크기 조정 

그림 14. 보기를 선택할 때 Attributes 창에는 1 크기 비율, 2 제약조건 삭제, 3 높이/너비 모드, 4 여백 및 5 제약조건 편향을 제어하는 기능이 포함되어 있습니다. 6 제약조건 목록에서 제약조건을 클릭하여 Layout Editor에서 개별 제약조건을 강조 표시할 수도 있습니다.

 

 그림 14에서 번호 3으로 표시된 기호를 클릭하여 높이와 너비를 계산하는 방법을 변경할 수 있습니다.

 

  • 고정 : 아래 텍스트 상자에서 특정 측정기준을 지정하거나 편집기에서 뷰의 크기를 조정합니다.

 

 

  • 콘텐츠 래핑 : 뷰가 콘텐츠에 맞게 필요한 만큼만 확장됩니다.

 

 

  • 제약조건과 일치 : 뷰가 양쪽의 제약조건에 맞게 최대한 많이 확장됩니다(뷰의 여백을 처리한 후).
    그러나 다음 속성과 값을 사용하여 동작을 수정할 수 있습니다.

  • layout_constraintWidth_default

    • 넓히기 : 양쪽의 제약조건에 맞게 뷰를 최대한 확장합니다. 이것이 기본 동작입니다.

    • 래핑 : 콘텐츠에 맞게 필요한 만큼만 뷰를 확장하지만, 제약조건에 필요하면 여전히 뷰를 그보다 작게 만들 수 있습니다. 따라서 이 모드를 사용하는 것과 콘텐츠 래핑을 사용하는 것의 차이점은 너비를 콘텐츠 래핑으로 설정하면 너비가 항상 콘텐츠 너비와 정확히 일치하는 반면, layout_constraintWidth_default를 래핑으로 설정한 상태에서 제약조건과 일치를 사용하면 뷰가 콘텐츠 너비보다 작아질 수 있습니다.

  • layout_contraintWidth_min

    뷰의 최소 너비에 dp 측정기준을 사용합니다.

  • layout_constraintWidth_max

    뷰의 최대 너비에 dp 측정기준을 사용합니다.

키 프레임 애니메이션

 ConstraintLayout에서 ConstraintSet 및 TransitionManager를 사용하여 요소의 크기와 위치 변경사항을 애니메이션으로 보여줄 수 있습니다.

★ 참고 : TransitionManager는 Android 4.0 (API 레벨 14) 이상용 지원 라이브러리에서 사용할 수 있습니다.

ConstraintSet 는 ConstraintLayout 에 있는 모든 하위 요소의 제약조건, 여백 및 패딩을 나타내는 경량 개체입니다.

ConstraintSet 를 보고있는 ConstraintLayout에 적용하면 레이아웃에서 모든 하위 요소의 제약조건을 업데이트합니다.

 

ConstraintSet 를 사용하여 애니메이션을 빌드하려면 애니메이션의 시작 및 종료 키프레임 역할을 할 두 개의 레이아웃 파일을 지정합니다. 그런다음 두 번째 키프레임 파일에서 ConstraintSet 를 로드하고 보고있는 ConstraintLayout에 적용할 수 있습니다.

 ★ 중요 : ConstraintSet 애니메이션은 하위 요소의 크기와 위치만 애니메이션으로 보여줍니다.
색상과 같은 다른 속성은 애니메이션으로 보여주지 않습니다

간단한 코드 예에서는 단일 버튼을 맨 아래로 이동하는 애니메이션을 만드는 방법을 보여줍니다.

 

layout/keyframe_one.xml

<?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"
    android:id="@+id/constraint_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:backgroundTint="@android:color/holo_blue_bright"
        android:onClick="animateToKeyframeTwo"
        android:text="Button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

layout/keyframe_tow.xml

<?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"
    android:id="@+id/constraint_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button2"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:backgroundTint="@android:color/holo_blue_bright"
        android:text="Button"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

 MainActivity.kt

class MainActivity : AppCompatActivity() {

    lateinit var constraintLayout: ConstraintLayout

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.keyframe_one)
        constraintLayout = findViewById(R.id.constraint_layout)
    }

    fun animateToKeyframeTwo(view:View) {
        val constraintSet = ConstraintSet()
        constraintSet.load(this, R.layout.keyframe_two)
        TransitionManager.beginDelayedTransition(constraintLayout)
        constraintSet.applyTo(constraintLayout)
    }
}

실행화면

Reference

ConstraintLayout : developer.android.com/training/constraint-layout?hl=ko

 

'Android > Android Developer' 카테고리의 다른 글

MotionLayout  (0) 2020.12.02
Layout 레이아웃  (0) 2020.12.01

네트워크 상태체크하기

enum class NETWORK_STATUS {
    TYPE_WIFI,
    TYPE_MOBILE,
    TYPE_NOT_CONNECTED
}

class NetworkStatus {
    companion object {

        fun getConnectivityStatus(context: Context): NETWORK_STATUS {
            val manager: ConnectivityManager? =
                context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager

            if (manager != null) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    val networkCapabilities = manager.getNetworkCapabilities(manager.activeNetwork)

                    if (networkCapabilities != null) {
                        if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
                            return NETWORK_STATUS.TYPE_WIFI
                        } else if (networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
                            return NETWORK_STATUS.TYPE_MOBILE
                        }
                    }
                } else {
                    val networkInfo = manager.activeNetworkInfo

                    if (networkInfo != null) {
                        val type = networkInfo.type

                        if(type == ConnectivityManager.TYPE_WIFI) {
                            return NETWORK_STATUS.TYPE_WIFI
                        } else if (type == ConnectivityManager.TYPE_MOBILE) {
                            return NETWORK_STATUS.TYPE_MOBILE
                        }
                    }
                }
            }

            return NETWORK_STATUS.TYPE_NOT_CONNECTED
        }
    }
}

 

안드로이드 프레임워크에서 제공되는 위젯들(TextView, EditText, Button 등)이나 레이아웃으로는 원하는 뷰를 만들기가 어려울 때가 있습니다. 그렇다고 포기하는 게 아니라 직접 뷰를 만들어 구현하는 건 어떨까요? 이번 글에서는 CustomView를 만드는 방법에 대해 알아보겠습니다.

 

CustomView를 만드는 기본적인 원리

  • 기존에 존재하는 View클래스를 상속합니다.
  • 시작하는 키워드가 'on'인 super 클래스 메서드를 오버라이드 합니다.
  • 새로 만든 커스텀 뷰를 사용합니다.

onDraw()

  • 개발자가 원하는 대로 구현할 수 있는 Canvas를 제공합니다. 
    • Canvas는 도화지라고 생각하면 이해가 조금 쉽습니다.
  • Canvas를 이용해 그리고 싶은 내용을 화면에 그리면 됩니다.

onMeasure()

  • 뷰와 뷰에 포함된 콘텐츠를 측정하여 측정된 Width와 Height을 결정합니다.
  • onMeasure()는 measure(int, int)에 의해 호출됩니다. 
    • measure() 메서드에서는 뷰의 사이즈를 측정하고 실제 측정된 사이즈가 수행되는 곳은 onMeasure()입니다.
  • onMeasure()를 오버라이드 하는 경우 setMeasuredDimension(int, int)를 호출해서 측정된 사이즈를 저장할 수 있도록 해야 합니다.

프레임워크가 호출하는 다른 View 메서드

생성

  • 생성자
    • 뷰가 런타임에 코드로부터 생성되는 경우
    • xml파일을 파싱하는 경우
  • onFinishInflate()
    • 뷰, 뷰그룹의 경우 자식 뷰들이 인플레이션이 다 끝나면 호출됩니다.

레이아웃

  • onMeasure(int, int)
    • 뷰, 자식뷰의 사이즈가 결정될 때 호출됩니다.
  • onLayout(boolean, int, int, int, int)
    • 뷰, 자식뷰의 위치가 결정될 때 호출됩니다.
  • onSizeChanged(int, int, int, int)
    • 뷰의 사이즈가 변경되었을 때 호출됩니다.

그리기

  • onDraw(Canvas)
    • 뷰가 가지고 있는 컨텐츠를 그릴 때 호출됩니다.

이벤트 처리

  • onKeyDown(int, KeyEvent)
    • 키 이벤트가 눌렸을 때 호출됩니다.
  • onKeyUp(int, KeyEvent)
    • 키가 눌린 게 떨어졌을 때 호출됩니다.
  • onTrackballEvent(MotionEvent)
    • 트랙볼 이벤트 발생 시 호출됩니다.
  • onTouchEvent(MotionEvent)
    • 터치 이벤트 발생 시 호출됩니다

포커스

  • onFocusChanged(boolean)
    • 포커스를 잃을 때 호출됩니다.
  • onWindowFocusChanged(boolean)
    • 윈도우가 포함하고 있는 뷰가 포커스를 얻거나 잃을 때 호출됩니다.

Attaching

  • onAttachedToWindow()
    • 윈도우에 뷰가 붙을 때 호출됩니다.
  • onDetachedFromWindow()
    • 윈도우로부터 뷰가 떨어질 때 호출됩니다.
  • onWindowVisibilityChanged(int)
    • 윈도우가 가지고 있는 뷰의 가시성이 변경될 때 호출됩니다.

Custom View에 대해서 조금 알아봤는데요. 그럼 직접 Cutstom View를 만들어 보겠습니다.

 

이번에 만들 Custom View는 LinedEditText입니다. LiendEditText는 밑줄이 있는 EditText입니다.

 

LinedEditText

 

위의 사진처럼 EditText에 각 줄마다 밑줄이 생기는 EditText입니다.

 

LinedEditText는 EditText를 상속하는 클래스를 만들면 되는 간단한 Custom View입니다.

 

추가적으로 작업한 것은 EditText의 크기에 비해 Cursor의 크기가 너무 커서 Cursor의 크기를 줄이기 위한 Drawable을 만들었습니다. EditText의 android:textCursorDrawable에 만든 Drawable을 적용해 Cursor를 수정할 수 있습니다.

 

EditText의 Default로 보여지는 밑줄은 android:background의 속성을 수정하여 없앨 수 있습니다.

 

Code

Reference

EditText에 입력을 할 때 핸드폰의 키패드가 올라오게 됩니다. 이 때, 뷰의 하단에 있는 UI가 키패드가 올라옴에 따라 어떻게 보이길 원하나요? 보이는게 좋은가요? 아니면 가려지길 바라나요? 이번 글에선 이부분을 다뤄보겠습니다.

 

먼저 아래와 같은 뷰가 있습니다.

 

 

 

이 때 EditText에 입력을 하는데 RecyclerView가 키패드와 같이 따라 올라와 뷰가 이상해집니다.

이 문제는 어렵지 않게 해결할 수 있습니다. 

 

AndroidManifest.xml 파일에서는 키패드가 나타날 때 UI를 어떻게 처리할지 정할 수 있습니다.

 

AndroidManifest.xml 파일로 이동하여 해당 액티비티에 아래와 같은 속성을 적용해줍니다.

android:windowSoftInputMode="adjustPan"

이 속성은 키패드가 올라올 때 UI를 덮도록 설정해 줍니다.

android:windowSoftInputMode="adjustNothing"

이 속성도 UI와 관계없이 키패드가 올라오도록 해줍니다.

 

 

만약 UI가 잘리지 않고 그대로 보여지고 싶다면 아래와 같이 속성을 적용해주면 됩니다.

android:windowSoftInputMode="adjustResize"

 

'Android' 카테고리의 다른 글

@JvmOverloads  (0) 2020.12.15
@JvmOverloads  (0) 2020.12.15
[Android] 테스트  (0) 2020.04.11
[Android] Gradle  (0) 2020.04.10
[Android] ContentProvider, Service, BroadcastReceiver  (0) 2020.04.03

테스트는 왜 필요할까요? 다음의 몇가지를 들 수 있습니다.

  • 필요한 조건을 만족하는지 확인
  • 오류 발견
  • 앱의 사용성을 확인

자동테스트 & 수동테스트

수동테스트는 코딩 작업이 끝난 상태에서 apk를 만들고, 체크리스트로 작성한 테스트 케이스를 차례대로 실행하는 형태로 이뤄지는 경우가 많습니다.

 

수동 테스트의 특성

  • 최종 사용자와 완전히 똑같이 동작을 확인할 수 있다.
  • 사용성이나 쾌적함 등 정성적인 면에서 평가할 수 있다.

자동테스트는 '단위 테스트' , 'UI 테스트' 로 불리는 테스트 코드를 만들어 테스트 코드를 실행함으로써 앱이 바르게 동작하는지 확인합니다.

 

자동테스트의 특성

  • 반복 실행이 가능하다.
  • 테스트 실행 상황을 시각화할 수 있다.
  • 클래스와 메서드 등 사용자 조작으로 확인하기 어려운 단위의 동작을 확인할 수 있다.

이런 특성들이 있어 자동 테스트로 동작에 대체로 이상이 없다는 것을 확인한 다음, 수동 테스트를 통해 공개 전 최종 확인 및 사용자 경험을 최적화하는 식으로 구분해서 생각합니다. 

 

단위 테스트

클래스나 메서드처럼 아주 작은 단위를 검증하기 위해 실행되는 테스트입니다. 소스코드 자체의 타당성이나 품질을 확인할 수 있으며, 가장 작은 단위로 검증이나 실행이 이뤄지므로 실행 시간도 아주 짧습니다. 그러므로 일반적으로 통신이나 IO 처리와 같은 외부 리소스와의 통신을 무효로 하고, 단말이나 환경에 의존하지 않는 상태로 테스트할 필요가 있습니다.

UI 테스트

실제 동작 환경을 중요시하므로 일반적으로는 실제 기기나 에물레이터 상에서 실행됩니다. 

이름 그대로 단말의 표시를 바탕으로 확인해 갑니다. 또한 통신이나 IO 처리 등 외부 리소스를 이용해 테스트하는 경우도 있습니다.

 

JUnit

자바에서 사실상의 표준이 되는 단위 테스트 프레임워크이며, 버전 3과 버전 4의 큰 차이 중 하나는 테스트 코드에 어노테이션이 이용되는 점을 들 수 있습니다.

 

 

Mockito를 이용한 객체의 목화

단위 테스트에서는 IO 처리 등과 같은 외부의 영향을 받지 않게 테스트를 작성해야 합니다.

 

예를 들어, HTTP 통신을 하는 경우를 생각해 봅시다.
테스트할 때 HTTP 통신을 실제로 하는 테스트를 작성해 버리면 단말 설정이나 서버 사오항에 따라 테스트 결과가 달라집니다.  그런 상황에서는 테스트에 실패해도 코드에 문제가 있는지, 그렇지 않으면 설정 등 주변 환경에 문제가 있는지 판단할 수 없기에 신뢰할 수 있는 테스트라고 할 수 없습니다.

 

이런 사태를 피하고자 테스트할 때는 의존하는 처리를 위장할 필요가 생깁니다.

HTTP 통신의 예에서 위장이란 어떤 테스트에서는 성공 응답을 반드시 반환하고, 반대로 다른 테스트에서는 반드시 실패 응답을 반환하는게 되겠지요. 추상적으로 말하면 위장이란 '실제 처리는 하지 않고 원하는 값을 반환하거나 처리를 실행시키는 것' 이라고 할 수 있습니다. 위장한 객체를 잘 사용하면 언제 어떤 때라도 동작이 항상 일정한 테스트를 작성할 수 있게 됩니다.

 

이런 위장을 간편하게 구현할 때 사용하는 것이 '목 객체' 입니다. 

(목 라이브러리를 사용하지 않아도 테스트할 때 클래스를 상속하는 방식 등으로 위장을 실현할 수 있지만 구현할 게 너무 많아지므로 목 라이브러리를 사용하는 경우가 많습니다.)

'Android' 카테고리의 다른 글

@JvmOverloads  (0) 2020.12.15
[Android] 키패드에 따른 화면변경 설정하기  (0) 2020.04.20
[Android] Gradle  (0) 2020.04.10
[Android] ContentProvider, Service, BroadcastReceiver  (0) 2020.04.03
[Android] Activity, View, Layout  (0) 2020.04.03

Gradle

Gradle은 안드로이드뿐 아니라 다양한 빌드에 사용할 수 있는 빌드 시스템입니다.

Gradle로 안드로이드 애플리케이션 파일(apk 파일) 빌드는 물론이고, 빌드 스크립트로 라이브러리 등의 의존관계나 설정을 관리할 수 있다. 또한 소스코드를 체크하는 등 다양한 일을 할 수 있다. 그뿐만 아니라 Gradle의 빌드 스크립트는 그루비라는 스크립트 언어로 빌드를 커스터마이징할 수 있다.

 

Gradle의 장점

다른 환경에서 똑같이 실행할 수 있다.

GradleWrapper라는 매커니즘이 있어 사용시 설정도니 버전으로 자동으로 셋업할 수 있다.

그러므로 프로젝트 팀 간이나 서버에서 같은 버전의 Gradle로 빌드를 실행할 수 있다.

의존관계를 기술할 수 있다.

Gradle의 빌드 스크립트 안에서는 한 줄만 써도 라이브러리 등을 추가할 수 있다.


Android Gradle Plugin의 특징 : 앱에 특화된 설정

앱의 버전을 기술하거나 빌드에 이용할 툴의 버전을 지정할 수 있다.

그 밖에도 빌드 변형(Biuld Variant) 기능으로 제품 특성에 따른 앱을 만들 수 있다.

예를 들어, 무료 버전과 유료 버전으로 앱을 만들거나 디버그 버전과 릴리스 버전에 따라 서로 다르게 동작하게 할 때 이용할 수 있다. 매번 코드를 변경할 필요가 없으므로 효율적으로 개발할 수 있다.

 

안드로이드의 Build 파일

프로젝트의 build.gradle (/build.gradle)

Gradle 파일에서 중괄호로 에워싸인 부분을 가리켜 '블록'이라고 부른다.

우선 buildscript 블록을 보면 빌드 시 Android gradle 플러그인을 이용하기 위한 설정이 들어간다.

  • repository 블록에서 Android Gradle Plugin은 jcenter의 리포지토리에 있으므로 jcenter를 리포지토리로서 지정한다
  • dependencies 블록에서 실제로 Android Gradle Plugin을 의존관계로서 기술한다. 
  • 또한 모든 프로젝트에서 jcenter의 리포지토리를 볼 수 있게 하고자 allprojects 블록에서 jcenter를 리포지토리로서 추가한다.
의존성(dependency)
코드에서 두 모듈 간의 연결.
객체 지향 언어에서는 두 클래스 간의 관계라고도 말함.
일반적으로 둘 중 하나가 다른 하나를 어떤 용도를 위해 사용함.

모듈의 build.gradle (/app/build.gradle)

빌드에 필요한 앱의 설정 등을 기술하는 빌드 스크립트이다.

프로젝트의 build.gradle에서는 Android Gradle Plugin은 의존관계에는 들어 있었지만 실제로 적용은 하지 않았는데

apply plugin: 'com.android.application'

위와 같이 기술함으로써 Gradle Plugin 으로서 Android Gradle Plugin을 이용할 수 있다.

 

android 블록은 빌드 시 이용할 설정이 기술 돼 있다. 

defaultConfig 블록에는 앱의 설정에 관한 사항이 기술돼 있다.

dependencies 블록은 의존하는 라이브러리를 기술하고 있다.

빌드에 필요한 앱의 설정 등을 기술하는 빌드 스크립트. 이 파일은 수정할 일이 아주 많다.

// 1번 Android Gradle 플러그인 적용
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: "androidx.navigation.safeargs.kotlin"

// 2번 Android Gradle 플러그인에서 이용되는 Android 블록
android {
    compileSdkVersion 28
    buildToolsVersion "29.0.1"
    
    // 3번 앱 자체의 설정
    defaultConfig {
        applicationId "com.avon.lv"
        minSdkVersion 20
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables.useSupportLibrary = true
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    dataBinding {
        enabled = true
    }
}

dependencies {
	// 4번 libs 디렉터리에 있는 jar 파일을 읽어온다.
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    // 5번 의존관계
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

1번처럼 기술함으로써 Gradle 플러그인으로서 Android Gradle 플러그인을 이용할 수 있다.

2번은 Android 블록은 빌드시 이용할 설정이 기술돼 있다.

3번 defaultConfig블록에는 앱의 설정에 관한 사항이 기술돼 있다.

4번과 5번은 의존하는 라이브러리를 기술하고 있다.

4번의 fileTree에서 인수로 libs와 *.jar을 지정함으로써 libs 디렉터리에 있는 jar 확장자 파일을 가져와 의존관계로서 기술한다.

5번은 이용할 라이브러리를 Maven 리포지토리에서 가져와 이용한다. 그 밖에도 라이브러리를 늘리고 싶을 때는 이곳에 추가해서 이용할 수 있다.

프로젝트의 settings.gradle (/settings.gradle)

gradle에는 일반적으로 settings.gradle 파일이 있어야 한다.

include ':app', ':lib'

위와 같이 어떤 모듈이 있는지 기술한다.

 

위의 3개의 파일이 가장 자주 이용됩니다.

나머지도 알아두면 편리하므로 계속해서 살펴보겠습니다.

Gradle Wrapper 설정 (/gradle/wrapper/gradle-wrapper.properties)

Gradle에는 Gradle Wrapper라는 매커니즘이 있다. Gradle Wrapper는 앱을 빌드할 때 필요한 gradle이 설치돼 있지 않아도 Gradle Binary를 다운로드해서 설정합니다. 이로써 서버 상에 gradle이 설치돼 있지 ㅇ낳아도 실행할 수 있고, 팀으로 개발할 때 모두 같은 환경에서 빌드할 수 있습니다.

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip

distributionUrl 에서 다운로드할 gradle의 URL설정 등을 할 수 있지만 기본값으로 설정돼 있으니 기본적으로 편집할 필요는 없습니다.

 

proguard 설정 파일 (/app/proguard-rules.pro)

프로가드는 빌드 시 코드를 난독화하거나 최적화하는 도구입니다. 또한 빌드 시 사용하지 않는 메서드를 지워줍니다.

코드를 난독화하면 디컴파일해 apk로부터 소스코드를 쉽게 읽어내는 행위를 막을 수 있습니다.

안드로이드에서는 한 앱에서 메서드 수가 65,536개를 넘으면 Multidex라는 특수한 기능을 이용해야 하지만 그것도 프로가드를 이용하면 대개는 관련 오류를 피할 수 있습니다. 기본값은 주석뿐이고 아무것도 쓰여 있지 않지만 이곳에 프로가드로 난독화하지 않을 클래스 등을 지정할 수 있습니다.

 

그 밖에도 라이브러리를 설치할 때 '프로가드 설정에 추가하세요' 와 같은 내용이 있을 경우에 이 파일에 설정할 필요가 있습니다. 또한 디버그할 때는 잘 작동했는데릴리스 빌드일 때 빌드할 수 없거나 메서드를 찾을 수 없는 등의 충돌이 있을 때는 프로가드 설정을 의심해보세요.

 

Gradle 표준 프로퍼티 파일 (/gradle.properties)

gradle.properties에 기술하면 build.gradle에서 참조할 수 있게 됩니다.

 

gradle 표준 프로퍼티 파일(/gradle.properties)

testMessage=this is test!

build.gradle 파일 (/app/build.gradle)

println testMessage

위 와 같이 기술하고 앱을 빌드해보겠습니다.

 

앱을 빌드할 때 아래와 같이 표시되는 것을 확인할 수 있습니다.

 

SDK 위치를 지정하는 프로퍼티 파일 (/local.properties)

이 파일에 SDK 위치가 적혀있지만 이 설정보다도 안드로이드 스튜디오의 설정을 우선합니다.

그래서 SDK 디렉터리를 변경하고 싶을 때는 안드로이드 스튜디오의 설정에서

[Appearance & Behavior] -> [System Settings] -> [Android SDK]를 열어 변경합니다.

이 파일은 개발자마다 다를 수 있어 Git 등의 VCS(Version Control System)에는 포함하지 않도록 합시다.

 

 

+ Recent posts