개발모음집

[안드로이드 프로그래밍] Day07 ~ Day08 인플레이션 ~ 인텐트, 부가데이터 본문

Android

[안드로이드 프로그래밍] Day07 ~ Day08 인플레이션 ~ 인텐트, 부가데이터

void 2016. 6. 5. 00:22

애플리케이션 구성하기



인플레이션: 메모리에 객체화 



setContentView() 메소드의 역할

: 액티비티를 구성하고 있는 상위레이아웃에 붙여준다.

- 화면에 나타낼 뷰를 지정하는 역할

- XML 레이아웃의 내용을 메모리 상에 객체화하는 역할


LayoutInflater 클래스: 이 클래스는 시스템 서비스로 제공됨

일부분만을 차지하는 화면 구성요소들을 XML 레이아웃에서 로딩하여 보여주는 방법

* 시스템서비스: 미리 설정해 놓아 사용자가 사용할 수 있게 해놓은 것

부분 레이아웃 파일 ---LayoutInflater로 인플레이션-- > 뷰 그룹 --> 전체 레이아웃


정리

안드로이드 화면: 소스와 화면 구성이 분리되어 있다.

자바소스 1개, XML 레이아웃 1개



메인화면에 부분 화면 띄우기 예제


Android 레이아웃생성법


Android app.res.layout에서 오른쪽버튼 클릭 - new - XML - Layout XML File

레이아웃 파일 네임 지정

Root Tag: 상위레이아웃 지정


MyLayoutInflater의 MainActivity.java파일

package org.androidtown.mylayoutinflater;

import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

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

public void onButton1Clicked(View V) {
// 레이아웃 인플레이션으로 부분화면으로 넣어준다.
LinearLayout container = (LinearLayout) findViewById(R.id.container);

LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); // 시스템 서비스 참조
inflater.inflate(R.layout.sub_layout, container, true); // (부분 레이아웃, 전체 레이아웃(붙임을 당하는 레이아웃, true))

Button button2 = (Button) container.findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(), "부분화면 버튼 클릭됨", Toast.LENGTH_LONG).show();
}
});
}

}


MyLayoutInflater의 activity_main.xml파일

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="org.androidtown.mylayoutinflater.MainActivity"
android:nestedScrollingEnabled="false">

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="구분 화면 띄우기"
android:id="@+id/button"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:onClick="onButton1Clicked" />

<LinearLayout
android:id="@+id/container"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/button"
android:layout_centerHorizontal="true"></LinearLayout>
</RelativeLayout> <!-- fill_parent와 match_parent는 같은 뜻-->


MyLayoutInflater의 sub_layout.xml파일

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="구분화면"
android:id="@+id/textView"
android:textSize="30dp"
android:textColor="#ff0000ff" />

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New Button"
android:id="@+id/button2" />

<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New CheckBox"
android:id="@+id/checkBox" />
</LinearLayout>

화면 구성과 화면 간 이동


액티비티: 하나의 화면으로 구성, 앱의 근간



안드로이드 애플리케이션

**시스템에서 관리한다.


액티비티 

+

서비스 : 화면이 없는 경우

+

브로드캐스트 수신자

+

내용 제공자


StartActivity가 intent(Extra를 포함하여)를 만들어 시스템을 요청



MyActivity의 MainActivity.java파일


package org.androidtown.myactivity;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void onButton1Clicked(View V) {
Intent intent = new Intent(getApplicationContext(), MenuActivity.class); //MenuActivity.class 이 액티비티를 띄어주세요라는 정보를 가진 객체생성
intent.putExtra("title", "EXID");// 인테트안에 정보넣기 정보전달은 Extra로 전달
// "타이틀", "값" 이게 시스템에 전달, 그리고 메뉴액티비티로 전달
startActivityForResult(intent, 1001); // 응답받기 위한 메소드: startActivityForResult
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(data != null){
String name = data.getStringExtra("name");
Toast.makeText(getApplicationContext(),"전달받은 값: "+name, Toast.LENGTH_LONG).show();
}
super.onActivityResult(requestCode, resultCode, data);
} // 메뉴액티비티에게 받은 정보를 띄우기
}


MyActivity의 activity_main.xml파일


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="org.androidtown.myactivity.MainActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="메인화면"
android:id="@+id/textView"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:textSize="30dp" />

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="새로운 화면 띄우기"
android:id="@+id/button"
android:layout_below="@+id/textView"
android:layout_centerHorizontal="true"
android:layout_marginTop="73dp"
android:onClick="onButton1Clicked" />
</RelativeLayout>

MyActivity의 ManuActivity.java파일

package org.androidtown.myactivity;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;

public class MenuActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_manu);
// 메뉴액티비티에서 정보확인
Intent intent = getIntent();
if (intent != null) {
String title = intent.getStringExtra("title");
Toast.makeText(getApplicationContext(),"전달받은 값: "+title, Toast.LENGTH_LONG).show();
}

}
public void onButton1Clicked(View V) {

// 이전 화면에 전달에 해야하는 법
Intent intent = new Intent();
intent.putExtra("name", "솔지");
setResult(RESULT_OK, intent); // RESULT_OK: 정상이라는 뜻의 상수
finish();
} // 메니페스트에 <activity android:name="MenuActivity" /> 추가해야함. 메니페스트는 액티비티관리하는 파일
}


MyActivity의 activity_manu.xml파일

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="org.androidtown.myactivity.MainActivity">

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="메뉴화면"
android:id="@+id/textView"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:textSize="30dp" />

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="돌아가기"
android:id="@+id/button"
android:layout_below="@+id/textView"
android:layout_centerHorizontal="true"
android:layout_marginTop="73dp"
android:onClick="onButton1Clicked" />
</RelativeLayout>


인텐트


인텐트란 무엇일까?

자바를 공부하고도 어려운 부분이다. 인텐트의 메커니즘을  이해하기 어려웠다.

그래도 도서관의 여러 책을 보니 대충 감이 왔다.

한 책에서 "인텐트는 자바기초에는 없는 내용이라 이해하기 어려운 부분일 수 도 있다"고 한다.


인텐트는 무언가 작업을 수행하기 위해 사용되는 일종의 전달 수단이라고 하는데

내 식대로 이해하기로는 "액티비티를 띄어주기위한 명령문서"라고 이해했다.



인텐트에는 액션과 데이터가 있는데 액션은 "액티비티를 띄어라 등" 수행할 기능,

데이터는 "띄어질 액티비티" 정보를 말한다.


액션에는 ACTION_MAIN, ACTION_EDIT, ACTION_VIEW 등 다양한 것이 있지만 EDIT과 VIEW를 많이 쓴다고 한다.



메커니즘이 제일 이해가 안되었는데


액티비티A에서 startActivityForResult()로 액티비티B에 인텐트를 보내면

setResult로 액티비티B가 반환한다.

그리고 액티비티A에 있는 onActivityResult()가 반환값을 받아 처리한다.


모양


Intent()

Intent(Intent o)

Intent(String action [,Uri uri])

Intent(Context packageContext, Class<?> cls)

Intent(String action, Uri uri, Context packageContext, Class<?> cls)




인텐트 전달


액티비티 액티비티 사이에

브로드캐스트 수신자와 액티비티사이에

서비스와 액티비티 사이에


인텐트 전달 메소드

startActivity()

startService() or bindService()

broadcastIntent()



메니페스트

액티비티선언, 권한부여기능 (애플리케이션을 구성하는 구성요소 네 가지는 새로 만들 때마다 메니패스트 파일에 추가해야한다.)


권한부여 (메니페스트 파일에 선언)

<uses-permission android:name="android.permission.CALL_PHONE" />
// Permission is only granted to system apps more... 이라고 경고창 뜨는 건 시스템앱, 제조사가 만드는 앱한테만 권한을 줄 수 있는 그런 게 있다

플랫폼 설정은 gradle에서 가능하다 (이클립스는 메니페스트)

Gradle Scripts.build.gradle(Modual: app)

defaultConfig {
applicationId "org.androidtown.myintent"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
}

액티비티 스택


액티비티 스택은 이전에 띄었던 액티비티 스택을 저장한다.


옵션

Flag_ACTIVITY_SINGLE_TOP: 액티비티 스택에 중복된 액티비티가 저장되지않도록 하는 옵션


플래그


액티비티를 여러 번 실행한다면 동일한 액티비티가 여러 개 스택에 들어가 있고 동시에 데이터를 여러 번 접근하거나 리소스를 여러 번 사용하는 문제가 발생할 수 있다.

이 점을 해결하기 위하여 플래그가 생겨났다.


참조


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


다시 사용하는 경우 부모 액티비티가 전달하는 인텐트는 새로 만들어지는 인텐트의 onCreate() 안에서 getIntent() 를 이용해 참조한 후 처리할 수 있다.

그런데 onCreate()는 액티비티가 생성될 떄 한 번만 호출되게 되는 메소드

그래서 별도의 메소드가 필요, onNewIntent()

FLAG_ACTIVITY_NO_HISTORY: 처음 이후에 실행된 액티비티는 액티비티 스택에 추가되지 않는다. 

FLAG_ACTIVITY_CLEAR_TOP:항상 하나의 객체가 메모리 상에 존재하면서 그 상위의 액티비티를 모두 종료시킬 수 있다.



MyIntent의 MainActivity.java파일


package org.androidtown.myintent;

import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

public class MainActivity extends AppCompatActivity {
private static final int ACTIVITY_MENU = 1001;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

public void onButton1Clicked(View V) {
/*Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:010-0000-0000"));
startActivity(intent);*/
// 인텐트로 띄어야한다. 첫 번째 방법
/*Intent intent = new Intent(getApplicationContext(), MenuActivity.class);
startActivityForResult(intent, ACTIVITY_MENU);*/
// 두 번째 방법, 이름을 지정
Intent intent = new Intent();
ComponentName name = new ComponentName("org.androidtown.myintent","org.androidtown.myintent.MenuActivity"); //(패키지명, 패키지명을 포함한 클래스명) 클래스를 가리키는 것
intent.setComponent(name);
intent.putExtra("title", "EXID");
intent.putExtra("age", "22");


Person person01 = new Person("걸스데이", 21);
intent.putExtra("person", person01);
// intent.putExtra("", ); putExtra(String name, Parcelable value)
startActivityForResult(intent, ACTIVITY_MENU);
}


}




MyIntent의 MenuActivity.java파일



package org.androidtown.myintent;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;

public class MenuActivity extends AppCompatActivity {

// 자바로 추가하여 파일을 생성하면 메니페스트에 액티비티태그가 자동으로 추가된다.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_menu);


Intent intent = getIntent();
if (intent != null) {
String title =intent.getStringExtra("title");
int age =intent.getIntExtra("age", 0); // age 값이 없을 경우, 0값으로 초기화
Person person01 = (Person) intent.getSerializableExtra("person"); //Serializable을 상속받은 Person객체
// Toast.makeText(getApplicationContext(), "Title: "+title, Toast.LENGTH_LONG).show();
Toast.makeText(getApplicationContext(), "Person name:: "+person01.getName(), Toast.LENGTH_LONG).show();
}
}
}

MyIntent의 Person.java파일



package org.androidtown.myintent;
import java.io.Serializable;

public class Person implements Serializable {

String name;
int age;

public Person () {

}

public Person (String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}




에러: 

 Error running app: Instant Run requires 'Tools | Android | Enable ADB integration' to be enabled. -> 해결: 

[Tools] - [Android] - [Enable ADB Integration 체크]

 



수명주기

MyLIfeCycle의 MenuActivity.java파일

package org.androidtown.mylifecycle;

import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

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

Toast.makeText(getApplicationContext(), "1. onCreate() 호출됨", Toast.LENGTH_LONG).show();
} // onCreate(): 메모리에 만들어지는 것, 프로그램 시작점

@Override
protected void onStop() {

Toast.makeText(getApplicationContext(), "5. onStop() 호출됨", Toast.LENGTH_LONG).show();
super.onStop();
}

@Override
protected void onDestroy() {
Toast.makeText(getApplicationContext(), "6. onDestroy() 호출됨", Toast.LENGTH_LONG).show();
super.onDestroy();
}

@Override
protected void onPause() {
Toast.makeText(getApplicationContext(), "4. onPause() 호출됨", Toast.LENGTH_LONG).show();
saveScore();
super.onPause();
}

@Override
protected void onResume() {
Toast.makeText(getApplicationContext(), "3. onResume() 호출됨", Toast.LENGTH_LONG).show();
loadScore();
super.onResume();
} // onResume() 화면에 보여지기 전 단계

@Override
protected void onStart() {
Toast.makeText(getApplicationContext(), "2. onStart() 호출됨", Toast.LENGTH_LONG).show();
super.onStart();
} // onStart() 동작되도록 준비하는 것

private void saveScore() {
SharedPreferences pref = getSharedPreferences("gostop", Activity.MODE_PRIVATE);
//SharedPreferences 간단한 자료 저장시 사용하는 메소드, 조회
SharedPreferences.Editor editor = pref.edit(); // 저장
editor.putInt("score", 10000);
editor.commit();
}
private void loadScore() {
SharedPreferences pref = getSharedPreferences("gostop", Activity.MODE_PRIVATE);
int score = pref.getInt("score", 0);
Toast.makeText(getApplicationContext(), "읽어온 점수: " + score, Toast.LENGTH_LONG).show();
}


}



부가데이터: 액티비티 간에 데이터를 전달하기 위해 사용하는 번들 객체는 인텐트 안에 들어 있기 때문에 putXXX(), getXXX()를 이용해 데이터를 넣거나 볼 수 있다.

기본적으로 기본 데이터 타입만 지원 문자열이나 정수와 같은 데이터를 키와 데이터 값의 쌍으로 만들어 넣게 된다.


Intent putExtra(String name, String value)

Intent putExtra(String name, int value)

Intent putExtra(String name, boolean value)


String getStringExtra(String name)

int getIntExtra(String name, int defaultValue)

boolean getBooleanExtra(String name, boolean defaultValue)



getXXX()  형태인 경우, 데이터 값이 없으면 디폴트로 설정된 값이 리턴

그리고 전달하고 싶은 데이터가 기본 데이터 타입이 아닌 객체인 경우에는 객체 자체를 전달 할 수 없다.  따라서 데이터들을 바이트 배열로 변환하여 전달하거나 또는 Serializable 인터페이스를 구현하는 객체를 만들어 직렬화

하지만 안드로이드에서는 Parcelable 인터페이스를 구현하는게 더 좋다.

그리고 두 가지 메소드를 구현해야한다.;

@Override
public int describeContents() {
return 0;
}

@Override
public void writeToParcel(Parcel dest, int flags) {

}