2018년 12월 27일 목요일

[Android]EditText 에서 disits 설정 시 오류 현상과 해결방법

안녕하세요.

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년 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을 지우고 익숙한 LinearLayout 이나 RelativeLayout로 작업을 했지만, 매번 바꾸는 것도 귀찮고 해서 ConstraintLayout을 살펴 보았습니다.

사실 LinearLayout이 익숙하기 때문에 많이 사용 하지만 다른 Layout에 비하여 성능이 떨어지는 측면이 있다는걸 알고 있을 겁니다.

LinearLayout 자체가 성능이 떨어지는게 아니라, LinearLayout을 사용 하게 되면 어쩔 수 없이 한 레벨씩 안으로 들어 가면서 UI를 구성하게 됩니다.

모든 UI는 같은 레벨에서 작성될때 성능이 좀 더 나오는데 그런면에서 RelativeLayout를 한동안 사용 하기도 했습니다.

하지만 요세 Device 성능이 워낙 좋아서 정말 많은 레벨을 요구하지 않는 이상 일반적인 앱을 개발할 때에는 크게 신경 안써도 되지 않을까 생각합니다.

하지만!! 그래도!! 이왕이면!!


같은 레벨에서 작성하는게 조금이라도 성능 개선이 된다고 하니 ConstraintLayout에 대해서 알아보겠습니다.

오늘 포스팅할 내용은 위에서도 말씀 드렸지만  ConstraintLayout의 전체전인 부분이 아니라 기본적인 속성 입니다. 

그 이유는 처음부터 많은 속성을 보고 하나하나 적용 하는것도 의미가 있겠지만,
그것보다는 기본적인 핵심 특성만 보고 프로젝트에 조금씩 적용 하다 보면 점점 ConstraintLayout에 적응할 수 있지 않을까 해서 입니다.


가장 기본적인 속성입니다.

가장 기본적이면서도 가장 많이 사용 하며, 응용 하기에 따라 이정도만 알고 있어도 많은 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을 여기저거 적용 하다보면 바로 느낌 오실 겁니다.







2015년 6월 21일 일요일

DAETH (죽음이란 무엇인가)








죽음이란 무엇인가? 죽음에 대해서 진지하게 생각해 본적은 없는거 같다.
다만 종교적인 이유에선지 동양인이라는 점 때문인지는 모르겠지만 자연스럽게 영혼이란 존재를 믿었으며 환생을 믿고 있었다. 언제부터 어떤 영향으로 그렇게 믿고 있었는지는 나도 모른다. 하지만 나는 믿고 있었다. 영혼이란 존재와 환생을...

이 책은 나한테 이렇게 물어보았다. 왜??? 냉정하고 이성적으로 생각해 볼때 그 판단이 맞는지 물어보고 있다. 사실 죽은 이후를 생각하는데 어떻게 이성적으로 생각 할 수 있겠는가? 이성적이란 합리적인 판단을 기준으로 생각해야 의미가 있는것인데 사후 세계는 합리적인 판단이 불가능한 영역이다. 이런 합리적인 판단을 할 수 없는 주제를 합리적으로 생각해 보자라고 했을때 동,서양의 관념차이가 아닐까라는 생각도 들었다. 여기에 대해 셀리 케이건의 합리적인 추론을 바탕으로 죽음 이후에는 그냥 끝이라는 것이다.

영혼, 환생은 있을 수도 있고 없을 수도 있다. 이책을 다 읽은 지금 그건 중요하지 않다. 있으면 어떻고 없으면 어떻겠는가? 현재를 사는데 그게 중요한 일인가? 어차피 지금 나라는 자아를 가지고 있는 존재는 이번생을 살아가는 지금 나밖에는 없다. 죽으면 환생을 하고 다시 다른 자아로 살아가는 내 영혼이 있다 쳐도 그게 무슨의미가 있겠는가? 어차피 다음 생에서도 지금 처럼 이전 생의 기억은 없을 것이다.


죽음을 두려워 하는 이유는 삶에 대한 박탈 때문이라는 말에 공감이 간다. 앞으로 살아가면서 내가 누릴 모든것, 내가 살아오면서 누렸던 모든것을 잃는다는 두려움이 죽음을 두려워 하는 이유다. 죽음은 살면서 누렸던 모든것을 앗아가기 때문에 당연히 두려운 존재 일수 밖에 없다.
소유가 없는 박탈은 없기 때문에 박탈이론이 성립되려면 무언가를 소유해야된다. 그럼 상대적으로 많은걸 가지고 있는 사람은 그렇지 못한 사람보다 죽음을 더 두려워 할까?

셀리 케이건이 책에서 말하고 싶은것은  사실 죽음에 대해서가 아니라 삶을 어떻게 살아가야 하는가를 말하고 있다. 셀리 케이건은 질문한다.

'가치있게 생각하는것은 무엇인가?'

사람은 어차피는 죽는다. 그러면 내가 사는동안 가치 있는 삶을 살아야 되는게 아닌가라고 말하고 있다. 가치 없게 살다가 죽고 싶은 사람이 있을까? 그냥 숨만 쉬면서 일차원적 쾌락만 즐기다 죽고 싶은 사람이 있을까? 있을수도 있다. 하지만 100년동안 일차원적 쾌락에만 젖어 사는 삶을 생각해보면 그리 부러운 삶은 아니다.  그러면 어떻게 살아야 되는것인가?

'가치있은 삶'을 살아라.




2014년 12월 24일 수요일

[Android] 비행기 모드 설정에 관하여...

App 에서 비행기 모드를 ON / OFF 하는 방법은 간단합니다. 

아래 순서데로 포스팅 하겠습니다.
  1. 비행기 모드 상태값 알아오기.
  2. 비행기 모드 설정하기.
  3. 기타 설정(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 패키지로 이전 되었졌기 때문입니다. 그렇기 때문에 당연히 버전별로 분기처리를 했습니다. 

■ 비행기 모드의 상태값을 가져오는 메서드
static intgetInt(ContentResolver cr, String name, int def)
Convenience function for retrieving a single secure settings value as an integer.
getInt 메서드를 이용하여 현재 비행기 모드의 상태값을 int형으로 리턴 받을 수 있습니다.
리턴값이 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"값이 셋팅되게 됩니다.

■ 비행기 모드를 설정하는 메서드
static booleanputInt(ContentResolver cr, String name, int value)
Convenience function for updating a single settings value as an integer.
비행기 모드를 설정하는 소스를 보면 JellyBean(API 17) 이전 버전에서만 셋팅을 해주는 것을 확인 할 수 있습니다. JellyBean(API 17) 이후로는 비행기 모드를 직접 셋팅 할 수 없으며 환경설정을 통해서만 변경 할 수 있습니다. 
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
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 추가

  1. ActionBar에 Tab을 추가하기 위해서는 getActionBar()을 이용하여 ActionBar를 가지고 온다.
  2. bar.addTab(bar.newTab().setText("Tab 1").setTabListener(this));를 사용하여 Tab을 추가한다
  3. bar.setNavigationMode()를 설정해 준다.(ActionBar.NAVIGATION_MODE_TABS로 설정해 주시면 Tabs 영역이 나타나게 된다.)
  4. 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기능을 사용하여 개발 할 수도 있습니다.


참조