출처
정재곤, 『Do it! 안드로이드 앱 프로그래밍 - 개정 4판』, 이지스퍼블리싱(주)(2017-06-26), 248-331p
03-1 레이아웃 인플레이션 이해하기
- XML 레이아웃: 단순 화면을 어떻게 배치하는지 정의하는 파일
- 화면 배치; XML 레이아웃 파일 + 자바 소스 파일이 필요
- setContentView(): XML 레이아웃 파일과 자바 소스코드 파일을 매칭시키는 메소드
- R.layout.레이아웃 파일 이름: XML 레이아웃 파일 확장자 없이 지정하여 보는 형식
- 인플레이션(Inflation): XML 레이아웃에 정의된 내용이 메모리에 로딩된 후 객체화되는 과정
1. SampleLayoutInflater 프로젝트 생성
package com.example.kai01.samplelayoutinflater;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(getApplicationContext(), "버튼이 눌렸어요.", Toast.LENGTH_LONG.show());
}
};
setContentView(R.layout.activity_main);
}
}
- activity_main.xml에 있는 텍스트뷰를 삭제한 후 버튼 추가
- setContentView() 메소드를 호출하는 코드 위에 버튼을 찾아 변수에 할당
- setOnClickListener() 메소드를 호출하는 코드 입력
2. 오류 발생 확인
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
- 메모리에 객체화되지 않은 버튼 객체를 참조하려고 했기 때문에 오류 발생
- 로그로 오류 내용 확인 가능
setContentView() 메소드의 두 가지 역할
- 화면에 나타낼 뷰를 지정하는 역할
public void setContentView(int layoutResID)
- XML 레이아웃의 내용을 메모리에 객체화하는 역할
public void setContentView(View view [, ViewGroup.LayoutParams params])
LayoutInflater
- setContentView() 메소드를 이용하여 전체가 아닌 부분 화면을 보여줄 때 사용
- LayoutInflater는 시스템 서비스로 제공됨
- 시스템 서비스: 단말이 시작되면 항상 실행되는 서비스
getSystemService(Context.LAYOUT_INFLATER_SERVICE)
화면의 일부분을 XML 레이아웃 파일의 내용으로 적용하는 과정
- SampleLayoutInflater 프로젝트 안에 새로운 화면을 더 추가하기
1. app 폴더 마우스 오른쪽 버튼 클릭 후 [New –> Activity –> Empty Activity]
- Activity Name: MenuActivity
- 생성하면 activity_menu.xml 레이아웃 파일과 MenuActivity.java 소스 파일로 구성됨
2. activity_menu.xml 파일에서 [Text]탭을 눌러 다음 코드 입력
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- 안내글을 표시하는 텍스트뷰 정의 -->
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="버튼을 눌러 부분 화면을 추가하세요."
android:textSize="20dp" />
<Button
android:id="@+id/button2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="추가하기"/>
<!-- 컨텐츠를 따로 추가할 레이아웃 -->
<LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</LinearLayout>
</LinearLayout>
- 이제 부분 화면으로 추가할 XML 레이아웃 파일을 만들어 봄
3. res/layout 폴더 마우스 오른쪽 버튼 누르고 [New –> Layout resource file]
- File name: sub1.xml
4. sub1.xml 파일 안에 다음 코드 입력
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffaaccff">
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="부분 화면 1"
android:textSize="30dp"/>
<CheckBox
android:id="@+id/checkBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="동의합니다. "/>
</LinearLayout>
5. MenuActivity.java 파일 안에 다음 코드 입력
package com.example.kai01.samplelayoutinflater;
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.LinearLayout;
public class MenuActivity extends AppCompatActivity {
LinearLayout container;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_menu);
container = (LinearLayout) findViewById(R.id.container);
Button button = (Button) findViewById(R.id.button2);
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 = (CheckBox) container.findViewById(R.id.checkBox);
checkBox.setText("로딩되었어요.");
}
});
}
}
- activity_menu.xml 파일 안 리니어 레이아웃의 id는 container
- findViewById(R.id.container);: id가 container인 리니어 레이아웃 객체를 전달 –> 리니어 레이아웃 객체 참조 가능해짐
- getSystemService(Context.LAYOUT_INFLATER_SERVICE);: LayoutInflater 객체를 참조
- inflater.inflate(R.layout.sub1, container, true);: container를 id로 갖는 리니어 레이아웃 객체에 sub1.xml 파일의 레이아웃을 설정하라는 의미
- 위 메소드는 레이아웃에 정의된 뷰의 내부적인 객체화 과정을 거침
- sub1.xml이 메모리에 객체화되면 container 객체의 findViewById() 메소드를 사용해 sub1.xml의 레이아웃 텍스트뷰나 체크박스를 참조할 수 있음
6. AndroidManifest.xml 파일에 MenuActivity와 MainActivity 이름 바꾸기
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.samplepdfview">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
7. 실행결과 확인
- 위 화면: XML 레이아웃을 로딩하기 전
- 아래 화면: 인플레이션을 통해 레이아웃을 적용한 이후의 것
- XML 레이아웃을 만들지 않고 코드에 직접
new Button(this)
과 같은 코드를 추가할 수도 있음
inflate() 메소드 정의
View inflate (int resource, ViewGroup root)
- 첫 번째 파라미터: XML 레이아웃 리소스
- 두 번째 파라미터: 뷰들을 객체화하여 추가할 대상이 되는 부모 컨테이너 지정
- 보통 LayoutInflater 객체는 getSystemService() 메소드를 사용할 수 있지만
- LayoutInflater 객체의 from() 메소드를 사용할 수도 있음
from() 메소드 정의
static LayoutInflater LayoutInflater.from(Context context)
- LayoutInflater를 내부적으로 지원하는 View 클래스 메소드를 이용하는 방법도 있음
다른 inflate() 메소드 정의
static View inflate (Context context, int resource, ViewGroup root)
- 인플레이션 과정에 익숙해지자!
03-2 화면 구성과 화면 간 전환
- 안드로이드에서 하나의 화면 = 액티비티
안드로이드 애플리케이션의 기본 구성 요소 네 가지
- 액티비티(Activity)
- 서비스(Service)
- 브로드캐스트 수신자(Broadcast Receiver)
- 내용 제공자(Content Provider)
- 이들의 정보를 담고 있는 곳 = AndroidManifest.xml
- startActivity(): 액티비티를 띄우기 위해 사용하는 메소드
- startActivityForResult(): 액티비티로부터 다시 원래의 액티비티로 돌아오면서 응답을 받아 처리하는 메소드
startActivityForResult(Intent intent, int requestCode)
- 첫 번째 파라미터: 인텐트
- 두 번째 파라미터: 정수로 된 코드 값 –> 액티비티 구분용으로 사용
- 기본 액티비티 외 새로운 액티비티를 추가하고 전환하는 프로젝트를 만들어 보겠음
1. SampleIntent 프로젝트 만들고 app 폴더에 오른쪽 마우스 클릭 후 [New –> Activity –> Empty Activity]
- Activity name: MenuActivity
2. AndroidManifest.xml 파일에 다음 코드 추가
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.kai01.sampleintent">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".MenuActivity"
android:label="메뉴 액티비티"
android:theme="@style/Theme.AppCompat.Dialog">
</activity>
</application>
</manifest>
- android:label: 화면의 타이틀 설정
- android:theme: 테마 설정
3. activity_menu.xml 파일에 돌아가기 버튼 추가
- 돌아가기 버튼을 누르면 정말 돌아가게 하려 함
4. MenuActivity.java에 다음 코드 추가
package com.example.kai01.sampleintent;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MenuActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_menu);
Button button = (Button) findViewById(R.id.button); // 버튼 객체 참조
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Intent intent = new Intent(); // 인텐트 객체 생성하고 name의 값을 부가 데이터로 넣기
intent.putExtra("name", "mike");
setResult(RESULT_OK, intent);
finish(); // 현재 액티비티 없애기
}
});
}
}
- onClick() 메소드 안에 Intent 클래스를 사용, 객체를 만든 후 setResult() 메소드 호출
- setResult(): 새로 띄운 액티비티에서 이전 액티비티로 인텐트를 전달할 때 사용
setResult(응답 코드, 인텐트)
- finish(): 액티비티를 화면에서 없애고 싶을 때 사용
4. activity_main.xml 파일에서 텍스트뷰 삭제 후 버튼 추가
- 버튼 내용은 “메뉴 화면 띄우기”
- onClick 속성에 onButton1Clicked 메소드 입력
5. MainActivity.java 코드를 다음과 같이 수정
package com.example.kai01.sampleintent;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
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);
}
public void onButton1Clicked(View v) {
Intent intent = new Intent(getApplicationContext(), MenuActivity.class); // 또 다른 액티비티를 띄우기 위한 인텐트 객체 생성
startActivityForResult(intent, REQUEST_CODE_MENU); // 액티비티 띄우기
}
}
- startActivity()와의 차이점은 startActivityForResult() 메소드를 쓴다는 것
- startActivityForResult()는 새로 띄운 액티비티로부터 응답을 받을 수 있음
onButton1Clicked() 메소드
- onButton1Clicked() 메소드 안에서 인텐트 객체를 만든 후 startActivityForResult() 메소드를 호출
- 인텐트 객체: 액티비티를 띄울 목적으로 사용되며 액티비티 간에 데이터를 전달하는 데에도 사용
- 인텐트 객체의 첫 번째 파라미터: Context 객체 –> 주로 this 변수 사용하나 이벤트 처리 메소드 안에서는 this 변수로 MainActivity 객체를 참조하지 못함
- 따라서 getApplicationContext() 메소드를 사용해 이 애플리케이션의 Context 객체를 참조한 후 전달
- 인텐트 객체의 두 번째 파라미터: MenuActivity.class
- 이제 응답하는 코드를 작성해봄
6. MainActivity 클래스안에 마우스 커서를 두고 오른쪽 버튼 누른 후 [Generate –> Override Methods] 클릭
- onActivityResult 메소드를 찾아 선택 후 [OK] 클릭
7. MainActivity.java의 onActivityResult 메소드에 다음 코드 추가
package com.example.kai01.sampleintent;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
public static final int REQUEST_CODE_MENU = 101; // 다른 액티비티를 띄우기 위한 요청코드 정의
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_MENU) {
Toast.makeText(getApplicationContext(), "onActivityResult 메소드 호출됨. 요청코드: " + requestCode + ", 결과 코드: " + resultCode, Toast.LENGTH_LONG).show();
}
if (resultCode == RESULT_OK) {
String name = data.getExtras().getString("name");
Toast.makeText(getApplicationContext(), "응답으로 전달된 name: " + name, Toast.LENGTH_LONG).show();
}
}
@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); // 또 다른 액티비티를 띄우기 위한 인텐트 객체 생성
startActivityForResult(intent, REQUEST_CODE_MENU); // 액티비티 띄우기
}
}
- onActivityResult() 메소드는 새로 띄운 메뉴 액티비티가 응답을 보내면 그 응답을 처리하는 역할을 함
protected void onActivityResult(int requestCode, int resultCode, Intent intent);
- 첫 번째 파라미터: 액티비티를 띄울 때 요청한 코드 –> 어떤 액티비티로부터 응답을 받았는지 구분
- 두 번째 파라미터: 응답을 보내 온 액티비티에서 온 응답 코드 –> 처리 결과가 정상인지 아닌지 구분, Activity_RESULT_OK 상수가 정상 처리를 뜻함
- 세 번째 파라미터: 새로 띄운 메뉴 액티비티로부터 받은 인텐트 –> 인텐트 안에는 데이터를 넣을 수 있음
- putExtra(): 인텐트 객체에 데이터를 넣는 메소드 –> 키(Key)와 데이터(Data) 값을 넣어야 함
8. 실행하여 결과 확인하기
9. 정리
- 새로운 액티비티를 추가하면 XML 레이아웃 파일 하나와 자바 소스 파일 하나가 만들어지고 매니페스트 파일에 액티비티 태그가 추가됨
- 새로 만들어진 XML 레이아웃을 수정하여 새롱누 액티비티의 화면이 어떻게 배치될지 작성
- 메인 액티비티의 버튼을 클릭하면 startActivityForResult() 메소드로 새로운 액티비티를 띄움
- 새로운 액티비티가 보이고 그 안에 들어 있는 버튼을 클릭하면 setResult() 메소드로 응답을 보냄
- 메인 액티비티에서 onActivityResult() 메소드를 재정의하여 새로 띄웠던 액티비티에서 보내오는 응답을 처리
03-3 인텐트 살펴보기
- 인텐트: 다른 액티비티를 띄우거나 기능을 동작시키기 위한 수단
인텐트의 역할과 사용 방식
- android.content 패키지: 인텐트가 정의되어 있는 장소
다른 애플리케이션 구성 요소에 인텐트를 전달할 수 있는 대표 메소드
- startActivity(): 액티비티를 화면에 띄울 때 사용
- startActivityForResult()
- startService(): 서비스를 시작할 때 사용
- bindService()
- broadcastIntent(): 브로드캐스팅을 수행할 때 사용
인텐트의 기본 구성 요소
- 액션(Action): 수행할 기능 –> ex) ACTION_VIEW, ACTION_EDIT
- 데이터(Data): 액션이 수행될 대상 데이터
액션과 데이터를 사용하는 대표적인 예
속성 | 설명 |
---|---|
ACTION_DIAL tel:01077881234 | 주어진 전화번호를 이용해 전화걸기 화면을 보여줌. |
ACTION_VIEW tel:01077881234 | 주어진 전화번호를 이용해 전화걸기 화면을 보여줌. URI 값의 유형에 따라 VIEW 액션이 다른 기능을 수행함. |
ACTION_EDIT content://contacts/people/2 | 전화번호부 데이터베이스에 있는 정보중에서 ID 값이 2인 정보를 편집하기 위한 화면을 보여줌. |
ACTION_VIEW content://contacts/people | 전화번호부 데이터베이스의 내용을 보여줌. |
인텐트 생성자
- Intent()
- Intent(String action, [,Uri uri])
- Intent(Context packageContext, Class<?> cls)
- Intent(String action, Uri uri, Context PackageContext, Class<?> cls)
- 인텐트의 데이터는 적절한 MIME 타입으로 구분함
- 인텐트 객체는 액션과 데이터를 인수로 하여 만들 수도 있지만 다른 인텐트나 클래스 객체를 인수로 하여 만들기도 함
- 명시적 인텐트(Explicit Intent): 인텐트에 클래스 객체나 컴포넌트 이름을 지정하여 호출할 대상을 확실히 알 수 있는 인텐트
- 암시적 인텐트(Implicit Intent): 액션과 데이터를 지정하긴 했지만 호출할 대상이 달라질 수 있는 인텐트
- 암시적 인텐트는 안드로이드 시스템이 인텐트를 이용해 요청한 정보를 처리할 수 있는 적절한 컴포넌트를 찾고 사용자에게 그 대상과 처리 결과를 보여줌
- 범주(Category): 액션이 실행되는데 필요한 추가적인 정보 제공
- 타입(Type): 인텐트에 들어가는 데이터의 MIME 타입을 명시적으로 지정
- 컴포넌트(Component): 액티비티와 같은 독립적인 구성 요소
- 부가 데이터(Extra Data): 번들(Bundle) 객체를 통해 추가적인 정보를 넣음
- 인텐트에서 액션과 데이터를 이용하는 경우와 컴포넌트 이름을 이용하는 경우를 비교해보겠음
1. SampleCallIntent 프로젝트 생성 후 activity_main.xml에 입력상자 하나와 버튼 하나 추가
- editText는 안드로이드 스튜디오 팔레트에 없고 xml에 직접 editText 태그 입력하면 됨 참고
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="164dp"
android:layout_marginLeft="164dp"
android:layout_marginTop="80dp"
android:onClick="onButton1Clicked"
android:text="전화걸기"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/editText" />
<EditText
android:id="@+id/editText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="132dp"
android:layout_marginLeft="132dp"
android:text="tel:010-1000-1000"
app:layout_constraintStart_toStartOf="parent"
tools:layout_editor_absoluteY="170dp" />
</android.support.constraint.ConstraintLayout>
2. MainActivity.java 파일 다음과 같이 수정하기
package com.example.kai01.samplecallintent;
import android.content.Intent;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.editText); // 뷰 객체 참조
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String data = textView.getText().toString(); // 입력상자에 입력된 전화번호 확인
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(data)); // 전화걸기 화면을 보여줄 인텐트 객체 생성
startActivity(intent); // 액티비티 띄우기
}
});
}
}
3. 실행하여 확인하기
- 이제 인텐트를 사용하여 액티비티를 띄우는 두 번째 경우를 보겠음
4. SampleIntent 프로젝트를 열고 MainActivity.java 코드 수정하여 다른 방식의 인텐트 여는 법 확인하기
package com.example.kai01.sampleintent;
import android.content.ComponentName;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
public static final int REQUEST_CODE_MENU = 101; // 다른 액티비티를 띄우기 위한 요청코드 정의
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_MENU) {
Toast.makeText(getApplicationContext(), "onActivityResult 메소드 호출됨. 요청코드: " + requestCode + ", 결과 코드: " + resultCode, Toast.LENGTH_LONG).show();
}
if (resultCode == RESULT_OK) {
String name = data.getExtras().getString("name");
Toast.makeText(getApplicationContext(), "응답으로 전달된 name: " + name, Toast.LENGTH_LONG).show();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void onButton1Clicked(View v) {
Intent intent = new Intent();
ComponentName name = new ComponentName("com.example.kai01.sampleintent", "com.example.kai01.sampleintent.MenuActivity"); // 컴포넌트 이름을 지정할 수 있는 객체 생성
intent.setComponent(name); // 인텐트 객체에 컴포넌트 지정
startActivityForResult(intent, REQUEST_CODE_MENU); // 액티비티 띄우기
}
}
- 그럼 위에서 한 sampleintent 프로젝트와 똑같이 작동함
인텐트 클래스에 정의된 액션 정보
ACTION_MAIN | ACTION_SENDTO |
---|---|
ACTION_VIEW | ACTION_ANSWER |
ACTION_ATTACH_DATA | ACTION_INSERT |
ACTION_EDIT | ACTION_DELETE |
ACTION_PICK | ACTION_RUN |
ACTION_CHOOSER | ACTION_SYNC |
ACTION_GET_CONTENT | ACTION_PICK_ACTIVITY |
ACTION_DIAL | ACTION_SEARCH |
ACTION_CALL | ACTION_ACTION_WEB_SEARCH |
ACTION_SEND | ACTION_FACTORY_TEST |
- ACTION_MAIN과 ACTION_EDIT을 가장 많이 사용
PDF 파일 보여주기
- PDF 파일을 보여주는 인텐트는 암시적 인텐트를 사용할 예정
명시적, 암시적 인텐트
1. SamplePDFView 프로젝트 생성 및 activity_main.xml 작성
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<EditText
android:id="@+id/editText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24dp"
android:hint="PDF 파일명을 입력하세요." />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="열기"
android:onClick="onButton1Clikced" />
</LinearLayout>
2. MainActivity.java 작성
package com.example.samplepdfview;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import java.io.File;
public class MainActivity extends AppCompatActivity {
EditText editText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
editText = findViewById(R.id.editText);
}
public void onButton1Clicked(View v) {
String filename = editText.getText().toString(); // 입력상자에 입력된 파일명 확인
if (filename.length() > 0) {
openPDF(filename.trim()); // openPDF() 메소드 호출
} else {
Toast.makeText(getApplicationContext(), "PDF 파일명을 입력하세요.", Toast.LENGTH_LONG).show();
}
}
public void openPDF(String filename) { // PDF 파일 열기 기능을 정의한 메소드
String sdcardFolder = Environment.getExternalStorageDirectory().getAbsolutePath();
String filepath = sdcardFolder + File.separator + filename;
File file = new File(filepath);
if (file.exists()) {
Uri uri = Uri.fromFile(file); // Uri 객체로 생성
Intent intent = new Intent(Intent.ACTION_VIEW); // ACTION_VIEW 액션을 가지는 인텐트 객체 생성
intent.setDataAndType(uri, "application/pdf"); // Uri 객체와 MIME 타입 지정
try {
startActivity(intent); // 액티비티 띄우기
} catch (ActivityNotFoundException ex) {
Toast.makeText(this, "PDF 파일을 보기 위한 뷰어 앱이 없습니다.", Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(this, "PDF 파일이 없습니다.", Toast.LENGTH_SHORT).show();
}
}
}
- 사용자는 입력창에 PDF 파일명을 입력하고 버튼을 누름
- 버튼을 누르면
openPDF()
메소드 호출 openPDF()
는 인텐트 객체를 만들고startActivity()
메소드 호출- 인텐트: 액션은 ACTION_VIEW,
setDataAndType
으로 URI와 MIME 타입 지정 Environment.getExternalStorageDirectory().getAbsolutePath();
: SD 카드의 절대 경로를 가져오는 함수- SD 카드에 접근하려면 권한 필요
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
3. AndroidManifest.xml에서 SD카드 권한 추가
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.samplepdfview">
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
4. Gradle Scripts > build.gradle(Module: app)에서 target SDK 22로 맞추기
apply plugin: 'com.android.application'
android {
compileSdkVersion 27
defaultConfig {
applicationId "com.example.samplepdfview"
minSdkVersion 21
targetSdkVersion 22
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
- targetSdkVersion 22
5. SD카드가 들어있는 단말기에 연결하고 빌드하고 PDF 누르면 된다고 함
- 귀찮으니 생략
03-4 액티비티를 위한 플래그와 부가 데이터
플래그
- 다양한 액티비티가 쌓일 때 이를 조절해 줌
- FLAG_ACTIVITY_SINGLE_TOP
- FLAG_ACTIVITY_NO_HISTORY
- FLAG_ACTIVITY_CLEAR_TOP
액티비티 스택
액티비티를 새로 생성하여 인텐트를 전달하는 것과 생성하지 않고 인텐트를 전달하는 방법 비교
public void onNewIntent(Intent intent)
각 플래그 옵션을 사용하지 않았을 때와 사용하였을 때의 비교
부가데이터
- 인텐트를 생성할 때 데이터를 더 보내고 싶을 때 사용
Intent putExtra(String name, String value)
Intent putExtra(String name, int value)
Intent putExtra(String name, boolean value)
String getString(String name)
int getIntExtra(String name, int defaultValue)
boolean getBooleanExtra(String name, boolean defaultValue)
Parcelable 인터페이스
- 자바의 Serializable과 유사하게 데이터를 직렬화하여 전송할 때 씀
public abstract int describeContents()
public abstract void writeToParcel(Parcel dest, int flags)
두 개의 메소드를 구현해야 함
1. SampleParcelable 프로젝트 생성
2. activity_main.xml에 버튼 하나 추가
3. app에 우측 마우스 클릭 후 New -> Activity -> Empty Activity 클릭 후 이름은 MenuActivity로 설정
4. app -> java -> com.example.sampleparcelable 폴더에 오른쪽 마우스 클릭 후 New -> Java Class 이름은 SimpleData
5. SimpleData.java 작성
package com.example.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) {
// Parcel 객체에서 읽기
number = src.readInt();
message = src.readString();
}
// SimpleData 생성자를 호출해 Parcel 객체에서 읽기
public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
public SimpleData createFromParcel(Parcel in) {
return new SimpleData(in); // CREATOR 상수 정의
}
public SimpleData[] newArray(int size) {
return new SimpleData[size];
}
};
public int describeContents() {
return 0;
}
// Parcel 객체로 쓰기
public void writeToeParcel(Parcel dest, int flags) {
dest.writeInt(number);
dest.writeString(message);
}
}
Comments