출처 : http://tigerwoods.tistory.com/31


예제 프로젝트 다운로드



 

  

1. 환경설정 개요 (Preferences)

 

안드로이드 플랫폼은 Data를 저장하는 방법으로 환경설정(이하 Preferences), 파일, Local DB, 네트워크를 제공한다.

 

그 중 Preferences는 가장 간단하게 정보를 저장하는 방법(mechanism)을 제공하며, App이나 그 컴포넌트 (Activity, Service 등)의 환경 설정 정보를 저장/복원하는 용도로 사용된다.

 

 

▌Preferences의 형태▐

안드로이드에서 Preferences는 ListView의 형태로 표현되며 쉬운 Preferences의 구현을 위해 PreferenceActivity 클래스를 제공한다. PreferenceActivity 클래스는 XML 기반의 Preference 정의 문서를 통해 App 파일 저장소에 Preferences 파일을 생성하고 사용하는 방식으로 작동한다. (참고: 일반 Activity를 사용해 ListView형태의 Preference Activity가 아닌 커스텀 Preference Activity를 구현 할 수도 있지만 (Container + 각종 UI 위젯 사용) 이 글에서는 그 방법에 대해 다루지 않으려고 합니다)

 

 

▌Preferences 데이터 저장▐

Preferences를 위한 데이터 저장 시 사용되는 자료구조는 Map 방식이다. 즉 키(key)-값(value) 한 쌍으로 이루어진 1~n개의 항목으로 구성된다. 키(key) 는 String 타입으로, 말 그대로 각 데이터에 접근할 수 있게 하는 유일한 구분자며, 값(Value)은 기본 자료형 (int, boolean, string 등) 기반의 데이터로 구성된다. 다음은 Map 데이터 구조의 한가지 예이다.

 키(Key) - String 형
 값(Value) - 기본자료형
 Font
 "Noraml"
 FontSize 20
 FontColor FFFFFFFF
 ... ...
 ... ...


 

위와 같은 Preferences데이터는 안드로이드 디바이스의

data/data/App 패키지 이름/shared_prefs/

경로에 XML형태의 파일로 생성된다. xml 파일 이름은, 파일 생성/접근을 위해 어떤 메서드를 사용하느냐에 따라 시스템이 지정 할 수도 있고, 개발자가 임의의 파일 이름을 지정할 수도 있다.

 

 

참고로, 안드로이드에서 Preferences 데이터 파일을 다수의 App간에 공유하는 것은 기본적으로 불가능 하게 디자인 되어 있다. (참고: 여기서 불가능하다는 뜻은 Preferences Framework에서는 타 App의 환경 설정 파일에 접근할 수 있는 메서드를 제공하지 않는다는 뜻이다. 그럴 필요가 있을지 모르겠지만, 굳이 접근해야 한다면 Preferences 파일 생성 모드를 MODE_WORLD_READABLE로 설정해 guest가 파일을 쓰고 읽을 수 있도록 하고(위에서 MyCustomePref.xml 처럼) Preferences 데이터 파일을 파일 스트림을 통해 읽어와 직접 파싱하고 사용할 수 있는 편법이 있긴 하다)

 

XML파일을 디바이스로부터 추출해 열어보면 Preference 데이터가 다음과 같은 형식으로 저장되어 있음을 볼 수 있다.

1<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
2<map>
3    <boolean name="GreetingMsg" value="true" />
4    <boolean name="sub_checkbox" value="true" />
5    <string name="AdditionalMsg">tigerwoods.tistory.com</string>
6    <string name="TextColor">FF00FF00</string>
7    <string name="Ringtone">content://settings/system/ringtone</string>
8</map>

 

 

Preferences 구현 시 위와 같은 XML 기반 Preferences 데이터 파일을 File I/O API를 사용해 직접 생성/사용 하는 것은 아니다. 안드로이드는 Preferences구현/운용을 위해 android.preference 패키지 및 몇 가지 유용한 클래스/인터페이스(PreferenceActivity, SharedPreferences 등)를 제공하는데, 그것들의 도움을 받으면 손쉽게 Preferences를 구현/운용 할 수 있다.

 

 

▌Preferences 구현▐

안드로이드에서는 Preference 구현을 위해 많은 관련 클래스를 제공하기 때문에 그 클래스들을 적절히 이용하여 다음과 같이 몇 가지 단계만 추가하기만 하면 쉽게 프로젝트에 Preferences 기능을 추가 할 수 있다.

  • 프로젝트 내 어떤 환경정보를 Preferences로 관리 할지 설계
  • 위 설계에 따라 Preferences XML 문서를 작성하고 프로젝트 리소스로 공급 (\res\xml\ 밑)
  • 작성된 Preferences XML문서를 사용하는 PreferenceActivity 구현
  • Preferences가 필요한 Activity로부터 PreferenceActivity를 호출 (Intent 이용)
  • App이 다시 실행될 때 이미 저장된 Preference데이터 파일이 있다면 onResume()과 같은 callback내부에서 환경 설정 값 설정을 불러와 사용

 

위에 나열된 사항 이외에 Preference 데이터 파일의 생성, 데이터 변경 이벤트 listener, 데이터의 저장과 같은 부분은 PreferenceActivity 클래스가 자동으로 관리해 줌으로 개발자는 별로 신경 쓸 필요가 없다. (참고: PreferenceActivity를 사용하지 않고 일반 Activity로 구현한다면 안드로이드가 제공하는 여러 Preferences 관련 클래스 등을 이용해 파일 생성, 이벤트 listener, 저장 등을 직접 구현 해야 한다.)

 

그럼 위 Preference 구현 5단계 중 두 번째인 Preference XML 문서 작성부터 한 번 살펴보자.

 

 

 

 

2. Preference XML 문서 작성하기

 

Preference XML 문서를 작성하기 전에 문서를 구성할 Preferences 관련 클래스들에 관해 약간의 지식이 필요함. 이 글 끝 부분에 "참고. Preference XML 문서 작성시 사용되는 클래스" 단락에 간단한 클래스 설명과 중요 XML 속성, 메서드 등이 설명 되어 있음으로 참고.

 

Preferences XML 문서는 프로젝트 폴더\res\xml\파일이름.xml의 위치에 생성 한다. xml 파일의 이름은 개발자 임의로 지정할 수 있으며 추 후 code 상에서 R.xml.파일이름으로 접근 할 수 있다.

 

이렇게 생성된 XML 문서 내부를 여러 Preference 관련 클래스를 사용해 정의 해야 하는데, 이 글에 포함된 예제 프로젝트에서 사용된 Preference XML 문서를 살펴보면 다음과 같다.

소스 펼치기

 

 

그럼 settings.xml에서 사용된 요소들을 하나씩 분석해 보자

 

 

<PreferenceScreen>

Preferences XML 문서의 root 요소는 항상 <PreferenceScreen>이며 root 요소 내부에는 반드시 xmlns 속성 (xmlns:android=http://......)을 정의해 주어야 한다.

 

root로 사용된 <PreferenceScreen>는 child를 포함하는 container 역할을 하며, PreferenceActivity에게 Preferences 계층 구조를 전달하는 역할을 한다. root로 사용된 <PreferenceScreen>은 container역할 만 하고 자기 자신은 화면에 직접 표현이 안됨으로 title, summary 속성을 지정할 필요가 없다.

 

key 속성에 지정된 문자열은 추 후 code에서 이 preference 계층 구조 내부의 특정 요소를 찾을 수 있는 키 값이 된다.


Preferencescreen root = (PreferenceScreen)getPreference("preference_root");

CheckBoxPreference cb = (CheckBoxPreference)getPreference("checkbox");

 

 

<CheckBoxPreference> & <EditTextPreference>

Root <PreferenceScreen> 밑에 처음 나오는 두 개의 child이며, XML에 추가된 순서대로 Preference view에 표시된다.

각 아이템은 key, title, summary 속성이 지정되어 있으며, checkbox의 경우 초기 설정이 check 상태로 지정되어 있다.

<EditTextPreference> 같은 경우 DialogPreference로부터 상속하며, 클릭 시 별도의 다이얼로그 창을 popup 한다.

 

 

<PreferenceCategory>

예제는 총 2개의 <PreferenceCategory>가 있으며 첫 번째는 <ListPreference>와 <RingtonePreference>를 하나의 Category로 묶는다. 예제 XML의 34번째 라인에 보면 두 번째 Category도 지정되어 있으며 <PreferenceScreen>을 Catefory 멤버로 가지고 있다.

 

<PreferenceCategory>에서 title속성은 다른 Preference 객체와 같이 제목을 표시하지만, summary는 지정되어도 화면에 표시 되지 않는다.

 

또, 첫 번째 category의 속성 중 android:enabled 속성이 false로 지정되어 있음으로 Preference초기 실행 시 child인 <ListPreference>와 <RingtonePreference>는 비활성화 상태로 표현된다. 이와 같이 여러 Preference 아이템을 하나의 category로 묶으면 그룹 속성을 지정하는 것이 가능하다. (Preference의 마지막 항목, checkbox를 check하면 첫 번째 category를 활성화 함)

 

 

<ListPreference>

다른 항목들과 마찬가지로 key, title, summary가 설정되어 있으며, entries 속성에는 사용자에게 보일 ListView 아이템을 지정하며, entryValues속성에는 컴퓨터가 처리할 처리 할 정보를 제공한다. 예를 들면 entries – "Red", "Green", "Blue" 등 사람이 읽을 수 있는 값을 제공하고, entryValues에는 "FFFF0000", "FF00FF00", "FF0000FF" 등 컴퓨터가 사용할 정보를 제공한다.

 

또, android:defaultValue 속성이 "FFFFFFFF"으로 지정되어 있어 사용자가 설정 값을 바꾸기 전까지는 "FFFFFFFF" (흰색)이 기본 값으로 사용된다.

 

 

<RingtonePreference>

key, title, summary가 지정되어 있으며, showDefault와 showSilent가 true로 설정되어 Default 항목과 Slient 항목이 리스트에 표시된다. (<RingtonePreference>는 사용자가 지정한 정보를 바탕으로 Ringtone 설정 기능을 제공하는 Activity들을 찾는 Intent를 발생 시킨다. 디바이스에 여러 종류의 ringtone과 ringtone 관련 activity를 제공하는 app이 설치되어 있다면 더 많은 ringtone 옵션이 화면에 표시 될 수도 있다)

 

 

<PreferenceCategory>

두 번째 그룹으로 CheckBox를 별도의 화면에 표현하는 <PreferenceScreen> child를 포함.

 

 

<PreferenceScreen>

<PreferenceScreen>가 child 요소로 사용된 경우. Preferences에서 하나의 아이템으로 표현되기 때문에 title, summary가 지정되어 있다. 이 아이템을 선택하면 별도의 Preference 창 (dialog 기반)이 Popup한다.

 

 

<CheckBoxPreference>

key, title이 지정되어 있으며, summayOn과 summaryOff에는 각각 check상태의 도움말과 uncheck상태의 도움말이 설정 되어 있다.

 

 

 

 

3. XML + PreferenceActivity이용해 Preference 구현하기

 

작성된 Preference XML 파일을 화면에 표현 가능한 Preference로 만드는데 핵심적인 역할을 하는 것이 바로 PreferenceActivity이다. 전에 살펴본 적이 있는 ListActivity, TabActivity와 마찬가지로 Activity로 상속하며, Preference를 화면에 표현하고 운영하는데 특화된 클래스이다.

 

PreferenceActivity는 Preference 생성/운영에 도움이 되는 다음과 같은 편리한 기능이 구현되어 있다.

  • XML부터 Preference 계층 구조를 전달 받는 메서드 제공
  • 타 Activity의 Preference 계층 구조를 받아오는 메서드 제공
  • 제공된 Preference 계층 구조를 ListView 형태로 자동 구성/표현
  • 제공된 Preference 계층 구조에 따라 디바이스에 Preference 데이터 파일 자동 생성
  • 사용자가 변경한 사항들을 Preference 데이터 파일에 자동 저장

 

 

PreferenceActivity를 이용해 Preference를 구현하려면 크게 두 가지 단계만 거치면 되는데 다음과 같다. (물론 PreferenceActivity도 Activity 생명 주기를 따르기 때문에 onPause(), onResume() 메서드 같은 생명 주기 관련 callback의 구현도 신경 써야 하지만 여기서는 Preference를 구현하는 최소한의 기능만 설명한다)

  • PreferenceActivity를 상속하는 커스텀 클래스 정의.
  • onCreate()에 내부에서 Preference 계층 구조를 가져오는 메서드 호출.

 

위에서 두 번째 구현 순서로 설명된 Preference 계층 구조를 가져오는 메서드는 2개가 제공된다.

우선, \res\xml 밑의 Preference XML로부터 Preference 계층 구조를 얻어올 수 있는 메서드는 다음과 같다.

void addPreferencesFromResource(int)

Parameter:

  • int: Preference XML Resource. (예. R.xml.preferences)

 

두 번째로 타 Activity가 사용하는 Preference 계층 구조를 가져와 적용 시킬 수도 있는데, 이 때 사용되는 메서드는 다음과 같다.

(이 부분은 해결이 안 되는 중요한 버그가 있습니다. main preference까지는 잘 가져와 지는데 별도의 dialog를 띄우는 Preference 아이템을 클릭하면 WindowsManager가 BadTokenException을 발생합니다. 자세한 증상은 첨부된 예제 프로젝트에서 확인하시고, 혹시 해결 방법을 아시는 분은 꼭 댓글 부탁 드립니다)

void addPreferencesFromIntent(Intent)

Parameter:

  • Intent: Preference 계층 구조를 제공할 Activity를 지정하는 Intent

 

구현이 완료된 PreferenceActivity는 타 Activity로 부터 호출 되면 ListView 형태의 Preference 화면을 사용자에게 제공하고, 사용자가 정보를 수정하면 자동으로 Preference 데이터에 저장 하게 된다.

 

PreferenceActivity가 많은 편의 기능을 제공하지만 저장된 설정 복원은 개발자의 몫이다. 즉, 포함된 App이 다시 실행될 때, 기존에 저장된 Preferences설정 값을 Preference 데이터 파일로부터 읽어와 App에 적용 시키는 작업 (ex. App시작 시 Preference 파일에 저장된 폰트 색깔 등을 로드 해 App에 적용시키는)은 개발자가 직접 구현해 주어야 한다. 이를 위해 다음 단락에는 저장된 Preference 데이터 파일에 접근하여 저장된 정보를 가져오는 방법에 대해 알아본다.

 

 

 

 

4. Preference 데이터 파일로부터 환경 복원하기

 

안드로이드에서는 SharedPreferences 인터페이스를 이용해 저장된 Preferences 데이터 파일에 접근한다.

 

 

▌SharedPreferences 인터페이스 얻기▐

 

안드로이드 코드 내에서 SharedPreferences 인터페이스를 얻는 방법은 다음 나열된 세 개의 메서드를 통해서 가능하다. 



SharedPreferences Activity.getPreferences(int)

Parameter:

  • int: Preference 데이터 파일의 생성 시 접근 권한을 설정. Context클래스에 상수로 선언된 MODE_PRIVATE, MODE_WORLD_READABLE, MODE_WORLD_WRITABLE을 설정 가능. MODE_PRIVATE는 지금 App 만 접근 가능하도록 파일을 생성하며 (_rw_rw____), MODE_WORLD_READABLE/WRITABLE은 타 App에서도 파일을 읽고/쓸 수 있도록 생성한다 (_rw_rw_r__ 또는 _rw_rw__w_ 또는 |를 이용해 두 속성을 모두 지정하면 _rw_rw_rw_)

Return:

  • SharedPreferences: 메서드가 호출되는 Activity 클래스의 이름으로 저장된 Preference 데이터 파일의 SharedPreferences 인터페이스를 리턴. 같은 이름의 Preference 데이터 파일이 없을 경우 새로 생성

 

예를 들면 Activity를 상속하는 A, B라는 클래스 이름의 커스텀 Activity가 정의 되어 있다. A 내부에서 getPreference를 호출할 경우 디바이스의 Preference 저장 위치(data/data/패키지이름/shared_prefs/)에 A.xml 이라는 Preference 데이터 파일을 검색하고 파일이 존재 한다면 해당 파일에 대한 SharedPreferences 인터페이스를 리턴 하며, 파일이 존재하지 않을 경우에는 A.xml 파일을 생성한 후 생성한 파일에 대한 SharedPreferences 인터페이스를 리턴한다. 마찬가지로, B 내부에서 getPreference를 호출할 경우 디바이스에서 B.xml 파일을 검색 후 해당 파일에 대한 SharedPreferences 인터페이스를 리턴 한다 (없으면 파일 생성 후 인터페이스 리턴).


 

SharedPreferences Context.getSharedPreferences(String, int)

Parameter:

  • String: 사용(생성)할 Preference 데이터 파일의 이름을 지정
  • int: 생성될 Preference 데이터 파일의 생성 시 접근 권한을 설정

Return:

  • SharedPreferences: 첫 번째 인자로 전달되는 문자열과 일치하는 이름의 Preference 데이터 파일의 SharedPreferences 인터페이스를 리턴. 만약 같은 이름의 파일이 없다면 파일을 새로 생성하고 생성된 파일에 대한 SharedPreferences 인터페이스를 리턴

 

한가지 참고할 사항은 이 메서드를 통해 개발자가 생성/사용될 Preference 데이터 파일의 이름을 임의로 설정할 수 있다는 것인데, 예를 들어, A라는 Activity에서 getSharedPreferences("initial_setting", MODE_PRIVATE)라는 메서드를 실행한다면, preference framework는 /data/data/패키지/shared_pref/ 밑에서 initial_setting.xml이라는 파일을 검색하고 없다면 이를 새로 생성 후 initial_setting.xml에 대한 SharedPreferences 인스턴스를 리턴 하게 된다..

 

제일 처음에 설명한 Activity.getPreferences(int)도 실제로는 자기자신을 호출한 Activity(또는 컴포넌트)의 이름과 입력된 int 형 인자를 가지고 본 메서드를 호출하는 형태로 구현되어 있다.




Static SharedPreferences PreferenceManager.getDefaultSharedPreferences(Context)

Parameter:

  • Context: 사용하기 원하는 SharedPreferences 인터페이스를 포함하는 Context 지정

Return:

  • SharedPreferences: 인자로 지정된 Context (app 구동 환경)가 기본으로 생성하는 Preference 데이터 파일에 대한 SharedPreferences를 리턴

 

이 메서드는 App level의 기본 Preference 데이터 파일을 생성/사용하는데 사용된다. 예를 들어 com.holim.test.aaa 라는 패키지에 포함되는 A Activity에서 본 메소드를 호출 하면 Preference 프레임웍은 /data/data/패키지이름/shared_pref/com.holim.test.aaa_preferences.xml 이라는 Preference 데이터 파일을 생성한다. 전달된 Context 인자에 따라 패키지이름_preferences.xml 형태로 생성/사용할 Preference 데이터 파일의 이름이 시스템에 의해 자동으로 지정되게 되기 때문에, 호출되는 위치가 어디이든 같은 Context를 인자로 전달해 호출한 getDefaultSharedPreferences(…)메서드는 항상 같은 SharedPreferences 인스턴스를 리턴한다.

 

한가지 참고할 사항은 본 메서드는 static 메서드이기 때문에 메서드를 제공하는 PreferenceManager 클래스를 따로 생성할 필요 없이 다음과 같이 사용하면 된다.

SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);

 

 

이와 같이 여러 메서드를 사용해 얻어온 SharedPreferences 인터페이스로 무엇을 할 수 있는 지 알아 보자. 다음은 SharedPreferences 인터페이스가 제공하는 중요 메서드들이다.

 

 

▌SharedPreferences 인터페이스▐

 

SharedPreferences 인터페이스가 제공하는 기능은 다음과 같다.

  • 디바이스에 저장되어 있는 Preference 데이터 파일로부터 데이터를 추출하는 여러 메서드
  • Preference 데이터를 수정하는 방법을 제공하는 Editor 내부 인터페이스
  • Preference 데이터가 변경되었을 때 호출되는 callback 인터페이스

 

중요 메서드

SharedPreferences.Editor edit() – SharedPreferences가 연결된 Preference 데이터 파일을 수정 할 수 있게 여러 메서드를 제공하는 Editor 인터페이스 리턴. Editor 인터페이스는 자료 수정을 완료 한 후 Editor.commit() 메소드를 사용해야 파일에 변경내용이 저장됨.

void registerOnSharedpreferenceChangeListener(…) – Preference 변경 이벤트 Listener 등록

void unregisterOnSharedPreferenceChangeListener(…) – 위에 등록한 이벤트 Listener 등록 해지

Map <String, ?> getAll() – 모든 Preferences 값을 Map 자료형으로 리턴

boolean contains(String) – 인자로 제공되는 문자열과 같은 key의 Preference 항목이 있으면 true 리턴

타입 get타입(String, 타입) – Boolean, Float, Int, Long, String 형 메서드가 있으며 첫 인자는 데이터를 가져올 Key 값이며, 두 번째 인자는 찾는 preference 데이터가 존재하지 않을 때 리턴 할 값이다.

Boolean isMarried = sharedPref.getBoolean("결혼여부", false);

int fontSize = sharedPref.getInt("폰트사이즈", 20);

 

중요 인터페이스

  • SharedPreferences.Editor – Preference 파일에 저장된 항목을 수정/제거 할 수 있는 여러 메서드를 제공하는 인터페이스
  • SharedPreferences.OnSharedPreferenceChangeListener – Preference 데이터가 변경되었을 때 호출되는 callback 인터페이스

 

 

예제 프로젝트 중 MainActivity.java의 onCreate(…)와 onPause(…)메서드에서 보면 지금까지 설명한 SharedPreferences 인터페이스 얻기와 인터페이스가 제공하는 여러 메서드를 사용해 Preference 데이터 파일로부터 저장된 정보를 가져와 사용하는 것을 볼 수 있다.


MainActivity.onCreate()

소스 펼치기

 

MainActivity.onResume() 

소스 펼치기

 

 

 

 

참고. Preference XML 문서 작성시 사용되는 클래스

 

전 글들에서 다루었던 XML을 이용한 UI 및 메뉴 구성에서도 봤듯이

XML을 이용하면 디자인과 Logic을 분리 시킬 수 있어 간결한 코드의 구성이 가능할 뿐 아니라 현지화/유지보수 등 작업을 하는데 훨씬 편리 할 수 있음으로 권장되는 구현 방식 이라고 설명 한 적이 있다.

 

Preference에서도 마찬가지로 XML을 이용해 Preferences 구조를 쉽게 정의할 수 있다.

 

Preference XML 문서 작성시 android.preference 패키지 밑의 여러 클래스가 사용되는데 패키지 내부의 중요한 클래스의 상속 구조는 다음과 같다.

 

 

붉은 색 사각형 내부의 클래스가 Preferences 과 직접적으로 연관이 있는 클래스이며, 그 중 Preference 클래스를 비롯한 하위 클래스들이 XML Preference 문서 작성에 사용된다.

 

그럼 Preference 클래스부터 한번 살펴보자

  


 

▌Preference 클래스▐

여러 Preference 관련 클래스의 최상위 부모 클래스 이므로, 제공하는 XML 속성, 메서드는 자식 Preferences 관련 클래스에서도 모두 사용할 수 있다. XML 내부에서 직접적으로 사용되지는 않는다.

 

중요 XML 속성

  • android:key – Preferences 데이터 저장/불러올 때 사용되는 키(key) 지정
  • android:title – Preference 항목의 제목을 지정
  • android:summary – Preference 항목을 자세히 설명하는 문자열 지정
  • android:enabled – Preference 항목의 활성화 비활성화 여부 지정 (true/false)
  • android:selectable – Preference 항목의 선택 가능 여부 결정 (true/false)
  • android:order – Preference 항목이 표시되는 순서를 결정 (0-based 정수 사용: 낮은 값 먼저 보임). 순서가 명시적으로 지정되지 않는다면 XML에 정의된 순서대로 표시됨

 

중요 메서드

  • void setKey/Title/Summary(…) – Preference 항목의 키/제목/설명을 지정
  • void setEnabled(boolean) - Preference항목의 활성화/비활성화 여부 지정
  • void setSelectable(boolean) – Preference 항목의 선택 가능 여부 결정
  • void setLayoutResource(int) – Preference 항목에 표시할 View를 R 클래스로부터 지정
  • void setOnPreferenceChangedListener(…) – Preference 항목에 변화가 있으면 호출될 callback을 지정
  • void setOnPreferenceClickListener(…) – Preference 항목에 click 이벤트가 발생하면 호출될 callback을 지정

 

중요 인터페이스

  • Preference.OnPreferenceChangeListener - 사용자가 Preference를 변경하였을 때 호출되는 callback에 대한 인트페이스
  • Preference.OnPreferenceClickListener - 사용자가 Preference를 클릭하였을 때 호출되는 callback에 대한 인터페이스

 

  


▌PreferenceGroup 클래스▐

 

Preferences를 구성하는 객체들을 담을 수 있는 Container 클래스. PreferenceCategory와 PreferenceScreeen을 파생. XML 내부에서 직접적으로 사용되지는 않음.

 

중요 메서드

  • boolean addPreference(Preference) – 새로운 Preference 항목을 그룹에 추가. 추가 성공/실패 리턴.
  • Preference findPreference(String) – 인자로 전달되는 문자열과 같은 key값을 갖는 group 내 (자기자신포함) Preference 항목 리턴
  • Preference findPreference(int) – 인자(0-based정수)에 명시된 위치의 Preference 항목 리턴. 기본적으로 가장 처음 추가된 항목이 index 0
  • void removeAll() – 이 그룹 내 모든 Preference 항목을 삭제함
  • void removePreference(Preference) - 인자로 전달된Preference 항목을 삭제함
  • void setEnabled(boolean) – 그룹 전체의 활성화/비활성화 여부 지정
  • void setOrderingAsAdded(boolean) – true일 경우 그룹에 추가된 순서로 표현. false일 경우 Preference아이템 내 순서 정보가 있으면 그걸 따르고 순서 정보가 없으면 Preference 아이템의 title을 이용해 알파벳순 정렬.

  


 

▌PreferenceScreen 클래스▐

 

PreferenceGroup에서 파생하며, 실제로 Preference XML 문서 내부에 사용 되는 클래스이다. 여러 Preference 구성 요소를 담을 수 있는 root container역할을 하며 Activity에 표현될 Preference의 계층구조를 나타내는 클래스이다. PreferenceActivity에 전달되어 이 클래스의 인스턴스가 포함하는 것들을 화면에 표현한다.

 

이 클래스가 Preference XML 문서에서 사용될 때는 다음과 같이 두 가지 용도로 사용된다.

  • Preference XML 문서의 root 요소 사용 시 - 경우에는 PreferenceActivity에 전달되어 preference의 전체 구조를 전달하는 용도로 사용. 이 경우 자기 자신은 화면에 표현 안되고 child 요소를 포함하는 Container의 용도로 사용됨 (XML UI에서 Containter는 화면에 표현 안되고 것과 같은 의미)
  • Preference XML 문서에서root가 아닌 child 요소로 사용 시 - Preference 아이템 중 하나로 취급. Preference 아이템 중 하나로 화면에 표시되며, 클릭 시 별도의 preference 화면으로 이동

 

!!! 그림 (main pref. -> sub pref)

 


 

▌PreferenceCategory 클래스▐

 

Preferences를 구성하는 객체들을 특정 category별로 분류 할 수 있게 하는 클래스. 중요한 XML 속성이나 메서드 없음

!!! 그림 (category 분류)

 

  


▌DialogPreference 클래스▐

 

모든 dialog 기반 Preference 객체의 부모 클래스. Preference 화면에는 링크만 표시되고, 링크를 클릭했을 때 실제 Preference를 컨트롤 할 수 있는 Dialog가 popup한다.

!!! 다이얼로그 그림

 

중요 XML 속성

  • android:dialogIcon – Dialog의 Icon 지정
  • android:dialogTitle – Dialog의 제목 지정
  • android:dialogMessage – Dialog 내에 표시될 문자열 내용 지정
  • android:negativeButton – 부정 버튼에 표시될 Text설정 (취소, cancel 등)
  • android:positiveButton – 긍정 버튼에 표시될 Text 설정 (적용, OK 등)

 

중요 메서드

  • void setDialogIcon(Drawable) – Dialog의 Icon 지정
  • void setDialogTitle(…) – Dialog의 제목 지정. 문자열 직접 지정 또는 문자열 리소스 사용
  • void setDialogMessage(…) – Dialog 내에 표시될 문자열 내용 지정
  • void setPositiveButtonText(…) – 부정 버튼에 표시될 Text설정 (취소, cancel 등)
  • void setNegativeButtonText(…) – 긍정 버튼에 표시될 Text 설정 (적용, OK 등)

  


 

▌EditeTextPreference 클래스▐

 

DialogPreference로부터 상속하며, 사용자로부터 문자열을 입력 받아 저장 하기 위한 Preference 아이템을 구현한 클래스.

 

중요 메서드

  • EditText getEditText() – Dialog가 포함하는 EditBox인스턴스를 리턴
  • String getText() – SharedPreferences 객체로부터 저장된 문자열 리턴
  • void setText() – SharedPreferences에 문자열 저장

  


 

▌ListPreference 클래스▐

 

DialogPreference로부터 상속하며, Dialog기반의 ListView 위젯에 미리 제공되는 문자열들을 표현하고 사용자가 그 문자열 중 하나를 선택하여 설정 값을 지정할 수 있도록 구현한 클래스.

 

중요 XML 속성

  • android:entries – ListView의 각 raw에 표현될 문자열을 array 리소스를 통해 공급. 사용자(human)를 위한 정보.
  • android:entryValues – ListView의 특정 raw가 사용자에 의해 선택되었을 때 프로그램 내부적으로 처리 할 문자열 data를 array 리소스 형식으로 제공. 컴퓨터가 처리 하기 위한 정보. (ex. 남/여: 사람을 위한 정보 -> 0/1: 컴퓨터를 위한 정보)
  • android:defaultValue – 초기 선택 항목 지정. (0-based 정수)

 

중요 메서드

  • CharSequence[] getEntries() – ListView의 각 row에 표현될 문자열들을 리턴 (사용자 위한 정보)
  • CharSequence[] getEntryValues() – Entry(사람을 위한 정보)와 연계된 컴퓨터가 처리할 문자열들을 리턴
  • void setEntries(…) – ListView의 각 row에 표현할 문자열 지정. Array리소스 또는 CharSequence[] 전달
  • void setEntryValues(…) – 컴퓨터가 처리할 문자열 지정. Array또는 CharSequence[] 전달

  


 

▌CheckBoxPreference 클래스▐

 

CheckBox 기능의 Preference 아이템을 구현한 클래스. SharedPreferences에 boolean 값으로 정보 저장

 

중요 XML 속성

  • android:summayOn – CheckBox가 check상태일 때 사용자에게 보일 안내문
  • android:summaryOff – CheckBox가 uncheck 상태일 때 사용자에게 보일 안내문

 

중요 메서드

  • boolean isChecked() – 현재 check/uncheck 상태 리턴
  • void setChecked(boolean) – CheckBox를 program 상에서 check/uncheck 함
  • void setSummaryOn(…) – CheckBox가 check상태일 때 사용자에게 보일 안내문 설정. String 리소스나 CharSequence 인스턴스 전달
  • void setSummaryOff(…) – CheckBox가 uncheck상태일 때 사용자에게 보일 안내문 설정

  


 

▌RingtonPreference▐

 

사용자가 디바이스에서 제공하는 전화 벨 소리를 지정할 수 있게 구현된 클래스. Intent를 이용해 어떤 벨 소리 Picker를 화면에 표시할 지 결정함.

 

중요 XML 속성

  • android:ringtoneType – 어떤 종류의 벨 소리 종류를 선택 가능하게 할지 지정. ringtone, notification, alarm, all 중에 하나 또는 복수(| 사용)개 지정 가능
  • android:showDefault – Default 벨소리 항목 표시 여부 결정 (true/false)
  • android:showSilent – 무음(Silent) 항목 표시 여부 결정 (true/false)

 

중요 메서드

  • setRingtoneType(int type) – XML 속성 중 ringtoneType에 대응. RingtoneManager 클래스에 선언되어 있는 상수 TYPE_RINGTONE, TYPE_NOTIFICATION, TYPE_ALARM, TYPE_ALL 중 하나 또는 복수(|사용) 전달
  • setShowDefault(boolean) – XML showDefault 속성에 대응
  • setShowSlient(boolean) – XML showSilent 속성에 대응

  


 

 PreferenceManager 클래스 

 

Activity나 XML로부터 Preferences 계층구조의 생성을 돕는 helper 클래스이지만 이 클래스를 이용하여 직접 Preferences를 구성하기보다는 PreferenceActivity.addPreferenceFromResource(int) 또는 PreferenceActivity.addPreferenceFromIntent(Intent)를 사용하는 것이 일반적이다.

 

혹시, PreferenceActivity를 사용하지 않고 Activity를 이용해 직접 Preferences 화면을 구성해야 한다면 필요한 클래스이다.

 

중요 메서드

  • Preference findPreference(CharSequence) – 인자로 전달되는 Key값을 가지는 Preference 항목의 인스턴스를 가져옴
  • SharedPreferences getDefaultSharedPreferences(Context) – 제공되는 context가 기본으로 사용하는 Preference파일로부터 SharedPreferences인스턴스 생성해 리턴
  • SharedPreferences getSharedPreferences() – this객체가 사용하는 context와 연결될 Preference파일로부터 SharedPreferences 인스턴스 생성해 리턴
  • void setDefaultValues(Context, int resId, boolean) – 제공되는 context가 사용하는 SharedPreferences를 두 번째 인자로 제공되는 XML 리소스에 정의된 기본 속성값들로 설정 함. 세 번째 인자가 false 일 경우엔 이 메서드가 전에 실행된 적이 있다면 무시하고, true일 경우 전에 실행 여부화 상관없이 재 실행된다. 참고: 마지막 인자를 true로 지정 했다고 해서 이 메서드를 이용해 Preferences를 바로 초기화 할 수 있는 것은 아니고, SharedPrefernces.clear() 메서드를 사용해 Preferences 항목을 모두 삭제 후 이 메서드를 사용해 XML에 지정된 본래의 값으로 Preferences를 초기화 할 수 잇다.

 

 

프로젝트 폴더에서 마우스오른쪽 클릭 -> Refactor -> Rename

  원문https://source.android.com/devices/tech/input/keyboard-devices.html#keyboard-classification

  밑줄 내용참고

Keyboard Devices



Android supports a variety of keyboard devices including special function keypads (volume and power controls), compact embedded QWERTY keyboards, and fully featured PC-style external keyboards.


This document decribes physical keyboards only. Refer to the Android SDK for information about soft keyboards (Input Method Editors).

Keyboard Classification


An input device is classified as a keyboard if either of the following conditions hold:

  • The input device reports the presence of any Linux key codes used on keyboards including 0 through 0xff orKEY_OK through KEY_MAX.

  • The input device reports the presence of any Linux key codes used on joysticks and gamepads including BTN_0 through BTN_9BTN_TRIGGER through BTN_DEAD, or BTN_A through BTN_THUMBR.

Joysticks are currently classified as keyboards because joystick and gamepad buttons are reported by EV_KEY events in the same way keyboard keys are reported. Thus joysticks and gamepads also make use of key map files for configuration. Refer to the section on Joystick Devices for more information.

Once an input device has been classified as a keyboard, the system loads the input device configuration file and keyboard layout for the keyboard.

The system then tries to determine additional characteristics of the device.

  • If the input device has any keys that are mapped to KEYCODE_Q, then the device is considered to have an alphabetic keypad (as opposed to numeric). The alphabetic keypad capability is reported in the resource Configuration object as KEYBOARD_QWERTY.

    -> 키 KEYCODE_Q 는 알파벳 문자 입력장치로 인식.

  • If the input device has any keys that are mapped to KEYCODE_DPAD_UPKEYCODE_DPAD_DOWNKEYCODE_DPAD_LEFT,KEYCODE_DPAD_RIGHT, and KEYCODE_DPAD_CENTER (all must be present), then the device is considered to have a directional keypad. The directional keypad capability is reported in the resource Configuration object asNAVIGATION_DPAD.

    -> KEYCODE_DPAD_UPKEYCODE_DPAD_DOWNKEYCODE_DPAD_LEFT,KEYCODE_DPAD_RIGHT, and KEYCODE_DPAD_CENTER 는 방향 키패드 입력장치로 인식

  • If the input device has any keys that are mapped to KEYCODE_BUTTON_A or other gamepad related keys, then the device is considered to have a gamepad.

    -> KEYCODE_BUTTON_A or other gamepad related keys 면 게임패드로 인식

Keyboard Driver Requirements


  1. Keyboard drivers should only register key codes for the keys that they actually support. Registering excess key codes may confuse the device classification algorithm or cause the system to incorrectly detect the supported keyboard capabilities of the device.

    -> 실제로 사용하기 위해선 key의 key code들을 등록해야만 한다. Key code들을 너무 많이 등록하면 제대로 인식 못하거나 Device에 혼란을 줄 수 있으므로 주의

  2. Keyboard drivers should use EV_KEY to report key presses, using a value of 0 to indicate that a key is released, a value of 1 to indicate that a key is pressed, and a value greater than or equal to 2 to indicate that the key is being repeated automatically.

    -> key press를 위해선 EV_KEY 를 사용해야한다. '0'은 key release, '1'은 key press, '2'이상은 반복적으로 인식한다.

  3. Android performs its own keyboard repeating. Auto-repeat functionality should be disabled in the driver.

    -> 안드로이드에선 자체적으로 keyboard repeating을 수행하므로, 드라이버에서 auto_repeat 은 중지해야한다. ( ? : auto repeat이 뭘 의미하지..??)

  4. Keyboard drivers may optionally indicate the HID usage or low-level scan code by sending EV_MSC with MSC_SCANCODE and a value indicating the usage or scan code when the key is pressed. This information is not currently used by Android.

  5. Keyboard drivers should support setting LED states when EV_LED is written to the device. The hid-input driver handles this automatically. At the time of this writing, Android uses LED_CAPSLOCKLED_SCROLLLOCK, andLED_NUMLOCK. These LEDs only need to be supported when the keyboard actually has the associated indicator lights.

  6. Keyboard drivers for embedded keypads (for example, using a GPIO matrix) should make sure to send EV_KEYevents with a value of 0 for any keys that are still pressed when the device is going to sleep. Otherwise keys might get stuck down and will auto-repeat forever.

Keyboard Operation (안드로이드에서 Keyboard 동작 요약)


  1. The EventHub reads raw events from the evdev driver and maps Linux key codes (sometimes referred to as scan codes) into Android key codes using the keyboard's key layout map.

    -> EventHub 는 evdev 드라이버에서 이벤트들을 읽고,  리눅스 Key code(또는 Scan Code)들을 'Keyboard Key layout map'을 사용해서 안드로이드 key로 매핑한다.

  2. The InputReader consumes the raw events and updates the meta key state. For example, if the left shift key is pressed or released, the reader will set or reset the META_SHIFT_LEFT_ON and META_SHIFT_ON bits accordingly.

  3. The InputReader notifies the InputDispatcher about the key event.

  4. The InputDispatcher asks the WindowManagerPolicy what to do with the key event by calling WindowManagerPolicy.interceptKeyBeforeQueueing. This method is part of a critical path that is responsible for waking the device when certain keys are pressed. The EventHub effectively holds a wake lock along this critical path to ensure that it will run to completion.

  5. If an InputFilter is currently in use, the InputDispatcher gives it a chance to consume or transform the key. The InputFilter may be used to implement low-level system-wide accessibility policies.

  6. The InputDispatcher enqueues the key for processing on the dispatch thread.

  7. When the InputDispatcher dequeues the key, it gives the WindowManagerPolicy a second chance to intercept the key event by calling WindowManagerPolicy.interceptKeyBeforeDispatching. This method handles system shortcuts and other functions.

  8. The InputDispatcher then identifies the key event target (the focused window) and waits for them to become ready. Then, the InputDispatcher delivers the key event to the application.

  9. Inside the application, the key event propagates down the view hierarchy to the focused view for pre-IME key dispatch.

  10. If the key event is not handled in the pre-IME dispatch and an IME is in use, the key event is delivered to the IME.

  11. If the key event was not consumed by the IME, then the key event propagates down the view hierarchy to the focused view for standard key dispatch.

  12. The application reports back to the InputDispatcher as to whether the key event was consumed. If the event was not consumed, the InputDispatcher calls WindowManagerPolicy.dispatchUnhandledKey to apply "fallback" behavior. Depending on the fallback action, the key event dispatch cycle may be restarted using a different key code. For example, if an application does not handle KEYCODE_ESCAPE, the system may redispatch the key event as KEYCODE_BACK instead.

Keyboard Configuration


Keyboard behavior is determined by the keyboard's key layout, key character map and input device configuration.

Refer to the following sections for more details about the files that participate in keyboard configuration:

Properties

The following input device configuration properties are used for keyboards.

keyboard.layout

Definition: keyboard.layout = <name>

Specifies the name of the key layout file associated with the input device, excluding the .kl extension. If this file is not found, the input system will use the default key layout instead.

Spaces in the name are converted to underscores during lookup.

Refer to the key layout file documentation for more details.

keyboard.characterMap

Definition: keyboard.characterMap = <name>

Specifies the name of the key character map file associated with the input device, excluding the .kcm extension. If this file is not found, the input system will use the default key character map instead.

Spaces in the name are converted to underscores during lookup.

Refer to the key character map file documentation for more details.

keyboard.orientationAware

Definition: keyboard.orientationAware = 0 | 1

Specifies whether the keyboard should react to display orientation changes.

  • If the value is 1, the directional keypad keys are rotated when the associated display orientation changes.

  • If the value is 0, the keyboard is immune to display orientation changes.

The default value is 0.

Orientation awareness is used to support rotation of directional keypad keys, such as on the Motorola Droid. For example, when the device is rotated clockwise 90 degrees from its natural orientation, KEYCODE_DPAD_UP is remapped to produce KEYCODE_DPAD_RIGHT since the 'up' key ends up pointing 'right' when the device is held in that orientation.

keyboard.builtIn

Definition: keyboard.builtIn = 0 | 1

Specifies whether the keyboard is the built-in (physically attached) keyboard.

The default value is 1 if the device name ends with -keypad0 otherwise.

The built-in keyboard is always assigned a device id of 0. Other keyboards that are not built-in are assigned unique non-zero device ids.

Using an id of 0 for the built-in keyboard is important for maintaining compatibility with theKeyCharacterMap.BUILT_IN_KEYBOARD field, which specifies the id of the built-in keyboard and has a value of 0. This field has been deprecated in the API but older applications might still be using it.

A special-function keyboard (one whose key character map specifies a type of SPECIAL_FUNCTION) will never be registered as the built-in keyboard, regardless of the setting of this property. This is because a special-function keyboard is by definition not intended to be used for general purpose typing.

Example Configurations

# This is an example input device configuration file for a built-in
# keyboard that has a DPad.

# The keyboard is internal because it is part of the device.
device
.internal = 1

# The keyboard is the default built-in keyboard so it should be assigned
# an id of 0.
keyboard
.builtIn = 1

# The keyboard includes a DPad which is mounted on the device.  As the device
# is rotated the orientation of the DPad rotates along with it, so the DPad must
# be aware of the display orientation.  This ensures that pressing 'up' on the
# DPad always means 'up' from the perspective of the user, even when the entire
# device has been rotated.
keyboard
.orientationAware = 1

Compatibility Notes

Prior to Honeycomb, the keyboard input mapper did not use any configuration properties. All keyboards were assumed to be physically attached and orientation aware. The default key layout and key character map was named qwerty instead of Generic. The key character map format was also very different and the framework did not support PC-style full keyboards or external keyboards.

When upgrading devices to Honeycomb, make sure to create or update the necessary configuration and key map files.

HID Usages, Linux Key Codes and Android Key Codes


The system refers to keys using several different identifiers, depending on the layer of abstraction.

For HID devices, each key has an associated HID usage. The Linux hid-input driver and related vendor and device-specific HID drivers are responsible for parsing HID reports and mapping HID usages to Linux key codes.

As Android reads EV_KEY events from the Linux kernel, it translates each Linux key code into its corresponding Android key code according to the key layout file of the device.
-> 안드로이드는 리눅스 커널에서 EV_KEY event 를 읽고, Device의 key layout file을 이용해 각 리눅스 Key code를 안드로이드 Key code로 번역한다.

When the key event is dispatched to an application, the android.view.KeyEvent instance reports the Linux key code as the value of getScanCode() and the Android key code as the value of getKeyCode(). For the purposes of the framework, only the value of getKeyCode() is important.

Note that the HID usage information is not used by Android itself or passed to applications.

Code Tables


The following tables show how HID usages, Linux key codes and Android key codes are related to one another.
-> HID가 어떻게 리눅스 키코드와 안드로이드 키코드가 연결되어 사용되는지 보여줌.

The LKC column specifies the Linux key code in hexadecimal.

The AKC column specifies the Android key code in hexadecimal.

The Notes column refers to notes that are posted after the table.

The Version column specifies the first version of the Android platform to have included this key in its default key map. Multiple rows are shown in cases where the default key map has changed between versions. The oldest version indicated is 1.6.

  • In Gingerbread (2.3) and earlier releases, the default key map was qwerty.kl. This key map was only intended for use with the Android Emulator and was not intended to be used to support arbitrary external keyboards. Nevertheless, a few OEMs added Bluetooth keyboard support to the platform and relied on qwerty.kl to provide the necessary keyboard mappings. Consequently these older mappings may be of interest to OEMs who are building peripherals for these particular devices. Note that the mappings are substantially different from the current ones, particularly with respect to the treatment of the HOME key. It is recommended that all new peripherals be developed according to the Honeycomb or more recent key maps (ie. standard HID).

  • As of Honeycomb (3.0), the default key map is Generic.kl. This key map was designed to support full PC style keyboards. Most functionality of standard HID keyboards should just work out of the box.

The key code mapping may vary across versions of the Linux kernel and Android. When changes are known to have occurred in the Android default key maps, they are indicated in the version column.

Device-specific HID drivers and key maps may apply different mappings than are indicated here.


안드로이드 TextView는 글씨를 다루는 뷰인 만큼 폰트를 교체 할 수 있습니다 .

안드로이드에서는 네가지의 기본 폰트를 제공하고, 추가로 폰트를 추가하면 해당 폰트를 

사용 할 수 있는데요, 먼저 기본폰트는 narmal , sans, serif, monospace 네가지로 


이런 폰트 모양입니다. 폰트 적용은 간단한데요 xml의 TextView에서 

android:typeface= "normal" 이런식으로 추가해 주시면 됩니다. 


이제 다른 폰트를 다운받아 적용시키는 방법인데요 , 



위에 Frutiger55Roman 이라는 이름의 폰트가 있습니다 , 이걸 프로젝트의 assets 폴더에 넣어주시고



소스 상에서 추가를 해주여야 합니다. 간단한 테스트 이므로 저는 onCreate에 추가를 하였습니다.



두번째 줄이 핵심입니다, 뒤에 확장자까지 써주셔야하는거 잊지마시구요 . 저렇게 등록을 해주면 


이런 추가된 폰트로 글씨가 나타나게 됩니다 ! 


항상 최상위에 나오는 뷰 만들기2 (팝업 비디오 + Q슬라이드)


이전에 쓴 글 '항상 최상위에 나오는 뷰 만들기'는 뷰는 나오지만 터치 이벤트를 받지 못했다. 터치 이벤트를 받더라도 ACTION_OUTSIDE 이벤트만 받을 수 있었다.


이제는 그냥 최상위 뷰만 나오게 하는 것이 아니라 뷰를 최상위에 나오게 하면서 모든 터치 이벤트를 받아보자. 터치로 뷰를 이동해보고(갤럭시의 팝업 비디오 처럼) 투명도를 조절해보자!!(옵티머스의 Q슬라이드)


이전에 쓴 '항상 최상위에 나오는 뷰 만들기' 와 방식은 같다.

1. 최상위에 나오게 하기 위해서는 Window에 뷰는 넣는다.

2. 다른 화면에서도 나오게 하기위해서는 서비스에서 뷰를 생성하여야 한다.

3. 뷰에 들어오는 터치 이벤트를 OnTouchListener를 통해서 받는다.


1. 서비스 생성

자신의 앱이 종료된 후에도 항상 해당 뷰가 떠 있어야 한다. 그래서 Activity에서 뷰를 추가하는 것이 아니라 Service에서 뷰를 추가 해야 한다.


AlwaysOnTopService.java

public class AlwaysOnTopService extends Service {
    @Override
    public IBinder onBind(Intent arg0) { return null; }
    
    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}


2. 뷰 생성 및 최상위 윈도우에 추가

간단하게 텍스트뷰 하나 추가하는 코드이다.

    private TextView mPopupView;                            //항상 보이게 할 뷰
    private WindowManager.LayoutParams mParams;  //layout params 객체. 뷰의 위치 및 크기
    private WindowManager mWindowManager;          //윈도우 매니저

    @Override
    public void onCreate() {
        super.onCreate();

        mPopupView = new TextView(this);                                         //뷰 생성
        mPopupView.setText("이 뷰는 항상 위에 있다.");                        //텍스트 설정
        mPopupView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 18); //텍스트 크기 18sp
        mPopupView.setTextColor(Color.BLUE);                                  //글자 색상
        mPopupView.setBackgroundColor(Color.argb(127, 0, 255, 255)); //텍스트뷰 배경 색

        //최상위 윈도우에 넣기 위한 설정
        mParams = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.WRAP_CONTENT,
            WindowManager.LayoutParams.TYPE_PHONE,//항상 최 상위. 터치 이벤트 받을 수 있음.
            WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,  //포커스를 가지지 않음
            PixelFormat.TRANSLUCENT);                                        //투명
        mParams.gravity = Gravity.LEFT | Gravity.TOP;                   //왼쪽 상단에 위치하게 함.
        
        mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);  //윈도우 매니저
        mWindowManager.addView(mPopupView, mParams);      //윈도우에 뷰 넣기. permission 필요.
    }


 이전 글에서는 TYPE을 TYPE_SYSTEM_OVERLAY로 주었다. 이러면 화면 전체를 대상으로 뷰를 넣지만 터치 이벤트를 받지는 못한다.

 하지만 TYPE을 TYPE_PHONE으로 설정하면 터치 이벤트를 받을 수 있다. 하지만 Status bar 밑으로만 활용가능하고 뷰가 Focus를 가지고 있어 원래 의도대로 뷰 이외의 부분에 터치를 하면 다른 앱이 터치를 사용해야 하는데 이것이 불가능 하고 키 이벤트 까지 먹어 버린다.

 이것을 해결하기 위해 FLAG 값으로 FLAG_NOT_FOCUSABLE을 주면 뷰가 포커스를 가지지 않아 뷰 이외의 부분의 터치 이벤트와 키 이벤트를 먹지 않아서 자연스럽게 동작할 수 있게 된다.


3. 매니페스트에 퍼미션 설정

WinodwManager에 addView 메소드를 사용하려면 android.permission.SYSTEM_ALERT_WINDOW 퍼미션이 필요하다.


<manifest  ................ >
    <application ................ >
        <activity
           ................
        </activity>
        <service 
           ................
        </service>
    </application>
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
    <uses-sdk android:minSdkVersion="7" />
</manifest>


이전 글에는 service 태그 안에 퍼미션을 주라고 했지만 service에 주지 않아도 된다. 그냥 uses-permission을 주면 된다.


4. 터치 이벤트 받기

뷰에 터치 리스너를 등록하면 터치 이벤트를 받을 수 있다.


mPopupView.setOnTouchListener(mViewTouchListener);              //팝업뷰에 터치 리스너 등록


private OnTouchListener mViewTouchListener = new OnTouchListener() {
    @Override public boolean onTouch(View v, MotionEvent event) {
        switch(event.getAction()) {
            case MotionEvent.ACTION_DOWN:                //사용자 터치 다운이면
                if(MAX_X == -1)
                    setMaxPosition();
                START_X = event.getRawX();                    //터치 시작 점
                START_Y = event.getRawY();                    //터치 시작 점
                PREV_X = mParams.x;                            //뷰의 시작 점
                PREV_Y = mParams.y;                            //뷰의 시작 점
                break;
            case MotionEvent.ACTION_MOVE:
                int x = (int)(event.getRawX() - START_X);    //이동한 거리
                int y = (int)(event.getRawY() - START_Y);    //이동한 거리
                
                //터치해서 이동한 만큼 이동 시킨다
                mParams.x = PREV_X + x;
                mParams.y = PREV_Y + y;
                
                optimizePosition();        //뷰의 위치 최적화
                mWindowManager.updateViewLayout(mPopupView, mParams);    //뷰 업데이트
                break;
        }
        
        return true;
    }
};


터치로 뷰를 이동하거나 크기 조절을 하려면 WindowManager.LayoutParams 객체의 값을 변경해 주면 된다. x, y는 뷰의 시작점 좌표이다. Q슬라이드 처럼 투명도 조절은alpha값을 변경하면 된다. 0~1사의 값을 넣어 주면 된다.

이렇게 WindowManager.LayoutParams의 값을 변경해준 다음 WindowManager의 updateViewLayout메소드를 사용하여 뷰의 변경사항을 적용한다.



5. 뷰 제거

서비스 종료시 뷰를 제거 해야 한다.

    @Override
    public void onDestroy() {
        if(mWindowManager != null) {        //서비스 종료시 뷰 제거. *중요 : 뷰를 꼭 제거 해야함.
            if(mPopupView != null) mWindowManager.removeView(mPopupView);
            if(mSeekBar != null) mWindowManager.removeView(mSeekBar);
        }
        super.onDestroy();
    }


6. 서비스 실행/중지 할 activity 만들기

AlwaysOnTopActivity.java


public class AlwaysOnTopActivity extends Activity implements on_clickListener {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        findViewById(R.id.start).seton_clickListener(this);        //시작버튼
        findViewById(R.id.end).seton_clickListener(this);            //중시버튼
    }
    
    @Override
    public void on_click(View v) {
        int view = v.getId();
        if(view == R.id.start)
            startService(new Intent(this, AlwaysOnTopService.class));    //서비스 시작
        else
            stopService(new Intent(this, AlwaysOnTopService.class));    //서비스 종료
    }
}


실행 결과


 앱 시작뷰 추가 바탕화면 (위치 이동)

 동영상 재생
 Dragon Flight 게임
인터넷 (투명값 변경)




 AlwaysOnTop.zip

전체 샘플 코드 첨부하였습니다.

*글과 자료는 출처만 밝히시면 얼마든지 가져다 쓰셔도 됩니다.

Bluetooth Permissions


In order to use Bluetooth features in your application, you need to declare at least one of two Bluetooth permissions: BLUETOOTH and BLUETOOTH_ADMIN.

You must request the BLUETOOTH permission in order to perform any Bluetooth communication, such as requesting a connection, accepting a connection, and transferring data.

You must request the BLUETOOTH_ADMIN permission in order to initiate device discovery or manipulate Bluetooth settings. Most applications need this permission solely for the ability to discover local Bluetooth devices. The other abilities granted by this permission should not be used, unless the application is a "power manager" that will modify Bluetooth settings upon user request. Note: If you use BLUETOOTH_ADMIN permission, then must also have the BLUETOOTH permission.

Declare the Bluetooth permission(s) in your application manifest file. For example:

 
<manifest ... >
 
<uses-permission android:name="android.permission.BLUETOOTH" />
  ...
</manifest>

See the <uses-permission> reference for more information about declaring application permissions.

Setting Up Bluetooth


Figure 1: The enabling Bluetooth dialog.

Before your application can communicate over Bluetooth, you need to verify that Bluetooth is supported on the device, and if so, ensure that it is enabled.

If Bluetooth is not supported, then you should gracefully disable any Bluetooth features. If Bluetooth is supported, but disabled, then you can request that the user enable Bluetooth without leaving your application. This setup is accomplished in two steps, using the BluetoothAdapter.

  1. Get the BluetoothAdapter

    The BluetoothAdapter is required for any and all Bluetooth activity. To get the BluetoothAdapter, call the static getDefaultAdapter() method. This returns a BluetoothAdapter that represents the device's own Bluetooth adapter (the Bluetooth radio). There's one Bluetooth adapter for the entire system, and your application can interact with it using this object. If getDefaultAdapter() returns null, then the device does not support Bluetooth and your story ends here. For example:

     
    BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if (mBluetoothAdapter == null) {
       
    // Device does not support Bluetooth
    }
  2. Enable Bluetooth

    Next, you need to ensure that Bluetooth is enabled. Call isEnabled() to check whether Bluetooth is currently enable. If this method returns false, then Bluetooth is disabled. To request that Bluetooth be enabled, callstartActivityForResult() with the ACTION_REQUEST_ENABLE action Intent. This will issue a request to enable Bluetooth through the system settings (without stopping your application). For example:

     
    if (!mBluetoothAdapter.isEnabled()) {
       
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult
    (enableBtIntent, REQUEST_ENABLE_BT);
    }

    A dialog will appear requesting user permission to enable Bluetooth, as shown in Figure 1. If the user responds "Yes," the system will begin to enable Bluetooth and focus will return to your application once the process completes (or fails).

    The REQUEST_ENABLE_BT constant passed to startActivityForResult() is a locally defined integer (which must be greater than 0), that the system passes back to you in your onActivityResult() implementation as the requestCode parameter.

    If enabling Bluetooth succeeds, your activity receives the RESULT_OK result code in the onActivityResult()callback. If Bluetooth was not enabled due to an error (or the user responded "No") then the result code isRESULT_CANCELED.

Optionally, your application can also listen for the ACTION_STATE_CHANGED broadcast Intent, which the system will broadcast whenever the Bluetooth state has changed. This broadcast contains the extra fields EXTRA_STATEand EXTRA_PREVIOUS_STATE, containing the new and old Bluetooth states, respectively. Possible values for these extra fields are STATE_TURNING_ONSTATE_ONSTATE_TURNING_OFF, and STATE_OFF. Listening for this broadcast can be useful to detect changes made to the Bluetooth state while your app is running.

Tip: Enabling discoverability will automatically enable Bluetooth. If you plan to consistently enable device discoverability before performing Bluetooth activity, you can skip step 2 above. Read about enabling discoverability, below.


+ Recent posts