안녕하세요.
disits 속성 사용 시 키보드 오류에 대해 말해 볼까 합니다.
개발을 하다보면 여러 변수가 생기고 거기에 맞게 대응 해야 되는데 그러기가 만만치 않습니다.
이번에 좀 당황하게 만들었던 일은 EditText의 inputType 속성으로는 부족함이 있어 disits 속성을 사용 하면서 생겼습니다.
많은 버전을 Test 하지는 않았지만 6.0 버전에서 disits 속성을 설정 하고 Test 해본 결과 원하는데로 작동하지 않았습니다.
증상은 EditText에서 disits로 설정하지 않은 값을 입력 할 시 EditText에 입력된 값이 복사 되는 현상과 disits에 설정된 값이 입력되지 않는 현상 으로 볼 수 있습니다.
결론 부터 말하자면 구글의 기본 키보드 오류 인거 같습니다. 단어를 제안 해주는 과정에서 오류가 생기는거 같으나 자세하게 보지는 않았습니다. 추측 입니다.
일단 해결은 android:inputType="textNoSuggestions|textCapCharacters" 으로 해결 했습니다.
inputType 속성 중 textNoSuggestions을 설정해 텍스트 제안을 안하도록 설정하니 원하는데로 동작 하였습니다.
저는 추가적으로 대문자로만 입력 받고 싶어서 textCapCharacters도 입력 했습니다.
이런 현상은 8.1 버전에서는 발생 하지 않았으며 6.0 버전 에서만 발생 하였습니다. 이게 버전 문제인지는 좀 더 Test 해봐야겠지만 간단한 제안 정도는 xml 속성을 이용해서 처리 가능 합니다.
2018년 12월 27일 목요일
2018년 10월 30일 화요일
[Android] LifeCycle의 중요성!
안드로이드 개발을 할때 잘 생각해야 되는 부분 중 하나가 LifeCycle이 아닌가 생각 합니다.
예전에 Fragment 생명주기도 포스팅 했지만 기본에 충실 하자는 의미로 Android의 기본적인 LifeCycle에 대해 포스팅 하려고 합니다.
기본적인 Activity의 LifeCyle은 아래 그림과 같습니다.
![]() |
| 출처:https://developer.android.com |
Android 개발 시 LifeCycle을 정확히 이해하고 개발 해야만 App이 의도한 대로 동작 하게 됩니다.
onCreate() 에서 객체생성 및 초기화 작업을 해주며 onDestory() 에서 메모리 누수가 예상 되는 객체는 해제를 해줘야 됩니다.
몇가지 상황을 예를 들어 설명 하자면
1. onResume() 과 onPause()는 App이 실행 되는 순간에도 자주 왔다갔다 합니다.
onPause()는 잠시 대기 상태라고 생각해도 좋으며 보통 다이어로그가 화면에 보여질때나, 상태창 같은 창을 볼때도 onPause()가 호출 되게 됩니다.
2. 앱이 실행 됐다가 홈 버튼을 누른다던지, 다른앱이 실행 되면 onPause() -> onStop() 메서드가 호출 되며, 다시 실행 할때는 onRestart() -> onStart() -> onResume() 순서로 호출 됩니다.
3. App이 강제 종료된 상태로 onPause() -> onStop() -> onCreate() -> onStart() -> onResume() 순으로 호출 됩니다.
여기서 유심히 살펴봐야 될 부분이 onStop() 이후 onCreate()가 호출 된다는 것입니다.
onDestory()는 생략되고 onRestart()가 아닌 Activity의 시작인 onCreate()부터 새로 시작되는걸 볼 수 있습니다.
이렇듯 여러 상황에 대비 하여 적절한 처리를 수행 해야 됩니다.
그럼 어떤 방식으로 적절하게 상태를 유지 시켜 줄 수 있는지 알아 보겠습니다.
객체의 해제는 onStop(), onDestory()에서 적절하게 해주면 되며, Data의 상태 저장인 경우는 onSaveInstanceState()와 onRestoreInstanceState() 를 살펴볼 필요가 있습니다.
onSaveInstanceState()는 onPause()와 onStop()사이에서 항상 호출 되며, onRestoreInstanceState()는 강제 종료시에만 호출 되는 메서드 입니다.
이러한 메서드는 LifeCycle에는 포함되어 있지 않지만, App의 상태유지를 위해서 반드시 알고 있어야 하는 메서드 입니다.
물론 요세는 Android Architecture Component 개념을 활용 하여 화면 회전에 대한 처리를 해결할 수 있지만 위와 같은 기본적인 개념을 완전히 이해한 후 유용한 Architecture를 도입 한다면 좀 더 안정적인 App을 만들수 있을꺼라 생각 합니다.
2018년 10월 23일 화요일
[Android] ConstraintLayout 사용에 익숙해 지자.
이 포스팅의 목적은 ConstraintLayout를 전부 알리고자 하는 목적이 아닌, 기본 속성만이라도 제대로 알고 조금씩이라도 활용도를 높이면 좋겠다는 생각으로 포스팅 합니다.
Android Studio에서 Project를 새로 생성하던지, Activity를 새로 생성 하던지, Layout를 생성하면 기본으로 생성되는 Layout이 ConstraintLayout 입니다.
오늘 포스팅할 내용은 위에서도 말씀 드렸지만 ConstraintLayout의 전체전인 부분이 아니라 기본적인 속성 입니다.
속성의 의미를 알아보겠습니다.
끝!
오늘 말하고 싶은 포스팅은 이게 전부 입니다.
저 속성들을 이용해서 하나하나 배치 하다 보면 ConstraintLayout을 사용 하고 있는 자기 자신을 발견할 수 있을 겁니다.
몇가지 예시
app:layout_constraintLeft_toRightOf="@id/anotherView"
: 내 View의 왼쪽을 @id/anotherView 오른쪽에 붙인다.
app:layout_constraintRight_toLeftOf="@id/anotherView"
: 내 View의 오른쪽을 @id/anotherView 왼쪽에 붙인다.
app:layout_constraintTop_toBottomOf="@id/anotherView"
: 내 View의 위를 @id/anotherView 아래에 붙인다.
app:layout_constraintBottom_toTopOf="@id/anotherView"
: 내 View의 아래를 @id/anotherView 위에 붙인다.
이런식으로 만들고자 하는 UI를 만들 수 있습니다.
하나만 더 알고 넘어가죠! 위 속성을 예로 설명 하겠습니다.
ConstrainLayout을 사용 하다보면 알겠지만 margin를 사용할 때 주의 사항 입니다.
margin은 anotherView에서 사용 하는게 아니라 내 View에서 사용해야 됩니다.
다른 Layout 에서는 margin을 사용 하고 싶은 곳에서 사용 하면 좌우로 위아래 어느 방향이던 적용이 되었던 반면
ConstrainLayout에서는 margin으로 인해 직접 영향 받는 뷰에 설정을 해야 됩니다.
말이 어려운거지 margin을 여기저거 적용 하다보면 바로 느낌 오실 겁니다.
Android Studio에서 Project를 새로 생성하던지, Activity를 새로 생성 하던지, Layout를 생성하면 기본으로 생성되는 Layout이 ConstraintLayout 입니다.
평소에는 ConstraintLayout을 지우고 익숙한 LinearLayout 이나 RelativeLayout로 작업을 했지만, 매번 바꾸는 것도 귀찮고 해서 ConstraintLayout을 살펴 보았습니다.
사실 LinearLayout이 익숙하기 때문에 많이 사용 하지만 다른 Layout에 비하여 성능이 떨어지는 측면이 있다는걸 알고 있을 겁니다.
LinearLayout 자체가 성능이 떨어지는게 아니라, LinearLayout을 사용 하게 되면 어쩔 수 없이 한 레벨씩 안으로 들어 가면서 UI를 구성하게 됩니다.
모든 UI는 같은 레벨에서 작성될때 성능이 좀 더 나오는데 그런면에서 RelativeLayout를 한동안 사용 하기도 했습니다.
하지만 요세 Device 성능이 워낙 좋아서 정말 많은 레벨을 요구하지 않는 이상 일반적인 앱을 개발할 때에는 크게 신경 안써도 되지 않을까 생각합니다.
같은 레벨에서 작성하는게 조금이라도 성능 개선이 된다고 하니 ConstraintLayout에 대해서 알아보겠습니다.
사실 LinearLayout이 익숙하기 때문에 많이 사용 하지만 다른 Layout에 비하여 성능이 떨어지는 측면이 있다는걸 알고 있을 겁니다.
LinearLayout 자체가 성능이 떨어지는게 아니라, LinearLayout을 사용 하게 되면 어쩔 수 없이 한 레벨씩 안으로 들어 가면서 UI를 구성하게 됩니다.
모든 UI는 같은 레벨에서 작성될때 성능이 좀 더 나오는데 그런면에서 RelativeLayout를 한동안 사용 하기도 했습니다.
하지만 요세 Device 성능이 워낙 좋아서 정말 많은 레벨을 요구하지 않는 이상 일반적인 앱을 개발할 때에는 크게 신경 안써도 되지 않을까 생각합니다.
하지만!! 그래도!! 이왕이면!!
같은 레벨에서 작성하는게 조금이라도 성능 개선이 된다고 하니 ConstraintLayout에 대해서 알아보겠습니다.
오늘 포스팅할 내용은 위에서도 말씀 드렸지만 ConstraintLayout의 전체전인 부분이 아니라 기본적인 속성 입니다.
그 이유는 처음부터 많은 속성을 보고 하나하나 적용 하는것도 의미가 있겠지만,
그것보다는 기본적인 핵심 특성만 보고 프로젝트에 조금씩 적용 하다 보면 점점 ConstraintLayout에 적응할 수 있지 않을까 해서 입니다.
가장 기본적인 속성입니다.
가장 기본적이면서도 가장 많이 사용 하며, 응용 하기에 따라 이정도만 알고 있어도 많은 UI를 만들 수 있습니다.
가장 기본적인 속성입니다.
가장 기본적이면서도 가장 많이 사용 하며, 응용 하기에 따라 이정도만 알고 있어도 많은 UI를 만들 수 있습니다.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"/>
</android.support.constraint.ConstraintLayout>
속성의 의미를 알아보겠습니다.
- app:layout_constraintLeft_toLeftOf=''parent'
: 내 View(여기서는 TextView)의 왼쪽을 부모View의 왼쪽에 붙인다.
끝!
오늘 말하고 싶은 포스팅은 이게 전부 입니다.
저 속성들을 이용해서 하나하나 배치 하다 보면 ConstraintLayout을 사용 하고 있는 자기 자신을 발견할 수 있을 겁니다.
몇가지 예시
app:layout_constraintLeft_toRightOf="@id/anotherView"
: 내 View의 왼쪽을 @id/anotherView 오른쪽에 붙인다.
app:layout_constraintRight_toLeftOf="@id/anotherView"
: 내 View의 오른쪽을 @id/anotherView 왼쪽에 붙인다.
app:layout_constraintTop_toBottomOf="@id/anotherView"
: 내 View의 위를 @id/anotherView 아래에 붙인다.
app:layout_constraintBottom_toTopOf="@id/anotherView"
: 내 View의 아래를 @id/anotherView 위에 붙인다.
이런식으로 만들고자 하는 UI를 만들 수 있습니다.
하나만 더 알고 넘어가죠! 위 속성을 예로 설명 하겠습니다.
ConstrainLayout을 사용 하다보면 알겠지만 margin를 사용할 때 주의 사항 입니다.
margin은 anotherView에서 사용 하는게 아니라 내 View에서 사용해야 됩니다.
다른 Layout 에서는 margin을 사용 하고 싶은 곳에서 사용 하면 좌우로 위아래 어느 방향이던 적용이 되었던 반면
ConstrainLayout에서는 margin으로 인해 직접 영향 받는 뷰에 설정을 해야 됩니다.
말이 어려운거지 margin을 여기저거 적용 하다보면 바로 느낌 오실 겁니다.
2014년 12월 24일 수요일
[Android] 비행기 모드 설정에 관하여...
App 에서 비행기 모드를 ON / OFF 하는 방법은 간단합니다.
■ 비행기 모드의 상태값을 가져오는 메서드
getInt 메서드를 이용하여 현재 비행기 모드의 상태값을 int형으로 리턴 받을 수 있습니다.
리턴값이 0 이면 비행기 모드가 OFF 이며 1 이면 ON 상태 입니다.
설정 후 sendBroadcast(intent)를 통해서 Device 내에 있는 BroadCast를 Intent와 함께 호출해 주면 putExtra를 통해서 넣은 "state"값이 셋팅되게 됩니다.
■ 비행기 모드를 설정하는 메서드
비행기 모드를 설정하는 소스를 보면 JellyBean(API 17) 이전 버전에서만 셋팅을 해주는 것을 확인 할 수 있습니다. JellyBean(API 17) 이후로는 비행기 모드를 직접 셋팅 할 수 없으며 환경설정을 통해서만 변경 할 수 있습니다.
airplaneModeSettings()라는 메소드는 단순히 비행기 모드를 설정 할 수 있는 환경설정 화면을 띄우는 메소드 입니다.
비행기 모드 설정하는 소스와 같이 코딩 하면 문제가 없으나 JellyBean(API 17) 이후에 비행기 모드 값을 세팅하는 메소드를 넣게 되면 아래와 같은 퍼미션을 선언하라고 나오나 Permission is only granted to system apps 라는 메세지가 뜨면서 선언되지 않습니다.
■ JellyBean(API 17) 변경 사항 중 포스트 내용과 관련된 내용입니다.
아래 순서데로 포스팅 하겠습니다.
- 비행기 모드 상태값 알아오기.
- 비행기 모드 설정하기.
- 기타 설정(Manifest) 및 참고.
1. 비행기 모드 상태값 알아오기
private Boolean isAirModeOn() {
Boolean isAirplaneMode;
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1){
isAirplaneMode = Settings.System.getInt(getContentResolver(),
Settings.System.AIRPLANE_MODE_ON, 0) == 1;
}else{
isAirplaneMode = Settings.Global.getInt(getContentResolver(),
Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
}
return isAirplaneMode;
}
Settings.System.AIRPLANE_MODE_ON 이 Deprecated 되어 있는 이유는 JellyBean(API 17) 이후로 Settings.System 안에 있던 비행기 모드 관련 메소드들이 Setting.Global 패키지로 이전 되었졌기 때문입니다. 그렇기 때문에 당연히 버전별로 분기처리를 했습니다. ■ 비행기 모드의 상태값을 가져오는 메서드
Convenience function for retrieving a single secure settings value as an integer.
|
리턴값이 0 이면 비행기 모드가 OFF 이며 1 이면 ON 상태 입니다.
2. 비행기 모드 설정하기
private void settingAirplaneMode (boolean mode){
if (mode){
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1){
Settings.System.putInt(getContentResolver(),
Settings.System.AIRPLANE_MODE_ON, 0);
Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intent.putExtra("state", !mode);
sendBroadcast(intent);
}else{
airplaneModeSettings();
}
}else {
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1){
Settings.System.putInt(getContentResolver(),
Settings.System.AIRPLANE_MODE_ON, 1);
Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intent.putExtra("state", !mode);
sendBroadcast(intent);
}else{
airplaneModeSettings();
}
}
}
마찬가지로 버전별로 분기 처리 후 putInt 메소드를 이용하여 비행기 모드를 셋팅 할 수 있습니다.설정 후 sendBroadcast(intent)를 통해서 Device 내에 있는 BroadCast를 Intent와 함께 호출해 주면 putExtra를 통해서 넣은 "state"값이 셋팅되게 됩니다.
■ 비행기 모드를 설정하는 메서드
Convenience function for updating a single settings value as an integer.
|
airplaneModeSettings()라는 메소드는 단순히 비행기 모드를 설정 할 수 있는 환경설정 화면을 띄우는 메소드 입니다.
3. 기타 설정(Manifest) 및 참고.
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>JellyBean(API 17) 이전에는 WRITE_SETTINGS 퍼미션만 설정해 주면 됩니다.
비행기 모드 설정하는 소스와 같이 코딩 하면 문제가 없으나 JellyBean(API 17) 이후에 비행기 모드 값을 세팅하는 메소드를 넣게 되면 아래와 같은 퍼미션을 선언하라고 나오나 Permission is only granted to system apps 라는 메세지가 뜨면서 선언되지 않습니다.
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>시스템 승인이 안된 퍼미션은 사용 할 수 없으므로 WRITE_SECURE_SETTINGS 퍼미션은 사용 할 수 없습니다. 위에서 말했듯이 이와 같은 문제로 인하여 JellyBean(API 17) 이후로는 비행기 모드를 직접적으로 셋팅 할 수 없습니다.
■ JellyBean(API 17) 변경 사항 중 포스트 내용과 관련된 내용입니다.
New Global Settings
The system settings have been updated to support multiple users with the addition of
Settings.Global. This collection of settings is similar to Settings.Secure settings because they are read-only, but applies globally across all user spaces on the device.
Several existing settings were relocated here from either
Settings.System or Settings.Secure. If your app is currently making changes to settings previously defined in Settings.System (such as AIRPLANE_MODE_ON), then you should expect that doing so will no longer work on a device running Android 4.2 or higher if those settings were moved to Settings.Global. You can continue to read settings that are in Settings.Global, but because the settings are no longer considered safe for apps to change, attempting to do so will fail silently and the system will write a warning to the system log when running your app on Android 4.2 or higher.2013년 5월 30일 목요일
[Java] Json는 Gson으로...(서버부터 안드로이드까지)
Android App을 개발하면서 정말 많이 쓰는 기술 가운데 하나가 Json 처리 입니다. 예전에는 XML을 많이 사용 하였는데 요세는 XML보다는 가벼운 Json을 많이 사용 하는거 같습니다. 저는 주로 Android에서만 작업을 하기 때문에 Json 처리를 별도의 라이브러리를 사용하지 않고 JsonObeject 와 JsonArray를 사용하여 개발해 왔었습니다. 그러던 중에 간단한 테스트를 하려고 제 데스크탑에 서버 설정을 하면서 Gson을 처음 사용해 봤는데 정말 편하고 좋았습니다. 다만 서버개발자와 서로 맞춰야 될게 몇가지가 있습니다.
먼저 Gson을 사용 하려면 라이브러리를 받아서 등록해야 됩니다. 서버든 Android든 라이브러리 등록 방법은 따로 설명드리지 않겠습니다. 서버에서 Gson을 사용하여 Json형태로 만드는 방법과 Android에서 Gson을 사용하여 Json 을 처리하는 과정을 순차적으로 포스팅 하겠습니다.
Gson 라이브러리 다운로드
Gson User Guide
서버 Gson
대략적인 소스 설명을 하면 Intro bean 객체에 데이터를 넣어서 names라는 ArrayList<intro>에 넣습니다. Gson에서는 데이터를 만들때는 toJson(object) 메서드를 사용합니다. 이러면 Json포맷이 만들어 졌습니다. 정말 간단하게 Json을 만들 수 있는걸 볼 수 있습니다. Gson의 생성 방법은 2가지가 있습니다. new GsonBuilder().setPrettyPrinting().create();와 같이 생성하면 Json양식이 갈끔하게 보이게 되고 new Gson();으로 생성하게 되면 보통의 Json 처럼 직렬로 보이게 됩니다.
GsonBuilder().setPrettyPrinting().create() 로 생성
new Gson()로 생성
Gson 라이브러리를 이용하여 Json 포맷을 만드는 코드를 살펴 보았으니 이제 Android에서 Gson 라이브러리를 이용하여 Json 포맷의 데이터를 어떻게 사용하는지 포스팅하겠습니다.
Gson을 사용할때 한가지 주의 사항은 위에서도 언급했듯이 서버와 클라이언트가 서로 맞추어야 되는 부분이 있습니다. 바로 Namming입니다. ArrayList<Name>를 만들때는 서버에서 사용하고 있는 변수명인 names로 맞춰주고 Bean 파일의 변수명도 서버와 동일하게 맞춰 줍니다. ArrayList<Name>의 변수명이 서버와 다른 경우 데이터가 ArrayList<Name>에 들어가지 않습니다.
Android Gson
먼저 Gson을 사용 하려면 라이브러리를 받아서 등록해야 됩니다. 서버든 Android든 라이브러리 등록 방법은 따로 설명드리지 않겠습니다. 서버에서 Gson을 사용하여 Json형태로 만드는 방법과 Android에서 Gson을 사용하여 Json 을 처리하는 과정을 순차적으로 포스팅 하겠습니다.
Gson 라이브러리 다운로드
Gson User Guide
서버 Gson
public class Intro {
private int nIndex;
private String nFirstName;
private String nSecondName;
private String nThirdName;
public Intro(int nIndex, String nFirstName, String nSecondName,
String nThirdName) {
this.nIndex = nIndex;
this.nFirstName = nFirstName;
this.nSecondName = nSecondName;
this.nThirdName = nThirdName;
}
}
public class Intros {
public ArrayList names = new ArrayList();
}
String json = null;
String[] nName = {"ㄱ","ㄴ","ㄷ","ㄹ","ㅁ","ㅂ","ㅅ","ㅇ","ㅈ","ㅊ","ㅋ","ㅌ","ㅍ","ㅎ"};
Gson gson = new GsonBuilder().setPrettyPrinting().create();
// Gson gson = new Gson();
Intros model = new Intros();
for (int i = 0; i < nName.length; i++) {
model.names.add(new Intro(i, nName[i], nName[i], nName[i]));
}
json = gson.toJson(model);
System.out.println(json);
대략적인 소스 설명을 하면 Intro bean 객체에 데이터를 넣어서 names라는 ArrayList<intro>에 넣습니다. Gson에서는 데이터를 만들때는 toJson(object) 메서드를 사용합니다. 이러면 Json포맷이 만들어 졌습니다. 정말 간단하게 Json을 만들 수 있는걸 볼 수 있습니다. Gson의 생성 방법은 2가지가 있습니다. new GsonBuilder().setPrettyPrinting().create();와 같이 생성하면 Json양식이 갈끔하게 보이게 되고 new Gson();으로 생성하게 되면 보통의 Json 처럼 직렬로 보이게 됩니다.
GsonBuilder().setPrettyPrinting().create() 로 생성
{
"names": [
{
"nIndex": 0,
"nFirstName": "ㄱ",
"nSecondName": "ㄱ",
"nThirdName": "ㄱ"
},
{
"nIndex": 1,
"nFirstName": "ㄴ",
"nSecondName": "ㄴ",
"nThirdName": "ㄴ"
},
{
"nIndex": 2,
"nFirstName": "ㄷ",
"nSecondName": "ㄷ",
"nThirdName": "ㄷ"
},
......... 이하 생략 .........
]
}
new Gson()로 생성
{"names":[{"nIndex":0,"nFirstName":"ㄱ","nSecondName":"ㄱ","nThirdName":"ㄱ"},{"nIndex":1,"nFirstName":"ㄴ","nSecondName":"ㄴ","nThirdName":"ㄴ"},{"nIndex":2,"nFirstName":"ㄷ","nSecondName":"ㄷ","nThirdName":"ㄷ"},
......... 이하 생략 .........
{"nIndex":13,"nFirstName":"ㅎ","nSecondName":"ㅎ","nThirdName":"ㅎ"}]}
Gson 라이브러리를 이용하여 Json 포맷을 만드는 코드를 살펴 보았으니 이제 Android에서 Gson 라이브러리를 이용하여 Json 포맷의 데이터를 어떻게 사용하는지 포스팅하겠습니다.
Gson을 사용할때 한가지 주의 사항은 위에서도 언급했듯이 서버와 클라이언트가 서로 맞추어야 되는 부분이 있습니다. 바로 Namming입니다. ArrayList<Name>를 만들때는 서버에서 사용하고 있는 변수명인 names로 맞춰주고 Bean 파일의 변수명도 서버와 동일하게 맞춰 줍니다. ArrayList<Name>의 변수명이 서버와 다른 경우 데이터가 ArrayList<Name>에 들어가지 않습니다.
Android Gson
public class Name {
private int nIndex;
private String nFirstName;
private String nSecondName;
private String nThirdName;
public int getnIndex() {
return nIndex;
}
public void setnIndex(int nIndex) {
this.nIndex = nIndex;
}
public String getnFirstName() {
return nFirstName;
}
public void setnFirstName(String nFirstName) {
this.nFirstName = nFirstName;
}
public String getnSecondName() {
return nSecondName;
}
public void setnSecondName(String nSecondName) {
this.nSecondName = nSecondName;
}
public String getnThirdName() {
return nThirdName;
}
public void setnThirdName(String nThirdName) {
this.nThirdName = nThirdName;
}
}
public class Names {
private ArrayList names = new ArrayList();
public ArrayList getnList() {
return names;
}
public void setnList(ArrayList names) {
this.names = names;
}
}
String introJson = HttpConnecter.getJson(url주소); FLLog.e(TAG, "introJson : " + introJson); this.mNames = new Gson().fromJson(introJson, Names.class); FLLog.e(TAG, "mNames : " + mNames.getnList().size());Bean파일을 보면 변수명은 똑같은걸 확인 할 수 있습니다. 다만 서버에서는 데이터를 넣기만 하면 되기때문에 생성자에서 세팅되게 하였으며 Android에서는 가져다가 써야 하므로 Getter and Setter를 만들어준 차이 밖에 없습니다. HttpConnecter.getJson(url주소)를 이용하여 Json 데이터를 가지고 옵니다. 소스에서는 보이지 않지만 당연히 Thread를 이용하여 데이터를 가지고 오고 있습니다. 가지고온 데이터는 introJson 이라는 String 변수에 저장을 했습니다. Gson으로 생성된 Json 포맷을 받을때는 formJson(data, format)으로 받으면 됩니다. JsonObject를 사용 할 때 처럼 하나하나 받아주지 않고 이런식으로 한번에 통으로 list에 넣을 수가 있습니다.
2013년 5월 20일 월요일
[Android] ActionBar Tabs
Android UI를 구성할때 많이 사용하는 것이 Tab기능입니다. 이런 Tab기능을 ActionBar에서 사용 할 수 있습니다. 대표적으로 Google Play 나 YouTube을 보시면 확인 할 수 있습니다.
ActionBar에서 Tab 기능을 사용하면 ActionBar의 여유공간에 따라 Tabs의 위치가 달라질 수 있습니다.
ActionBar에 Tab 추가
TabListener를 implements 받아서 처리 하면 아래와 같은 메서드를 Override하게 됩니다.
참조
ActionBar에서 Tab 기능을 사용하면 ActionBar의 여유공간에 따라 Tabs의 위치가 달라질 수 있습니다.
ActionBar에 Tab 추가
- ActionBar에 Tab을 추가하기 위해서는 getActionBar()을 이용하여 ActionBar를 가지고 온다.
- bar.addTab(bar.newTab().setText("Tab 1").setTabListener(this));를 사용하여 Tab을 추가한다
- bar.setNavigationMode()를 설정해 준다.(ActionBar.NAVIGATION_MODE_TABS로 설정해 주시면 Tabs 영역이 나타나게 된다.)
- setTabListener를 사용하여 TabListener를 등록 해준다.
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_navigator);
findViewById(R.id.btnNavi01).setOnClickListener(this);
final ActionBar bar = getActionBar();
bar.addTab(bar.newTab().setText("Tab 1").setTabListener(this));
bar.addTab(bar.newTab().setText("Tab 2").setTabListener(this));
bar.addTab(bar.newTab().setText("Tab 3").setTabListener(this));
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
final ActionBar bar = getActionBar();
int flags = 0;
switch (v.getId()) {
case R.id.btnNavi01:
bar.setNavigationMode(bar.getNavigationMode() == ActionBar.NAVIGATION_MODE_STANDARD ? ActionBar.NAVIGATION_MODE_TABS : ActionBar.NAVIGATION_MODE_STANDARD);
return;
}
int change = bar.getDisplayOptions() ^ flags;
bar.setDisplayOptions(change, flags);
}
TabListener를 implements 받아서 처리 하면 아래와 같은 메서드를 Override하게 됩니다.
@Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
Log.d(TAG, "onTabReselected " + tab.getText());
}
@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
Log.d(TAG, "onTabSelected " + tab.getText());
}
@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
Log.d(TAG, "onTabUnselected " + tab.getText());
}
ActionBar에서 Tab을 추가하는 것을 간단하게 포스팅 하였습니다. 실제 App을 개발할때는 Fragment와 결합하여 사용하게 되며 ViewPager와 연계하여 Swipe기능을 사용하여 개발 할 수도 있습니다.참조
2013년 4월 25일 목요일
[Android] APK Decompile (APK 추출)
안드로이드 앱을 보다보면 어떻게 만들었는지 궁금 할 때가 있을 겁니다. 이럴때 Apk Decompile을 통해서 전체적인 코드를 확인 하실 수가 있습니다. Apk Decompile이 가능하다고 악용하시지 마시고요 자기발전을 통한 재능기부로 이여졌으면 합니다. 참고로 Apk Decompile은 Mac, Window, Linux 에 따라 JD-GUI만 OS별로 설치하시면 나머지는 똑같습니다.
Apk Decompile을 하긴 위한 전체적인 프로세스
Es File Explorer 앱을 실행 시킨 후 메뉴를 클릭하시면 Facebook 메뉴처럼 이동하면서 그림과 같은 화면이 나타납니다. 메뉴 화면에서 Tools을 클릭하시고 App Manager를 클릭하시면 디바이스에 설치되어있는 앱들이 나타나게 됩니다. 그중 Decompile을 하고 싶은 앱을 Long Click을 하시면 앱이 선택되면서 하단 메뉴도 바뀝니다. 그중에 Backup 메뉴를 클릭하시면 Apk 파일이 자동으로 저장됩니다. 저장된 Apk 파일은 backups 폴더에 자동으로 저장되어 집니다. 이제 저장된 폴더를 작업하시고자 하시는 컴퓨터로 가지고 오시면 됩니다.
변환 될 파일위치로 가셔서 확인해 보시면 바이너리 코드로 있어던 xml 파일이 변환되어 보입니다. 완벽하게 변환 되는것은 아니면 값들은 코드값으로 보여지게 됩니다.
Apk Decompile을 하긴 위한 전체적인 프로세스
Apk Decompile을 하기 위해서는 우선 Apk 파일이 필요하겠습니다. Apk 파일을 추출 하는 방법은 여러 방법이 있으나 저는 Es File Explorer 앱을 이용하여 Apk 파일을 추출하는 방법을 소개시켜 드리겠습니다.(Astro, ApkManager,ApkTool 등등.. Apk 파일을 추출하는 방법은 많이 있습니다.) Apk 파일 추출 후 Apk 파일를 압축파일로 변환하고 압축을 풀어 dex2jar를 이용하여 classes.dex 파일을 jar로 만들어면 JD-GUI를 통해 보실 수가 있습니다. XML은 바이너리 코드로 보여지므로 AXMLPrinter2.jar를 통한 XML 코드 변환까지 포스팅 하겠습니다.
간단히 순서를 요약해 보면..
- APK 파일 추출
- APK 파일을 zip으로 변환 후 압축풀기
- dex2jar를 이용해 classes.dex를 jar로 변환(dex2jar 다운받기)
- JD-GUI 툴을 통해 java 소스보기(JD-GUI 다운받기기)
- AXMLPrinter2.jar를 통해 XML 보기 (AMLPrinter2.jar 다운받기)
APK 추출
Google Play에서 Es File Explorer를 다운로드 받습니다. Astro에서도 가능 합니다만 제가 Es File Explorer를 사용하고 있는 관계로 Es File Explorer로 설명 드리겠습니다. 사실 Apk 파일을 추출하는 용도로 사용 할 것이기 때문에 다른 방법으로 Apk 파일을 추출하실 수 있는 분이시나 이미 Apk 파일이 있으신 분들은 굳이 다운로드 받지 않아도 상관없습니다. Es File Explorer의 Backup기능을 이용 하여 Apk 파일을 추출 해야 되나 최근에 Es File Explorer의 업데이트로 인해 UI 변경이 있어으니 Backup 기능을 사용 하실 줄 모르시면 아래 글을 참조하면 됩니다.
Es File Explorer 앱을 실행 시킨 후 메뉴를 클릭하시면 Facebook 메뉴처럼 이동하면서 그림과 같은 화면이 나타납니다. 메뉴 화면에서 Tools을 클릭하시고 App Manager를 클릭하시면 디바이스에 설치되어있는 앱들이 나타나게 됩니다. 그중 Decompile을 하고 싶은 앱을 Long Click을 하시면 앱이 선택되면서 하단 메뉴도 바뀝니다. 그중에 Backup 메뉴를 클릭하시면 Apk 파일이 자동으로 저장됩니다. 저장된 Apk 파일은 backups 폴더에 자동으로 저장되어 집니다. 이제 저장된 폴더를 작업하시고자 하시는 컴퓨터로 가지고 오시면 됩니다.- Es File Explorer 다운
- Fast Access 버튼 클릭 (실행하자마자 왼쪽 상단버튼, 그림으로는 녹색화면 화면)
- Tools 클릭
- App Manager 클릭
- Apk 파일을 추출하고 하는 App Long Click
- 하단 Backup 버튼 클릭
Apk 파일을 컴퓨터로 가지고 오셨으면 이제 Apk 파일을 zip으로 변경해 주셔야됩니다. 변경방법은 확장자를 .zip으로 바꿔주시면 됩니다. app.apk 을 app.zip으로 바꿔주시면 zip파일로 변환 되며 바로 압축을 풀어 줍니다.
dex2jar사용하기
다운받은 dex2jar의 압축을 풉니다. 이번 단계에서 풀어놓았던 Apk 파일의 압축파일을 푼 폴더를 보면 src 폴더가 보이지가 않습니다. XML 파일은 다 바이너리 코드로 보일 거구요. 이제부터 하나하나 Decompile를 해보도록 하겠습니다.
터미널을 여시고 다운 받은 dex2jar 폴더로 이동하셔서 아래의 명령어를 입력합니다. 참고로 저는 Mac에서 Decompile 를 하고 있어서 경로를 Mac 경로를 입력하였습니다. Window에서는 Window 경로를 입력해 주시면 됩니다.아래 경로는 classes.dex의 경로 이니 OS에 맞게 경로를 입력해 주시면 됩니다. 아래 명령어를 실행시키면 그림 처럼 해당 폴더에 classes_dex2jar.jar파일이 생성된걸 볼 수 있습니다. 이제 JD-GUI를 이용하여 .java파일을 보시기만 하면 됩니다.
sh [dex2jar.sh 경로] [classes.dex파일 경로]
sh [dex2jar.sh 경로] [classes.dex파일 경로]
sh dex2jar.sh /Users/Downloads/appFolder/classes.dex
JD-GUI 사용하기
위의 주소로 들어가면 그림과 같은 화면이 나옵니다.
여기서 해당 OS에 맞는 JD-GUI를 다운로드 받으시면 됩니다. 저는 이것도 모르고 Mac에서는 다른 특별한 방법이 있는 줄 알고 한참 구글링 했었습니다. 하지만 별거 없었습니다. 그냥 JD-GUI 툴만 OS별로 다운 로드 받아서 보시면 됩니다. 그러니 Mac이나 Linux를 사용하시는 분들도 JD-GUI만 작업하시는 OS버전으로 다운 받아서 보면 됩니다. 저는 Mac을 사용하니 Mac 버전을 다운 받았습니다.
JD-GUI을 실행 시키고 classes_dex2jar.jar를 open 하시면 앱의 package부터 .java 파일 까지 전부 확인 하 실 수 있습니다. 이제 마지막으로 xml을 복구해 보도록 하겠습니다.
JD-GUI을 실행 시키고 classes_dex2jar.jar를 open 하시면 앱의 package부터 .java 파일 까지 전부 확인 하 실 수 있습니다. 이제 마지막으로 xml을 복구해 보도록 하겠습니다.
AXMLPrinter2.jar 사용하기
위의 주소에서 AXMLPrinter2.jar를 다운받은 후 터미널에서 AXMLPrinter2.jar가 있는 경로로 들어가 줍니다. 그후 아래와 같이 입력하시면 xml이 보여집니다.
java -jar [AXMLPrinter2.jar] [xml 파일 위치] > [변환 될 파일 위치]
java -jar AXMLPrinter2.jar /Users/aaaaa/Downloads/call_in.xml > /Users/aaaaa/Downloads/asdfg/call_in.xml
위의 주소에서 AXMLPrinter2.jar를 다운받은 후 터미널에서 AXMLPrinter2.jar가 있는 경로로 들어가 줍니다. 그후 아래와 같이 입력하시면 xml이 보여집니다.
java -jar [AXMLPrinter2.jar] [xml 파일 위치] > [변환 될 파일 위치]
java -jar AXMLPrinter2.jar /Users/aaaaa/Downloads/call_in.xml > /Users/aaaaa/Downloads/asdfg/call_in.xml
변환 될 파일위치로 가셔서 확인해 보시면 바이너리 코드로 있어던 xml 파일이 변환되어 보입니다. 완벽하게 변환 되는것은 아니면 값들은 코드값으로 보여지게 됩니다.
2013년 4월 24일 수요일
[Android] Action Bar Usage (Action Bar 사용방법 예제)
ActionBar 사용방법에 대한 예제가 Open Source에 있어서 포스팅 하려고 합니다. 소스에 특별한 내용은 없으나 우리가 Action Bar를 사용 할 때 생각해야 될 부분 중에 하나인 작은 화면에서의 Action Bar의 item을 효율에 관한 소스라고 생각 되어 포스팅 합니다. Action Bar내용이 아닌 소스 내용은 주석을 참고 해서 보시면 됩니다. Open Source 클래스 중 ActionBarUsage클래스 내용입니다.
id가 action_sort인 item은 서브 메뉴 아이템을 가지고 있는것을 보실 수 있습니다.
참조
<menu xmlns:android=”http://schemas.android.com/apk/res/android”>
<item android:id=”@+id/action_search”
android:icon=”@android:drawable/ic_menu_search”
android:title=”@string/action_bar_search”
android:showAsAction=”ifRoom”
android:actionViewClass=”android.widget.SearchView” />
<item android:id=”@+id/action_add”
android:icon=”@android:drawable/ic_menu_add”
android:title=”@string/action_bar_add”
android:showAsAction=”collapseActionView”/>
<item android:id=”@+id/action_edit”
android:icon=”@android:drawable/ic_menu_edit”
android:showAsAction=”always”
android:title=”@string/action_bar_edit” />
<item android:id=”@+id/action_share”
android:icon=”@android:drawable/ic_menu_share”
android:title=”@string/action_bar_share”
android:showAsAction=”ifRoom” />
<item android:id=”@+id/action_sort”
android:icon=”@android:drawable/ic_menu_sort_by_size”
android:title=”@string/action_bar_sort”
android:showAsAction=”ifRoom”>
<menu>
<item android:id=”@+id/action_sort_size”
android:icon=”@android:drawable/ic_menu_sort_by_size”
android:title=”@string/action_bar_sort_size”
android:onClick=”onSort” />
<item android:id=”@+id/action_sort_alpha”
android:icon=”@android:drawable/ic_menu_sort_alphabetically”
android:title=”@string/action_bar_sort_alpha”
android:onClick=”onSort” />
</menu>
</item>
</menu>
SearchView를 사용 하기 위해서 android:actionViewClass를 사용 하여 Android에서 제공하고 있는 android.widget.SearchView 경로를 사용한 것을 확인 할 수 있습니다. SearchViw는 ShareActionProvider와 마찬가지로 Android에서 제공하고 있는 클래스를 가져다가 사용만 하면 됩니다. android:showAsAction의 값이 "ifRoom"으로 설정하면 item들이 순차적으로 Action Bar에 보이다가 공간이 부족하면 Menu 버튼을 클릭해야지 보이는걸 확인 할 수 있습니다.id가 action_sort인 item은 서브 메뉴 아이템을 가지고 있는것을 보실 수 있습니다.
public class ActionBarUsage extends Activity implements OnQueryTextListener {
private static final String TAG = "ActionBar_Api_Usage";
private TextView mSearchText;
private int mSortMode = -1;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
mSearchText = new TextView(this);
setContentView(mSearchText);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// TODO Auto-generated method stub
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.actions, menu);
// Action Bar에서 SearchView를 보여주고 싶을때 사용하는 클래스입니다.
SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
searchView.setOnQueryTextListener(this);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
Toast.makeText(this, "Selected Item: " + item.getTitle(),
Toast.LENGTH_SHORT).show();
return true;
}
// Menu의 sort item에서 android:onClick="onSort" 기능으로 호출한다.
public void onSort(MenuItem item) {
mSortMode = item.getItemId();
// onPrepareOptionMenu를 호출 할 수 있어서 sort icon을 바꿀 수 있다.
invalidateOptionsMenu();
}
// invalidateOptionsMenu()메서드를 실행하면 호출된다.
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// TODO Auto-generated method stub
if (mSortMode != -1) {
Drawable icon = menu.findItem(mSortMode).getIcon();
menu.findItem(R.id.action_sort).setIcon(icon);
}
return super.onPrepareOptionsMenu(menu);
}
// SearchView에 텍스트를 입력하면 TextView에 바로 적용된다.
@Override
public boolean onQueryTextChange(String newText) {
// TODO Auto-generated method stub
newText = newText.isEmpty() ? "" : "Query so far : " + newText;
mSearchText.setText(newText);
return false;
}
// Submit을 누르면 onQueryTextSubmit 메소드가 호출된다.
@Override
public boolean onQueryTextSubmit(String query) {
// TODO Auto-generated method stub
Toast.makeText(this, "Searching for: " + query + "...",
Toast.LENGTH_SHORT).show();
return true;
}
}
참조
[Android] Action Bar ShareActionProvider( 공유 )
ShareActionProvider
ShareActionProvider는 공유를 위한 Provider입니다. 데이터를 공유할수 있고, 만약 overflow 메뉴에 배치된다면 서브 메뉴에서 공유 activity들을 보여 줍니다.
ShareActionProvider는 ActionProvider를 상속 받고 있는 클래스 이며, Action Bar에서 공유하는 기능을 담당하고, 간단하게 공유를 사용 할 수 있게 해줍니다.
이번 포스팅에서는 Open Source 중 ActionBarShareActionProviderActivity에 대해 살펴 볼려고 합니다.
이전 포스팅한 Action Bar ActionProvider와 같은 구조로 되어 있습니다. 다만 한가지 차이점은 ActionProvider를 사용하기 위해 설정해 줘야 했던 android:actionProviderClass의 경로가 안드로이드에서 기본으로 제공하고 있는 android.widget.ShareActionProvider로 설정해야 됩니다. 당연한 말이지만 android.widget.ShareActionProvider는 ShareActionProvider의 경로 입니다. Action Bar ActionProvider포스팅에서는 직접 ActionProvider를 상속받아 구현한 클래스가 있었으므로 ActionProvider를 상속받은 클래스의 경로를 사용 했었습니다. 상단 item 의 android:showAsAction 값은 "always"이며 하단 item의 android:showAsAction 값은 "never"입니다. "always"는 항상 ActionBar에 보여진다는 뜻이며 "never" Action Bar에 보여지지 않는다는 뜻입니다.
ShareActionProvider를 사용 할때는 onCreateOptionsMenu(Menu menu)에서 이미 ActionProvider를 상속받고 있는 ShareActionProvider 클래스를 사용하면 됩니다.
MenuItem으로 가져온 Menu의 item을 ShareActionProvider로 가져온 후 setShareHistoryFileName(ShareActionProvider.DEFAULT_SHARE_HISTORY_FILE_NAME);를 통해 공유 기록을 저장할 파일 이름을 설정하고 setShareIntent로 공유 할 앱들을 설정한 후 호출하면 된다.(DEFAULT_SHARE_HISTORY_FILE_NAME가 기본)
참조
ShareActionProvider는 공유를 위한 Provider입니다. 데이터를 공유할수 있고, 만약 overflow 메뉴에 배치된다면 서브 메뉴에서 공유 activity들을 보여 줍니다.
ShareActionProvider는 ActionProvider를 상속 받고 있는 클래스 이며, Action Bar에서 공유하는 기능을 담당하고, 간단하게 공유를 사용 할 수 있게 해줍니다.
이번 포스팅에서는 Open Source 중 ActionBarShareActionProviderActivity에 대해 살펴 볼려고 합니다.
<menu xmlns:android=”http://schemas.android.com/apk/res/android”>
<item android:id=”@+id/menu_item_share_action_provider_action_bar”
android:showAsAction=”always”
android:title=”@string/btn_api_provider_share”
android:actionProviderClass=”android.widget.ShareActionProvider” />
<item android:id=”@+id/menu_item_share_action_provider_overflow”
android:showAsAction=”never”
android:title=”@string/btn_api_provider_share”
android:actionProviderClass=”android.widget.ShareActionProvider” />
</menu>
이전 포스팅한 Action Bar ActionProvider와 같은 구조로 되어 있습니다. 다만 한가지 차이점은 ActionProvider를 사용하기 위해 설정해 줘야 했던 android:actionProviderClass의 경로가 안드로이드에서 기본으로 제공하고 있는 android.widget.ShareActionProvider로 설정해야 됩니다. 당연한 말이지만 android.widget.ShareActionProvider는 ShareActionProvider의 경로 입니다. Action Bar ActionProvider포스팅에서는 직접 ActionProvider를 상속받아 구현한 클래스가 있었으므로 ActionProvider를 상속받은 클래스의 경로를 사용 했었습니다. 상단 item 의 android:showAsAction 값은 "always"이며 하단 item의 android:showAsAction 값은 "never"입니다. "always"는 항상 ActionBar에 보여진다는 뜻이며 "never" Action Bar에 보여지지 않는다는 뜻입니다.
public class ActionBarShareActionProviderActivity extends Activity {
private static final String TAG = "ActionBar_Api_Provider_Share";
private static final String SHARED_FILE_NAME = "shared.png";
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
copyPrivateRawResuorceToPubliclyAccessibleFile();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// TODO Auto-generated method stub
getMenuInflater().inflate(R.menu.action_bar_share_action_provider, menu);
MenuItem actionitem = menu.findItem(R.id.menu_item_share_action_provider_action_bar);
ShareActionProvider actionProvider = (ShareActionProvider) actionitem.getActionProvider();
actionProvider.setShareHistoryFileName(ShareActionProvider.DEFAULT_SHARE_HISTORY_FILE_NAME);
actionProvider.setShareIntent(createShareIntent());
MenuItem overflowItem = menu.findItem(R.id.menu_item_share_action_provider_overflow);
ShareActionProvider overflowprovider = (ShareActionProvider) overflowItem.getActionProvider();
overflowprovider.setShareHistoryFileName(ShareActionProvider.DEFAULT_SHARE_HISTORY_FILE_NAME);
overflowprovider.setShareIntent(createShareIntent());
return true;
}
private Intent createShareIntent() {
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.setType("image/*");
Uri uri = Uri.fromFile(getFileStreamPath("shared.png"));
shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
return shareIntent;
}
private void copyPrivateRawResuorceToPubliclyAccessibleFile() {
InputStream is = null;
OutputStream os = null;
try {
is = getResources().openRawResource(R.raw.robot);
os = openFileOutput(SHARED_FILE_NAME, Context.MODE_APPEND);
byte[] buffer = new byte[1024];
int length = 0;
while ((length = is.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
} catch (Exception e) {
// TODO: handle exception
Log.e(TAG, "Exception : " + e);
} finally {
try {
is.close();
os.close();
} catch (Exception e2) {
// TODO: handle exception
Log.e(TAG, "Exception : " + e2);
}
}
}
}
copyPrivateRawResuorceToPubliclyAccessibleFile() 메서드는 raw 폴더안의 robot.png 파일을 device안에 저장하는 간단한 소스 입니다.ShareActionProvider를 사용 할때는 onCreateOptionsMenu(Menu menu)에서 이미 ActionProvider를 상속받고 있는 ShareActionProvider 클래스를 사용하면 됩니다.
MenuItem으로 가져온 Menu의 item을 ShareActionProvider로 가져온 후 setShareHistoryFileName(ShareActionProvider.DEFAULT_SHARE_HISTORY_FILE_NAME);를 통해 공유 기록을 저장할 파일 이름을 설정하고 setShareIntent로 공유 할 앱들을 설정한 후 호출하면 된다.(DEFAULT_SHARE_HISTORY_FILE_NAME가 기본)
참조
2013년 4월 23일 화요일
[Android] Action Bar ActionProvider
ActionProvider
ActionProvider는 하나의 component 안에서 다양한 메뉴의 상호작용을 의미한다. ActionProvider는 Action Bar에서 사용 할 수 있는 Action View를 생성하며, MenuItem에 서브메뉴를 동적으로 채운다. 그리고 기본 메뉴의 아이템을 처리합니다. ActionProvider는 MenuItem만을 위한 옵션을 설정할 수도 있으며, 간단한 버튼을 표시할때는 Action View를 만들어 표시 할 것이다.
ActionProvider는 추상 메서드로서 필요한 메서드를 Override해서 사용 할 수 있습니다. API Level 16부터 onCreateActionView()는 deprecated 되었으며 onCreateActionView(MenuItem forItem)이 추가 되었습니다. onCreateActionView() 메서드를 사용 하려면 android:targetSdkVersion을 16미만으로 적용 하시면 됩니다. onCreateActionView(MenuItem forItem)에서 MenuItem이 매개변수로 추가된 이유는 MenuItem을 재정의 할 수 있게 할려고 한게 아닌가 싶습니다. onCreateOptionsMenu(Menu menu)에서 정의된 Menu를 사용 할 수도 있겠지만 MenuItem만을 위한 재정의를 할 수 있게 추가 된게 아닐까 생각합니다. targetSdkVersion만 맞춰준다면 현재로서는 어떤 메서드를 사용하든지 상관 없으나 이왕이면 deprecated된 메서드 보다는 새로 추가 된 메서드를 사용 하시는걸 추천 합니다.
이번 포스팅에서는 Open Source 중 ActionBarSettingsActionProviderActivity에 대해 살펴 볼려고 합니다.
android:actionProviderClass를 설정하면 ActionProvider를 사용 할 수 있습니다. ActionProvider를 extends 하고 있는 클래스 경로명을 적어주면 되며, setActionProvider(ActionProvider)를 사용하여 자바코드에서도 설정해 줄 수 있습니다. 만약 공간이 부족해서 Overflow부분으로 빠진다면 텍스트가 보여져야 되므로 이왕이면 Title은 각 item마다 설정해 주는게 좋습니다.
소스에서는 onCreateActionView(MenuItem forItem)에서 inflate해준 view에 클릭 이벤트를 줬습니다. 이 결과 onPerformDefaultAction()와 onOptionsItemSelected(MenuItem item)가 호출 되지 않았으며 바로 onClick(View v)메서드가 호출되는 것을 확인 하였습니다. 간단하게 onCreateActionView(MenuItem forItem)메서드에서 만들어 놓은 레이아웃을 inflate 해주고 바로 이벤트를 걸어 사용 하면 되지 않을까 생각됩니다. onCreateActionView(MenuItem forItem)에서 버튼 하나를 만들어서 붙여 본 결과 onCreateOptionsMenu(Menu menu)에서 추가된 item 개수만큼 버튼이 생기는걸 확인 할 수 있었습니다.(물론 Action Bar에서 보여 질수 있는 item에 한해서 말하는 것입니다.)
참조
ActionProvider는 하나의 component 안에서 다양한 메뉴의 상호작용을 의미한다. ActionProvider는 Action Bar에서 사용 할 수 있는 Action View를 생성하며, MenuItem에 서브메뉴를 동적으로 채운다. 그리고 기본 메뉴의 아이템을 처리합니다. ActionProvider는 MenuItem만을 위한 옵션을 설정할 수도 있으며, 간단한 버튼을 표시할때는 Action View를 만들어 표시 할 것이다.
ActionProvider는 추상 메서드로서 필요한 메서드를 Override해서 사용 할 수 있습니다. API Level 16부터 onCreateActionView()는 deprecated 되었으며 onCreateActionView(MenuItem forItem)이 추가 되었습니다. onCreateActionView() 메서드를 사용 하려면 android:targetSdkVersion을 16미만으로 적용 하시면 됩니다. onCreateActionView(MenuItem forItem)에서 MenuItem이 매개변수로 추가된 이유는 MenuItem을 재정의 할 수 있게 할려고 한게 아닌가 싶습니다. onCreateOptionsMenu(Menu menu)에서 정의된 Menu를 사용 할 수도 있겠지만 MenuItem만을 위한 재정의를 할 수 있게 추가 된게 아닐까 생각합니다. targetSdkVersion만 맞춰준다면 현재로서는 어떤 메서드를 사용하든지 상관 없으나 이왕이면 deprecated된 메서드 보다는 새로 추가 된 메서드를 사용 하시는걸 추천 합니다.
이번 포스팅에서는 Open Source 중 ActionBarSettingsActionProviderActivity에 대해 살펴 볼려고 합니다.
<?xml version=”1.0” encoding=”utf-8”?>
<menu xmlns:android=”http://schemas.android.com/apk/res/android”>
<item android:id=”@+id/menu_item_action_provider_action_bar”
android:showAsAction=”ifRoom”
android:title=”@string/btn_api_provider_setting”
android:actionProviderClass=”com.example.wawoops_test_actionbar.ActionBar_Api_Provider_Setting$SettingActionProvider”/>
<item android:id=”@+id/menu_item_action_provider_overflow”
android:showAsAction=”never”
android:title=”@string/btn_api_provider_setting”
android:actionProviderClass=”com.example.wawoops_test_actionbar.ActionBar_Api_Provider_Setting$SettingActionProvider”/>
</menu>
Menu를 보면 item이 2개인 것을 보실 수 있습니다. 여기서 주의깊에 봐야 할 부분은 android:showAsAciton부분입니다. 첫번째 item은 "ifRoom"으로 되어있고 두번째 item은 "never"로 되어있습니다. 첫번째 item은 만약 ActionBar에 공간이 있다면 표시하라는 뜻이며 두번째 item의 "never"는 overflow 영역으로 무조건 빼라는 뜻입니다. 즉 화면서 바로 보지 않겠다는 뜻입니다.android:actionProviderClass를 설정하면 ActionProvider를 사용 할 수 있습니다. ActionProvider를 extends 하고 있는 클래스 경로명을 적어주면 되며, setActionProvider(ActionProvider)를 사용하여 자바코드에서도 설정해 줄 수 있습니다. 만약 공간이 부족해서 Overflow부분으로 빠진다면 텍스트가 보여져야 되므로 이왕이면 Title은 각 item마다 설정해 주는게 좋습니다.
public class ActionBarSettingsActionProviderActivity extends Activity {
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// TODO Auto-generated method stub
getMenuInflater().inflate(R.menu.action_bar_settings_action_provider, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
Toast.makeText(this, "선택된 버튼의 타이틀 : " + item.getTitle(), Toast.LENGTH_SHORT).show();
return true;
}
public static class SettingActionProvider extends ActionProvider{
private static final String TAG = "SettingActionProvider";
private final Context mContext;
private final Intent sSettingsIntent = new Intent(Settings.ACTION_SETTINGS);
public SettingActionProvider(Context context) {
super(context);
// TODO Auto-generated constructor stub
mContext = context;
}
@Override
public View onCreateActionView(MenuItem forItem) {
// TODO Auto-generated method stub
// actionBar 영역에 들어가는 itme을 재정의 해 줄 수 있다.
// 메뉴 버튼과 actionBar의 버튼을 서로 다르게 정의 할 수 있다.
// forItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
LayoutInflater layoutInflater = LayoutInflater.from(mContext);
View view = layoutInflater.inflate(R.layout.action_bar_settings_action_provider, null);
ImageButton button = (ImageButton) view.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
mContext.startActivity(sSettingsIntent);
}
});
return view;
}
@Override
@Deprecated
public View onCreateActionView() {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean onPerformDefaultAction() {
// TODO Auto-generated method stub
Log.e(TAG, "onPerformDefaultAction()");
mContext.startActivity(sSettingsIntent);
return true;
}
}
}
onCreateOptionsMenu(Menu menu)에서 Menu를 inflate 해주면 Menu 버튼과 Action Bar 두곳 모두에서 사용 할 수가 있습니다. 이전 포스팅한 글에서 Menu는 Menu 버튼을 클릭하면 나타나는 것이고 MenuItem은 Action Bar에서 나타난다고 말했습니다. ActionProvider가 onCreateOptionsMenu(Menu menu)에서 inflate되는 Menu를 MenuItem에 추가 하는 기능을 합니다. 만약 onOptionsItemSelected(MenuItem item)가 호출 되지 않았다면 ActionProvider의 onPerformDefaultAction()이 호출 되었을 겁니다. onPerformDefaultAction()은 Overflow 영역에 있을때 호출되며 이 메서드가 호출되면 onOptionsItemSelected(MenuItem item)은 호출 되지 않습니다.소스에서는 onCreateActionView(MenuItem forItem)에서 inflate해준 view에 클릭 이벤트를 줬습니다. 이 결과 onPerformDefaultAction()와 onOptionsItemSelected(MenuItem item)가 호출 되지 않았으며 바로 onClick(View v)메서드가 호출되는 것을 확인 하였습니다. 간단하게 onCreateActionView(MenuItem forItem)메서드에서 만들어 놓은 레이아웃을 inflate 해주고 바로 이벤트를 걸어 사용 하면 되지 않을까 생각됩니다. onCreateActionView(MenuItem forItem)에서 버튼 하나를 만들어서 붙여 본 결과 onCreateOptionsMenu(Menu menu)에서 추가된 item 개수만큼 버튼이 생기는걸 확인 할 수 있었습니다.(물론 Action Bar에서 보여 질수 있는 item에 한해서 말하는 것입니다.)
참조
2013년 4월 21일 일요일
[Android] Action Bar Mechanics
Android에서는 고맙게도 Open Source를 제공해 주고 있습니다.
Android의 어떤 기능을 배우고 싶을때 Open Source를 보고 분석하는 것 보다 더 좋은 방법은 없다고 생각합니다.
Action Bar 역시 Open Source를 분석하면서 습득하는게 가장 확실한 방법이 아닐까 생각합니다. Open Source에서 ActionBar의 예제는 총 6가지 입니다. 각각의 클래스 이름은ActionBarDisplayOptions, ActionBarMechanics, ActionBarSettingsActionProviderActivity, ActionBarShareActionProviderActivity, ActionBarTab, ActionBarUsage 입니다. 이 중 블로그 제목처럼 ActionBarMechanics에 대해 작성해 보겠습니다. ActionBarMechanics 클래스는 이름처럼 ActionBar를 구현하기 위한 기본 구조를 설명하는 클래스입니다.
Action Bar를 설정하기 위해서는 Content View를 Setting하기전에 getWindow().requestFeature(Window.FEATURE_ACTION_BAR)를 설정해 줘야 됩니다. 그러나 보통 Activity의 테마가 기본으로 적용하고 있어서 위와 같이 getWindow().requestFeature(Window.FEATURE_ACTION_BAR)를 적용하지 않고도 Action Bar를 사용 할 수 있습니다. 물론 Theme.NoTitleBar를 적용하여 Action Bar영역을 사라지게 할 수 도 있습니다.
Action Bar에 기능을 추가 하는 방법은 Menu버튼을 사용 할 때와 크게 다르지 않습니다.
onCreateOptionMenu(Menu menu)에서 Menu를 추가해주고 onOptionsItemSelected(MenuItem item)에서 추가해준 Component의 이벤트를 받아서 처리해 주면 됩니다. 한가지 차이점은 ActionBar에 추가되는 Component는 MenuItem 클래스 추가해 줘야 된다는 점입니다. 위의 Source를 실행해 보면 Menu 버튼을 클릭하면 "Normal item"이 출력이 되며, Action Bar에 있는 Share 버튼을 클릭하면 "Action Bar"가 Toast에 출력 됩니다. 만약 Theme.NoTitleBar를 적용해 주면 Menu 버튼을 클릭했을때 "Normal item" 과 "Action Bar"가 Menu 버튼에 표시 되게 됩니다.
Action Bar에 많은 Item를 추가해 주면 Overflow 영역으로 빠지게 됩니다. setShowAsAction() method를 이용하여 Action Bar에서 item의 보여지는 방법을 설정 할 수 있습니다. 위의 Source 에서 설정한 SHOW_AS_ATION_IF_ROOM은 ActionBar의 공간이 있으면 item의 icon만으로 Action Bar에 나타나도록 합니다.
참조
Android의 어떤 기능을 배우고 싶을때 Open Source를 보고 분석하는 것 보다 더 좋은 방법은 없다고 생각합니다.
Action Bar 역시 Open Source를 분석하면서 습득하는게 가장 확실한 방법이 아닐까 생각합니다. Open Source에서 ActionBar의 예제는 총 6가지 입니다. 각각의 클래스 이름은ActionBarDisplayOptions, ActionBarMechanics, ActionBarSettingsActionProviderActivity, ActionBarShareActionProviderActivity, ActionBarTab, ActionBarUsage 입니다. 이 중 블로그 제목처럼 ActionBarMechanics에 대해 작성해 보겠습니다. ActionBarMechanics 클래스는 이름처럼 ActionBar를 구현하기 위한 기본 구조를 설명하는 클래스입니다.
public class ActionBar_Api_Mechanics extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
getWindow().requestFeature(Window.FEATURE_ACTION_BAR);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// TODO Auto-generated method stub
// Menu 버튼
menu.add("Normal item");
// Action Bar 버튼
MenuItem actionItem = menu.add("Action Button");
actionItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
actionItem.setShowAsAction(android.R.drawable.ic_menu_share);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
Toast.makeText(this, "Selected Item : " + item.getTitle(), Toast.LENGTH_SHORT).show();
return true;
}
}
Action Bar를 설정하기 위해서는 Content View를 Setting하기전에 getWindow().requestFeature(Window.FEATURE_ACTION_BAR)를 설정해 줘야 됩니다. 그러나 보통 Activity의 테마가 기본으로 적용하고 있어서 위와 같이 getWindow().requestFeature(Window.FEATURE_ACTION_BAR)를 적용하지 않고도 Action Bar를 사용 할 수 있습니다. 물론 Theme.NoTitleBar를 적용하여 Action Bar영역을 사라지게 할 수 도 있습니다.
Action Bar에 기능을 추가 하는 방법은 Menu버튼을 사용 할 때와 크게 다르지 않습니다.
onCreateOptionMenu(Menu menu)에서 Menu를 추가해주고 onOptionsItemSelected(MenuItem item)에서 추가해준 Component의 이벤트를 받아서 처리해 주면 됩니다. 한가지 차이점은 ActionBar에 추가되는 Component는 MenuItem 클래스 추가해 줘야 된다는 점입니다. 위의 Source를 실행해 보면 Menu 버튼을 클릭하면 "Normal item"이 출력이 되며, Action Bar에 있는 Share 버튼을 클릭하면 "Action Bar"가 Toast에 출력 됩니다. 만약 Theme.NoTitleBar를 적용해 주면 Menu 버튼을 클릭했을때 "Normal item" 과 "Action Bar"가 Menu 버튼에 표시 되게 됩니다.
Action Bar에 많은 Item를 추가해 주면 Overflow 영역으로 빠지게 됩니다. setShowAsAction() method를 이용하여 Action Bar에서 item의 보여지는 방법을 설정 할 수 있습니다. 위의 Source 에서 설정한 SHOW_AS_ATION_IF_ROOM은 ActionBar의 공간이 있으면 item의 icon만으로 Action Bar에 나타나도록 합니다.
| Constants | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|
| int | SHOW_AS_ACTION_ALWAYS | Always show this item as a button in an Action Bar. | |||||||||
| int | SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW | This item's action view collapses to a normal menu item. | |||||||||
| int | SHOW_AS_ACTION_IF_ROOM | Show this item as a button in an Action Bar if the system decides there is room for it. | |||||||||
| int | SHOW_AS_ACTION_NEVER | Never show this item as a button in an Action Bar. | |||||||||
| int | SHOW_AS_ACTION_WITH_TEXT | When this item is in the action bar, always show it with a text label even if it also has an icon specified. | |||||||||
참조
2013년 4월 18일 목요일
[Android] 이제 Action Bar 사용은 선택이 아닌 필수.
ActionBar는 Android 3.0(API Level 11)부터 추가되었으며 사용 할 때는 Manifest에서 targetSdkVersion 이나 minSdkVersion을 11로 맞춰줘야 되며, Theme.Holo 테마를 사용하여야 됩니다.(Theme.Holo가 Default 테마입니다.)
Android 3.0 이전에는 Title Bar에서 단순 Application의 이름정도만 보여주는게 전부 였으나 Android 3.0 부터는 Action Bar라는 놈이 추가되어서 Application의 이름 뿐만 아니라, 사용자가 보고 있는 화면의 위치, Navigation Button, Tab, Share 등을 빠르게 사용 할 수 있게 되었습니다.
사실 Android 3.0 이전부터 우리는 Action Bar의 기능을 전부 사용하고 있었습니다.
초창기 아이폰 App들의 UI를 벤치마킹 하여 디자인했기 때문에 안드로이드에는 back버튼이 있음에도 불구하고 상단에 back 버튼은 안드로이드 App에서도 흔히 볼 수 있었습니다.(물론 ActionBar의 Navigation Button과 back Button은 기능적으로 차이가 있습니다.) Back Button 뿐만 아니라 App에서 Home으로 바로 이동 할 수 있는 Button역시 관련 Activity에서 Inflate를 사용하던지 해당 View에 바로 적용하던지 하는 방법으로 사용 했었습니다. 뿐만 아니라 아이폰의 Tab이 아래에 있다는 이유만으로 안드로이드에서도 Tab을 하단에 적용해야 했던 적도 있었습니다.(안드로이드는 아이폰과 달리 하단에 Menu, Home, Back Button 이 있어서 Tab을 잘못 눌러 하단의 기본 Button들을 클릭 하는걸 방지하고자 Tab이 위에 있었던 겁니다.) 우리는 Share Button 역시 만들어서 사용 했었습니다. 그러나!! 이제는 Action Bar를 사용하여 위에 열거했던 모든 기능들을 간편하게 사용 할 수 있게 되었습니다.
안드로이드 개발자 사이트에 있는 Action Bar 이미지입니다. 여기서는 기능별로 4가지로 분류했습니다.
1. App Icon
Action Bar의 좌측에 App의 Icon을 넣을 수 있습니다. Icon을 터치하면 App의 Home으로 보낼 수도 있으며, Back Button과 마찬가지로 Task의 상위단계로 이동하게 만들 수도 있습니다.
ActionBar에서 Home로 이동하게 만들경우는 setHomeButtonEnabled(true);를 사용하여 나타낼수 있으며 Intent.FLAG_ACTIVITY_CLEAR_TOP를 추가해 주는것이 좋습니다. 그렇지 않으면 Back Button을 누를때 엄청난 양의 Task를 목격 하실 수도 있으실 겁니다.
ActionBar에서 Task의 상위 단계로 이동하게 만든다면setDisplayHomeAsUpEnabled(true); 사용하여 나타낼 수 있습니다. 상위단계로 이동하게 만들때의 주의점은 Back 버튼과 동일한 프로세스로 만드는게 아니라 상위 카테고리로 보내게끔 만드는게 더 나을 수도 있다는 점입니다. 예를 들자면 Home -> A -> B -> a -> b 와 같이 화면전환이 이루어 졌다면 (A와 B는 같은 레벨이며 a, b는 A, B의 하위 레벨 입니다.) b 에서 Back Button을 누르면 a로 이동되나 ActionBar의 Navigation Button은 B로 이동되어져야 된다는 것입니다. Back Button이 있는 안드로이드에서 굳이 똑같은 기능을 하는 버튼을 하나 더 만들어야 되는지 생각해 볼 필요가 있습니다.
Home과 Task의 상위 단계로 보낼때의 Icon 모양은 아래와 같이 차이가 있습니다.강제성이 있는것은 아니나 이런 약속들은 지키는게 좋습니다. 이미지에 나타난 데로 Home으로 가는 버튼에는 " < " 기호가 없으며 상위 Task로 이동하는 버튼에는 " < "기호가 있음을 알 수 있습니다.
2. View control
Tab을 사용 할때는 device화면의 크기에 따라 그림과 같이 Tablet 처럼 큰 화면에서의 배치와 작은 화면에서의 배치가 다르게 표현 된다는 걸 알 수 있습니다.
3. Action Button
Action Bar에 버튼을 추가하는 영역입니다. 보통 화면에서 가장 중요한 기능을 한 두가지 넣어주거나 Search 기능이나 Share기능을 추가 할 수도 있습니다. Action Bar에서 사용 할 Icon은 안드로이드 UX팀에 의해 디자인된 Action Icon을 사용하는것을 추천합니다. 만약 Custom 하려고 한다면 Android에서 제안하는 기준에 맞게 디자인 하기를 추천합니다. Action Bar안의 ActionView영역은 사라졌다가 확장되었다가 할 수 있기 때문에 device 화면의 크기에 따라 유동적으로 바뀌게끔 설정해 주면 됩니다. 이런 기능은 device화면이 작을 때 공간 효율을 더욱 좋게 해줍니다. 사용자가 Back Button이나 Navigation Button을 터치하면 확장되었던 화면은 다시 사라지게 되며 MenuItem의 expandActionView()와 colapseActionView()를 이용하여 ActionView를 사용자가 컨트롤 할수 있게 만들 수도 있습니다.
4. Action overflow
ActionBar에 메뉴 버튼이라고 생각하면 됩니다. 자주 사용하지 않은 기능들이나 화면에 넘치는 기능 또는 메뉴버튼으로 사용 되어지고 싶은 기능을 추가하는 영역입니다. Android 3.0이전에는 물리적인 메뉴버튼을 이용하였지만 Android 3.0이후에는 물리적인 메뉴 버튼 대신 Action Bar의 overflow 영역이 생겼습니다. Android 3.0이전의 모든 device에는 메뉴 버튼이 있었으나 Android 3.0 이후 부터는 물리적인 메뉴 버튼이 없는 device도 있습니다. Android 3.0이전에 옵션메뉴로 개발되어진 App은 targetSdkVersion 이나 minSdkVersion를 11또는 그 이상으로 설정한다면 이전에 설정했던 옵션 기능들은 API 레벨에 근거하여 Action overflow에 추가하지 않습니다.
보통 메뉴버튼은 해당 Activity의 옵션기능을 설정하였습니다. overflow영역 역시 마찬가지로 해당 Activity나 Fragment의 옵션 기능을 설정하는 영역입니다.
참조
- [Android] Action Bar Mechanics
- [Android] Action Bar ActionProvider
- [Android] Action Bar ShareActionProvider( 공유 )
- [Android] Action Bar Usage (Action Bar 사용방법 예제)
- [Android] ActionBar Tabs
2013년 2월 6일 수요일
[Android] Fragment example(프레그먼트 예제)
Fragment 예제가 제일 잘 나와 있는 곳은 Android API 소스입니다.
사실 Fragment가 만들어지게 된 큰 이유중에 하나가 타블렛에서의 화면 비효율성 때문입니다. 이제는 스마트폰의 크기도 커지면서 LandScape일때와 Portrait일때의 화면 구성도 달라져야 합니다. 예제 중 Fragment의 활용을 가장 잘 보여준다고 생각하는 FragmentLayout 예제를 가지고 Fragment를 설명 하겠습니다.
Portrait XML(res - layout)
LandScape XML(res - layout-land)
일단 위와 같이 portrait 화면 일 때와 LandScape 화면일때를 구성해 줍니다. 두 파일명은 같아야 합니다. 저는 activity_fragment_layout.xml로 만들어 줬습니다.
두 화면의 차이점은 LandScape 화면일때 옆에 화면을 하나 더 구성하기 위해 FrameLayout을 추가해 주었습니다.
간단하게 프로세스를 설명 드리자면 Activity 클래스에서 setcontentView(activity_fragment_layout); 와 같이 layout을 호출하면 layout안에 fragment 태그안에서 java 파일의 TitlesFragment 를 호출 하게 됩니다.
FragmentLayoutActivity$TitlesFragment
API 소스중 App → Fragment → FragmentLayout 의 소스이며 약간 수정한 부분이 있습니다.
수정한 부분은 API에서는 DetailsActivity에서 DetailsFragment를 새로 만들어서 사용하던것을 newInstance를 활용하는 방법을 사용 하였으며, 자동화면 전환 시 마지막 클릭한 정보를 Portrait 와 LandScape와 상관없이 알고 있다가 LandScape 화면 전화시 보여주게 하기 위하여 getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE), getListView().setItemChecked(index, true)의 위치를 수정 하였습니다.
관련글
- App → Fragment 에 들어가 보면 많이 나와있습니다.
사실 Fragment가 만들어지게 된 큰 이유중에 하나가 타블렛에서의 화면 비효율성 때문입니다. 이제는 스마트폰의 크기도 커지면서 LandScape일때와 Portrait일때의 화면 구성도 달라져야 합니다. 예제 중 Fragment의 활용을 가장 잘 보여준다고 생각하는 FragmentLayout 예제를 가지고 Fragment를 설명 하겠습니다.
Portrait XML(res - layout)
<?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"
android:orientation="vertical" >
<fragment
android:id="@+id/fragment1"
android:name="com.example.wawoops_fragment_test.FragmentLayoutActivity$TitlesFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
LandScape XML(res - layout-land)
<?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"
android:orientation="horizontal" >
<fragment
android:id="@+id/fragment1"
android:name="com.example.wawoops_fragment_test.FragmentLayoutActivity$TitlesFragment"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1"/>
<FrameLayout
android:id="@+id/flLandFragmentLayout"
android:layout_width="0dip"
android:layout_height="match_parent"
android:layout_weight="1">
</FrameLayout>
</LinearLayout/>
일단 위와 같이 portrait 화면 일 때와 LandScape 화면일때를 구성해 줍니다. 두 파일명은 같아야 합니다. 저는 activity_fragment_layout.xml로 만들어 줬습니다.
android:name="com.example.wawoops_fragment_test.FragmentLayoutActivity$TitlesFragment" 의 의미는 com.example.wawoops_fragment_test 패키지 안에 FragmentLayoutActivity 클래스 안에 TitlesFragment 라는 Fragment를 상속받은 클래스가 있다는것을 의미 합니다. 이처럼 Fragment Component는 Fragment를 extends 하는 클래스를 만들고 그 경로를 써줘야 됨을 알 수 있습니다.
두 화면의 차이점은 LandScape 화면일때 옆에 화면을 하나 더 구성하기 위해 FrameLayout을 추가해 주었습니다.
간단하게 프로세스를 설명 드리자면 Activity 클래스에서 setcontentView(activity_fragment_layout); 와 같이 layout을 호출하면 layout안에 fragment 태그안에서 java 파일의 TitlesFragment 를 호출 하게 됩니다.
FragmentLayoutActivity$TitlesFragment
public static class TitlesFragment extends ListFragment{
boolean mLandPane;
int mCurCheckPosition = 0;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
Log.v(TAG, "onActivityCreated");
setListAdapter(new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_activated_1, Shakespeare.TITLES));
View detailsFrame = getActivity().findViewById(R.id.flLandFragmentLayout);
mLandPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE;
if (savedInstanceState != null) {
mCurCheckPosition = savedInstanceState.getInt("curChoice", 0);
}
if (mLandPane) {
showDetails(mCurCheckPosition);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
// TODO Auto-generated method stub
super.onSaveInstanceState(outState);
Log.e(TAG, "onSaveInstanceState");
outState.putInt("curChoice", mCurCheckPosition);
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// TODO Auto-generated method stub
showDetails(position);
}
void showDetails(int index){
Log.v(TAG, "index : " + index);
mCurCheckPosition = index;
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
getListView().setItemChecked(index, true);
if (mLandPane) {
Log.v(TAG, "mLandPane index : " + index);
DetailsFragment details = (DetailsFragment) getFragmentManager().findFragmentById(R.id.flLandFragmentLayout);
if (details == null || details.getShowIndex() != index) {
details = DetailsFragment.newInstance(index);
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.flLandFragmentLayout, details);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
}
}else {
Intent intent = new Intent();
intent.setClass(getActivity(), DetailsActivity.class);
intent.putExtra("index", index);
startActivity(intent);
}
}
}
- TitlesFragment는 ListFragment를 상속 받았습니다.
- public void onActivityCreated(Bundle savedInstanceState) 는 Fragment의 생명주기중 화면에 보여지기 바로 직전의 method 입니다.
- View detailsFrame 에 LandScape 화면일때 상세화면을 보여줄 FrameLayout를 findViewById를 해줍니다. 여기서 getActivity()는 TitlesFragment를 띄우고 있는 Activity를 말합니다. Fragment를 사용하다보면 많이 사용하게 됩니다.
- mLandPane는 화면이 LandScape일때를 구분해 주는 flag 입니다.
- public void onListItemClick(ListView l, View v, int position, long id) 는 ListView를 클릭 했을때의 CallBack Method 입니다.
- void showDetails(int index) 부분이 FragmentLayout 프로젝트의 핵심 부분으로 LandScape 화면일때는 FrameLayout에 DetailsFragment를 매칭시키며 Portrait 화면일 경우에는 DetailsActivity를 호출하는 내용입니다.
- (DetailsFragment)getFragmentManager().findFragmentById(R.id.flLandFragmentLayout) : Fragment 안에서 Activity의 findViewById와 마찬가지로 findFragmentById를 사용하여 fragment로 선언해 줄수 있습니다.
- details = DetailsFragment.newInstance(index) : DetailsFragment클래스안에 static newInstance method를 만들어 놨습니다.
- FragmentTransaction ft = getFragmentManager().beginTransaction() : FragmentManager().beginTransaction()를 통해 FragmentTransaction를 가져옵니다.
- ft.replace(R.id.flLandFragmentLayout, details) : FragmentTransaction를 이용하여 fragment를 Activity에 추가하였다가 삭제할 수 있습니다. 현재는 R.id.flLandFragmentLayout에 details를 replace 하는 Method 입니다.
- ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE) : setTransition는 Fragment 에서 애니메이션 효과를 주는 기능입니다.
- ft.commit() : 마지막에 꼭 commit 해줘야 화면에서 보입니다.
참고)
◆ setTranstion (int transit) : 애니메이션 효과를 준다.
◆ setTranstion (int transit) : 애니메이션 효과를 준다.
- TRANSIT_NONE : 애니메이션 효과를 제공하지 않는다.
- TRANSIT_FRAGMENT_OPEN : 스택에서 꺼내는 효과를 창출한다.
- TRANSIT_FRAGMENT_CLOSE : 스택에 넣는 효과를 창출한다.
◆ setCustomAnimation (int enter, int exit) : 사용자가 제작한 애니메이션을 사용하여 Fragment를 출력한다.
- enter : 시작시점 효과
- exit : 끝나는 시점의 효과
FragmentLayoutActivity$DetailsFragment
public static class DetailsFragment extends Fragment {
public static DetailsFragment newInstance(int index){
DetailsFragment f = new DetailsFragment();
Bundle args = new Bundle();
args.putInt("index", index);
f.setArguments(args);
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
if (container == null) {
return null;
}
ScrollView scroller = new ScrollView(getActivity());
TextView text = new TextView(getActivity());
int padding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getActivity().getResources().getDisplayMetrics());
text.setPadding(padding, padding, padding, padding);
scroller.addView(text);
text.setText(Shakespeare.DIALOGUE[getShowIndex()]);
return scroller;
}
public int getShowIndex(){
return getArguments().getInt("index", 0);
}
}
DetailsFragment에서 유심히 봐야할 메소드는 setArguments입니다. Activity에서는 Intent.putExtra()와 Intent.getExtra()와 같은 method를 이용하여 데이터를 주고 받았지만 Fragment에서는 setArguments()와 getArguments()메소드를 이용하여 데이터를 주고 받을 수 있습니다.
FragmentLayout.java 소스 전문- onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) 는 Fragment의 생명주기중 View를 inflater할때 사용할수 있는 곳입니다. 매개변수를 보면 여기에는 화면을 붙일 수 있는 inflater와 부모View를 가지고 올 수 있는 container와 저장된 정보를 사용 할 수 있는 savedInstanceState로 구성되어 있습니다. 하단 코딩은 UI를 자바 코드로 표현한 것입니다.
- getShowIndex()에서 getArguments()를 사용하여setArguments()한 데이타를 가지고 오도록 했습니다.
package com.example.wawoops_fragment_test;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.app.ListFragment;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.Log;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.ScrollView;
import android.widget.TextView;
public class FragmentLayoutActivity extends Activity {
private static final String TAG = "FragmentLayoutActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment_layout);
}
public static class DetailsActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
finish();
return;
}
if (savedInstanceState == null) {
// DetailsFragment details = new DetailsFragment();
// details.setArguments(getIntent().getExtras());
// Log.e(TAG, "getIntExtra : " + getIntent().getIntExtra("index", 0));
DetailsFragment details = DetailsFragment.newInstance(getIntent().getIntExtra("index", 0));
getFragmentManager().beginTransaction().add(android.R.id.content, details).commit();
}
}
}
public static class TitlesFragment extends ListFragment{
boolean mLandPane;
int mCurCheckPosition = 0;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
Log.v(TAG, "onActivityCreated");
setListAdapter(new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_activated_1, Shakespeare.TITLES));
View detailsFrame = getActivity().findViewById(R.id.flLandFragmentLayout);
mLandPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE;
if (savedInstanceState != null) {
mCurCheckPosition = savedInstanceState.getInt("curChoice", 0);
}
if (mLandPane) {
showDetails(mCurCheckPosition);
}
}
@Override
public void onSaveInstanceState(Bundle outState) {
// TODO Auto-generated method stub
super.onSaveInstanceState(outState);
Log.e(TAG, "onSaveInstanceState");
outState.putInt("curChoice", mCurCheckPosition);
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
// TODO Auto-generated method stub
showDetails(position);
}
void showDetails(int index){
Log.v(TAG, "index : " + index);
mCurCheckPosition = index;
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
getListView().setItemChecked(index, true);
if (mLandPane) {
Log.v(TAG, "mLandPane index : " + index);
DetailsFragment details = (DetailsFragment) getFragmentManager().findFragmentById(R.id.flLandFragmentLayout);
if (details == null || details.getShowIndex() != index) {
details = DetailsFragment.newInstance(index);
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.flLandFragmentLayout, details);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
}
}else {
Intent intent = new Intent();
intent.setClass(getActivity(), DetailsActivity.class);
intent.putExtra("index", index);
startActivity(intent);
}
}
}
public static class DetailsFragment extends Fragment {
public static DetailsFragment newInstance(int index){
DetailsFragment f = new DetailsFragment();
Bundle args = new Bundle();
args.putInt("index", index);
f.setArguments(args);
return f;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
if (container == null) {
return null;
}
ScrollView scroller = new ScrollView(getActivity());
TextView text = new TextView(getActivity());
int padding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getActivity().getResources().getDisplayMetrics());
text.setPadding(padding, padding, padding, padding);
scroller.addView(text);
text.setText(Shakespeare.DIALOGUE[getShowIndex()]);
return scroller;
}
public int getShowIndex(){
return getArguments().getInt("index", 0);
}
}
}
}
API 소스중 App → Fragment → FragmentLayout 의 소스이며 약간 수정한 부분이 있습니다.
수정한 부분은 API에서는 DetailsActivity에서 DetailsFragment를 새로 만들어서 사용하던것을 newInstance를 활용하는 방법을 사용 하였으며, 자동화면 전환 시 마지막 클릭한 정보를 Portrait 와 LandScape와 상관없이 알고 있다가 LandScape 화면 전화시 보여주게 하기 위하여 getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE), getListView().setItemChecked(index, true)의 위치를 수정 하였습니다.
관련글
2013년 2월 5일 화요일
[Android] Fragment 화면연결
Fragment를 Activity와 연결하기 위해서는 FragmentManager 객체와 FragmentTransaction 객체 가 필요하다.
화면구성순서는 아래와 같다.
FragmentTransaction.add() method의 매개변수이다.
public abstract FragmentTransaction add (int containerViewId, Fragment fragment)
public abstract FragmentTransaction add (int containerViewId, Fragment fragment, String tag)
◆ Fragment 화면
Fragment LifeCycle은 이전에 포스팅 했지만 코드 작성을 한김에 한번 확인 보면 아래 와 같이 나타 난다.
화면구성순서는 아래와 같다.
- Fragment Class를 생성해 준다.
- getFragmentManager() Method를 통해 FragmentManager객체를 생성한다.
- FragmentManager 객체의 beginTransaction() 메소드를 호출하여 FragmentTransaction을 생성해준다.
- FragmentTransaction 객체의 add() 메소드를 이용하여 Fragment를 추가해준다.
- commit()를 호출해 주면 연결이 된다.
- commit()이 호출된 이후 바로 Fragment를 호출해 주는 것이 아니므로 사양이 낮은 Tablet은 시간차가 발생할 수 있다. 즉시 화면이 출력하고자 한다면 FragmentManager.executePendingTransactions() method를 호출하면 된다.
◆ 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”
android:orientation=”vertical”>
<FrameLayout
android:id=”@+id/fragmentLayout”
android:layout_width=”match_parent”
android:layout_height=”match_parent”>
</FrameLayout>
</LinearLayout>
◆ Activity 화면
public class LifeCycleOfFragmentActivity extends Activity {
private static final String TAG = "LifeCycleOfFragment";
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lifecycle_fragment);
Log.e(TAG, "Activity >> onCreate");
WawooPsFragment wf = new WawooPsFragment();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(R.id.fragmentLayout, wf).commit();
}
@Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
Log.e(TAG, "Activity >> onStart");
}
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
Log.e(TAG, "Activity >> onResume");
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
Log.e(TAG, "Activity >> onPause");
}
@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
Log.e(TAG, "Activity >> onStop");
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.e(TAG, "Activity >> onDestroy");
}
}
FragmentTransaction.add() method의 매개변수이다.
public abstract FragmentTransaction add (int containerViewId, Fragment fragment)
public abstract FragmentTransaction add (int containerViewId, Fragment fragment, String tag)
- containerViewId : Fragment를 식별하는 사용하는 Unique한 ID가 된다. 만약 Id를 0으로 설정하며 되면 Activity에 Fragment가 등록되지 않는다.
- fragment : Activity와 연결 시킬 fragment이다.
- tag : Unique한 ID 를 대신하여 Fragment를 찾는데 사용하는 옵션이다.
◆ Fragment 화면
public class WawooPsFragment extends Fragment {
private static final String TAG = "WaooPsFragment";
@Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
Log.d(TAG, "Fragment >> onAttach");
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
Log.d(TAG, "Fragment >> onActivityCreated");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
Log.d(TAG, "Fragment >> onCreateView");
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
Log.d(TAG, "Fragment >> onCreate");
}
@Override
public void onStart() {
// TODO Auto-generated method stub
super.onStart();
Log.d(TAG, "Fragment >> onStart");
}
@Override
public void onResume() {
// TODO Auto-generated method stub
super.onResume();
Log.d(TAG, "Fragment >> onResume");
}
@Override
public void onPause() {
// TODO Auto-generated method stub
super.onPause();
Log.d(TAG, "Fragment >> onPause");
}
@Override
public void onStop() {
// TODO Auto-generated method stub
super.onStop();
Log.d(TAG, "Fragment >> onStop");
}
@Override
public void onDestroyView() {
// TODO Auto-generated method stub
super.onDestroyView();
Log.d(TAG, "Fragment >> onDestroyView");
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.d(TAG, "Fragment >> onDestroy");
}
@Override
public void onDetach() {
// TODO Auto-generated method stub
super.onDetach();
Log.d(TAG, "Fragment >> onDetach");
}
}
Fragment LifeCycle은 이전에 포스팅 했지만 코드 작성을 한김에 한번 확인 보면 아래 와 같이 나타 난다.
Activity_onCreate() ▶ Fragment_onAttach() ▶ Fragment_onCreate() ▶ Fragment_onCreateView ▶ Fragment_onActivityCreated() ▶ Activity_onStart() ▶ Fragment_onStart() ▶ Activity_onResume() ▶ Fragment_onResume() ▶ Fragment_onPause() ▶ Activity_onPause() ▶ Fragment_onStop() ▶ Activity_onStop() ▶ Fragment_onDestoryView() ▶ Fragment_onDestory() ▶ Fragment_Detach() ▶ Activity_onDestory()
관련글
2013년 2월 4일 월요일
[Android] Fragment LifeCycle (프레그먼트 생명주기)
◆ Fragment 란?
Tablet이 나오기 전에는 화면의 크기가 그리 크지 않았기 때문에 Activity에서 하나의 화면만 보여주면 됐습니다. 하지만 Tablet이 나오면서 부터 화면을 분할해야 할 필요성이 생겼습니다. 같은 디자인을 Tablet에서 보면 공간 효율이 너무 좋지 않았습니다. 여백에 미도 좋긴 하지만 공간을 디자인 하는 입장에서 보면 화면 구성이 너무 비효율적이였습니다. 그래서 생긴 개념이 Fragment 입니다.
사실 허니콤이 나왔을때 까지만 해도 타블렛 앱을 개발할 일이 없어서 Fragment를 눈여겨 보지는 않았지만 아이스크림 샌드위치 이후 타블렛과 스마트폰의 운영체계가 합쳐지면서 Fragment를 사용 안할 수 없게 되었습니다. 물론 업데이트 될때마다 기존에 있든 API들이 하나씩 Deprecated 되면서 Fragment 사용을 유도하고 있는 것 또한 이제는 Fragment를 사용하지 않으면 안되지 않을까 생각합니다.
Tablet이 나오기 전에는 화면의 크기가 그리 크지 않았기 때문에 Activity에서 하나의 화면만 보여주면 됐습니다. 하지만 Tablet이 나오면서 부터 화면을 분할해야 할 필요성이 생겼습니다. 같은 디자인을 Tablet에서 보면 공간 효율이 너무 좋지 않았습니다. 여백에 미도 좋긴 하지만 공간을 디자인 하는 입장에서 보면 화면 구성이 너무 비효율적이였습니다. 그래서 생긴 개념이 Fragment 입니다.
사실 허니콤이 나왔을때 까지만 해도 타블렛 앱을 개발할 일이 없어서 Fragment를 눈여겨 보지는 않았지만 아이스크림 샌드위치 이후 타블렛과 스마트폰의 운영체계가 합쳐지면서 Fragment를 사용 안할 수 없게 되었습니다. 물론 업데이트 될때마다 기존에 있든 API들이 하나씩 Deprecated 되면서 Fragment 사용을 유도하고 있는 것 또한 이제는 Fragment를 사용하지 않으면 안되지 않을까 생각합니다.
- Fragment는 Activity와 1:1 개념이 아닌 1:N 의 개념입니다.
- Fragment는 Activity와 비슷한 개념이 아닌 Activity에 안에 항상 포함되어 있는 개념입니다.
◆ Fragment LifeCycle
Activity에도 생명주기가 있듯이 Fragment에도 생명주기가 있습니다. Activity보다 다서 많은 것 같지만 크게 보면 똑같습니다. 다만 세부적으로 들어가면 몇가지 추가 되기는 했지만 생각해 보면 더 편리해 진거 같습니다.
아래는 Activity 생명주기와 Fragment의 생명주기를 비교해 놓은 그림입니다.
그림을 보시면 아시겠지만 다른 생명주기는 똑같습니다. 다만 Activity의 onCreate() 와 onDestory() method가 좀더 세분화 되었습니다. 아래 그림은 Fragment의 생명주기 입니다.
- onAttach() : Fragment가 Activity에 연결되기 전에 호출된다. Fragment는 아직 생성되지 않았다.
- onCreate() : Fragment가 생성되는 시점에 호출된다. 하지만 아직 연결되어 있지는 않는다.
- onCreateView() : 화면에 출력할 레이아웃을 Inflate 할 수 있도록 제공되는 옵션적인 성격의 CallBack Method이다.
- onActivityCreated() : Fragment와 Activity가 연결되는 시점이다.
- onStart() ~ onStop() : Activity 생명주기와 똑같다.
- onDestroyView() : Fragment View를 제거하는 곳이다.
- onDestory() : Fragment 객체를 제거하기 전이다.
- onDetach() : Fragment 객체가 제거 된 후 Activity와 연결관계를 삭제할 때 호출된다.
2013년 1월 31일 목요일
[Android] Spannable 객체 사용
Span 새로 맞춘 문자서식, 문단서식, 이미지 정보등 문자열 표현하는 용어나 어떤 동작이나 행위로 인해 추가로 전달하는 정보를 말한다.
안드로이드에서는 스타일 문자를 처리하는 기능을 갖고 있는 클래스들은 공통적으로 Span이라하는 영문명이 접두어 또는 접미어로 들어간다.
◆String.xml
◆Java Code
◆Span 객체들
관련글
안드로이드에서는 스타일 문자를 처리하는 기능을 갖고 있는 클래스들은 공통적으로 Span이라하는 영문명이 접두어 또는 접미어로 들어간다.
◆String.xml
<?xml version=”1.0” encoding=”utf-8”?> <resources> <string name=”style_text_1”>This is bold! and This is highlighted!</string> </resources>
◆Java Code
private void spannable() {
TextView tv = (TextView) findViewById(R.id.tv);
Spannable sp = tv.getEditableText();
sp.setSpan(new StyleSpan(android.graphics.Typeface.BOLD), 0, 12, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
sp.setSpan(new BackgroundColorSpan(Color.GREEN), 18, 37, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
- Spannable sp는 Editable 로 받을 수 있기때문에 tv.setText() method 대신tv.getEditableText() method를 사용 하였다.
- StyleSpan Class 는 문자열의 속성을 변경 할 수 있으며 위 소스는 android.graphics.Typerface.BOLD 처리 한것이다.
- BackgroundColorSpan Class는 문자열의 백그라운드를 설정 할수 있다.
private void spannable(){
TextView tv = (TextView) findViewById(R.id.tv);
SpannableString ss = new SpannableString("When in Rome do as the Romans do.");
ss.setSpan(new RelativeSizeSpan(0.5f), 0, 3, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
ss.setSpan(new ForegroundColorSpan(Color.RED), 0, 8, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
ss.setSpan(new AbsoluteSizeSpan(30), 6, 11, 0);
ss.setSpan(new StrikethroughSpan(), 13, 18, 0);
tv.setText(ss, BufferType.SPANNABLE);
}
- RelativeSizeSpan(0.5f) : 폰트 사이즈를 변경하는 Class 이다.
- ForegroundColorSpan(Color.RED) : 폰트의 색상을 변경하는 Class 이다.
- AbsoluteSizeSpan(30) : 폰트의 크기를 물리적인 크기로 변경하는 Class 이다.
- StrikethroughSpan() : 해당 문자를 삭제한 형태로 표기한다.
- 이 외에 URLSpan 이라는 클래스도 있다. 기능은 링크 연결을 한다.(인터넷, 전환연결 등)
[Android] CharSequence 와 String 비교
JAVA 와 XML 에서의 문자열 관리하는 방법에는 근본적인 차이가 존재한다.
◉String.xml에서 마크업 문자로 작성.
◉CharSequence 객체로 받아서 처리 할 때.
이와 같이 getText() method로 읽어 CharSequence 객체 필드로 보관하면 스타일이 유지된다. 강조나 이탤리체가 적용 안되보이는것은 폰트가 지원을 안하기 때문이다. 지원하는 폰트를 사용하면 적용이 잘된다.
◉String 객체로 받아서 처리 할 때.
이와 같이 getString() method로 읽어 String 객체 필드로 보관하면 스타일이 유지되지 않는다. 위에 설명 했다시피 String은 변경금지 문자이기 때문이다.
관련글
String Calss 란?
JAVA에서 String 객체내 보관하는 문자열은 유니코드로 변형하여 보관하므로 HTML과 같이 마크업 문자를 입력하고 출력할 때 문제가 발생한다. 따라서 String 객체에 입력되는 문자열은 마크업 문자를 입력하여 사용할 수 없는 문자열인란 의미로 변경금지 문자라 부른다.
CharSequence 란?
반면에 CharSequence 객체내 보관하는 문자열은 같은 String 클래스와 같은 유니코드라 하더라도 마크업 문자를 사용하여 변형과 가공이 가능한 문자열이란 의미로 스타일 문자 또는 연속되는 문자라고 한다.
◆ CharSequence 객체의 제공 Method
- char charAt(int index) : 인덱스가 가리키는 문자를 반환한다.
- int length() : CharSequence 객체의 문자수를 반환한다.
- CharSequence subsequence(int start, int end) : start부터 end까지의 문자를 반환한다.
- String toString() : CharSequence 객체를 문자열로 반환한다.
◆ XML 문서와 JAVA 프로그램간의 출력하거나 읽을 때 사용 할 수 있도록 XML 버퍼타입을 제공한다.버퍼타입은 Enum 타입으로 지원한다.
- NORMAL : 디폴트 CharSequence 객체로 반환된다. XML 문서내 문자열은 입력시 소스 문자가 스타일 문자면 스타일 문자로 반환되고 아닌 경우는 일반 String 객체의 문자열로 반환된다.
- SPANNABLE : 마크업문자를 넣을 수 있는 스타일 문자이다. 항상 스타일 문자로 반환된다.
- EDITABLE : 편집가능한 스타일 문자이다. EditText는 EDITABLE 속성으로 반환된다.
◆ TextView 클래스에서 제공하는 메서드
- CharSequence getText()
- void setText (CharSequence text)
- void setText (int resid, TextView.BufferType type)
- void setText (CharSequence text, TextView.BufferType type)
◉String.xml에서 마크업 문자로 작성.
<?xml version=”1.0” encoding=”utf-8”?>
<resources>
<string name=”styled_text”>기본(Plain), <b>강조 bold</b> <i>이텔리체 italic</i>
<b><i>bold-italic</i></b></string>>
</resources>
◉CharSequence 객체로 받아서 처리 할 때.
private void charSequence() {
CharSequence cs = getText(R.string.styled_text);
tv.setText(cs);
}
이와 같이 getText() method로 읽어 CharSequence 객체 필드로 보관하면 스타일이 유지된다. 강조나 이탤리체가 적용 안되보이는것은 폰트가 지원을 안하기 때문이다. 지원하는 폰트를 사용하면 적용이 잘된다.
◉String 객체로 받아서 처리 할 때.
private void string() {
String str = getString(R.string.styled_text);
tv.setText(str);
}
이와 같이 getString() method로 읽어 String 객체 필드로 보관하면 스타일이 유지되지 않는다. 위에 설명 했다시피 String은 변경금지 문자이기 때문이다.
관련글
2013년 1월 30일 수요일
[Android] XML 특수문자 처리(String.xml)
- String.xml 에서 특수문자를 처리할때 아래 표와 같이 사용하면 된다.
- String.xml 에서 마크업 문자를 사용하여 스타일 문자를 만들때는 아래와 같이 사용한면된다.
관련글
피드 구독하기:
글 (Atom)











