4장 화면전환

모래사우르스
|2024. 12. 1. 13:32

실습

- 부분 화면 구현 -

- 팝업 화면 구현 -

 

<객체화>

인플레이션(Inflation) : XML 레이아웃의 내용이 메모리에 객체화되는 과정

XML 레이아웃은 앱이 실행되는 시점에 메모리에 객체화되기 때문에, setContentView 메서드가 호출되기 전에 XML 레이아웃에 정의된 객체를 참조하면 오류(NulllPointerException)가 발생한다.

 

<소스코드에서 XML 레이아웃을 전달하는 코드>

setContentView(R.layout.파일명)

R은 res 폴더를 의미한다. res/layout 폴더 안에 있는 파일을 가져오겠다는 뜻.

 

setContentView는 화면에 나타낼 뷰를 지정하거나, 레이아웃 내용을 메모리에 객체화하는 두가지 역할을 수행한다.

 

 

<부분 화면을 메모리에 객체화하기>

setContentView는 화면 전체만 설정할 수 있어서 인플레이터를 사용해야한다.

getSystemService 메서드를 이용하여 LayoutInflater 객체를 참조하면 인플레이터를 사용할 수 있다.

(과정 : LayoutInflater 객체를 사용해 부분화면을 뷰그룹 객체로 객체화(인플레이션)한 다음 메인 레이아웃에 추가)

 

1. 새 프로젝트를 만든다. 이번 프로젝트에선 activity_menu.xml, MenuActivity.java라는 이름을 사용함.

 

2. activity_menu.xml은 아래와 같이 만들어준다.

TextView, Button 그리고 LinearLayout(vertical)을 추가한 다음 id를 container로 바꿔준다.

 

3. sub1.xml 파일 생성

res/layout 폴더에 새 레이아웃 파일을 생성한다. 

Root element는 LinearLayout으로 설정해준다.

아래와 같이 화면구성을 만들어준다.

 

4. MenuActivity.java 파일을 수정한다.

<MenuActivity.java>

package com.example.chapter4;

public class MenuActivity extends AppCompatActivity {
    LinearLayout container;

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

        container = findViewById(R.id.container);

        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                LayoutInflater inflater = (LayoutInflater)
                        getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                inflater.inflate(R.layout.sub1, container, true);
                CheckBox checkBox = container.findViewById(R.id.checkBox);
                checkBox.setText("로딩되었어요.");
            }
        });
    }
}

(바보같이 코드 중간에 빼먹어서 계속 오류 뜸..)

 

(1) 먼저 activity_menu.xml에 들어갈 부분 공간인 container를 정의함.

    LinearLayout container;

 

(2) onCreate 안에 container랑 button을 기본화면인 activity_menu.xml에서 참조하게 함.

container = findViewById(R.id.container);
Button button = findViewById(R.id.button);

 

(3) onClick 안에서 인플레이터. inflaterinflate 메서드를 사용하여 containersub1.xml 파일 레이아웃으로 설정해준다. 1번째 파라미터로 XML 레이아웃 리소스를, 2번째 파라미터로 부모 컨테이너를 지정한다.

LayoutInflater inflater = (LayoutInflater)
                        getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.sub1, container, true);

 

(4) 체크박스를 누를 시 "로딩되었어요."가 보이도록 함. 여기서 그냥 findViewById를 사용하면 기본화면인 activity_menu.xml에서 찾기 때문에, container에서 찾을 수 있도록 앞에 container를 붙여준다.

CheckBox checkBox = container.findViewById(R.id.checkBox);
checkBox.setText("로딩되었어요.");

 

5. app/manifests 폴더에 들어있는 AndroidManifest.xml 수정

name이 기본적으로 MainActivity로 설정되어있다. MenuActivity로 바꾸어준다.

6. 실행화면

 

버튼을 누르면 부분화면이 생성된다.

 

 

<액티비티>

안드로이드 앱의 4가지 구성 요소 : 액티비티(Activity) / 서비스(Service) / 브로드캐스트 수신자(Broadcast Receiver) / 내용 제공자(Content Provider)

AndroidManifest.xml 파일에서 위 요소에 대한 정보를 가지고 있다.

 

startActivity : 액티비티를 띄우는 메서드

startActivityForResult : 띄웠던 액티비티로부터 다시 원래의 액티비티로 돌아오면서 응답을 받아 처리할 수 있는 메서드

 

새 액티비티를 띄우기만 할 것이라면 startActivity로 구현해도 되지만, 응답을 받아 처리하려면 startActivityForResult를 사용해야한다.

 

<새로운 액티비티 추가하기 (팝업창)>

1. 새 프로젝트를 만든 상태에서 app폴더 우클릭 > New > Activity > Empty Views Activity를 추가한다.

 

2. Activity Name을 MenuActivity로 설정해주었다.

(@@Activity를 만들면 자동으로 activity_@@ 레이아웃이 만들어진다.)

 

3. AndroidManifest.xml에 들어간다.

MenuActivity가 추가된 것을 볼 수 있다.

MenuActivity의 label과 theme 속성을 변경해준다.

android:label : 화면의 제목

android:theme : 테마 설정

@style/Theme.AppCompat.Dialog : 액티비티가 대화상자 형태로 나타남

 

4. activity_main.xml과 activity_menu.xml을 열어서 화면을 구성한다.

둘 다 버튼의 id는 button으로 설정한다.

activity_main(왼쪽), activity_menu(오른쪽)

 

5. java 코드 작성

<MenuActivity.java>

package com.example.chapter4_2;

public class MenuActivity extends AppCompatActivity {

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

        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                intent.putExtra("name","amy");
                setResult(RESULT_OK, intent); // 응답 보내기
                finish(); // 현재 액티비티 없애기
            }
        });
    }
}

 

<onClick 안의 코드 보기>

(1) 인텐트 객체를 생성하고, name의 값을 부가 데이터로 넣는 코드.

Intent intent = new Intent();
intent.putExtra("name","amy");

putExtra 메서드를 사용하면 인텐트 객체에 데이터를 넣을 수 있다. 키(Key)와 데이터 값(Value)를 쌍으로 넣어야 한다.

(근데 이 예제에서 이 값을 확인하는 부분은 없다.. 굳이 있을 필요없는 코드가 아닌가 싶지만 일단 넘어가자)

 

(2) RESULT_OK라는 응답을 보내고, 현재 액티비티를 없애는 코드.

setResult(RESULT_OK, intent); // 응답 보내기
finish(); // 현재 액티비티 없애기

setResult(응답코드, 인텐트) : 새로 띄운 액티비티에서 이전 액티비티로 인텐트를 전달하고 싶을 때 사용하는 메서드

finish() : 액티비티를 화면에서 없애고 싶을 때 사용

 

<MainActivity.java>

package com.example.chapter4_2;

public class MainActivity extends AppCompatActivity {
    public static final int REQUEST_CODE_MENU = 101;

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

        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(getApplicationContext(), MenuActivity.class);
                startActivityForResult(intent, REQUEST_CODE_MENU);
            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if(requestCode==REQUEST_CODE_MENU){
            Toast.makeText(getApplicationContext(), "onActivityResult 메소드 호출됨, 요청코드 : "+
                    requestCode+", 결과 코드"+resultCode, Toast.LENGTH_LONG).show();
        }
        if(requestCode==RESULT_OK){
            String name = data.getStringExtra("name");
            Toast.makeText(getApplicationContext(),"응답으로 전달된 name : "+ name, Toast.LENGTH_LONG).show();
        }
    }
}

 

(1) 새 액티비티를 띄울 때 보낼 요청 코드

public static final int REQUEST_CODE_MENU = 101;

여기선 101로 지정했지만 아무 값이나 지정해도 상관없다. 단 액티비티가 여러개라면 요청 코드는 중복되어선 안된다.

이 값을 통해 어떤 액티비티로부터 온 응답인지 구분할 수 있음.

 

(2) onClick안의 코드

Intent intent = new Intent(getApplicationContext(), MenuActivity.class);
startActivityForResult(intent, REQUEST_CODE_MENU);

인텐트 객체를 생성함. 인텐트 객체액티비티를 띄울 목적으로 사용되며, 액티비티 간의 데이터를 전달하는 데에도 사용될 수 있다.

Intent() 함수의 첫 파라미터엔 getApplicationContext()를 넣음으로써, 이 앱의 Context 객체를 참조한 후 전달한다.

 

(3) onActivityResult 재정의

@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if(requestCode==REQUEST_CODE_MENU){
        Toast.makeText(getApplicationContext(), "onActivityResult 메소드 호출됨, 요청코드 : "+
                requestCode+", 결과 코드"+resultCode, Toast.LENGTH_LONG).show();
    }
    if(requestCode==RESULT_OK){
        String name = data.getStringExtra("name");
        Toast.makeText(getApplicationContext(),"응답으로 전달된 name : "+ name, Toast.LENGTH_LONG).show();
    }
}

OnActivityResult 메서드는 새로 띄웠던 메뉴 액티비티가 응답을 보내오면 그 응답을 처리하는 역할을 한다.

requestCode(요청코드), resultCode(응답코드), intent(인텐트)를 파라미터로 받는다.

 

요청코드 : 어떤 액티비티로부터 응답을 받은 것인지 구분

응답코드 : 새 액티비티에서 처리한 결과가 정상인지 아닌지를 구분

인텐트 : 새 액티비티로부터 원래의 액티비티로 데이터를 전달할 때 사용

 

만약 요청코드(requestCode)로 REQUEST_CODE_MENU 값이 들어오면 요청코드와 응답코드를 출력하고,

RESULT_OK 값이 들어오면 MenuActivity에서 putExtra로 넣은 amy를 출력한다.

(보통 RESULT_OK 상수를 전달하는 방법으로 정상처리임을 알린다고 한다.)

 

putExtra로 인텐트 객체에 키(Key)값과 데이터 값(Value)를 넣고,

getStringExtra에 키(Key)값을 넣어서 데이터 값(Value)을 가져올 수 있다.

 

<실행화면>

 

'메뉴 화면 띄우기' 버튼을 클릭하면 메뉴 액티비티가 뜨고,

'돌아가기' 버튼을 클릭하면 초기 액티비티가 뜨고 토스트로 요청코드와 결과코드(응답코드)가 출력된다.

 

만약 '돌아가기'버튼을 클릭할 경우 결과코드=-1이 되고, 메튜 액티비티의 바깥 영역을 눌러서 초기화면으로 돌아갈 경우 결과코드=0이 된다.

 

의문점 : MenuActivity에서 RESULT_OK 값을 보내면서 MainActivity에서 해당 값을 받고, name 값인 amy를 토스트에 띄울 줄 알았는데 그렇지 않았다. name 값이 나타나지 않는 건 책이랑 동일한데, 왜 name 값이 보이지 않는지에 대한 설명이 없다..