[Do it! 안드로이드 앱 프로그래밍 #3] 레이아웃 익히기
Android

[Do it! 안드로이드 앱 프로그래밍 #3] 레이아웃 익히기

03 레이아웃 익히기

 

03-1 안드로이드에 포함된 대표적인 레이아웃 살펴보기

레이아웃 이름 설명
제약 레이아웃
(ConstraintLayout)
제약 조건(Constraint) 기반 모델
연결선을 제약 조건으로 하여 화면을 구성하는 방법
안드로이드 스튜디오에서 자동으로 설정하는 디폴트 레이아웃
리니어 레이아웃
(LinearLayout)
박스(Box) 모델
한 쪽 방향으로 차례대로 뷰를 추가하며 화면을 구성하는 방법
뷰가 차지할 수 있는 사각형 영역을 할당
상대 레이아웃
(RelativeLayout)
규칙(Rule) 기반 모델
부모 컨테이너나 다른 뷰와의 상대적 위치로 화면을 구성하는 방법
제약 레이아웃을 사용하게 되면서 상대 레이아웃은 권장하지 않음
프레임 레이아웃
(FrameLayout)
싱글(Single) 모델
가장 상위에 있는 하나의 뷰 또는 뷰그룹만 보여주는 방법
여러 개의 뷰를 중첩한 후 각 뷰를 전환하여 보여주는 방식으로 자주 사용함
테이블 레이아웃
(TableLayout)
격자(Grid) 모델
격자 모양 배열을 사용하여 화면을 구성하는 방법
HTML에서 많이 사용하는 정렬 방식과 유사하지만 많이 사용하지는 않음

레이아웃도 뷰이기 때문에 뷰의 필수 속성인 layout_width, layout_height 속성을 꼭 입력해야 한다. 또, 각각의 레이아웃마다 추가되는 필수 속성도 있다.

 

 

뷰 영역 알아보기

뷰 영역 - 마진(Margin)
뷰 영역 - 마진(Maigin)

버튼의 연결점과 대상과의 거리를 마진(Margin)이라고 하는 걸 저번 글에서 배웠다. 상하좌우로 뻗어있는 파란색 선이 마진이다. 그렇다면 패딩(Padding)은 버튼과 실제 내용물과의 거리를 의미한다. 내용물은 BUTTON이라는 텍스트이다. 즉, 뷰의 테두리를 기준으로 바깥쪽 공간은 마진, 안쪽 공간은 패딩이다.

 

03-2 리니어 레이아웃 사용하기

리니어 레이아웃의 방향 설정하기

한 방향으로만 뷰를 쌓는 리니어 레이아웃의 필수 속성은 방향이다. 방향을 설정할 때는 orientation 속성을 사용하며 가로 방향은 horizontal, 세로 방향은 vertical이라는 값으로 설정한다.

 

 

자바 코드에서 화면 구성하기

디자인 화면에서 레이아웃을 만들고 뷰를 추가하면 XML 파일이 만들어진다. 이렇게 만들어진 XML 파일은 소스 파일과 연결된다. 그러나 화면을 미리 만들 수 없는 경우 또는 필요할 때마다 바로바로 레이아웃을 만들어야 하는 경우에는 자바 소스 코드에서 화면 레이아웃을 구성해야 할 수도 있다.

 

▼ 화면 레이아웃 자바 소스 코드로 구성하기

public class LayoutCodeActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // new 연산자로 리니어 레이아웃을 만들고 방향 설정 - 세로로 뷰를 추가하는 레이아웃
        LinearLayout mainLayout = new LinearLayout(this);
        mainLayout.setOrientation(LinearLayout.VERTICAL);

        // new 연산자로 레이아웃 안에 추가될 뷰들에 설정할 파라미터 생성
        LinearLayout.LayoutParams params =
                new LinearLayout.LayoutParams(
                        LinearLayout.LayoutParams.MATCH_PARENT,
                        LinearLayout.LayoutParams.WRAP_CONTENT
                );

        // 버튼에 파라미터 설정하고 레이아웃에 추가
        Button button1 = new Button(this);
        button1.setText("Button1");
        button1.setLayoutParams(params);
        mainLayout.addView(button1);

        // 새로 만든 레이아웃을 화면에 설정
        setContentView(mainLayout);
    }
}

화면 레이아웃 자바 소스 코드로 구성하기
화면 레이아웃 자바 소스 코드로 구성하기

앱을 실행했을 때 처음 보이는 화면은 메인 액티비티라고 부른다. 메인 액티비티는 AndroidManifest.xml 파일 안에서 설정한다. activity android:name=".MainActivity"에서 ".LayoutCodeActivity"로 변경한다.

 

 

화면 생성 과정 분석하기

자바 소스 코드 파일을 보면 this라는 키워드가 있다. 이것은 Context 객체가 전달된 것을 의미한다. new 연산자를 사용해서 뷰 객체를 만들 때는 항상 Context 객체가 전달되어야 한다. 표준 자바에는 없고 안드로이드에만 있는 특징이다.

 

*Context(컨텍스트)란 일반적으로 어떤 일이 발생한 상황을 의미하는 말인데, 프로그래밍 언어에서는 객체의 정보를 담고 있는 객체를 의미하는 경우가 많다. 안드로이드는 UI 구성 요소인 뷰에 대한 정보를 손쉽게 확인하거나 설정할 수 있도록 뷰의 생성자에 Context 객체를 전달하도록 한다.

 

 

뷰 정렬하기

정렬 속성 설명
layout_gravity 여유 공간 안에서 뷰를 정렬함
(부모의 여유 공간에 뷰가 모두 채워지지 않아 여유 공간이 생겼을 때)
gravity 뷰 안에 표시하는 내용물을 정렬함
(텍스트뷰의 경우 내용물은 글자가 되고, 이미지뷰의 경우 내용물은 이미지가 됨)

baselineAligned 속성을 사용해 뷰가 아닌 텍스트 정렬을 우선으로 설정할 수도 있다.

 

 

여유 공간을 분할하는 layout_weight 속성

layout_weight 속성은 부모 레이아웃에 남아 있는 여유 공간을 분할하여 기존에 추가했던 뷰들에게 할당한다.

layout_weight 속성
layout_weight 속성

 

구분 layout_width layout_weight 결과
두 번째 줄 둘 다 wrap_content 각각 1과 2로 설정 wrap_content 속성 값에 의해 내용물만큼 할당한 다음 남은 여유 공간을 1/3, 2/3씩 할당
세 번째 줄 둘 다 0dp 각각 1과 2로 설정 0dp의 값에 의해 여유 공간은 가로 한 줄 전체가 되고, 이 공간을 1/3, 2/3씩 할당

두 번째 줄은 여유 공간을 1:2의 비율로 나눠 가진 것이고, 세 번째 줄은 가로 공간을 1:2의 비율로 나눠 가진 것이다. 세 번째 줄이 진정한 1:2의 비율로 공간을 나눠 가졌다고 할 수 있다.

 

03-3 상대 레이아웃 사용하기

상대 레이아웃으로 만들 수 있는 화면 레이아웃은 대부분 제약 레이아웃으로 만들 수 있다. 따라서 상대 레이아웃을 사용하는 것은 권장하지 않는다. 다만 예전에 만든 레이아웃이 상대 레이아웃을 사용한 경우가 있기 때문에 상대 레이아웃에 대해 이해하는 것은 필요하다.

상대 레이아웃은 부모 레이아웃이나 다른 뷰와의 상대적 위치를 이용해 뷰의 위치를 결정할 수 있도록 한다.

 

*책에는 버튼의 배경색 설정을 위해 background, backgroundTint, backgroundTintMode를 변경하도록 되어 있는데, 현재는 변경되어 backgroundTint 속성 값에 "#0088ff"와 같은 색상 코드를 바로 지정해 주면 된다.

 

03-4 테이블 레이아웃

테이블 레이아웃은 단어 뜻에서 유추할 수 있듯이 표나 엑셀 시트와 같은 형태로 화면을 구성하는 레이아웃이다.

 

LinearLayout으로 변경 > Palette > Layouts > TableLayout 추가 > TableRow 추가

TableRow 뷰는 한 번에 다섯 개가 추가된다. TableRow의 개수가 행의 개수가 되고, 각 TableRow마다 추가된 뷰의 개수가 열의 개수가 된다.

 

03-5 프레임 레이아웃과 뷰의 전환

프레임 레이아웃은 가장 기본적이고 단순한 레이아웃이다. 프레임 레이아웃은 뷰를 하나 이상 추가할 경우 추가된 순서로 쌓인다. 가장 먼저 추가한 뷰가 가장 아래쪽에 쌓이고 그 다음에 추가한 뷰는 그 위에 쌓여서 가장 나중에 쌓인 뷰만 보이게 된다. 이것이 프레임 레이아웃이 갖고 있는 중첩(Overlay) 기능이다. 뷰의 가시성(Visibility) 속성을 이용해 여러 개의 뷰를 전환하는 것처럼 보이게 할 수 있다.

 

▼ 프레임 레이아웃으로 뷰 전환 효과 만들기

public class MainActivity extends AppCompatActivity {
    ImageView imageView;
    ImageView imageView2;
    Button button;

    int imageIndex = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        imageView = findViewById(R.id.imageView);
        imageView2 = findViewById(R.id.imageView2);
        button = findViewById(R.id.button);
    }

    public void onButton1Clicked(View v){
        changeImage();
    }

    private void changeImage(){
        if(imageIndex == 0){
            button.setText("STOP!");
            imageView.setVisibility(View.INVISIBLE);
            imageView2.setVisibility(View.VISIBLE);

            imageIndex = 1;
        } else if(imageIndex == 1){
            button.setText("DANCE!");
            imageView.setVisibility(View.VISIBLE);
            imageView2.setVisibility(View.INVISIBLE);

            imageIndex = 0;
        }
    }
}
프레임 레이아웃으로 뷰 전환 효과 만들기

나는 책의 내용을 활용해서 춤추는 쿠로미와 마이멜로디를 만들어봤다. 버튼의 text도 바꿔야 했기 때문에 코드를 조금 수정했다.

 

-findViewById 메서드: id를 이용해 뷰를 찾는 메서드이다.

 

-XML 레이아웃 파일에서 id 값으로 "@+id/imageView"를 부여했다면 자바 소스 코드에서는 R.id.imageView라는 값으로 id를 참조할 수 있다. 여기서 R은 Resource를 의미한다.

XML 레이아웃 파일에서 id 지정할 때 @+id/아이디
자바 소스 코드에서 id 참조할 때 R.id.아이디

 

*이미지뷰에 이미지를 설정하려면 원하는 이미지 파일을 프로젝트 폴더 안의 app > res > drawable 폴더에 복사해야 한다. 파일 탐색기에서 추가할 때는 app > src > main > res > drawable의 경로로 들어간다. 이때 이미지의 이름에는 영문 소문자, 숫자 또는 _ 기호만 들어가야 하고, 이름은 숫자로 시작할 수 없다.

 

03-6 스크롤뷰 사용하기

스크롤뷰는 추가된 뷰의 영역이 한눈에 다 보이지 않을 때 사용한다.

 

Palette의 ScrollView는 수직 방향의 스크롤을 지원한다. 수평 방향의 스크롤을 사용하려면 HorizontalScrollView를 사용하면 된다. 수직과 수평 스크롤을 모두 사용하려고 한다면 ScrollView 안에 HorizontalScrollView를 추가하면 된다. 그 반대도 가능하다.

스크롤뷰 사용하기
스크롤뷰 사용하기

스크롤뷰를 추가하니 스크롤뷰의 하위에 리니어 레이아웃도 같이 추가가 된다. 책에 설명은 안 되어 있지만 스크롤뷰와 리니어 레이아웃을 하나의 세트로 생각하면 될 것 같다.

 

▼ 스크롤뷰 사용하기

public class MainActivity extends AppCompatActivity {
    ScrollView scrollView;
    ImageView imageView;
    BitmapDrawable bitmap;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 레이아웃에 정의된 뷰 객체 참조
        scrollView = findViewById(R.id.scrollView);
        imageView = findViewById(R.id.imageView);

        // 리소스의 이미지 참조
        Resources res = getResources();
        bitmap = (BitmapDrawable) res.getDrawable(R.drawable.kuromi2);	// 리소스 수정 후 사용
        int bitmapWidth = bitmap.getIntrinsicWidth();
        int bitmapHeight = bitmap.getIntrinsicHeight();

        // 이미지 리소스와 이미지 크기 설정
        imageView.setImageDrawable(bitmap);
        imageView.getLayoutParams().width = bitmapWidth;
        imageView.getLayoutParams().height = bitmapHeight;
    }

    public void onButton1Clicked(View v){
        changeImage();
    }

    private void changeImage(){
        Resources res = getResources();
        bitmap = (BitmapDrawable) res.getDrawable(R.drawable.kuromi5);	// 리소스 수정 후 사용
        int bitmapWidth = bitmap.getIntrinsicWidth();
        int bitmapHeight = bitmap.getIntrinsicHeight();

        imageView.setImageDrawable(bitmap);
        imageView.getLayoutParams().width = bitmapWidth;
        imageView.getLayoutParams().height = bitmapHeight;
    }
}
수직 수평 스크롤뷰 사용하기

BitmapDrawable 객체의 getIntrinsicWidth와 getIntrinsicHeight 메서드를 사용하면 원본 이미지의 가로와 세로 크기를 알 수 있으며, 이렇게 알아낸 가로와 세로 크기 값은 이미지뷰에 설정된 LayoutParams 객체의 width와 height 속성으로 설정할 수 있다.

 

*Intrinsic: 고유한, 본질적인

 

*책에는 뷰 객체 참조하는 부분의 마지막 줄에 scrollView.setHorizontalScrollBarEnabled(true);라는 코드가 있는데, 추가하지 않아도 정상적으로 작동하길래 지웠다.