본문 바로가기
컴퓨터 공학/Android

[Do it 개정6판_안드로이드 스튜디오]둘째마당_04 여러 화면 간 전환하기

by hahehohoo 2019. 11. 29.
반응형

04-1 레이아웃 인플레이션 이해하기

- 이제까지 하나의 화면에 대하여 화면을 어떻게 보여줄지 결정하는 XML 레이아웃을 정의했다. 

- 구조를 나타내는 XML과 화면의 기능을 담당하는 java 파일을 연결해줘야 한다. 

- 화면의 기능을 담당하는 소스 파일에는 AppCompatActivity를 상속하는 MainActivitiy 클래스가 자동으로 만들어진다. - 여기서 R은 프로젝트 창에 보이는 res 폴더를 의미한다. 

- 앱이 실행될 때 XML 레이아웃의 내용이 메모리에 객체화되고 객체화된 XML 레이아웃을 소스 파일엥서 사용

- 이렇게 XML 레이아웃의 내용이 메모리에 객체화되는 과정을 인플레이션이라고 한다. 

 

- 메모리에 객체화되지 않은 버튼 객체를 참조하려고 했기 때문에 심각한 오류로 앱 자체를 멈추게 만들었다. 

- 오류 코드 : Caused by: java.lang.NullPointerException: Attempt to invoke virtual method

-> 소스 코드에서 레이아웃을 객체화하기 전에 레이아웃의 버튼을 참조했기 때문에 발생하는 문제

 

- setContentView() 메서드가 매우 중요하다는 것을 알 수 있다. 

- 이 메서드는 화면에 표시할 XML 레이아웃을 지정하거나 화면에 표시할 뷰 객체를 지정하는 역할

- 즉 이 메서드로 전달할 수 있는 파라미터는 뷰 객체 또는 XML 레이아웃이 정의된 리소스가 될 수 있다. 

 

- 화면 전체에 보여줄 XML 레이아웃이 아니라 별도의 XML 레이아웃 파일로 만든 부분 레이아웃을 소스 파일에 ㅗ딩하여 보여줄 수 었을까? OK

- 부분 화면도 별도의 XML 레이아웃 파일에 정의한 후 불러와 보여줄 수 있다. -> 인플레이터 사용해야함

 

- 안드로이드는 이를 위햇 시스템 서비스로 LayoutInflater라는 클래스를 제공함

- LayoutInflater 클래스는 시스템 서비스로 제공하는 클래스이므로 다음 getSystemServie() 메서드를 이용하여 LayoutInflater 객체를 참조한 후 사용

 

- 앱이 실행되었을 때 MainActivity 화면이 아니라 프로젝트에 새로 추가한 00 화면이 나타나도록 매니페스트 파일을 수정해야 한다. 

- app/manifests  폴더에 안에 있는 AndroidManifest.xml 파일에서 <activity> 태그 수정

 

 

 

04-2 여러 화면 만들고 화면 간 전환하기

- 대부분 앱은 여러 화면으로 구성되어 있고 호면을 전환하며 실행

- 화면은 액티비티로 구현 즉, 화면을 필요에 따라 띄우거나 닫는 과정은 액티비티를 전환하는 것과 같다. 

- 앱을 만들어 단말에 설치했을 때 안드로이드 시스템이 이 요소에 대한 정보를 요구하지 않기 때문ㅇ

- 이 정보는 어디 들어있을까? 프로젝트 만들면 자동으로 만들어지는 Manifest.xml 파일에

 

- 액티비티를 소스 코드에서 띄울 때는 startActivity()메서드를 사용하면 된다 

 

▼ MainActivity.java코드

더보기

package com.ogrg.sampleintent;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
public static final int REQUEST_CODE_MENU = 101;
TextView textView = findViewById(R.id.textView2);


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

if(requestCode == REQUEST_CODE_MENU){
Toast.makeText(getApplicationContext(),
"onActivitiyResult 메서드 호출됨. 요청 코드:" + requestCode+
", 결과 모드 : "+requestCode, Toast.LENGTH_LONG).show();

if(resultCode == RESULT_OK){
String name = data.getStringExtra("name");
Toast.makeText(getApplicationContext(), "응답으로 전달된 name : " + name,
Toast.LENGTH_LONG).show();
}
}
}

@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 v){
Intent intent = new Intent(getApplicationContext(), MenuActivity.class);
startActivityForResult(intent, REQUEST_CODE_MENU);
}
});
}
}

- onActivitiyResult() 메서드는 새로 띄웠던 메뉴 액티비티가 응답을 보내면 그 응답을 처리하는 역할

- 메서드의 첫 번째 파라미터는 액티비티를 띄울 때 전달했던 요청 코드

- 메서드의 두 번재 파라미터는 새 액티비티로부터 전달된 응답 코드(새 액티비티에서 처리한 결과가 정상인지 아닌지 구분)

- 메서드의 세 번째 파라미터는 새 액티비티로부터 전달 받은 인텐트

 

- 인텐트 객체에 데이터를 넣는 방법은 키와 데이터값을 쌍으로 넣어야 한다. 

 

04-3 인텐트 살펴보기

 

■ 인텐트의 역할과 사용 방식

- 인텐트를 만든 후 startActivity()나 startActivityForResult() 메서드를 호출하면서 전달하는 방법으로 인텐트를 시스템에 전달

- 그 시스템은 그 인텐트 안에 들어 있는 명령을 확인하고 만들어진 액티비티 또는 이미 단말에 설치되어 있는 다른 앱들의 액티비티를 띄운 것

- 이렇게 인텐트를 앱 구성 요소가 해야 할 일을 지정

startActivity() 액티비티를 띄울 때 
startService() 서비스를 시작할 때
broadcastIntent() 인턴트 객체를 브로드캐스팅 방식으로 전송할 때

- 인텐트 기본 구성 요소는 Action(수행할 기능)과 Data(액션이 수행될 대상의 데이터) 이다. 

 

- 인텐트에 포함되어 있는 데이터는 그 포맷이 어떤 것인가를 시스템이 확인한 후 적절한 액티비티를 자동으로 찾아 띄워주기도 한다. 만약 http처럼 특정 포맷을 사용하면 그 포맷은 등록된 MIME 타입으로 구분

- MIME 타입은 일반적을 웹 서버에서 사용하는 MIME 타입과 같다. 

- 인텐트 전달 매커니즘도 이렇게 MIME 타입을 구분한 후 설치된 앱들 중에 적절한 것을 찾아 액티비티를 띄워주는 것

 

- 인텐트의 생성자들을 보면 인텐트 객체는 액션과 데이터를 인수로 하여 만들 수도 있지만 다른 인텐트나 클래스 객체를 인수로 하여 만들기도 함

- 인텐트에 클래스 객체나 컴포넌트 이름을 지정하여 호출할 대상을 확실히 할 수 있는 경우 'Explict Intent'이라고 하고, 액션과 데이터를 지정하긴 했지만 호출할 대상이 달라질 수 있는 경우에는 'Implict Intent' 

- 그 밖의 다른 속성들이 있음

범주(Category)

예) CATEGORY_LAUNCHER는 최상위 앱으로 설치된 앱들의 목록을 보여주는 애플리케이션 런처 하면에 이 앱을 보여주여 한다는 것을 의미

타입(Type) 인텐트에 들어가는 데이터의 MIME 타입을 명시적으로 지정, 
컴포넌트 인텐트에 사용될 컴포넌트 클래스 이름을 명시적으로 지정
부가 데이터

인텐트에 추가적인 정보를 넣을 수 있도록 번들(Bundle) 객체를 담고 있음

인텐트에 액션과 데이터를 넣어 다른 앱의 액티비티를 띄우는 경우

 

04-4 플래그와 부가 데이터 사용하기

- 액티비티로 만든 화면이 한 번 메모리에 만들어졌는데도 계속 startActivity()나 startActivityForResult() 메서드를 여러 번 호출하며 동일한 액티비티가 메모리에 여러 개 만들어질 것 why? 시스템이 인텐트 별로 새 액티비티를 띄워주기 때문에 

 

■ 플래그

- 중복된 액티비티를 띄우지 않기 위해

- 액티비티가 처리되는 방식 : 액티비티는 액티비티 매니저(Activitiy Manager)라는 객체에 의해 '액티비티 스택(Activity Stack)' 이라는 것으로 관리 

 

- 대표적인 플래그 

FLAG_ACTIVITY_SINGLE_TOP 

- 액티비티를 생성할 때 이미 생성된 액티비티가 있으면 그 액티비티를 그대로 사용하라는 플래그

- 기존에 만든 동일한 액티비티를 그대로 사용 ->화면에 보이는 액티비티가 새로 만들어지지 않고 기존에 있는 것이 보인다면 시스템에서 전달하는 인텐트 객체는 어떻게 전달받을 수 있을까? 

- 새로운 액티비티를 띄워주는 액티비티를 부모 액티비티라고 부른다면, 부모 액티비티로부터 전달하는 인텐트는 새로 만들어진 인텐트의 onCreate() 메서드 안에서 getIntent() 메서드로 참조할 수 있음 

- 그런데 액티비티가 새로 만들어지지 않고 재사용된다면 액티비티의 onCreate() 메서드가 호출되지 않음

- 이 경우에는 새로 띄워지는 액티비티에서 인텐트를 전달 받아 처리하는 방법이 따로 있어야 한다. -> onNewIntent() 메서드

- onNewIntent 메서드를 재정의하면 액티비티가 새로 만들어지지 않았을 때 인텐트의 객체만 전달 받을 수 있다. 

FLAG_ACTIVITY_NO_HISTORY

- 이 플래그로 설정하면 처음 이후에 실행된 액티비티는 액티비티 스택에 추가되지 않는다.

 

예) 알림 이벤트  

FLAG_ACTIVITY_CLEAR_TOP

- 액티비티 위헤 있는 다른 액티비티를 모두 종료 

예) 홈 페이지

 

■ 부가 데이터

- 한 액티비티에서 다른 액티비티를 띄울 때 데이터를 전달해야 하는 경우

- 별도의 클래스를 만든 다음 그 안에 클래스 변수(static 키워드를 이용해 선언한 변수)를 만들어 두 개의 화면에서 도무 그 변수를 참조하게 하는 방법

 

- 안드로이드는 다른 앱에서 여러분이 만든 화면을 띄울 수도 있기 때문에 변수를 공유하는 방식으로 데이터를 전달하는 것이 불가능할 수도 있다. 

- 그래서 기본적으로 액티비티를 띄울 때 전달되는 인테트 안에 부가 데이터(Extra Data)를 넣어 전달하는 방법을 권장

 

- 인텐트 안에는 번들(Buddle) 객체가 들어 있는데, 번들 객체는 해시테이블과 유사해서 putExtra()와 get000Extra() 메서드로 데이터를 넣거나 빼낼 수 있다. (000은 기본 자료형의 이름, 배열이나 Serializable 객체도 넣었다 뺄 수 있음)

 


▶ SampleParcelable 프로젝트

- SimpleData 클래스 만들기

- 이 클래스에 Parcelable 인터페이스 구현하기

 

▼SimpleData.java 코드

package com.ogrg.sampleparcelable;

import android.os.Parcel;
import android.os.Parcelable;

public class SimpleData implements Parcelable {
    int number;
    String message;

    public SimpleData(int num, String msg){
        number = num;
        message = msg;
    }

    public SimpleData(Parcel src){
        number = src.readInt();
        message = src.readString();
    }

    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
        public SimpleData createFromParcel(Parcel in){
            return new SimpleData(in);
        }

        @Override
        public Object[] newArray(int size) {
            return new Object[0];
        }
    };
    public int describeContents(){
        return 0;
    }

    public void writeToParcel(Parcel dest, int flags){
        dest.writeInt(number);
        dest.writeString(message);
    }
}

- 클래스 안에 정의된 인스턴스 변수는 정수, 문자열 두 개이다.

- writeToParcel() 메서드는 이 SimpleData 객체 안에 있는 데이터를 Parcel 객체로 만드는 역할을 한다. 

 

- CREATOR 객체는 상수로 정의되어 있으며 새로운 객체가 만들어지는 코드가 들어가므로 new SimpleData()와 같이 SimpleData 객체를 만드는 부분을 볼 수 있다. 

 

- 메인 액티비티를 띄우기 위해 만드는 인텐트 객체에는 SimpleData 객체를 부가 데이터로 넣어 전달할 것

- parcelable 인터페이스를 사용하면 객체를 정의해 데이터를 전달할 수 있으므로 코드가 좀 더 단순해짐, 재사용도 높아지고

 

04-5 태스크 관리 이해하기

- 앱을 실행하면 그 앱은 하나의 프로세스 위에서 동작

- 내가 만든 앱에서 안드로이드의 기본 앱 중 하나인 전화 앱의 화면을 어떻게 띄울까? 

앱에서 시스템으로 인텐트를 보내는 방법으로 전화 앱을 띄울 수 있다. 

즉 프로세스를 띄울려면 시스템의 도움이 필요하다. 

 

- 프로세스는 독립적인 상자와 같아서 프로세스 간의 정보 공유는 어렵다. 그래서 task라는 것이 만들어짐

- 즉 태스크를 이용하면 프로세스처럼 독립적인 실행 단위와 상관없이 어떤 화면들이 같이 동작해야 하는지 흐름을 관리할 수 있다. 

 

■ 프로세스와 테스크

 

▶ SampleTask 프로젝트

- 버튼을 클릭했을 때 onClick 메서드가 호출되나

- 화면을 띄울 때는 Intent 객체의 startActivity 메서드를 사용한다

=> 버튼을 누를 때마다 첫 화면이 반복해서 뜨게  된다. 여러 화면이 중첩할 수 있다

 

▼Manifests.xml

<activity android:name=".MainActivity" android:launchMode="singleTop">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

- 액티비티 태그 안에 launchMode를 singleTop로 설정하면 테스크의 가장 위쪽에 있는 액티비티는 더 이상 새로 생기지 않는다.

 

04-6 액티비티의 수명주기와 SharedPreferences 이해하기

- 안드로이드 시스템은 실행되는 앱의 상태를 직접 관리

- 액티비티는 처음 실행될 때 메모리에 만들어지는 과정부터 시작해서 실행과 중지, 그리고 메모리에서 해제되는 여러 과정의 상태 정보를 갖고 있으며, 이런 상태 정보는 시스템이 관리하면서 각각의 상태에 해당하는 메서드를 자동으로 호출하게 됨

실행(Running) 화면상에 액티비티가 보이면서 실행되어 있는 상태, 액티비티 스택의 최상위에 잇으며 포커스를 가지고 있음
일시 정지(Paused) 사용자에게 보이지만 다른 액티비티가 위에 있어 포커스를 받지 못하는 상태, 대화상자가 위에 있어 일부가 가려진 경우에 해당함
중지(Stopped) 다른 액티비티에 의해 완전히 가려져 보이지 않는 상태

- 액티비티의 상태 정보가 변화하는 것을 Life Cycle 또는 생명주기라고 함

 

- 사용자가 게임하다가 중간에 전화를 받고 다시 돌아올 때 사용되는 액티비티의 생명주기 메서드는  onPause()와 onResume()이다. 

- 액티비티를 중지시키기 전에 onSaveInstanceState() 메서드를 이용해 데이터를 임시로 저장할 수 있다. 

 

▶ SampleLifeCycle 프로젝트

 

- 버튼을 눌렀을 때 메뉴 엑티비티를 띄우면 메인 액티비티는 액티비티 스택에 들어가므로 수명주기의 상태 변화가 들어간다. 

- 토스트 메세지는 여러 번 실행 될 경우 이전 메시지가 보이지 않을 수도 있다. 따라서 디버깅 목적으로 사용할 때는 Logcat 창에 메시지를 출력하는 것이 좋다. 

- Log cat 창에서 특정 단어 검색 가능, 필터에 사용되는 글자를 태그라고 부른다. 

- 시스템에서 자동으로 호출하는 메서드는 '콜백 메서드'

- 액티비티 상태 변화 순서

onCreate → onStart onResume →onPause onStop onDestroy

- 화면이 전환 될 때는 onDestory() 메서드가 호출되지 않음. 

 

- 앱 안에는 간단한 데이터를 저장하거나 복원할 때는 SharedPreferences를 사용할 수 있음

- 앱 내부에 파일을 하나 만드는데 이 파일 안에서 데이터를 저장하거나 읽어올 수 있게 한다. 

- 개발자는 실제로 파일을 하나 만들 필요없이 SharedPreferenced 의 저장, 복원 메서드를 호출하면 된다. 

 

 

<코드 결과 안나옴>


도전07

도전08

반응형


댓글