출처 : http://www.kmshack.kr/323

Android View와 관련된 오픈소스들이 많이 공개 되고 있다. 그래서 많이 쓰이고 유용한 오픈소스를 정리 해보았다.  아직 국내에서는 오픈소스가 인색한지 모두 외국에서 만든것들이다. 나도 View관련 오픈소스를 하나준비 하겠다고 준비중인데.. 준비중이기만 하다.

 

참고로, 잘 알려지고 검증된 오픈소스라도 코드를 하나하나씩 보고 어떻게 구현되어 있는지 꼭 살펴보고 썼으면 좋겠다. 그리고 이제 개발을 시작한지 얼마 안된 분들이라면 이런 오픈소스를 사용함으로 자기 실력을 죽일 수도 있으니 한번씩 구현해보는 것도 좋을듯 하다.

 

그리고 국내 개발 커뮤니티에 보면 자기가 개발할 것에 대해 오픈소스를 찾음으로써 개발해서 삽질 하기전에 찾아서 다행이다라는 글들을 보면 씁쓸하다. 오픈소스는 좋은점도 있지만 그에 반하는 양면성을 가지고 있다는 것에 조심했으면 좋겠다.

 

 

아무튼 아래것들은 이미 유명하고 잘만들어진 오픈소스들이다.

 

 

 

 

1. ActionbarSherlock

허니컴부터 적용된 액션바를 이전버전에도 사용할 수 있게 해준다.

 

http://www.actionbarsherlock.com/
https://github.com/JakeWharton/ActionBarSherlock

 

ActionBarSherlock is an standalone library designed to facilitate the use of the action bar design pattern across all versions of Android through a single API.

The library will automatically use the native ActionBar implementation on Android 4.0 or later. For previous versions which do not include ActionBar, a custom action bar implementation based on the sources of Ice Cream Sandwich will automatically be wrapped around the layout. This allows you to easily develop an application with an action bar for every version of Android from 2.x and up.

 

 

 

2. Android-PullToRefresh

ListView, ScrollView, Viewpager, WebView 등 새로고침 기능의 View를 만들어 준다.

 

https://github.com/chrisbanes/Android-PullToRefresh

 

This project aims to provide a reusable Pull to Refresh widget for Android. It was originally based on Johan Nilsson's library (mainly for graphics, strings and animations), but these have been replaced since.

 

 

 

 

3. StickyListHeaders

ListView의 Section Header 정보를 넣을 수 있다.

 

https://github.com/emilsjolander/StickyListHeaders

 

StickyListHeaders is an Android library that makes it easy to integrate section headers in your ListView. These section headers stick to the top like in the new People app of Android 4.0 Ice Cream Sandwich. This behavior is also found in lists with sections on iOS devices. This library can also be used for without the sticky functionality if you just want section headers.

StickyListHeaders actively supports android versions 2.3 (gingerbread) and above That said, it should be compatible with much older versions of android as well but these are not actively tested.

Here is a short gif showing the functionality you get with this library:

 

 


4. MenuDrawer
좌측 또는 우측의 슬라이드 메뉴를 구성 할 수 있다.


https://github.com/SimonVT/android-menudrawer

 

A slide-out menu implementation, which allows users to navigate between views in your app. Most commonly the menu is revealed by either dragging the edge of the screen, or clicking the 'up' button in the action bar.

 

 


5. SlidingMenu
MenuDrawer와 같다.

 

https://github.com/jfeinstein10/SlidingMenu


SlidingMenu is an Open Source Android library that allows developers to easily create applications with sliding menus like those made popular in the Google+, YouTube, and Facebook apps. Feel free to use it all you want in your Android apps provided that you cite this project and include the license in your app.]

 

 


6. FadingActionBar

리스트뷰 스크롤시 헤더 컨텐츠에 따라 액션바의 알파값이 변한다. 구글 음악플레이어의 아티스트정보 페이지

 

https://github.com/ManuelPeinado/FadingActionBar

 

FadingActionBar is a library which implements the cool fading action bar effect that can be seen in the new Play Music app.

 


7. DragSortListView

리스트 소팅

 

https://github.com/bauerca/drag-sort-listview

 

DragSortListView (DSLV) is an extension of the Android ListView that enables drag-and-drop reordering of list items.

 

 


8. IndexableListView

리스트뷰의 알파벳 인덱스 기능

 

https://github.com/woozzu/IndexableListView

 

 


9. ListViewAnimations

ListView 스크롤시 애니메이션(구글 플러스)

 

https://github.com/nhaarman/ListViewAnimations

 

ListViewAnimations is an Open Source Android library that allows developers to easily create ListViews with animations. Feel free to use it all you want in your Android apps provided that you cite this project and include the license in your app.

 

 


10. ViewPagerIndicator

ViewPager 인디케이터

 

https://github.com/JakeWharton/Android-ViewPagerIndicator

 

Android-ViewPagerIndicator is presented as an Android library project. A standalone JAR is not possible due to the theming capabilities offered by the indicator widgets.


 


11. PagerSlidingTabStrip

ViewPager 인디케이터

 

https://github.com/astuetz/PagerSlidingTabStrip

 

Interactive paging indicator widget, compatible with the ViewPager from the Android Support Library.

 

 


12. JazzyViewPager

ViewPager 스크롤시 애니메이션

 

https://github.com/jfeinstein10/JazzyViewPager

 

An easy to use ViewPager that adds an awesome set of custom swiping animations. Just change your ViewPagers to JazzyViewPagers and you're good to go!

 

 


14. ViewPager3D

ViewPager 스크롤시 3D 효과

 

https://github.com/inovex/ViewPager3D

 

 


15. DirectionalViewPager

ViewPager 좌우, 아래위 스크롤

 

https://github.com/JakeWharton/Android-DirectionalViewPager

 

Implementation of the compatibility library ViewPager class that supports paging both vertically and horizontally as well as changing between the two at runtime.

 

 

 

16. VerticalSeekBarAndroid
수직 SeekBar

 

https://github.com/AndroSelva/Vertical-SeekBar-Android

 

This project is all about Customizing the normal SeekBar to Vertical Seekbar.

 

 


17. HoloCircleSeekBar
원형으로 생긴 SeekBar

 

https://github.com/JesusM/HoloCircleSeekBar

 

A Circle SeekBar inspired by Android Holo ColorPicker designed by Marie Schweiz and developed by Lars Werkman.

 

 


18. MultiChoiceAdapter
ListView, GridView에서 다중선택을 쉽게 할 수 있도록 도와준다.

 

https://github.com/ManuelPeinado/MultiChoiceAdapter

 

MultiChoiceAdapter is an implementation of ListAdapter which adds support for modal multiple choice selection as in the native Gmail app.

 

 


19. TwoWayGridView
GridView를 가로, 세로방향으로 스크롤 되도록 한다.

 

https://github.com/jess-anders/two-way-gridview

 

An Android GridView that can be configured to scroll horizontally or vertically.

 

 


20. ScrollBarPanel
스크롤바 옆에 View를 생성(Path 2.0에서 볼 수 있는 기능)

 

https://github.com/rno/Android-ScrollBarPanel

 

Android-ScrollBarPanel allows to attach a View to a scroll indicator like it's done in Path 2.0.

Posted by outliers
,

안드로이드 ICS 이후 부터는 versionName 과 versionCode 를 같이 올려주어야 설치가 됨.

C2DM 푸시 안오는 문제
 - 구글 계정이 로그인 되어 있지 않으면 푸시가 오지 않음 (4.0 이하 버전)

Posted by outliers
,

ListView에서 각 row를 클릭 할 때 onItemClick 함수가 호출이 안될때가 종종 있다.
row layout 중에 auto focusing 되는 widget 들이 존재해서 onItemClick 가 호출 되지 않는것이다.
보통 Button, EditText 들이 들어 있는 경우에 발생을 한다.

이럴 경우 onItemClick 함수가 호출되게 하기 위해서는 auto focusing widget 에 속성을 추가해 주면 된다.

android:focusable="false"
android:clickable="false"    // focusable 만으로 안되는 경우 추가

 

Posted by outliers
,
Posted by outliers
,

출처 : http://mocorian.tistory.com/51

GridLayout은 Android 4.0에서 처음 소개 되었다.(API Level 14).

그러므로 하위 버전에서 사용하기 위해서는 이를 지원하는 라이브러리를 이용해야 하는데

XML로 레이아웃을 작성할 때 두 버전사이에 다소 차이가 있다.

- API Level 14 버전을 이용해 작성할 때

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<?xml version="1.0" encoding="utf-8"?>
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:columnCount="4"
          android:rowCount="4">
  
         <Button android:text="0.0" />
         <Button android:text="0.1" />
         <Button android:text="0.2" />
         <Button android:text="0.3" />
  
         <Button android:text="1.0" />
         <Button android:text="1.1" />
         <Button android:text="1.2" />
         <Button android:text="1.3" />
  
         <Button android:text="2.0" />
         <Button android:text="2.1" />
         <Button android:text="2.2" />
         <Button android:text="2.3" />
  
         <Button android:text="3.0" />
         <Button android:text="3.1" />
         <Button android:text="1.2" />
         <Button android:text="3.3" />
</GridLayout>

- 하위 버전 지원 라이브러리 이용시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.GridLayout 
          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"
          app:columnCount="4"
          app:rowCount="4">
  
         <Button android:text="0.0" />
         <Button android:text="0.1" />
         <Button android:text="0.2" />
         <Button android:text="0.3" />
  
         <Button android:text="1.0" />
         <Button android:text="1.1" />
         <Button android:text="1.2" />
         <Button android:text="1.3" />
  
         <Button android:text="2.0" />
         <Button android:text="2.1" />
         <Button android:text="2.2" />
         <Button android:text="2.3" />
  
         <Button android:text="3.0" />
         <Button android:text="3.1" />
         <Button android:text="1.2" />
         <Button android:text="3.3" />
</android.support.v7.widget.GridLayout>


GridLayout의 행과 열 개수를 지정할 때 하위버전 지원 라이브러이 에서는

app:columnCount app:rowCount attribute를 이용한다.

(API Level 14에서는 android:ColumnCount, android:rowCount)

또한 이를 위해서 xmlns:app=http://schemas.android.com/apk/res-auto 를 추가해 줘야 한다

 

Posted by outliers
,

안드로이드 화면 가로/세로 관련하여 가로 모드일 경우 특정 화면에서는 가로로, 세로 모드일 경우에는 특정 화면에서 세로 화면으로의 기능이 필요해서 개발을 하다 보니 가로세로의 정의가 폰과 탭이 달라서 폰과 탭을 구분지을 필요가 있어 조사를 해봤다.

테스트 예제 소스


	private int getScreenOrientationPhone(int orientation) {
		switch (orientation) {
        case Surface.ROTATION_0:
        	return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
        	
        case Surface.ROTATION_90:
        	return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;


        case Surface.ROTATION_180:
        	return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
        	
        case Surface.ROTATION_270:
        	return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
        }
		
		return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
	}
	
	private int getScreenOrientationTab(int orientation) {
		switch (orientation) {
        case Surface.ROTATION_0:
        	return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
        	
        case Surface.ROTATION_90:
        	return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;


        case Surface.ROTATION_180:
        	return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
        	
        case Surface.ROTATION_270:
        	return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
        }
		
		return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
	}
	
	private int getScreenOrientation(int orientation) {
		if (isTablet()) {
			return getScreenOrientationTab(orientation);
		} else {
			return getScreenOrientationPhone(orientation);
		}
	}

	private boolean isTablet() {
        Configuration config = getContext().getResources().getConfiguration();
        return ((config.screenLayout & Configuration.SCREENLAYOUT_SIZE_XLARGE) == Configuration.SCREENLAYOUT_SIZE_XLARGE);
	}



출처 : http://beom3x2.iptime.org/tc/Android/entry/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%BD%94%EB%93%9C%EC%83%81%EC%97%90%EC%84%9C-%EC%8A%A4%ED%81%AC%EB%A6%B0-%EC%82%AC%EC%9D%B4%EC%A6%88-%EA%B5%AC%EB%B6%84%ED%95%98%EA%B8%B0-%ED%8F%B0-%ED%83%80%EB%B8%94%EB%A6%BF-%EA%B5%AC%EB%B6%84?category=1

다음과같은코드를이용하여코드상에서폰과태블릿을구분할수있다.

(코드상에서폰인지태블릿인지를가져올수있다.)


int screenSizeType = (/*context.*/getResources().getConfiguration().screenLayout &

Configuration.SCREENLAYOUT_SIZE_MASK);

if(screenSizeType == Configuration.SCREENLAYOUT_SIZE_XLARGE) {

// 매우 화면 사이즈, 10인치 이상 태블릿 : 갤럭시탭 10.1, 갤럭시노트10.1, 기타 등등.

}

else if(screenSizeType == Configuration.SCREENLAYOUT_SIZE_LARGE) {

// 화면 사이즈, 7인치 태블릿 : 넥서스7, 갤탭, 기타 등등.;

}

else/* if(screenSizeType == Configuration.SCREENLAYOUT_SIZE_NORMAL ||

screenSizeType == Configuration.SCREENLAYOUT_SIZE_SMALL) */ {

 // 태블릿이아니다() : 갤럭시노트, 갤럭시 S, 베가, 옵티머스시리즈기타폰들.

}



이에대한 자세한 설명은 Configuration  대해서 설명한  Android Developer 사이트에서 확인할 있다.


  대략 요약하자면,context에서 가져온 Configuration 인스턴스 내부의 screenLayout 필드 값을  SCREENLAYOUT_SIZE_MASK 으로 인코딩하여 화면 사이즈 종류를 가져올 있다.  


  Android Developer 페이지에 나와있는 설명을 대충 요약하면 이렇다.


SCREENLAYOUT_SIZE_SMALL

              설명 : 소형 화면. 저밀도(low-density) QVGA 비슷한 크기. - 싸고 화면 작은 저가 폰들이 범주에 속하

 는 같다.

              레이아웃 사이즈 :  최소, 320 x 426 dp.

              :  low-density QGVA,  high-density VGA.


SCREENLAYOUT_SIZE_NORMAL

 설명 :  일반 화면,  medium-density HVGA 비슷한 크기. -  일반적인 . 갤럭시 노트 포함.

 레이아웃 사이즈 :  최소, 320x470 dp.

  :   low-density WQVGA , medium-density HVGA , high-density WVGA.


SCREENLAYOUT_SIZE_LARGE

 설명 :  대형 화면,  medium-density VGA 비슷한 크기.  - 넥서스7 갤탭등.

 레이아웃 사이즈 :  최소, 480x640 dp.

  :  medium-density VGA, medium-density  WVGA


SCREENLAYOUT_SIZE_XLARGE

 설명 :  (?)대형 화면,  medium-density VGA 비슷한 크기 - 현재까지는 갤탭 10.1 갤노트 10.1, 기타 10인 처의 타블릿등등.

 레이아웃 사이즈 :   720x960 dp 이상.

API 9 부터추가. (진저브레드부터사용할있다.)

 

 

Posted by outliers
,

매니페스트에 orientation|keyboardHidden 를 해주어도 onConfigurationChanged() 함수가 호출이 안되는 경우가 있다.
안드로이드 Android 3.2 이상 부터는 "orientation|keyboardHidden" 대신 "orientation|screenSize"로 변경해야 호출이 된다.
매니페스트에서 android:targetSdkVersion 도 같이 확인..

Posted by outliers
,

XML 에서 EditText 속성에
android:inputType="textVisiblePassword" 또는 android:inputType="textUri" 로 설정

아이스크림에서는 안나오는 경우가 발생
그럴 경우 소스 코드에서
editText.setPrivateImeOptions("defaultInputmode=english;"); 로 사용 하면 됩니다.

Posted by outliers
,
 
		// XML 에 키보드를 추가하면 Dialog 뒤의 Activity 의 화면만 줄어든다.
		// Dialog 의 window 의 값을 따로 설정
        // Dialog 의 onCreate 에서 또는 Dialog dialog.getWindow() 해서 사용한다.

		Window win = getWindow();
		WindowManager.LayoutParams winLp = win.getAttributes();
		winLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
		win.setAttributes(winLp);

'Programs > Android' 카테고리의 다른 글

onConfigurationChanged() 호출이 안되는 문제  (0) 2013.06.03
영문 키패드 나오게 하는 방법  (0) 2013.03.19
Content Observer  (0) 2013.02.27
안드로이드 멀티 해상도 지원  (2) 2012.12.04
zxing 세로 모드 스캐너  (1) 2012.11.20
Posted by outliers
,

Content Observer

Programs/Android 2013. 2. 27. 10:45

출처 : http://cafe.daum.net/superdroid/aAfL/110?docid=1MWA2aAfL11020111110130336

Content Observer

Provider는 공용 저장소를 제공하였다.

여기서 좀 찜찜한 기분이 들지 않는가?

공용 저장소라고 하면 나도 데이터를 쓰고/읽고가 가능하고

다른 패키지에서도 데이터를 쓰고/읽고가 가능할 것이다.

 

만일 내가 특정값을 Provider를 통해 읽어와 사용하고 있는 중에,

다른 패키지에서 그 값을 변경했을 경우 문제가 되지 않겠는가?

 

이러한 문제를 해결하기 위해 Content Observer라는 것이 존재한다.

이 녀석의 역할은 특정 URI에 해당하는 데이터가 변경되었는지

감시하는 역할을 한다.

 

테스트 패키지로 이해해 보자.

!!! 테스트 할 패키지는 바로 전 강좌의 소스를 그대로 사용하겠다.

 

먼저 Provider 패키지 부분을 약간 수정해 보자.

아래는 StudentsProvider.java 소스이고

delete() 함수 부분만을 수정할 것이다.

getContext().getContentResolver().notifyChange(uri,null);

이라는 부분이 존재한다.

notifyChange()라는 함수명만 보아도 대충 알만하지 않는가?

변경된 사실을 누군가에게 알린다는게 아닐까?

 

그렇다!  해당 함수는 ContentResolver들에게 DB의 내용이 변경되었다는 것을

알리는 역할을 한다.

위의 소스는 delete() 함수 즉 특정 레코드를 삭제하고 있다.

그러므로 DB의 내용이 레코드 삭제를 통해 변경 되었으므로,

ContentResolver 들에게 알리는 것이다.

 

getContext().getContentResolver().notifyChange(uri,null);

 

notifyChange()함수에서 첫번째 인자 uri 는 무엇을 말할까?

변경된 부분의 Uri 즉 경로를 말한다.

예를 들어 Uri가

content://com.provider.students/students/4

와 같다면

students 테이블의 4번째 id 레코드가 삭제되었다는 것을 의미 하는 것이다.

 

content://com.provider.students/students

와 같다면

students 테이블의 여러개의 레코드가 삭제되었다는 것을 의미 하는 것이다.

 

위의 소스와 같이 Provider 구현자는 DB가 변경되는 부분에

notifyChange() 함수를 넣우 둬야 한다.

DB가 변경된 사실을 꼭 알릴 필요가 없다면 마음대로 하라. ^^

내가 하고 싶은 말은 구현자의 몫이라는 것이다.

 

 

 

  !!! 그냥 참조만 하자.

 

 

  우리가 사용하는 ContentObserver는 System Service이다.

  위의 소스에서 ContentService 가 바로 ContentObserver를 관리하는 서비스 인 것이다.

  아래는 Framework의 ContentService 소스이다.

  ContentProvider에서 notifyChange() 함수를 호출하면 바로

  ContentService의 notifyChange() 함수가 호출되는 것이다.

 

이제는 위의 변경되었다는 사실을 받는 부분을 구현해야 한다.

이전 패키지에 Resolver 부분을 그대로 수정하자.

아래는 StudentsResolverTestActivity.java 파일이다.

1번과 같이 ContentObserver 객체 변수를 선언하고

2번과 같이 ContentObserver 를 구현한다.

ContentObserver class는 abstruct class이다.

그러므로 추가적으로 3번과 같이 0nChange ()함수를 구현해 주어야 한다.

해당 함수는 ContentProvider가 변경 사실을 알려 줄때

호출되는 Callback 함수 이다.

해당 함수가 정말 호출되었는지 확인하기 위해서 Log를 남겨 두자.

이 ContentObserver가 동작하기 위해서는

5번과 같이 ContentResolver에 등록해 주어야 한다.

등록하는 함수는 registerContentObserver()이다.

또한 해지 하는 함수는 6번과 같이 unregisterContentObserver()이다.

 

그런데 만일 해지를 하지 않으면 큰일 날까?

Framework 소스 확인해 본 결과 특별히 문제가 되진 않았다.

그런데 왜 책이나 인터넷에는 꼭 해지를 해야 한다고 할까? ^^a

 

개인적인 생각으로는

더 이상 noti를 받기 싫은 경우에 unregisterContentObserver()를 사용해서

해지 하는 것이 주 용도라고 생각한다.

하지만 등록과정이 있으므로 해당 Activity가 종료하기 전에

해제 하도록 하여 Framework의 부하를 조금이라도 줄여 주는 것이 맞다.

 

수정한 Provider와 Resolver 패키지를 모두 설치하고

Resolver를 아래와 같이 실행해 보자.

위의 1번 Insert를 눌러 ContentPorvider에 데이터를 추가하고

2번의 Delete를 눌러 데이터를 삭제해 보자.

Delete를 누르면 Provider에서 notifyChange() 가 호출될 것이다.

3번에서 정상적으로 호출된 로그가 출력되었다,

해당 notifyChange()가 Provider에서 호출된후,

Resolver 측에서 그 결과를 전달받게 된다.

4번에서 정상적으로 0nChange() 함수가 호출된 것을 볼 수 있다.

정말 간단하지 않은가? ^^

 

자 좀더 자세히 알아보기 위해

registerContentObserver() 함수에 대해서 살펴 보자.

 

해당 함수의 원형은 아래와 같다.

 

public final void registerContentObserver

(

Uri                       uri,  <<== 첫번째 인자.

boolean                notifyForDescendents,

ContentObserver    observer

)

 

위에서 세번째 인자 observer는 무엇을 의미하는지 알 것이다.

0nChange() 가 구현된 ContentObserver 객체를 넣어주면 된다.

그렇다면 첫번째 인자 URI는 무엇을 의미하는 것일까?

아래의 그림을 보고 이해해 보자.

우선 Resolver 측에서

1번에서 registerContentObserver()함수를 이용해서 등록하였다.

2번을 보면 등록할때 첫번째 인자로 URI를 넘긴다.

3번과 같이 URI는 "content :// com.provider.students / students"이다.

         즉 "content :// com.provider.students / students" 경로의 데이타가

         변경되면 알려 달라는 의미이다.

자 이제 Provider 측이다.

4번을 보면 특정 데이타가 변경되어 notifyChange() 함수를 호출하여 알린다.

5번은 변경된 데이터의 경로 Uri가 첫번째 인자로 전달된다.

6번의 Uri은 "content :// com.provider.students / students" 이다.

        즉 "content :// com.provider.students / students" 경로의 데이타가 변경되었다는 의미이다.

7번에서 ContentService 측에서

        Resolver에서 등록한 Uri와 Provider에서 알린 Uri가 같은지 체크한다.

8번에서 같으면 ContentService는 해당 Resolver에 0nChange() 함수를 호출해 주는 것이다.

 

이해가 되는가?

 

 

자 그렇다면 두번째 인자가 의미하는 것은 무엇일까?

 

public final void registerContentObserver

(

Uri                       uri,

boolean                notifyForDescendents, <<== 두번째 인자.

ContentObserver    observer

)

 

위에서 보면 Provider에서 보낸 Uri와 Resolver에서 등록한 Uri가

완전 동일해야만 noti를 받을 수 있었다.

하지만 예를들어

Resolver에서 등록한 Uri : "content :// com.provider.students / students"

Provider에서 보낸     Uri : "content :// com.provider.students / students / 4"

이라면 서로 Uri가 달라 받을 수 없을 것이다.

하지만 자세히 보라. 상위 경로는 모두 동일하고 하위 경로 "/ 4" 만 다른다.

이렇게 하위 경로만 다른 경우에도 받고 싶을 때가 많을 것이다.

(하위 경로를 일일이 다 알 수 있는 것다 아니지 않은가?)

 

그때 바로 두번째 인자인 notifyForDescendents 를 "true"로 설정하면 된다.

 

아래의 그림으로 이해해 보자.

Provier측에 7번에서 하위 경로가 "/125" 로 추가 되었다.

하지만

Resolver측에서 2번과 같이 notifyForDescendents 를 "true"로 설정하였다.

그로 인해 8번과 같이 일치하지 않아도

9번이 실행된 것이다.

 

자 테스트를 해 보자.

아래의 Resolver 소스를 약간 변경해 보자.

자 "true"로 변경하였다.

 

 

 

자 정상적으로 받을까?

실행해 보자.

 

1번버튼을 눌러 insert로 데이터를 추가해 두자.

데이터를 추가할때 화면에 "content :// com.Provider.Students / students / 3" 번 경로의 데이터가

추가된 것을 알 수 있다.

 

!!! 자 2번에 delete 버튼을 눌러 지우기 전에 한가지 소스를 더 수정해야 한다.

위와 같이 "content :// com.Provider.Students / students / 3" 이 수정 되었으므로

해당 데이터만 지원 주는 코드를 테스트를 위해 변경한 것이다.

다시 실행해 보자.

 

자 이제 2번과 같이 Delete 버튼을 눌렀 이전에 1번에서 추가된

"content :// com.Provider.Students / students / 3" 을 삭제해 보자.

과연 0nChange() 함수가 호출이 될까?

위에 로그를 보면 4번과 같이 정상적으로 호출된 것을 알 수 있다.

 

정리해 보면

 

Provider에는

    특정 레코드가 삭제되었으므로

    "content :// com.Provider.Students / students / 3"  라고 Uri이 notifyChange()를 날렸을 것이고,

Resolver에는

    "content :// com.Provider.Students / students" 라고

    Uri가 registerContentObserver() 함수로 등록되었다.

 

그렇다면 서로 Uri가 달라서 0nChange ()가 호출되지 않아야 하나.

Resolver에서 registerContentObserver() 함수로 등록할때

두번째 인자인 notifyForDescendents 를 "true"로 설정했지 때문에

하위 경로가 추가되어도 호출이 된 것이다.

 

아~ 늘 말하는 거지만 간단한 것을 글로 설명하자니 또 길어 졌다. ^^;;

 

자 마지막으로

ContentObserver 객체를 생성할때

구현해 주어야 하는 0nChange() 함수를 자세히 보자.

1번과 같이 첫번째 인자로 selfChange 라는 녀석이 있다.

무엇을 위해 존재하는 것일까?

테스트를 위해 바로 전에 수정한 내용을 모두 원복하고

2번과 같이 selfChange 값이 어떻게 넣어 오는지 로그를 남겨 보자.

 

자 실행하자.

1번 버튼을 눌러 데이터를 추가하고

2번 버튼을 눌러 지워보자.

3번과 같이 로그를 확인해 보면 0nChange() false" 라고 출력 되었다.

 

이 값은 늘 "false" 일 것이다. ^^;

그렇다면 "true"인 경우가 없는가?

물론 존재한다.

위에서 늘 "false"라고 했던 이유는 우리가 특별한 처리를 해 주지 않은 경우이다.

 

자 그 특별한 경우가 무엇일까?

 

잠시 아래의 내용을 고민해 보자.

만일 Provider에서 notifyChange()를 날렸을때 말고

Resolver에서 강제로 0nChange ()를 호출 되게 할 방법이 없을까?

존재한다.

아래의 Resolver 소스를 수정해 보자.

위의 1번은 insert 버튼을 눌렀을때

생성된 ContentObserver 객체 멤버 함수인 dispatchChange() 함수를 호출하는 것이다.

 

이 함수가 바로 0nChange () 함수를 강제로 호출하게 해준다.

 

dispatchChange()  함수 인자에는 boolean 값이 들어 간다.

이 값을 "true"라고 넣자.

 

자 이제 실행해 보자.

1번에 insert 버튼을 누르면

2번과 같이 dispatchChange()  함수 호출되면서 0nChange () 함수가 강제로 호출되고

         로그가 남았다.

         여기서 주목할 것은 0nChange () 함수의 인자가 "true"로 넣어 온 것이다.

         즉 dispatchChange(boolean) 함수의 인자 값에 따라

         0nChange() 의 인자가 결정되는 것이다.

         우리가 강제로 dispatchChange() 함수를 호출할때는

         우리 스스로가 호출한 것을 구분하기 위해 dispatchChange() 함수의

         인자를 "true"로 넣어 준다.

         그렇다면 0nChange () 함수 내에서 인자 "selfChange" 를 보고

         구분에서 원하는 처리를 하면 된다.

 

 

위에서 작성된 테스트 코드는 아래의 첨부를 참조하자.

 

Posted by outliers
,