될 때까지 안드로이드 정리 (오준석의 생존코딩) 1
- 2장 첫 번째 앱 만들기, 3장 뷰와 뷰그룹
- 5장 레이아웃: 화면에 뷰를 수 놓는 방법
- 6장 안드로이드는 액티비티로부터
- 7장 인텐트와 인텐트 필터
- 8장 UpNavigation과 메뉴 구현하기
- 9장 웹뷰 웹 페이지 표기하기
- 10장 화면 제약을 극복하는 방법 – ListView
- 11장 기기에 데이터 저장하기 SharedPreference
- 12장 액티비티 생명주기
- 13장 프래그먼트
될 때까지 안드로이드 #1 [2장 첫 번째 앱 만들기, 3장 뷰와 뷰그룹]
-
activity_main.xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/edit_message"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="@string/edit_message" /> // 나중에 국제화를 위해서 string 값을 리소스로 빼기
// (2가지 방법: values/strings.xml 파일에 직접 입력, Extract string resource 메뉴 이용)
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="sendMessage" // xml에서 직접 클릭 이벤트 추가
android:text="@string/button_send" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
public final static String EXTRA_MESSAGE = "com.example.myfirstapp.MESSAGE"; // 실수 방지 위해 변수 등록
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void sendMessage(View view) {
Intent intent = new Intent(this, DisplayMessageActivity.class);
EditText editText = findViewById(R.id.edit_message);
String message = editText.getText().toString();
intent.putExtra(EXTRA_MESSAGE, message);
startActivity(intent);
}
}
activity_display_message.xml
<LinearLayout
android:id="@+id/activity_display_message"
android:orientation="vertical">
</LinearLayout>
DisplayMessageActivity.java
public class DisplayMessageActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_display_message);
Intent intent = getIntent();
String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);
TextView textView = new TextView(this); // 텍스트뷰를 코드로 추가
textView.setTextSize(40);
textView.setText(message);
ViewGroup layout = findViewById(R.id.activity_display_message);
layout.addView(textView);
}
}
values/strings.xml
<resources>
<string name="app_name">01_MyFirstApp</string>
<string name="edit_message">메시지를 입력하세요.</string>
<string name="button_send">보내기</string>
</resources>
될 때까지 안드로이드 #2 [5장 레이아웃: 화면에 뷰를 수 놓는 방법]
될 때까지 안드로이드 #3 [6장 안드로이드는 액티비티로부터]
activity_main.xml
<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/name_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="이름" />
<EditText
android:id="@+id/age_edit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="나이" />
<Button
android:id="@+id/submit_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="전송" />
</LinearLayout>
activity_second.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/message_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffff00" />
<Button
android:id="@+id/result_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="결과 전달" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
public static final int REQUEST_CODE = 1000;
private EditText mNameEditText;
private EditText mAgeEditText;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 화면에 layout 표시
setContentView(R.layout.activity_main);
// 이름, 나이
mNameEditText = (EditText) findViewById(R.id.name_edit);
mAgeEditText = (EditText) findViewById(R.id.age_edit);
// 버튼 이벤트 처리
findViewById(R.id.submit_button).setOnClickListener(this);
}
@Override
public void onClick(View v) {
// SecondActivity로 전환하겠다는 intent
Intent intent = new Intent(this, SecondActivity.class);
// 이름, 나이 가져와서 intent에 추가
intent.putExtra("name", mNameEditText.getText().toString());
intent.putExtra("age", mAgeEditText.getText().toString());
// intent의 정보를 토대로 다른 Activity를 시작
startActivityForResult(intent, REQUEST_CODE);
}
// SecondActivity에서 돌려받은 결과를 처리하는 콜백
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE
&& resultCode == RESULT_OK
&& data != null) {
// 결과를 받음
String result = data.getStringExtra("result");
// 토스트 메시지 표시
Toast.makeText(MainActivity.this, result, Toast.LENGTH_SHORT).show();
}
}
}
SecondActivity.java
public class SecondActivity extends AppCompatActivity implements View.OnClickListener {
private TextView mMessageTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
// 넘어온 값을 화면에 표시
Intent intent = getIntent();
String name = intent.getStringExtra("name");
String age = intent.getStringExtra("age");
mMessageTextView = (TextView) findViewById(R.id.message_edit_text);
mMessageTextView.setText(age + "살 " + name);
// 버튼 이벤트 연결
findViewById(R.id.result_button).setOnClickListener(this);
}
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.putExtra("result", mMessageTextView.getText().toString());
// 결과 전달
setResult(RESULT_OK, intent);
// 이 액티비티 종료
finish();
}
}
될 때까지 안드로이드 #4 [7장 인텐트와 인텐트 필터]
<LinearLayout
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="@+id/phone_number_edit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="전화번호를 입력 해 주세요"
android:inputType="phone" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="dialPhone"
android:text="전화걸기" />
</LinearLayout>
public void dialPhone(View view) {
EditText editText = (EditText) findViewById(R.id.phone_number_edit);
dialPhoneNumber(editText.getText().toString());
}
// 전화걸기
public void dialPhoneNumber(String phoneNumber) {
// 암시적 인텐트인 ACTION_DIAL을 설정
Intent intent = new Intent(Intent.ACTION_DIAL);
// URI 형태의 전화번호를 데이터로 설정
intent.setData(Uri.parse("tel:" + phoneNumber));
// 이러한 Intent를 처리할 수 있는 Activity를 찾는다면 액티비티를 시작
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
common intents
https://developer.android.com/guide/components/intents-common?hl=ko
될 때까지 안드로이드 #5 [8장 메뉴 구현하기] + UpNavigation
AndroidManifest.xml
<application
<activity android:name=".ChildActivity" android:parentActivityName=".ParentActivity" />
<activity android:name=".ParentActivity" android:parentActivityName=".MainActivity" />
<!-- 4.0 이하
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".MainActivity" />
-->
activity_main.xml
<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">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="moveParentActivity"
android:text="ParentActivity로 이동" />
</LinearLayout>
activity_parent.xml
<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=".ParentActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="moveChildActivity"
android:text="ChildActivity로 이동" />
</LinearLayout>
activity_child.xml
<LinearLayout> 비었음 </LinearLayout>
MainActivity.java
public void moveParentActivity(View view) {
startActivity(new Intent(this, ParentActivity.class));
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.action_menu1:
Toast.makeText(this, "첫 번째 메뉴", Toast.LENGTH_SHORT).show();
return true;
case R.id.action_menu2:
Toast.makeText(this, "두 번째 메뉴", Toast.LENGTH_SHORT).show();
return true;
}
return super.onOptionsItemSelected(item);
}
ParentActivity.java
public void moveChildActivity(View view) {
startActivity(new Intent(this, ChildActivity.class));
}
ChildActivity.java
추가 없음
menu/main_menu.xml
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/action_menu1"
android:icon="@drawable/ic_launcher_foreground" // title 보다 icon 이 우선
android:title="Item"
app:showAsAction="ifRoom" /> // 공간이 있다면 전면에 표시
<item
android:id="@+id/action_menu2"
android:title="Item"
app:showAsAction="never" /> // 전면에 표시 안함
</menu>
될 때까지 안드로이드 #6 [9장 웹뷰 웹 페이지 표기하기]
<uses-permission android:name="android.permission.INTERNET" />
android:usesCleartextTraffic="true"
MainActivity.java
public class MainActivity extends AppCompatActivity {
private EditText mAddressEdit;
private WebView myWebView;
private Button mMoveButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myWebView = (WebView) findViewById(R.id.web_view);
// WebView의 설정
WebSettings webSettings = myWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
myWebView.setWebViewClient(new WebViewClient());
mAddressEdit = (EditText) findViewById(R.id.address_edit);
mMoveButton = (Button) findViewById(R.id.move_button);
// 소프트키보드의 돋보기를 클릭했을 때 이동 버튼을 클릭하도록 함
mAddressEdit.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
mMoveButton.callOnClick();
// 키보드 내리기
InputMethodManager imm = (InputMethodManager)
getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
return true;
}
return false;
}
});
}
public void onClick(View view) {
String address = mAddressEdit.getText().toString();
if (address.startsWith("http://") == false) {
address = "http://" + address;
}
myWebView.loadUrl(address);
}
@Override
public void onBackPressed() {
if (myWebView.canGoBack()) {
myWebView.goBack();
} else {
super.onBackPressed();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_back:
// 뒤로 가기
if (myWebView.canGoBack()) {
myWebView.goBack();
}
break;
case R.id.action_forward:
// 앞으로 가기
if (myWebView.canGoForward()) {
myWebView.goForward();
}
break;
case R.id.action_refresh:
// 새로고침
myWebView.reload();
break;
}
return super.onOptionsItemSelected(item);
}
}
activity_main.xml
<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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="@+id/address_edit"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="http://"
android:imeOptions="actionSearch" // 돋보기 버튼 나오게
android:inputType="textUri"
android:maxLines="1" />
<Button
android:id="@+id/move_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClick"
android:visibility="gone"
android:text="이동" />
</LinearLayout>
<WebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
back, forward, refresh 아이콘 추가
drawable 폴더에서 New -> Vector Asset
menu_main.xml
<menu>
<item
android:id="@+id/action_back"
android:icon="@drawable/ic_arrow_back"
android:title="뒤로 가기"
app:showAsAction="always" />
<item
android:id="@+id/action_forward"
android:icon="@drawable/ic_arrow_forward"
android:title="앞으로 가기"
app:showAsAction="always" />
<item
android:id="@+id/action_refresh"
android:icon="@drawable/ic_refresh"
android:title="새로고침"
app:showAsAction="always" />
</menu>
될 때까지 안드로이드 #7 [10장 화면 제약을 극복하는 방법 – ListView ]
ScrollView 예제
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- 스크롤뷰는 반드시 하나의 자식만 가질 수 있음 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:textSize="100dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:textSize="100dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:textSize="100dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:textSize="100dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:textSize="100dp" />
</LinearLayout>
</ScrollView>
</LinearLayout>
ListView 예제 (안드로이드 기본 제공 레이아웃 사용 : android.R.layout.simple_list_item_1)
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayList<String> data = new ArrayList<>();
for (int i = 0; i < 30; i++) {
data.add("data " + i);
}
// 어댑터
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, data);
// 뷰 (리스트뷰에 어댑터를 설정하면 어댑터가 데이터를 표시)
ListView listView = findViewById(R.id.list_view);
listView.setAdapter(adapter);
// 클릭
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
Toast.makeText(MainActivity.this, position + " 번째 아이템 선택", Toast.LENGTH_SHORT).show();
}
});
}
}
ListView Custom Adapter 최종 예제
activity_main.xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
item_weather.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/weather_image"
android:layout_width="100dp"
android:layout_height="100dp"
android:src="@mipmap/ic_launcher" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical"
android:padding="8dp">
<TextView
android:id="@+id/city_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="도시명"
android:textSize="30sp" />
<TextView
android:id="@+id/temp_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:gravity="end"
android:text="기온" />
</LinearLayout>
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ArrayList<Weather> data = new ArrayList<>();
data.add(new Weather("수원", "25도", "맑음"));
data.add(new Weather("서울", "26도", "비"));
data.add(new Weather("안양", "24도", "구름"));
data.add(new Weather("부산", "29도", "구름"));
data.add(new Weather("인천", "23도", "맑음"));
data.add(new Weather("대구", "28도", "비"));
data.add(new Weather("용인", "25도", "비"));
data.add(new Weather("수원", "25도", "맑음"));
data.add(new Weather("서울", "26도", "비"));
data.add(new Weather("안양", "24도", "구름"));
data.add(new Weather("부산", "29도", "구름"));
data.add(new Weather("인천", "23도", "맑음"));
data.add(new Weather("대구", "28도", "비"));
data.add(new Weather("용인", "25도", "비"));
MyFirstAdapter adapter = new MyFirstAdapter(data);
// 뷰 (리스트뷰에 어댑터를 설정하면 어댑터가 데이터를 표시)
ListView listView = findViewById(R.id.list_view);
listView.setAdapter(adapter);
// 클릭
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
Toast.makeText(MainActivity.this, position + " 번째 아이템 선택", Toast.LENGTH_SHORT).show();
}
});
}
}
MyFirstAdapter.java
public class MyFirstAdapter extends BaseAdapter {
private List<Weather> mData;
private Map<String, Integer> mWeatherImageMap;
public MyFirstAdapter(List<Weather> data) {
mData = data;
mWeatherImageMap = new HashMap<>();
mWeatherImageMap.put("맑음", R.drawable.sunny);
mWeatherImageMap.put("폭설", R.drawable.blizzard);
mWeatherImageMap.put("구름", R.drawable.cloudy);
mWeatherImageMap.put("비", R.drawable.rainy);
mWeatherImageMap.put("눈", R.drawable.snow);
}
@Override
public int getCount() {
return mData.size();
}
@Override
public Object getItem(int i) {
return mData.get(i);
}
// 데이터 베이스 커서 관련해서 쓰이지만 평소에는 그대로 i를 넘겨줌
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
// 계속 뷰의 레이아웃을 인플레이트 하는 것은 비용이 많이 든다. 그래서 재사용을 한다.
if (convertView == null) { // 처음에는 null
holder = new ViewHolder();
convertView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_weather, parent, false);
// 날씨, 도시, 기온 View
ImageView weatherImage = (ImageView) convertView.findViewById(R.id.weather_image);
TextView cityText = (TextView) convertView.findViewById(R.id.city_text);
TextView tempText = (TextView) convertView.findViewById(R.id.city_text);
holder.weatherImage = weatherImage;
holder.cityText = cityText;
holder.tempText = tempText;
convertView.setTag(holder);
} else { // 재사용될 때는 null 아님
holder = (ViewHolder) convertView.getTag();
}
Weather weather = mData.get(position);
holder.cityText.setText(weather.getCity());
holder.tempText.setText(weather.getTemp());
holder.weatherImage.setImageResource(mWeatherImageMap.get(weather.getWeather()));
return convertView;
}
static class ViewHolder {
ImageView weatherImage;
TextView cityText;
TextView tempText;
}
}
Weather.java
public class Weather {
private String city; // 도시명
private String temp; // 기온
private String weather; // 날씨 (맑음, 비, 구름, 눈)
public Weather(String city, String temp, String weather) {
this.city = city;
this.temp = temp;
this.weather = weather;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getTemp() {
return temp;
}
public void setTemp(String temp) {
this.temp = temp;
}
public String getWeather() {
return weather;
}
public void setWeather(String weather) {
this.weather = weather;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("Weather{");
sb.append("city='").append(city).append('\'');
sb.append(", temp='").append(temp).append('\'');
sb.append(", weather='").append(weather).append('\'');
sb.append('}');
return sb.toString();
}
}
리스트뷰를 그리드뷰로 바꾸기
<GridView
android:numColumns="2"
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
GridView listView = findViewById(R.id.list_view); listView.setAdapter(adapter);
될 때까지 안드로이드 #8 [11장 기기에 데이터 저장하기 SharedPreference]
activity_main.xml
<LinearLayout
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/shortcut_image"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="#ff00ff"
android:onClick="onImageClicked" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onButtonClicked"
android:text="바로가기 추가" />
</LinearLayout>
item_app.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/icon_image"
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@mipmap/ic_launcher" />
<TextView
android:id="@+id/app_name_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp"
android:text="앱 이름"
android:textSize="24sp" />
</LinearLayout>
activity_app_list.xml
<RelativeLayout
android:id="@+id/activity_app_list"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ListView
android:id="@+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/item_app" />
</RelativeLayout>
AppInfoAdapter.java
public class AppInfoAdapter extends BaseAdapter {
private List<ApplicationInfo> mInfos;
public AppInfoAdapter(List<ApplicationInfo> data) {
this.mInfos = data;
}
@Override
public int getCount() {
return mInfos.size();
}
@Override
public Object getItem(int i) {
return mInfos.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_app, parent, false);
holder.imageView = (ImageView) convertView.findViewById(R.id.icon_image);
holder.textView = (TextView) convertView.findViewById(R.id.app_name_text);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
// 앱 정보
ApplicationInfo info = mInfos.get(position);
// 앱 아이콘
Drawable icon = info.loadIcon(parent.getContext().getPackageManager());
holder.imageView.setImageDrawable(icon);
// 앱 이름
String name =
String.valueOf(info.loadLabel(parent.getContext().getPackageManager()));
holder.textView.setText(name);
return convertView;
}
// 뷰 홀더 패턴을 위한 홀더 클래스
private static class ViewHolder {
ImageView imageView;
TextView textView;
}
}
AppListActivity.java
public class AppListActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_list);
ListView listView = (ListView) findViewById(R.id.list_view);
// 기기에 설치된 모든 앱 목록
PackageManager pm = getPackageManager();
List<ApplicationInfo> infos =
pm.getInstalledApplications(PackageManager.GET_META_DATA);
AppInfoAdapter adapter = new AppInfoAdapter(infos);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ApplicationInfo info = (ApplicationInfo)
(parent.getAdapter()).getItem(position);
Intent intent = new Intent();
intent.putExtra("info", info);
setResult(RESULT_OK, intent);
finish();
}
});
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
public static final int REQUEST_CODE = 1000;
private ImageView mShortcut;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mShortcut = (ImageView) findViewById(R.id.shortcut_image);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
// 저장된 shortcut 값을 얻음. 만약 저장된 값이 없을 경우 기본값으로 null 반환
String packageName = preferences.getString("shortcut", null);
if (packageName != null) {
try {
Drawable icon = getPackageManager().getApplicationIcon(packageName);
// 아이콘을 이미지뷰에 표시
mShortcut.setImageDrawable(icon);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK && data != null) {
// AppListActivity.java로부터 넘겨받은 ApplicationInfo 객체
// Pacelable 객체를 받기 위해 getParcelableExtra()로 얻음
ApplicationInfo info = data.getParcelableExtra("info");
// loadIcon()에 PackageManager를 넘겨주면 아이콘을 Drawable로 얻을 수 있음
Drawable icon = info.loadIcon(getPackageManager());
// 기본 SharedPreferences 환경을 얻음
SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(this);
// SharedPrefences를 수정하기 위한 객체를 얻음
SharedPreferences.Editor edit = preferences.edit();
// info 객체로 packageName을 얻고, shortcut 키와 함께 프리퍼런스에 저장
edit.putString("shortcut", info.packageName);
// 변경사항 적용
edit.apply();
mShortcut.setImageDrawable(icon);
}
}
public void onButtonClicked(View view) {
Intent intent = new Intent(this, AppListActivity.class);
startActivityForResult(intent, REQUEST_CODE);
}
public void onImageClicked(View view) {
// 클릭된 이미지뷰에서 Drawable 객체 얻음
ImageView imageView = (ImageView) view;
Drawable drawable = imageView.getDrawable();
if (drawable != null) {
// 프리퍼런스에 shortcut 키로 저장된 패키지명을 가져옴
SharedPreferences preferences =
PreferenceManager.getDefaultSharedPreferences(this);
String packageName = preferences.getString("shortcut", null);
if (packageName != null) {
// 이 패키지를 실행할 수 있는 인텐트를 얻어서 액티비티 시작
Intent intent = getPackageManager().getLaunchIntentForPackage(packageName);
startActivity(intent);
}
}
}
@Override
public void onBackPressed() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("종료 확인");
builder.setMessage("정말로 종료하시겠습니까?");
builder.setPositiveButton("확인", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
finish();
}
});
builder.setNegativeButton("취소", null);
builder.show();
}
}
BACK 버튼 눌렀을 때 DialogFragment 이용해서 창 띄우기
MainActivity.java
@Override
public void onBackPressed() {
ExitDialogFragment fragment = new ExitDialogFragment();
fragment.show(getSupportFragmentManager(), "exit");
}
ExitDialogFragment.java
public class ExitDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// AlertDialog 빌더 클래스를 이용해서 다이얼로그를 생성
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("종료 확인");
builder.setMessage("정말로 종료하시겠습니까?");
builder.setPositiveButton("확인", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 종료 처리
getActivity().finish();
}
});
builder.setNegativeButton("취소", null);
// 생성된 다이얼로그를 반환함
return builder.create();
}
}
될 때까지 안드로이드 #9 [12장 액티비티 생명주기]
AndroidManifest.xml 에서 android:screenOrientation=”landscape” 화면을 고정한다하더라도 상태 정보를 저장해라.
<LinearLayout
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/level_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="레벨 : 0" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onLevelUp"
android:text="레벨 증가" />
<TextView
android:id="@+id/score_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="점수 : 0" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onScoreUp"
android:text="점수 증가" />
</LinearLayout>
public class MainActivity extends AppCompatActivity {
static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
private TextView mLevelText;
private TextView mScoreText;
private int mLevel = 0;
private int mScore = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLevelText = (TextView) findViewById(R.id.level_text);
mScoreText = (TextView) findViewById(R.id.score_text);
if (savedInstanceState == null) {
// 초기화 할 코드
} else {
// 상태 복원
mLevel = savedInstanceState.getInt(STATE_LEVEL);
mScore = savedInstanceState.getInt(STATE_SCORE);
mLevelText.setText("레벨 : " + mLevel);
mScoreText.setText("점수 : " + mScore);
}
}
// 상태 복원을 위한 또 다른 방법
// @Override
// protected void onRestoreInstanceState(Bundle savedInstanceState) {
// // EditText 등의 복원을 위해 항상 호출 해야 함
// super.onRestoreInstanceState(savedInstanceState);
//
// // 상태 복원
// mLevel = savedInstanceState.getInt(STATE_LEVEL);
// mScore = savedInstanceState.getInt(STATE_SCORE);
// mLevelText.setText("레벨 : " + mLevel);
// mScoreText.setText("점수 : " + mScore);
// }
public void onLevelUp(View view) {
mLevel++;
mLevelText.setText("레벨 : " + mLevel);
}
public void onScoreUp(View view) {
mScore += 100;
mScoreText.setText("점수 : " + mScore);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
// 상태 저장
outState.putInt(STATE_SCORE, mScore);
outState.putInt(STATE_LEVEL, mLevel);
// 항상 슈퍼클래스의 메서드를 호출해야 합니다
super.onSaveInstanceState(outState);
}
}
될 때까지 안드로이드 #10 [13장 프래그먼트]
# 프래그먼트끼리 통신을 할 수 없으므로 콜백 인터페이스를 이용한다 : 프래그먼트1(콜백) -> 액티비티(콜백) -> 프래그먼트2
activity_main.xml
<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">
<fragment
android:id="@+id/fragment_color_list"
android:name="com.example.myfragmentapplication.ColorListFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<fragment
android:id="@+id/fragment_color"
android:name="com.example.myfragmentapplication.ColorFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
ColorFragment.java
public class ColorFragment extends Fragment {
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return new View(getActivity());
}
public void setColor(int color) {
getView().setBackgroundColor(color);
}
}
onCreateView() : 뷰 생성 작업
onViewCreated() : 뷰 생성 이후의 연결 작업
ColorListFragment.java
public class ColorListFragment extends ListFragment {
private OnColorSelectedListener mListener;
interface OnColorSelectedListener {
void onColorSelected(int color);
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
try {
mListener = (OnColorSelectedListener) context;
} catch (ClassCastException e) {
throw new ClassCastException(((Activity) context).getLocalClassName() + " 는 OnColorSelectedListener 를 구현해야 합니다.");
}
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
List<String> colorList = Arrays.asList("Red", "Green", "Blue");
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, colorList);
setListAdapter(adapter);
}
@Override
public void onListItemClick(@NonNull ListView l, @NonNull View v, int position, long id) {
ArrayAdapter<String> adapter = (ArrayAdapter<String>) l.getAdapter();
// or ArrayAdapter<String> adapter = (ArrayAdapter<String>) getListAdapter();
String colorString = adapter.getItem(position);
int color = Color.RED;
switch (colorString) {
case "Red":
color = Color.RED;
break;
case "Green":
color = Color.GREEN;
break;
case "Blue":
color = Color.BLUE;
break;
}
if (mListener != null) {
mListener.onColorSelected(color);
}
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity implements ColorListFragment.OnColorSelectedListener {
private ColorFragment mColorFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mColorFragment = (ColorFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_color);
}
@Override
public void onColorSelected(int color) {
mColorFragment.setColor(color);
}
}
랜덤한 배경색을 가진 프래그먼트를 생성 예제
activity_main.xml
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" >
<fragment
android:id="@+id/color_fragment"
android:name="com.example.myfragmentapplication.ColorFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="change"
android:text="교체" />
MainActivity.java
public void change(View view) {
ColorFragment fragment = new ColorFragment();
int red = new Random().nextInt(256);
int green = new Random().nextInt(256);
int blue = new Random().nextInt(256);
fragment.setColor(Color.rgb(red, green, blue));
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, fragment)
.commit();
}
ColorFragment.java
private int color;
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.setBackgroundColor(color);
}
public void setColor(int color) {
this.color = color;
}
될 때까지 안드로이드 (오준석의 생존코딩) 유튜브 강의
https://www.youtube.com/watch?v=euTtMnN-TgI&list=PLxTmPHxRH3VWTd-8KB67Itegihkl4SVKe&index=2
될 때까지 안드로이드 (오준석의 생존코딩) 깃헙
https://github.com/junsuk5/android-first-book
될 때까지 안드로이드 소스 다운로드
android-first-book-master.zip