Le_Penseur
[안드로이드 스튜디오] Ch12. 다이얼로그 & 인텐트 (Dialog & Intent) 본문
[안드로이드 스튜디오] Ch12. 다이얼로그 & 인텐트 (Dialog & Intent)
LePenseur 2019. 8. 30. 13:032019년 8월30일 (금)
창업의 길로 가기 위한 두번째 걸음, [안드로이드 스튜디오]로 향해보자!
*질적으로 많이 부족한 글입니다. 지적 및 조언 환영합니다.
*이 글에선 "안드로이드 스튜디오"를 줄여서 "안스" 라고 표현합니다. 참고 바랍니다.
우선, 본인은 학원에서 'JAVA' 기초 수업을 선수강했다. 안드로이드 스튜디오를 배우기 위해선 피할 수 없는 언어이기 때문이다.
(그런데 Ch11까지 배운 현재, JAVA에 대한 공부가 더 필요하다고 느껴진다...)
잡담: 블로그 포스팅을 다 맞추면 늘 오후 1시가 넘어가는것같다. 이제 밥먹으러 가야지!
이번글에서는 바로 이전에서 배운 다이얼로그(dialog)에 대해 좀더 다양하게 알아보며,
어플제작에 있어서 '핵심' 중 하나라고 할 수 있는 인텐트(intent)가 무엇인지, 또 그 로직은 무엇인지에 대해 이해하는시간을 갖도록한다.
<차례>
1. MyAlertDialog
2. MyImageDialog
3. MyChangeActivity
1. MyAlertDialog
1.1 결과
설명: 이번에 만들어볼 알림창(Dialog)는 안드로이드의 '뒤로가기' 버튼을 눌렀을때 그냥 뒤로가지는게 아니라, 사진1처럼 알림창을 띄워주어 재차 확인하는 Dialog를 만들어본다. activity_main에는 건들 디자인이 따로 없다. 그말은 멤버변수화 시킬 클래스도 없다는것.
왜냐? '뒤로가기'버튼과 '은 이미 안스에 내장 되어있기 때문이다.
(ps. 개인적으로는 이 AlertDialog가 이전에 배운 Dialog보다 훨씬 효율적이고 편리하다고 느꼈다. 그 이유는 이전의 Dialog같은 경우 모델생성, 스타일 생성(물론 옵션이지만), 인플레이터 등 거쳐야할 과정이 많은데,
반면, 이 AlertDialog같은 경우 코드와 로직만 숙지하고 있다면 제한된 프레임안에서 다양한 기능을 넣어줄 수 있고 무엇보다 여러 파일을 이용하지 않아도 되기 때문이다.)
한 java에서 모두 해결하기에 코드가 조금은 어려울수 있다.
집중하자!
**activity_main 코드는 일절 사용하지 않았으므로 첨부하지않았다.
1.2 코드
1.2.1 MainActivity.java
package com.example.myalertdialog;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.KeyEvent;
//목표: 뒤로가기 버튼을 누르면 다이얼로그가 호출되도록 할것임.
//XML파일을 건들필요는 없음.
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
//키가 눌러졌을 때 실행되는 메소드 (keyCode: 키값, KeyEvent: 이벤트)
// onKeyDown이라는 메소드를 재정의한것이다. (준비동작 정도)
// keyCode와 keyEvent 를 처리할 논리형 변수다. ('눌렀을때', '안눌렀을때' 이렇게 두가지니까 당연하다)
//조건문을 이용한 "키가 눌러 졌을때 x를 할것"의 준비 동작이다. (이런 로직은 외우자)
if(event.getAction() == KeyEvent.ACTION_DOWN){
//또다른 조건문을 이용한 "뒤로가기 버튼이 눌러 졌을때~"의 로직이다.
//KEYCODE_BACK은 키값이 "뒤로가기"로 되어있다. 명심하자.
if(keyCode == KeyEvent.KEYCODE_BACK){
//alertDialog 객체를 생성하기에 앞서,
//alertDialog 구성을 먼저 해야하기에 -> alertDialog.Builder을 사용한다.
final AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);
//AlertDialog의 버튼 종류 3가지:
//(1) PositiveButton - 확인
//(2) NegativeButton - 취소
//(3) NeutralButton - 중립 -> 중립이라는 기능은 프로그래머가 어떤 기능을 추가하느냐에 따라 달라질수있다.
//버튼은 각각 재정의하는것을 권장한다.
//두개이상 중복되면 마지막으로 등록한 버튼으로 갱신된다.
dialog.setPositiveButton("ok", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//어플리케이션 종료
//엑티비티를 숨기고(hide),멈추고(stop),소멸(destroy)
//을 하지만 프로세스 자체는 살아있다.
//어플리케이션 실행 중에 액티비티
finish();
//프로세스를 종료하고 모든 리소스를 반환한다.
//System.exit(0);
//하지만 어플에서는 사용안함. 왜냐하면 어플에서는 프로세스 종료개념 아니고
//멈추고 숨기는 개념이기 때문에.
//현재 System.exit()로 앱이 종료 될떄 프로세스들이 완전히
//종료하지 못한다는 의견이 분분하다.
//그래서
android.os.Process.killProcess(android.os.Process.myPid());
//를 사용하는 사람들도 있다.
}
});
dialog.setNegativeButton("cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
//'취소' 기능을 넣어주자.
//dialog.dismiss(); -> 없어도 되는 로직이다. 그냥 위에 입력한 Negative 재정의 만으로도 Cancel 기능이 추가된다.
}
});
dialog.setNeutralButton("now", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
}
});
dialog.setTitle("Exit?");
dialog.show();
}
}
return super.onKeyDown(keyCode, event);
}
}
설명: 위 로직에서의 큰그림을 우선적으로 살펴보자.
순서는 이러하다:
(0) public boolean onKeyDown(int keyCode, KeyEvent event) 이 로직을 가슴속에 넣어두고 밑의 절차를 살피자.
(1) '버튼'이 아닌 '키'가 눌러졌을때 (ACTION_DOWN)의 '동작'에 대한 객체 생성 (동작 = event.Action // 키값 = keyCode)
(2) 그 '동작'(ACTION_DOWN)객체안에 '뒤로가기'라는 키값을 입력
(3) 이제부터는 '키가 눌러졌을때(ACTION_DOWN)' 에서 '뒤로가기가 눌러졌을때(KEYCODE_BACK, ACTION_DOWN)'로 인식!
(4) 잠깐! (KEYCODE_BACK, ACTION_DOWN) 이거 어디서 많이 보지않았는가? 맞다. 위 (int keyCode, KeyEvent event) 와 동일하다.
.
(5) 다음으로는 final AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this); 로직을 통해 알림창을 생성한다.
(6) 생성된 alertDialog 안에서 각각의 버튼만을 생성해주면 끝이다.
2. MyImageDialog
2.1 결과
설명: 이번 알림창(Dialog)은 본인이 직접 디자인하는 알림창이다. 그래서 imageDialog라고 불린다.
이러한 그림 파일로 무언가를 만들기 위해선 역시 xml파일을 만들어줘야한다.
2.2 코드
2.2.1 activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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_margin="40dp"
android:text="Show Dialog"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
설명: 우선 알림창을 어떤 '동작'을 취했을 띄울것이느냐가 관건이기에, 초기화면에는 알림창을 띄울수있는 버튼을 하나 만들자.
2.2.2 dialog.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">
<FrameLayout
android:id="@+id/frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone">
</FrameLayout>
</LinearLayout>
설명: 다음으로는 다이얼로그의 '프레임'을 만들어 주기 위해 layout폴더에서 xml파일을 생성한다.
(ps. framelayout으로 다이얼로그의 프레임을 만들어준다라... 이거 지금 처음 배운느낌인데..
하지만! 상식적으로 생각해봤을때, 이 안에 들어갈 (버튼과 같은) 구성요소들을 인플레이터 해주면 당연히 그 구성요소들은 이 프레임안에서 다시 재구성이 되므로 framelayout 사용에 일리는 있다.)
2.2.3 inner_dialog.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="@drawable/exit_menu"
android:gravity="center"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="EXIT???"
android:textSize="30dp"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/exit_cancel"/>
<Button
android:id="@+id/btn_ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/exit_ok"/>
</LinearLayout>
</LinearLayout>
설명: 방금 위에서 만든 framelayout안에 들어갈 다이얼로그의 내부 모습을 jpg파일들을 통해 구성한다.
2.2.4 MainActivity.java
package com.example.myimagedialog;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.text.Layout;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.FrameLayout;
public class MainActivity extends AppCompatActivity {
private FrameLayout frameLayout;
private View view;
private Button btn_cancel,btn_ok,button;
private Dialog dialog;
@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) {
dialog = new Dialog(MainActivity.this);
//dialog 인플레이션을 진행한다.
dialog.setContentView(R.layout.dialog);
frameLayout = dialog.findViewById(R.id.frame);
frameLayout.setVisibility(FrameLayout.VISIBLE);
//inner_dialog.xml 인플레이션
LayoutInflater layoutInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = layoutInflater.inflate(R.layout.inner_dialog, frameLayout);
//버튼 객체화
btn_cancel = view.findViewById(R.id.btn_cancel);
btn_ok = view.findViewById(R.id.btn_ok);
//리스너 설정
btn_ok.setOnClickListener(click);
btn_cancel.setOnClickListener(click);
//타이틀 설정
dialog.setTitle("My Dialog");
dialog.show();
}
});
}
private View.OnClickListener click = new View.OnClickListener() {
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_cancel:
frameLayout.setVisibility(FrameLayout.GONE);
dialog.dismiss();
break;
case R.id.btn_ok:
finish();
break;
}
}
};
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(event.getAction() == KeyEvent.ACTION_DOWN){
if(keyCode == KeyEvent.KEYCODE_BACK){
frameLayout.setVisibility(FrameLayout.VISIBLE);
dialog.show();
}
}
return true;
}
}
설명: 로직 자체는 앞에서 말한것과 동일하므로 유심히 살펴보길 바란다.
위 코드에서 가장 중요한점은 뭐니뭐니 해도 인플레이터 파트다.
밑의 코드는 위에서 가져온 필자가 중요하다고 생각한 부분이다.
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
dialog = new Dialog(MainActivity.this);
//dialog 인플레이션을 진행한다.
dialog.setContentView(R.layout.dialog);
frameLayout = dialog.findViewById(R.id.frame);
frameLayout.setVisibility(FrameLayout.VISIBLE);
//inner_dialog.xml 인플레이션
LayoutInflater layoutInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = layoutInflater.inflate(R.layout.inner_dialog, frameLayout);
설명: 다이얼로그 객체화를 진행후에 framelayout을 기존에 만들어둔 dialog.xml을 인플레이터 해주고, view를 위한 구성요소로는 inner_dialog를 인플레이터 해준것을 볼수있다.
(파일이름을 dialog.xml로 해서 잠시 해맸다. 이름은 좀 특성에 맞게 만들어야겠다.)
1. MyChangeActivity
1.1 결과
설명: 이번 프로젝트는 매우 설레는 (또 가장 기다리던!) 화면 전환 프로젝트다. 화면구성 자체는 심플하다.
java의 로직에 더 신경써야 하는 프로젝트고, 집중해야할 키포인트가 3가지 있다.
- 키포인트 3가지 -
- Intent
- manifests
- startActivity
Q. Intent는 무엇인가?
- 안드로이드 화면 간의 이동을 위해 사용하는 객체. Bundle 과 비슷하여 화면을 이동하면서 데이터도 가져갈수있고 모든 설정도 취할수있다.
Q. manifests는 무엇인가?
- 어플리케이션 제작의 '총책임자', '총괄자' 정도 되겠다. 이 파일에서 허가를 받지 못하면 activity 구현이 불가능하니 꼭! 염두해두자.
어려운건 절대아니고, 자세한 설명은 밑의 manifests 코드를 참고하자.
Q. startActivity는 무엇인가?
- 위 Intent의 메소드중 하나인데, 이렇게 독립적으로 빼둔이유는 ...... 잘 까먹어서 그렇다. 로직 다 잘~ 써놓고 이 startActivity(intent);를 까먹어서 당황할수있기때문이다. (*늘 마무리가 중요하다)
1.2 코드
1.2.1 activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_end="206dp" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="365dp" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:text="Next"
android:textSize="25dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/guideline"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="Link"
android:textSize="25dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/guideline"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
1.2.2 activity_sub.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">
<Button
android:id="@+id/button3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Previous"
android:textSize="30dp"
/>
</LinearLayout>
1.2.3 SubActivity.java
package com.example.mychangeactivity;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class SubActivity extends Activity {
private Button button3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sub);
button3 = (Button)findViewById(R.id.button3);
//intent? 안드로이드 화면 간의 이동을 위해 사용하는 객체.
//Bundle 과 비슷하게 화면을 이동하면서 데이터도 가져갈수있고 모든 설정도 취할수있다.
button3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//intent 객체 생성 로직
Intent intent = new Intent(getApplicationContext(),MainActivity.class);
//intent 이동 로직.
startActivity(intent);
//"현재 Activity 가 모두 끝이 났다."라는 로직.
finish();
}
});
}
}
1.2.4 MainActivity.java
package com.example.mychangeactivity;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import java.net.URI;
public class MainActivity extends AppCompatActivity {
private Button button,button2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button)findViewById(R.id.button);
button2 = (Button)findViewById(R.id.button2);
//리스너 설정
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//버튼이 눌러졌을때 activity 를 이동시키고자 한다.
//그럼 Intent 객체를 생성해야 한다. 준비하는것이다.
Intent intent = new Intent();
intent.setClass(getApplicationContext(),SubActivity.class);
//이동을 시켜보자!
startActivity(intent);
// 여기까지 작성해서 실행한 결과 오류가 나온다. 왜그럴까?
// 그 이유는 manifests 파일 안에 클래스 등록을 하지 않았기 때문에 Activity 가 인정되지 않았기 때문이다.
}
});
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//웹화면으로 이동하기 위한 준비로, intent 객체를 우선 생성할것이다.
//그리고 처리할 녀석은 바로 [Action_View: 사용자에게 데이터를 보여준다.]
Intent intent = new Intent(Intent.ACTION_VIEW);
//.setData(): 사용자에게 어떤 데이터를 보여줄지 설정하라
//uri: 어떠한 데이터의 정보나 위치를 가지고있는 데이터
//Uri: u가 대문자임을 주의하라. 해당 주소로 표현되는 데이터를 가지고있는 클래스를 의미한다.
//parse: 직역하면 "분석하다" 이지만 프로그래밍에서는 "변환하다" 라는 의미로 쓰인다.
//링크입력시 "https://" 를 반드시 적어주자.
intent.setData(Uri.parse("https://www.naver.com"));
//<필수>실행로직
startActivity(intent);
}
});
}
}
//.........하지만 이런식의 이전과 다음의 연속되는 로직은 그 데이터가 계속해서 쌓이기때문에 위험할수있다.
설명:
(1) 전체정리 - button과 button2를 각각 재정의하면서 그안에서 기능을 추가시켜주는것이다.
(2) 재정의 파트 - 그냥 뷰로 이동할것인지, 아니면 URI를 통한 인터넷 웹뷰로 이동한것인지에 따라 intent를 객체화하는 과정에서 처리해줄 요소가 다르다.
예를 들어 그냥 button(NEXT)의 경우 '다음페이지'와 '이전페이지'의 연속이므로 그 안에서는 마땅히 처리할 데이터가 없다.
그래서 intent의 객체 안에는 빈공간이다.
반면, 웹뷰로 이동시켜야하는 button2(LINK)의 경우에는 보여줄 '데이터'가 존재하므로, 괄호안에 ACTION_VIEW를 입력한다.
(3) 기능 파트 - 빈 다음 뷰로 이동시킬 버튼의 경우에는 setClass를, 링크로 이동시킬 버튼2는 setData 메소드를 이용한 것을 볼수있는데, 사실 이뿐만이 아니라 다양한 메소드가 존재한다. 자세한 설명과 타입은 밑의 안드로이드 가이드를 통해 참고하길바란다.
https://developer.android.com/guide/components/intents-filters?hl=ko
인텐트 및 인텐트 필터 | Android Developers
An Intent is a messaging object you can use to request an action from another app component . Although intents facilitate communication between components in several ways, there are three fundamental use cases: An Activity represents a single screen in…
developer.android.com
**마지막의 주석을 잘 보자. "(아무런 장치없이) 이전과 다음의 연속되는 로직은 그 데이터가 쌓여 위험할수있다." 라는 말을 유심히 기억하고 다음 포스팅을 참고하길 바란다. 매우 중요한 내용이기 때문이다.
1.2.5 AndroidManifests.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.mychangeactivity">
<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 등록!-->
<activity android:name=".SubActivity"/>
</application>
</manifest>
설명: <!--Activity 등록! --> 이 부분을 잘 보길바란다. 위 로직까지는 원래 존재하던, 즉 MainActivity의 허가 코드 이고, 밑의 <!--Activity 등록! --> 이 바로 새롭게 만든 Activity의 허가 부분이다.
'개인(Personal) > 안드로이드 (Andorid Studio)' 카테고리의 다른 글
[안드로이드 스튜디오] Ch14. 스탑워치 (StopWatch) (0) | 2019.09.03 |
---|---|
[안드로이드 스튜디오] Ch13. 플래그 & 핸들러 (Flag & Handler) (0) | 2019.09.01 |
[안드로이드 스튜디오] Ch11. 다이얼로그(Dialog) (1) | 2019.08.29 |
[안드로이드 스튜디오] Ch10. 버튼 이펙트 & 핵심 수신기(ButtonEffect & KeyListener) (0) | 2019.08.28 |
[안드로이드 스튜디오] Ch9. 버튼 클릭 이벤트 처리 & 인플레이터(OnClickListener & Inflater) (0) | 2019.08.27 |