2020. 5. 24. 01:22ㆍAndroid
Inflation이란?
인플레이션(inflation)이란 xml 레이아웃의 내용이 메모리에 객체화 되는 과정입니다.
이렇게 말하면 어렵게 들릴 수 있으니까 쉽게 말하자면 자바코드에서 setContentView()가 xml 파일이랑 연결시키는 역할을 한다했잖아요. 그래서 xml 파일이랑 연결하는 이 과정이 인플레이션이라고 생각하면 돼요.
그래서 setContentView()하기전에 xml에서 findViewById를 하려고하면 오류가 납니다.
그리고 화면 전체에 보여줄 xml 레이아웃이 아니라 부분 레이아웃을 소스 파일에 로딩하여 보여줄 수 있습니다.
그 역할을 하는게 LayoutInflater라는 클래스입니다.
실습 : LayoutInflater로 부분화면 만들기
activity_main.xml
<?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=".MainActivity"
android:orientation="vertical">
<Button
android:id="@+id/btn_inflater"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="inflater" />
<LinearLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
</LinearLayout>
그리고 res/layout 폴더를 우클릭하여
새로운 Layout Resource File을 만들어 줍니다. xml 이름은 sub로 하겠습니다.
sub.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">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="부분화면" />
</LinearLayout>
sub.xml 파일이 부분화면 역할을 할 것입니다.
MainActivity.java
package com.example.week3;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
public class MainActivity extends AppCompatActivity {
Button btn_inflater;
LinearLayout container;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_inflater = findViewById(R.id.btn_inflater);
container = findViewById(R.id.container);
btn_inflater.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.sub, container, true);
}
});
}
}
LayoutInflater가 R.layout.sub.xml을 container라는 LinearLayout에 추가해주는 코드입니다.
실행시켜 보겠습니다.
버튼을 누를 때마다 부분화면이 생성되는 것을 확인할 수 있습니다.
화면 전환하기
앱에는 여러 화면으로 구성되어 있고 화면을 전환하며 실행됩니다. 화면은 액티비티로 구현합니다. 즉, 화면을 띄우거나 닫는 과정은 액티비티를 전환하는 것과 같습니다. 따라서 액티비티에 대해서 공부하는 것은 중요합니다.
액티비티를 소스코드에서 띄울 때 startActicity() 함수를 사용하면 되는데 이 함수는 단순히 액티비티를 띄워 화면에 보이도록 하는 기능입니다. 하지만 화면을 닫고 원래의 화면으로 돌아올 때 응답을 받고 처리하는 코드가 필요합니다. 그때 사용하는 함수가 startActivityForResult()입니다.
실습 : 새 액티비티를 띄우고 닫으며 데이터 주고받기
activity_main.xml
<Button
android:id="@+id/btn_newactivity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="newactivity" />
그리고 또 res/layout 폴더에 새로운 Layout Resource File을 생성하고 이름은 activity_new로 하겠습니다.
activity_new.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">
<Button
android:id="@+id/btn_return"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="돌아가기" />
</LinearLayout>
이번엔 app/java/com.example.패키지명에 새로운 자바 클래스를 만들어보도록 합시다.
이름은 NewActivity로 하였습니다.
NewActivity.java
package com.example.week3;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
public class NewActivity extends AppCompatActivity {
Button btn_return;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_new);
btn_return = findViewById(R.id.btn_return);
btn_return.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.putExtra("name", "rina"); //부가데이터
setResult(RESULT_OK, intent); //응답 보내기
finish(); //현재 액티비티 종료
}
});
}
}
intent에 "name"이라는 키로 "rina"라는 데이터를 가진 Extra를 담아 응답을 보냅니다.
MainActivity.java
public static final int REQUEST_CODE_MENU = 101; //새 액티비티를 띄울 때 보낼 요청 코드
이 변수는 onCreate() 함수 위에 작성해주세요.
MainActivity.java
Button btn_newactivity = findViewById(R.id.btn_newactivity);
btn_newactivity.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), NewActivity.class);
startActivityForResult(intent, REQUEST_CODE_MENU);
}
});
이 코드는 현재 액티비티에서 NewActivity 액티비티로 화면을 전환해주는 코드입니다.
onCreate() 함수 안에 작성해주세요.
MainActivity.java
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == REQUEST_CODE_MENU) {
Toast.makeText(getApplicationContext(),
"onActivityResult 함수 호출됨. 요청 코드 : " + resultCode,
Toast.LENGTH_SHORT).show();
if(resultCode == RESULT_OK) {
String name = data.getStringExtra("name");
Toast.makeText(getApplicationContext(),
"응답으로 전달된 name : " + name,
Toast.LENGTH_SHORT).show();
}
}
}
NewActivity가 종료되었을 때 결과 값을 받는 코드입니다. 만약 내가 REQUEST_CODE_MENU를 전송했다면 첫 번째 Toast가 실행되고 그 응답으로 RESULT_OK라는 응답코드를 받았다면 두 번째 Toast가 실행됩니다.
이 코드는 onCreate() 함수 바깥에 작성해주세요.
마지막으로 app/manifests를 더블 클릭해주시면
AndroidManifest.xml 파일이 나타납니다.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.week3">
<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=".NewActivity" />
</application>
</manifest>
Activity를 추가할 때마다 위 처럼 <activity android:name=".NewActivity" />를 추가해주어야합니다.
실행시켜 보겠습니다.
화질이 안좋아 잘보이진 않지만 "onActivityResult 함수 호출됨. 요청 코드 : -1"과 "응답으로 전달된 name : rina"라는 토스트 메시지가 뜨는 것을 알 수 있습니다.
액티비티의 수명주기
안드로이드의 액티비티에는 수명주기가 있습니다.
• onCreate() : 액티비티 처음 만들어졌을 때 호출됨
화면에 보이는 뷰들의 일반적인 상태를 설정하는 부분
이 함수 다음에는 항상 onStart() 함수가 호출됨
• onStart() : 액티비티 화면에 보이기 바로 전에 호출됨
액티비티가 화면 상에 보이면 이 함수 다음에 onResume() 함수가 호출됨
액티비티가 화면에서 가려지게 되면 이 함수 다음에 onStop() 함수가 호출됨
• onResume() : 사용자와 상호작용하기 바로 전에 호출됨
• onRestart() : 액티비티 중지 이후 다시 시작되기 바로 전
이 함수 다음에는 항상 onStart()함수가 호출됨
• onPause() : 또 다른 액티비티 시작하려고 할 때 호출됨
저장되지 않은 데이터를 저장소에 저장하거나 애니메이션 중인 작업을 중지하는 등의 기능을 수행하는 함수임
이 함수가 리턴하기 전에는 다음 액티비티가 시작될 수 없으므로 이 작업은 매우 빨리 수행된 후 리턴되어야 함
• onStop() : 액티비티가 사용자에게 더 이상 보이지 않을 때 호출됨
액티비티가 소멸되거나 또 다른 액티비티가 화면을 가릴 때 호출됨
• onDestroy() : 액티비티가 소멸되어 없어지기 전에 호출됨
이 함수는 액티비티가 받는 마지막 호출이 됨
실습 : 디버깅 로그로 수명주기 확인하기
MainActivity.java에서 onCreate() 함수 밖을 클릭하고 Ctrl + O를 눌러줍니다.
그러면 Override 또는 Implement 함수를 찾을 수 있습니다.
여기서 onStart(), onPause(), onResume(), onRestart(), onDestroy() 함수를 찾아서 MainActivity에 추가해줍니다.
@Override
protected void onStart() {
super.onStart();
Log.d("tag", "onStart");
}
@Override
protected void onPause() {
super.onPause();
Log.d("tag", "onPause");
}
@Override
protected void onResume() {
super.onResume();
Log.d("tag", "onResume");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d("tag", "onRestart");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d("tag", "onDestroy");
}
이렇게 작성하고 앱을 실행시키면
log 창에 onCreate()함수가 실행된 후 바로 onStart와 onResume이 실행된 것을 확인할 수 있고
다른 액티비티가 실행됐을 땐 onPause
다시 메인액티비티로 돌아왔을 땐 onRestart, onStart, onResume
그리고 앱을 종료했을 땐 onPause, onDestroy 함수가 실행된 것을 알 수 있습니다.
Fragment
프래그먼트(Fragment)는 하나의 화면을 여러 부분으로 나눠서 보여주거나 각각의 부분 화면 단위로 바꿔서 보여주고 싶을 때 사용합니다.
그리고 BottomNavigationView는 하단탭을 만들어줄 수 있는 기능입니다. 카카오톡에도 하단탭이 사용된 것을 알 수 있습니다.
실습 : BottomNavigationView 사용하기
우선 activity_main.xml에 버튼 하나를 추가해줍니다.
activity_main.xml
<Button
android:id="@+id/btn_fragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="fragment" />
그리고 app/java/com.example.week3에 FragmentActivity.java 파일을 새로 만들겠습니다.
FragmentActivity.java
package com.example.week3;
import android.os.Bundle;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
public class FragmentActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
}
이 액티비티와 연결할 레이아웃 xml파일도 만들어주겠습니다.
우선 design 화면에서
Palette의 Containers안에 BottomNavigationView를 다운로드 받아줍니다.
OK 눌러주시면 됩니다.
그러고나서 다음과 같이 xml 파일을 작성하겠습니다.
activity_fragment.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"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<FrameLayout
android:id="@+id/mainFrame"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffffff"/>
</LinearLayout>
그리고 FragmentActivity.java의 super.onCreate(savedInstanceState); 코드 밑에 다음과 같이 작성해줍니다.
FragmentActivity.java
setContentView(R.layout.activity_fragment);
FragmentActivity와 activity_fragment.xml 파일을 연결한 것입니다.
이번엔 하단탭에 들어갈 메뉴를 만들어주겠습니다.
이번엔 res 폴더를 우클릭해서
Android Resource Directory를 생성해줍니다.
여기서 Resource type을 꼭 menu로 설정해주세요!
그리고 menu 폴더를 우클릭해서 Menu Resource File을 만들어줍니다.
이름은 bottom_menu로 하겠습니다.
이번에는 drawable 폴더를 우클릭해서 Vector Asset을 눌러줍니다.
여기서 Clip Art를 더블 클릭해주면
다음과 같이 여러가지의 아이콘들을 볼 수 있습니다. 여기서 아무거나 세 개를 drawable 폴더에 추가해주세요.
총 3개를 추가해주시면 됩니다.
그리고 아이템을 하단탭 메뉴에 추가시켜주겠습니다.
bottom_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/tab1"
android:icon="@drawable/ic_android_black_24dp"
android:title="tab1" />
<item
android:id="@+id/tab2"
android:icon="@drawable/ic_access_time_black_24dp"
android:title="tab2" />
<item
android:id="@+id/tab3"
android:icon="@drawable/ic_add_circle_outline_black_24dp"
android:title="tab3" />
</menu>
android:icon 부분은 본인이 추가하신 아이콘의 경로를 입력해주세요.
그리고 BottomNavigationView 태그에 다음 속성을 추가해주세요.
activity_fragment.xml
app:menu="@menu/bottom_menu"
이번엔 부분화면인 Fragment를 만들어보도록 하겠습니다.
Fragment(Blank)를 생성해줍니다.
이름은 Frag1으로 만들고나면 fragment_frag1.xml 파일이 layout 폴더에 자동으로 생성됩니다.
그러면 생성된 fragment_frag1.xml 내부의 TextView 태그의 text 속성 값에 fragment1이라고 입력해줍니다.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
tools:context=".Frag1">
<!-- TODO: Update blank fragment layout -->
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="fragment1" />
</FrameLayout>
같은 과정을 2번 더 반복하여 Frag2와 Frag3도 만들어 주세요.
그럼 이제 하단탭을 사용할 준비가 되었습니다. FragmentActivity.java로 돌아가 코드를 작성하겠습니다.
FragmentActivity.java
package com.example.week3;
import android.os.Bundle;
import android.view.MenuItem;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import java.util.ArrayList;
public class FragmentActivity extends AppCompatActivity {
BottomNavigationView bottomNavigationView;
Frag1 frag1;
Frag2 frag2;
Frag3 frag3;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
bottomNavigationView = findViewById(R.id.bottomNavigation);
frag1 = new Frag1();
frag2 = new Frag2();
frag3 = new Frag3();
getSupportFragmentManager().beginTransaction().replace(R.id.mainFrame, frag1).commit();
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.tab1: {
getSupportFragmentManager().beginTransaction().replace(R.id.mainFrame, frag1).commit();
return true;
}
case R.id.tab2: {
getSupportFragmentManager().beginTransaction().replace(R.id.mainFrame, frag2).commit();
return true;
}
case R.id.tab3: {
getSupportFragmentManager().beginTransaction().replace(R.id.mainFrame, frag3).commit();
return true;
}
}
return false;
}
});
}
}
그리고 앱을 실행시키기 위해서 AndroidManifest.xml 파일에
<activity android:name=".FragmentActivity" />
태그를 추가하는 것을 잊지 마세요.
MainActivity.java에 FragmentActivity로 전환되는 버튼 기능을 추가하고 앱을 실행시켜보도록 하겠습니다.
MainActivity.java
Button btn_fragment = findViewById(R.id.btn_fragment);
btn_fragment.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(getApplicationContext(), FragmentActivity.class);
startActivity(intent);
}
});
실행시키면
BottomNavigationView가 잘 실행되는 것을 볼 수 있습니다.
그리고 ViewPager라고 화면을 드래그해도 fragment를 전환할 수 있습니다.
여기에 바로 실습해보도록 하겠습니다.
실습 : ViewPager 사용하기
우선 activity_fragment.xml의 FrameLayout을 viewpager로 바꿔줍니다.
activity_fragment.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"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<androidx.viewpager.widget.ViewPager
android:id="@+id/mainFrame"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottomNavigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:menu="@menu/bottom_menu"
android:background="#ffffff"/>
</LinearLayout>
FragmentActivity.java
package com.example.week3;
import android.os.Bundle;
import android.view.MenuItem;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;
import androidx.viewpager.widget.ViewPager;
import com.google.android.material.bottomnavigation.BottomNavigationView;
import java.util.ArrayList;
public class FragmentActivity extends AppCompatActivity {
BottomNavigationView bottomNavigationView;
Frag1 frag1;
Frag2 frag2;
Frag3 frag3;
ViewPager pager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
bottomNavigationView = findViewById(R.id.bottomNavigation);
frag1 = new Frag1();
frag2 = new Frag2();
frag3 = new Frag3();
//getSupportFragmentManager().beginTransaction().replace(R.id.mainFrame, frag1).commit();
pager = findViewById(R.id.mainFrame);
pager.setOffscreenPageLimit(3);
final MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager());
adapter.addItem(frag1);
adapter.addItem(frag2);
adapter.addItem(frag3);
pager.setAdapter(adapter);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.tab1: {
//getSupportFragmentManager().beginTransaction().replace(R.id.mainFrame, frag1).commit();
pager.setCurrentItem(0);
return true;
}
case R.id.tab2: {
//getSupportFragmentManager().beginTransaction().replace(R.id.mainFrame, frag2).commit();
pager.setCurrentItem(1);
return true;
}
case R.id.tab3: {
//getSupportFragmentManager().beginTransaction().replace(R.id.mainFrame, frag3).commit();
pager.setCurrentItem(2);
return true;
}
}
return false;
}
});
pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
bottomNavigationView.getMenu().getItem(position).setChecked(true);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
class MyPagerAdapter extends FragmentStatePagerAdapter {
ArrayList<Fragment> items = new ArrayList<>();
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
public void addItem(Fragment item) {
items.add(item);
}
@Override
public Fragment getItem(int position) {
return items.get(position);
}
@Override
public int getCount() {
return items.size();
}
}
}
FragmentActivity.java 코드는 위와 같이 수정하면 ViewPager를 사용할 수 있습니다.
'Android' 카테고리의 다른 글
[DSC] 위치기반 서비스와 센서 사용하기 (0) | 2020.06.04 |
---|---|
[DSC] 서비스와 수신자 이해하기 (0) | 2020.05.28 |
[DSC] 또 다른 위젯(Widget)과 그래들(Gradle) (0) | 2020.05.19 |
[DSC] 위젯(Widget) 익히기 (0) | 2020.05.18 |
[DSC] 안드로이드 스튜디오와 친해지기 2 (0) | 2020.05.12 |