1. 안드로이드 4대 컴포넌트
1) Activity : 화면을 구성하는 객체
2) Service : 백그라운드에서 동작하는 객체
3) Content Provider : 앱들 간의 데이터를 주고 받기 위해 제공
4) Broadcast Receiver : 주소록에 등록되면 다른 앱들이 수신해서 필요한 정보인 경우 반영 할 수 있게 하는 것
2. 안드로이드 앱은 코드로 종료시키는 방법은 없다(즉, 액티비티를 끌 수는 있으나 종료 시킬 수 없다는 의미)
유일하게 앱을 종료시킬 수 있는 방법은 사용자가 직접 밀어서 종료시키는 방법 밖에 없다
3. 액티비티 생명 주기(Activity Life cycle)란?
: 액티비티의 상태 전환 과정
** 안드로이드 시스템은 앱 화면 (액티비티)를 관리하는 독특한 시스템 제공
** 액티비티가 생성 - 실행 - 중지 - 해제에 이르기까지 전 과정을 관리하며,
각각의 과정으로 전이될때마다 자동으로 호출되는 메소드를 제공
4. 액티비티 생명 주기(Activity Life cycle)가 필요한 이유?
: PC에 비해 낮은 성능 때문에
5. 액티비티의 상태(state)
1) 실행(Running)
: 화면상에 액티비티가 보여지며 실행중인 상태, 사용자가 입력을 받을 수 있는 상태(포커스를 갖고 있다고 함)
onResume() ~ onPause()
2) 일시정지(Paused)
: 화면상에 액티비티가 보여지나 사용자 입력을 받을 수 없는 상태(포커스를 받지 못한다고 함)
onStart() ~ onResume(), onPause() ~ onStop()
3) 중지(Stopped)
: 다른 액티비티에 완전히 가려져 보이지 않는 상태
onCreate() ~ onStart(), onStop() ~ onDestroy()
6. Killable state[onPause() ~ onDestroy()]는 언제든지 shut doun 될 수 있다.
(즉! 순서대로 진행되지 않을 수도 있다는 뜻...!!)
7. onCreate() : 액티비티가 처음 생성되었을때 호출,
onStart() : 화면에 보여지기 시작,
onResume() : 사용자 입력받기 시작,
onPause() : 다른 액티비티가 전면에 나옴,
onStop() : 화면에 안보임,
onDestroy() : 액티비티 소멸
[안드로이드 실습]
1. a006_widget2
1) Main4Activity 액티비티, activity_main4 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lec.android.a006_widget2">
<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=".Main4Activity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".Main3Activity" />
<activity android:name=".Main2Activity" />
<activity android:name=".MainActivity" />
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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=".Main4Activity"
android:orientation="vertical">
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="RatingBar"
android:textAppearance="@style/TextAppearance.AppCompat.Display1" />
<RatingBar
android:id="@+id/ratingBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:numStars="4"
android:rating="2.5"
android:stepSize="0.5"
android:isIndicator="false" />
<!-- isIndicator 가 false 이어야 사용자 조작 가능 (디폴트) -->
<!-- rating : 초기 값 -->
<TextView
android:id="@+id/tvResult"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="결과값"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
</LinearLayout>
package com.lec.android.a006_widget2;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.RatingBar;
import android.widget.TextView;
public class Main4Activity extends AppCompatActivity {
RatingBar rb;
TextView tvResult;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main4);
rb = findViewById(R.id.ratingBar);
tvResult = findViewById(R.id.tvResult);
// RatingBar 의 값이 변할 때 호출되는 콜백
rb.setOnRatingBarChangeListener(new RatingBar.OnRatingBarChangeListener() {
@Override
public void onRatingChanged(RatingBar ratingBar, float rating, boolean fromUser) {
tvResult.setText("rating : " + rating);
}
});
} // end onCreate()
} // and Activity
2. a007_activity 모듈
1) MainActivity 액티비티, activity_main 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lec.android.a007_activity">
<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>
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context=".MainActivity" >
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Activity LifeCycle"
android:textAppearance="@style/TextAppearance.AppCompat.Display2" />
<EditText
android:id="@+id/et1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="number"
android:text="100" />
<EditText
android:id="@+id/et2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="number"
android:text="33" />
<TextView
android:id="@+id/tvResult"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="결과창"
android:textAppearance="@style/TextAppearance.AppCompat.Display2" />
<Button
android:id="@+id/btnAction"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Button" />
</LinearLayout>
package com.lec.android.a007_activity;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
/**
* 액티비티 (Activity) :
* 안드로이드의 4대 컴포넌트중 하나인 '화면' UI 객체
* 앱에서 사용하는 액티비티는 반.드.시 메니페스트 에 등록되어야 함.
*
* 액티비티의 라이프 사이클
* onCreate() : 액티비티가 생성될때 호출, 사용자 인터페이스 초기화에 사용
* onStart() : 액티비티가 사용자에게 보여지기 바로 직전에 호출됨
* onResume() : 액티비티가 동작, 즉 사용자와 상호작용. (포커스를 갖기시작) 할때 호출
* onPause() : 다른 액티비티가 보여질때 (혹은 다른 액티비티에 의해 가려지기 시작할때) 호출됨
* onStop() : 액티비티가 더이상 안보여질때 호출되는 메소드.
* onDestory() : 액티비티 소멸될때 호출.
* onRestart() : onStop -> onStart 전환될때 호출됨.
*
* [공식] : CTRL+클릭 !
* https://developer.android.com/guide/components/activities/activity-lifecycle
* https://developer.android.com/reference/android/app/Activity
*
*
* 액티비티의 상태정보(state) 저장및 복원
* onRestoreInstanceState() : onStart 직후에 호출됨.
* onSaveInstanceState() : 액티비티 소멸전에 호출된다.
*
* ※ AVD/안드로이드폰 테스트시: '화면 회전' 옵션을 켜주세요.
*/
public class MainActivity extends AppCompatActivity {
EditText et1, et2;
TextView tvResult;
Button btnAction;
// 액티비티가 생성될때 호출
// 주로 사용자 인터페이스 초기화에 사용
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("myapp","onCreate");
et1 = findViewById(R.id.et1);
et2 = findViewById(R.id.et2);
tvResult = findViewById(R.id.tvResult);
btnAction = findViewById(R.id.btnAction);
btnAction.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int a = Integer.parseInt(et1.getText().toString().trim());
int b = Integer.parseInt(et2.getText().toString().trim());
tvResult.setText("" + (a + b));
}
});
} // end onCreate()
// 액티비티가 사용자에게 보여지기 시작할때(바로 직전)에 호출
@Override
protected void onStart() {
super.onStart();
Log.d("myapp", "onStart");
}
// 액티비티가 동작, 즉 사용자와의 상호작용(포커스를 갖기 시작)할때 호출
@Override
protected void onResume() {
super.onResume();
Log.d("myapp", "onResume");
}
// 다른 액티비티가 보여질때 (혹은 다른 액티비티에 의해 가려지기 시작할때) 호출
// 액티비티를 통해 다루고 있던 데이터 저장, 쓰레드 중지 ... 등의 처리를 해야 함.
@Override
protected void onPause() {
super.onPause();
Log.d("myapp", "onPause");
}
// 액티비티가 더이상 안보여질때 호출되는 메소드
// 메모리 상황에 따라 호출 안될 수도 있음
@Override
protected void onStop() {
super.onStop();
Log.d("myapp", "onStop");
}
// 액티비티 소멸될때 호출
// 액티비티 소멸은
// 시스템에 의해서 소멸되기도 하고
// 코드를 통해 제거되기도 함 : 예) finish()
@Override
protected void onDestroy() {
super.onDestroy();
Log.d("myapp", "onDestroy");
}
// onStop -> onStart 전환될때 호출
@Override
protected void onRestart() {
super.onRestart();
Log.d("myapp", "onRestart");
}
// 직전에 저장되어 있던 액티비티의 상태정보가 있다면,
// onRestoreInstanceState() 는 onStart 직전에 호출됨
@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
Log.d("myapp", "onRestoreInstanceState");
if(savedInstanceState != null) {
tvResult.setText(savedInstanceState.getString("value"));
}
}
// 액티비티 소멸 전에 호출된다. (주의! onPause 뒤에 호출된다고 간주하지 말것!)
// outState : <-- 액티비티 정보 저장(백업)하여 나중에 onCreate 에서 사용가능
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
Log.d("myapp", "onSaveInstanceState");
outState.putString("value", tvResult.getText().toString());
}
} // end Activity
** 앱 실행
** 앱 일시정지
** 앱 다시 실행
** 앱 중지
** 앱 다시 실행
** 회전하면서 결과 값을 잃었다..!!
** 아래의 코드를 추가하면 회전해도 값을 저장할 수 있다..!!
** 이젠 회전해도 값을 잃지 않는다..!!
2) Main2Activity, MyTwo 액티비티, activity_main2, activity_my_two 레이아웃, Persion 클래스
** manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lec.android.a007_activity">
<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=".MyTwo" />
<activity android:name=".Main2Activity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MainActivity" />
</application>
</manifest>
** activity_main2 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context=".Main2Activity">
<TextView
android:id="@+id/textView2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Intent(인텐트)"
android:textAppearance="@style/TextAppearance.AppCompat.Display3" />
<TextView
android:id="@+id/textView4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="이름과 나이 입력"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<EditText
android:id="@+id/etName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="이름을 입력하세요"
android:inputType="textPersonName"
android:text="슈퍼맨" />
<EditText
android:id="@+id/etAge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="나이를 입력하세요"
android:inputType="number"
android:text="10" />
<Button
android:id="@+id/btnStartTwo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="화면전환 startActivity()"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<!-- Button 은 기본적으로 대문자로 표시된다
textAllCaps 를 사용하면 이 기본 동작을 바꿀수 있다.-->
<Button
android:id="@+id/btnFinish"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAllCaps="false"
android:text="액티비티 종료 finish()" />
</LinearLayout>
** activity_my_two 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:background="#FFEB3B"
android:orientation="vertical"
tools:context=".MyTwo">
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="두번째 화면"
android:textAppearance="@style/TextAppearance.AppCompat.Display2" />
<Button
android:id="@+id/btnFinish"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="액티비티 종료 finish()"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<TextView
android:id="@+id/tv1"
android:layout_width="match_parent"
android:layout_height="75dp"
android:text="인텐트로 받은값"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<TextView
android:id="@+id/tv2"
android:layout_width="match_parent"
android:layout_height="97dp"
android:text="인텐트로 받은값"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<Button
android:id="@+id/btnToMain"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="메인액티비티 startActivity()"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
</LinearLayout>
** Main2Activity 액티비티
package com.lec.android.a007_activity;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
/**
* 화면(액티비티)전환 - 인텐트 사용 (인텐트 날린다?)
* 1. 다음에 넘어갈 액티비티 준비
* 2. 메니페스트에 액티비티 등록
* 3. Intent 객체 만들어서 startActivity() 한다
* - Intent 로 데이터 주고 받기 : putExtra() -> getXXXExtra()
* - 주고받은 Object 는 Serializable 되어 있어야 한다
*
* 안드로이드는 startActivity() 로 새 액티비티를 시작하면
* 적측형(stack) 구조로 액티비티가 운영된다.
*/
public class Main2Activity extends AppCompatActivity {
EditText etName;
EditText etAge;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
etName = findViewById(R.id.etName);
etAge = findViewById(R.id.etAge);
Button btnStartTwo = findViewById(R.id.btnStartTwo);
btnStartTwo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(
getApplicationContext(), // 현재 화면의 제어권자
MyTwo.class // 다음 화면의 액티비티 클래스 지정
);
// 데이터를 Intent 에 실어서 보내기
// name : 데이터 형태로 보냄
intent.putExtra("num", 3);
intent.putExtra("num2", 7);
intent.putExtra("long", 33L);
intent.putExtra("msg", "안녕하세요");
// 이름, 나이 --> Person 에 담은뒤 Intent 에 실어 보내기
Person p = new Person(
etName.getText().toString(),
Integer.parseInt(etAge.getText().toString())
);
intent.putExtra("Person", p);
startActivity(intent); // 다음 화면으로 넘어간다.
}
});
Button btnFinish = findViewById(R.id.btnFinish);
btnFinish.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish(); // 액티비티 종료
}
});
} // end onCreate()
}// and Activity
** MyTwo 액티비티
package com.lec.android.a007_activity;
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;
public class MyTwo extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_two);
Button btnFinish = findViewById(R.id.btnFinish);
btnFinish.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish(); // 액티비티 종료
}
});
// 넘겨받은 Intent 객체를 받는다
Intent intent = getIntent();
int a = intent.getIntExtra("num", 0); // "num" 이라는 name 으로 넘어온 값
// 만약 "num" 이라는 name 이 인텐트에 없었으면
// 디폴트값 (두번째 매개변수)를 리턴
int b = intent.getIntExtra("num2", 0);
int c = intent.getIntExtra("num3", 999); // "num3" 라는 이름은 없었다!
long l = intent.getLongExtra("long", 0);
String msg = intent.getStringExtra("msg"); // 리턴값이 Object 인 경우, 디폴트값 설정 없다.
// name 이 없으면 null 리턴
TextView tv1 = findViewById(R.id.tv1);
tv1.setText("인텐트 받은 값 : " + a + " : " + b + " : " + c + " : " + l + " : " + msg);
// Person 데이터 받기
Person p = (Person)intent.getSerializableExtra("Person");
TextView tv2 = findViewById(R.id.tv2);
tv2.setText("Person 받은 값 : " + p.getName() + " : " + p.getAge());
// 첫번째 액티비티로 인텐트를 날리면??
Button btnToMain = findViewById(R.id.btnToMain);
btnToMain.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), Main2Activity.class);
startActivity(intent);
}
});
} // end onCreate()
} // end activity
** Person 클래스
package com.lec.android.a007_activity;
import java.io.Serializable;
// Intent 에 담아 보내는 객체는 반.드.시 serializable 되어 있어야 한다.
public class Person implements Serializable {
String name;
int age;
// AndroidStudio 에서 Alt + Insert 로 자동 생성!
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;
}
}
3) Main3Activity, CalcActivity 액티비티, activity_main3 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lec.android.a007_activity">
<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=".CalcActivity"></activity>
<activity android:name=".Main3Activity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".MyTwo" />
<activity android:name=".Main2Activity" />
<activity android:name=".MainActivity" />
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context=".Main3Activity">
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Intent 값 돌려받기"
android:textAppearance="@style/TextAppearance.AppCompat.Display1" />
<EditText
android:id="@+id/et1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="number"
android:text="100" />
<EditText
android:id="@+id/et2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="number"
android:text="200" />
<Button
android:id="@+id/btnStart"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="startForResult()"
android:textAllCaps="false"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<TextView
android:id="@+id/tvResult"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="결과값"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
</LinearLayout>
** Main3Activity 액티비티
package com.lec.android.a007_activity;
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.EditText;
import android.widget.TextView;
public class Main3Activity extends AppCompatActivity {
EditText et1, et2;
Button btnStart;
TextView tvResult;
private final int REQUEST_CODE_CALC = 101;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
et1 = findViewById(R.id.et1);
et2 = findViewById(R.id.et2);
btnStart = findViewById(R.id.btnStart);
tvResult = findViewById(R.id.tvResult);
btnStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int a = Integer.parseInt(et1.getText().toString());
int b = Integer.parseInt(et2.getText().toString());
Intent intent = new Intent(getApplicationContext(), CalcActivity.class);
intent.putExtra("num1", a);
intent.putExtra("num2", b);
// 일반적인 화면전환
//startActivity(intent);
// 값을 돌려받기 위한 화면전환
startActivityForResult(intent, REQUEST_CODE_CALC);
}
});
} // end onCreate()
// startActivityForResult()로 넘어갔던 화면으로부터 결과값을 돌려받으면 호출되는 메소드
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == RESULT_OK) { // 정상 반환인 경우
switch (requestCode) {
case REQUEST_CODE_CALC:
int a = data.getIntExtra("plus", 0);
int b = data.getIntExtra("minus", 0);
tvResult.setText(requestCode + "] 받아온 값: " + a + " : " + b);
break;
}
} else {
// 정상 결과가 아닌 경우 처리
}
}
} // and Activity
** CalcActivity 액티비티
package com.lec.android.a007_activity;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
// 화면이 없는 액티비티 생성 가능
public class CalcActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
int num1 = intent.getIntExtra("num1", 0);
int num2 = intent.getIntExtra("num2", 0);
intent.putExtra("plus", num1 + num2);
intent.putExtra("minus", num1 - num2);
// 호출한 화면에 값 되돌려 주기
setResult(RESULT_OK, intent);
finish(); // onDestroy() 와 동일
} // end onCreate()
} // end Activity
3. a008_recycler 모듈
1) MainActivity, PhonebookDetail 액티비티, activity_main, item, activity_phonebook_detail 레이아웃,
Phonebook, PhonebookAdapter, D 클래스
** 필요한 사진 다운 받기 : res > drawable 폴더에 Ctrl + V
** RecyclerView는 Add Project Dependency를 해야 사용할 수 있음
** Dependency가 필요한 경우 그림과 같이 화살표 모양을 볼 수 있음
: 화살표 모양 클릭 > Project Dependency를 추가할 것인지 물어보는 팝업창 뜸 > OK 클릭 > 다운로드 완..!!
[중요] Project Dependency가 정상적으로 추가되면 아래의 gradle 파일에서
implementation 'androidx.recyclerview:recyclerview:1.0.0' 가 추가됨
** CardView도 Add Project Dependency를 해야 사용할 수 있음
** Dependency가 필요한 경우 그림과 같이 화살표 모양을 볼 수 있음
: 화살표 모양 클릭 > Project Dependency를 추가할 것인지 물어보는 팝업창 뜸 > OK 클릭 > 다운로드 완..!!
[중요] Project Dependency가 정상적으로 추가되면 아래의 gradle 파일에서
implementation 'androidx.cardview:cardview:1.0.0' 가 추가됨
** manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lec.android.a008_recycler">
<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=".PhonebookDetail"></activity>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
** activity_main 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context=".MainActivity" >
<TextView
android:id="@+id/tvTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Phonebook"
android:textAppearance="@style/TextAppearance.AppCompat.Display1" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btnInsert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="INSERT" />
<Button
android:id="@+id/btnAppend"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="APPEND" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
** item 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="wrap_content"
android:orientation="vertical">
<!--
CardView 의 atrrribute 속성은 위의 app 네임스페이스를 사용하게 됩니다.
cardCornerRadius : 코너 둥글게 하기
cardElevation : 바닥에서 떨어져 보이게 하기
cardUseCompatPadding : CardView 내용에 따라 그림자 보여줄지 결정. API21 이상에서도 호환되게 padding 사용
-->
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="183dp"
app:cardBackgroundColor="#FFFFFFFF"
app:cardCornerRadius="10dp"
app:cardElevation="5dp"
app:cardUseCompatPadding="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/ivPhoto"
android:layout_width="103dp"
android:layout_height="105dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@mipmap/ic_launcher" />
<TextView
android:id="@+id/tvName"
android:layout_width="184dp"
android:layout_height="37dp"
android:layout_marginStart="20dp"
android:layout_marginTop="16dp"
android:text="name"
android:textSize="30sp"
app:layout_constraintStart_toEndOf="@+id/ivPhoto"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tvPhone"
android:layout_width="214dp"
android:layout_height="24dp"
android:layout_marginStart="20dp"
android:layout_marginTop="12dp"
android:text="phone"
android:textSize="18sp"
app:layout_constraintStart_toEndOf="@+id/ivPhoto"
app:layout_constraintTop_toBottomOf="@+id/tvName" />
<TextView
android:id="@+id/tvEmail"
android:layout_width="214dp"
android:layout_height="17dp"
android:layout_marginStart="20dp"
android:layout_marginTop="12dp"
android:text="email"
app:layout_constraintStart_toEndOf="@+id/ivPhoto"
app:layout_constraintTop_toBottomOf="@+id/tvPhone" />
<Switch
android:id="@+id/swOnOff"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="12dp"
android:text="On/Off"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<ImageButton
android:id="@+id/btnDelItem"
android:layout_width="37dp"
android:layout_height="39dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="12dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@android:drawable/ic_delete" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
** activity_phonebook_detail 레이아웃
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context=".PhonebookDetail">
<ImageView
android:id="@+id/ivPhoto"
android:layout_width="match_parent"
android:layout_height="125dp"
app:srcCompat="@android:drawable/btn_dialog" />
<TextView
android:id="@+id/tvName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:textAppearance="@style/TextAppearance.AppCompat.Display1" />
<TextView
android:id="@+id/tvPhone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<TextView
android:id="@+id/tvEmail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:textAppearance="@style/TextAppearance.AppCompat.Large" />
<Button
android:id="@+id/btnBack"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="돌아가기" />
</LinearLayout>
** MainActivity 액티비티
package com.lec.android.a008_recycler;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
PhonebookAdapter adapter; // Adapter 객체
RecyclerView rv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rv = findViewById(R.id.rv);
// RecyclerView 를 사용하기 위해서는 LayoutManager 지정해주어야 한다.
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
//RecyclerView.LayoutManager layoutManager = new GridLayoutManager(this, 2);
rv.setLayoutManager(layoutManager);
// Adapter객체 생성
adapter = new PhonebookAdapter();
initAdapter(adapter);
rv.setAdapter(adapter); // RecyclerView 에 Adapter 장착!
Button btnInsert = findViewById(R.id.btnInsert);
Button btnAppend = findViewById(R.id.btnAppend);
btnInsert.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
insertData(v);
}
});
btnAppend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
appendData(v);
}
});
} // end onCreate()
// 샘플데이터 가져오기
protected void initAdapter(PhonebookAdapter adapter){
// 몇개만 생성
for(int i = 0; i < 10; i++){
int idx = D.next();
adapter.addItem(new Phonebook(D.FACEID[idx], D.NAME[idx], D.PHONE[idx], D.EMAIL[idx]));
}
}
protected void insertData(View v){
int idx = D.next();
// 리스트 맨 앞에 추가
adapter.addItem(0, new Phonebook(D.FACEID[idx], D.NAME[idx], D.PHONE[idx], D.EMAIL[idx]));
adapter.notifyDataSetChanged(); // 데이터변경을 Adapter 에 알리고, 리스트뷰에 반영 .
}
protected void appendData(View v){
int idx = D.next();
// 리스트 맨 뒤에 추가
adapter.addItem(new Phonebook(D.FACEID[idx], D.NAME[idx], D.PHONE[idx], D.EMAIL[idx]));
adapter.notifyDataSetChanged(); // 데이터변경을 Adapter 에 알리고, 리스트뷰에 반영 .
}
} // end Activity
[추가] LinearLayoutManager.HORIZONTAL로 설정
[추가] LinearLayoutManager.VERTICAL로 설정
[추가] GridLayoutManager로 설정
** PhonebookDetail 액티비티
package com.lec.android.a008_recycler;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
public class PhonebookDetail extends AppCompatActivity {
ImageView ivPhoto;
TextView tvName, tvPhone, tvEmail;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_phonebook_detail);
ivPhoto = findViewById(R.id.ivPhoto);
tvName = findViewById(R.id.tvName);
tvEmail = findViewById(R.id.tvEmail);
tvPhone = findViewById(R.id.tvPhone);
Intent intent = getIntent();
Phonebook pb = (Phonebook)intent.getSerializableExtra("pb");
ivPhoto.setImageResource(pb.getPhoto());
tvName.setText(pb.getName());
tvEmail.setText(pb.getEmail());
tvPhone.setText(pb.getPhone());
Button btnBack = findViewById(R.id.btnBack);
btnBack.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 리스트로 돌아가기
finish();
}
});
} // end onCreate()
} // end Activity
** Phonebook 클래스
package com.lec.android.a008_recycler;
import java.io.Serializable;
// 전화번호부 데이터를 담을 클래스
public class Phonebook implements Serializable {
int photo; // 사진
String name; // 이름
String phone; // 전화번호
String email; // 이메일
public Phonebook() {
}
public Phonebook(int photo, String name, String phone, String email) {
this.photo = photo;
this.name = name;
this.phone = phone;
this.email = email;
}
public int getPhoto() {
return photo;
}
public void setPhoto(int photo) {
this.photo = photo;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
} // end Phonebook
** PhonebookAdapter 클래스
package com.lec.android.a008_recycler;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CompoundButton;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
// Adapter 객체 정의
// 데이터(Phonebook) 을 받아서, 각 item 별로 View 를 생성
public class PhonebookAdapter extends RecyclerView.Adapter<PhonebookAdapter.ViewHolder> {
// Adapter는 리스트에서 다룰 데이터가 필요하다
// Adapter가 데이터에 연결되어야 하는 것은 사실이나, 데이터를 Adapter를 직접 다룰지
// 아니면 별도의 데이터 관리는 따로 하는 구조로 만들지는 선택의 몫
// 본 예제에서는 Adapter 안에 직접 데이터를 다루어보겠습니다
List<Phonebook> items = new ArrayList<Phonebook>();
static PhonebookAdapter adapter;
// Adapter 생성자
public PhonebookAdapter() {this.adapter = this;}
// onCreateViewHolder() : ViewHolder 가 생성될때 호출됨
// 각 item 을 위해 정의한 레이아웃(ex:XML) 으로 View 객체를 만들어 줍니다.
// 이들 View객체를 새로 만들 ViewHolder 에 담아 리턴.
//
// 'View 타입' 을 위한 정수값이 매개변수로 넘겨진 --> viewType
// 이를 통해 아이템별로 View를 다양하게 표현 가능. (ListView 에는 없던 개선점)
// 예를들면, 각각의 'View 타입' 별로 따로따로 XML레이아웃을 달리 하여 보여줄수 있는 겁니다.
// * 그러나, 일반적으로는 한가지만 운용함.*
//
// 매개변수로 전달된 ViewGroup 객체는 각 아이템을 위한 객체
// 이로부터 Context 객체를 뽑아내어 Layout inflation 을 해야 한다.
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// 주어진 ViewGroup 으로부터 LayoutInflater 추출
LayoutInflater inf = LayoutInflater.from(parent.getContext());
// 준비된 레이아웃(XML) 으로부터 View 를 만들어 ViewGroup 에 붙이고
// 그렇게 만들어진 View 를 리턴한다
View itemView = inf.inflate(R.layout.item, parent, false);
// 위에서 마들어진 새로운 View 를 ViewHolder 에 담아 리턴
return new ViewHolder(itemView);
}
// onBindViewHolder() : ViewHolder 가 '재사용' 될때 호출됨
// View 객체는 그대로 기존것을 사용 (이것이 재사용!) 하고 데이터만 바꾸어 주면 됨.
// 이전에 이미 만들어진. 재활용할수 있는 ViewHolder 객체 와
// 리스트 상에 몇번째 데이터인지에 대한 정보 (position) 가 넘어온다
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Phonebook item = items.get(position); // List<> 의 get()
holder.setItem(item);
}
// getItemCount() : 어댑터에서 다루는 현시점 아이템(데이터)의 개수
// Selection Widget 에서 수시로 getItemCount() 를 호출하여 뷰를 업데이트 한다
@Override
public int getItemCount() {
return items.size(); // List<> 의 size()
}
// nested class (static inner) 로 ViewHolder 클래스 정의
static class ViewHolder extends RecyclerView.ViewHolder{
// ViewHolder 에 담긴 각각의 View 들을 담을 변수
ImageView ivPhoto;
TextView tvName, tvPhone, tvEmail;
ImageButton btnDelItem;
Switch swOnOff;
// 생성자 필수
public ViewHolder(@NonNull View itemView) { // item 레이아웃의 View 객체가 전달됨.
super(itemView);
// View 객체 가져오기
ivPhoto = itemView.findViewById(R.id.ivPhoto);
tvName = itemView.findViewById(R.id.tvName);
tvPhone = itemView.findViewById(R.id.tvPhone);
tvEmail = itemView.findViewById(R.id.tvEmail);
btnDelItem = itemView.findViewById(R.id.btnDelItem);
swOnOff = itemView.findViewById(R.id.swOnOff);
// switch 누르면 전화번호, 이메일 숨기기/보이기
swOnOff.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if(isChecked){
tvPhone.setVisibility(View.INVISIBLE);
tvEmail.setVisibility(View.INVISIBLE);
} else {
tvPhone.setVisibility(View.VISIBLE);
tvEmail.setVisibility(View.VISIBLE);
}
}
});
// 삭제버튼 누르면 item 삭제되게 하기
btnDelItem.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// position 정보 필요하다?
// adapter 로 부터 데이터 삭제도 진행되어야 한다.
adapter.removeItem(getAdapterPosition()); // 데이터 삭제
// 데이터 변경 (수정, 삭제, 추가) 내역이 adapter 에 반영되어야 정상적으로 동작함!!! ★★★
adapter.notifyDataSetChanged();
}
});
// 클릭리스너 장착
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = getAdapterPosition(); // 이 리스너가 장착된 item 의 리스트상의 position 값
//Toast.makeText(v.getContext(), "position:" + position, Toast.LENGTH_LONG).show();
// 아이템을 클릭하면 해당 세부 정보 액티비티로 넘겨주기
Intent intent = new Intent(v.getContext(), PhonebookDetail.class);
intent.putExtra("pb", adapter.getItem(position));
v.getContext().startActivity(intent);
}
});
} // end 생성자
// Phonebook 데이터를 받아서 멤버변수 세팅
public void setItem(Phonebook item){
ivPhoto.setImageResource(item.getPhoto());
tvName.setText(item.getName());
tvPhone.setText(item.getPhone());
tvEmail.setText(item.getEmail());
}
} // end ViewHolder
// 데이터를 다루기 위한 메소드들
// ArrayList 의 메소드들 사용
public void addItem(Phonebook item) { items.add(item); }
public void addItem(int position, Phonebook item) { items.add(position, item);}
public void setItems(ArrayList<Phonebook> items) { this.items = items;}
public Phonebook getItem(int position) { return items.get(position);}
public void setItem(int position, Phonebook item) { items.set(position, item); }
public void removeItem(int position){ items.remove(position); }
} // end Adapter
** D 클래스
package com.lec.android.a008_recycler;
// 샘플 데이터
public class D {
public static int [] FACEID = {
R.drawable.face01,
R.drawable.face02,
R.drawable.face03,
R.drawable.face04,
R.drawable.face05,
R.drawable.face06,
R.drawable.face07,
R.drawable.face08,
R.drawable.face09,
R.drawable.face10,
R.drawable.face11,
R.drawable.face12,
R.drawable.face13,
R.drawable.face14,
R.drawable.face15,
R.drawable.face16,
R.drawable.face17,
R.drawable.face18,
R.drawable.face19,
R.drawable.face20,
R.drawable.face21
};
public static final String [] NAME = {
"아이언맨", "캡틴아메리카", "헐크", "블랙위도우", "팔콘", "울트론",
"로키", "토르", "그루트", "스타로드", "비젼", "앤트맨", "윈터솔져",
"로난", "토끼", "스파이더맨", "호크아이", "워머신", "가모라", "베놈",
"디스트로이어"
};
public static final String [] PHONE = {
"001-1111-1111",
"002-1111-1111",
"003-1111-1111",
"004-1111-1111",
"005-1111-1111",
"006-1111-1111",
"007-1111-1111",
"008-1111-1111",
"009-1111-1111",
"010-1111-1111",
"011-1111-1111",
"012-1111-1111",
"013-1111-1111",
"014-1111-1111",
"015-1111-1111",
"016-1111-1111",
"017-1111-1111",
"018-1111-1111",
"019-1111-1111",
"020-1111-1111",
"021-1111-1111"
};
public static final String [] EMAIL = {
"001@mail.com",
"002@mail.com",
"003@mail.com",
"004@mail.com",
"005@mail.com",
"006@mail.com",
"007@mail.com",
"008@mail.com",
"009@mail.com",
"010@mail.com",
"011@mail.com",
"012@mail.com",
"013@mail.com",
"014@mail.com",
"015@mail.com",
"016@mail.com",
"017@mail.com",
"018@mail.com",
"019@mail.com",
"020@mail.com",
"021@mail.com"
};
private static int idx = 0;
public static int next(){
idx = idx % FACEID.length;
return idx++; // idx 값 리턴하고 1증가
}
} // end D
'웹_프론트_백엔드 > JAVA프레임윅기반_풀스택' 카테고리의 다른 글
2020.04.21 (0) | 2020.04.21 |
---|---|
2020.04.20 (0) | 2020.04.20 |
2020.04.16 (0) | 2020.04.16 |
2020.04.14 (0) | 2020.04.14 |
2020.04.13 (0) | 2020.04.13 |