Android2012. 1. 19. 17:56

[목표]
안드로이드용 Appy Geek 앱에 있는 리스트에는 갱신될 때마다 리스트 항목이 차례대로 위에서 아래로 흐르는 애니메이션이 적용되어 있다. 이것을 따라 만들어 본다.

 



[리스트 항목 레이아웃 잡기]

리스트 항목의 레이아웃은 아래 그림과 같이 잡는다.

 
이를 /res/layout/list_item_layout.xml 에 구현한다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_height="fill_parent" 
	android:layout_width="fill_parent"
	android:orientation="vertical"
	android:background="#FFFFFF">
	<LinearLayout 
		android:id="@+id/listLayout_main"
		android:layout_height="wrap_content"
		android:layout_width="fill_parent" 
		android:layout_weight="5"
		android:orientation="horizontal">
		<ImageView
			android:id="@+id/main_icon"
			android:layout_height="35dip"
			android:layout_width="35dip"
			android:layout_gravity="center"
			android:layout_marginLeft="2dip"
			android:background="@drawable/icon01"/>
		<LinearLayout 
			android:id="@+id/listLayout02"
			android:layout_height="wrap_content"
			android:layout_width="fill_parent" 
			android:layout_weight="5"
			android:layout_marginLeft="10dip"
			android:orientation="vertical">
			<TextView 
				android:id="@+id/txt_top"
				android:layout_height="30dip"
				android:layout_width="wrap_content"
				android:gravity="center_vertical"
				android:textColor="@color/list_txt_top"
				android:textSize="@dimen/list_top_txt_size"
				android:singleLine="true" 
				android:ellipsize="marquee"
				android:text=""/>
			<TextView 
				android:id="@+id/txt_bottom"
				android:layout_height="20dip"
				android:layout_width="wrap_content"
				android:singleLine="true"
				android:gravity="center_vertical"
				android:textColor="@color/list_txt_bottom"
				android:textSize="@dimen/list_bottom_txt_size"
				android:ellipsize="marquee"
				android:text=""/>
		</LinearLayout>
		<Button 
			android:id="@+id/button01" 
			android:layout_height="35dip"
			android:layout_width="35dip" 
			android:background="@drawable/icon02" 
			android:clickable="true" 
			android:focusable="false" 
			android:focusableInTouchMode="true" 
			android:layout_gravity="center"
			android:layout_marginRight="2dip"/>
		<Button 
			android:id="@+id/button02"
			android:layout_height="35dip"
			android:layout_width="35dip"
			android:layout_gravity="center"
			android:background="@drawable/icon03"
			android:clickable="true" 
			android:focusable="false"
			android:focusableInTouchMode="true"
			android:layout_marginRight="2dip"/>
	</LinearLayout>
</LinearLayout>



[개별 Animation XML 정의하기]

res/anim/anim_alpha_translate_listview.xml 파일에 리스트 항목 한 개에 적용될 기본 애니메이션을 정의한다.
위에서 아래로 이동하고(translate), 페이드인 효과(alpha)를 동시에 표현하기 위해 아래와 같이 구현한다.
<set xmlns:android="http://schemas.android.com/apk/res/android" 
    android:interpolator="@android:anim/accelerate_decelerate_interpolator">
	<translate
		android:fromYDelta="-100%"
		android:toYDelta="0"
		android:duration="500">
	</translate>
	<alpha 
	    android:fromAlpha="0.0"
	    android:toAlpha="1.0"
	    android:duration="500">
	</alpha>
</set>
Line 04~06 : -100%에 해당하는 위치부터 제자리까지 0.5초 안에 이동하도록 설정한다.

Line 09~11 : 투명(0.0) 상태에서 본래 상태(1.0)까지 0.5초 안에 변하도록 설정한다.




[Layout Animation XML 정의하기]

위에서 정의한 애니메이션과 리스트뷰 자체를 연결하려면 제2의 XML 파일이 필요한데, 이를 레이아웃 컨트롤러 XML이라고 부른다. /res/anim/anim_layout_controller.xml 파일을 생성하고, 아래와 같이 구현한다.
<layoutAnimation xmlns:android="http://schemas.android.com/apk/res/android" 
	android:delay="40%"
	android:animationOrder="normal"
	android:animation="@anim/anim_alpha_translate_listview">
</layoutAnimation>
Line 02 : 항목별 애니메이션이 시작되는 지연시간이다. 애니메이션 총 시간의 30%에 해당되는 시간마다 다음 항목의 애니메이션을 시작한다. 만약, 100%를 지정하면, 첫 번째 리스트 항목의 애니메이션이 끝나야만 두 번째 항목의 애니메이션이 시작된다. (사용자가 지루함을 느끼지 않도록 할 자신이 있다면 100%를 사용해도 되지만.. 이 예제는 그럴 자신이 없으므로 40% 설정했다. ^^)

Line 03 : 애니메이션이 적용될 순서다. "normal"은 위에서 아래로 순차적으로 적용된다. 반대로 하려면 "reverse", 랜덤을 원하면 "random"을 적어 넣으면 된다.

Line 04 : 정의해 놓았던 개별 Animation XML 리소스(anim_alpha_translate_listview.xml)를 연결시킨다.




[ListView와 애니메이션 연결짓기]

위에서 준비했던 모든 과정은 바로 여기에서 사용하려고 했던 짓(?)이다.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="#FFFFFF">
	<ListView 
		android:id="@android:id/list"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:divider="@color/list_divider" 
		android:dividerHeight="1dip"
		android:smoothScrollbar="true"
		android:fastScrollEnabled="true"
		android:background="#FFFFFF"
		android:persistentDrawingCache="animation|scrolling"
		android:layoutAnimation="@anim/anim_layout_controller"/>
</LinearLayout>

Line 15 : 애니메이션과 스크롤링의 최적화를 위해 persistentDrawingCache 속성을 부여한다.
Line 16 : 레이아웃 컨트롤러를 지정함으로써 드디어 리스트 뷰에 애니메이션이 적용된 것이다.




[결과화면]
(기본적인 리스트뷰 관련 코드는 생략합니다. 맨 아래 전체 코드를 다운받아 참고하시면 됩니다. ^^)




[다운로드]
    전체소스



Posted by 데브로망스
Android2012. 1. 17. 14:07
AndroidManifest.xml에 BroadcastReceiver를 등록하는 방법이 있는데,
여기서는 런타임시에 한 액비티비 내에서 등록 및 해제하는 방법을 정리한다.
메시지가 많지 않고, 잠깐 등록해서 사용하는 경우 이 방법도 편리하다.


[목표]
액티비티에 버튼을 하나 클릭하면, 숫자를 증가시켜 Intent에 담아, 브로드캐시트 메시지를 쏜다.
onReceive()에서 이를 받아, Toast를 띄운다.
간단하게 한 액티비티 상에서 위의 작업을 수행한다.



[구현하기] 
public class BroadcastReceiverDemoActivity extends Activity implements OnClickListener {
	private final String BROADCAST_MESSAGE = "com.juno.broadcastreceiver.INCREASED_NUMBER";
	private BroadcastReceiver mReceiver = null;
	private int mReiceivedNumber = 0;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        ImageButton button = (ImageButton)findViewById(R.id.iv_send_msg);
        button.setOnClickListener(this);
    }
    
    @Override
	public void onResume() {
    	super.onResume();
    	registerReceiver();
	}
    
    @Override
	public void onPause() {
    	super.onPause();
		unregisterReceiver();
	}
    
    @Override
	public void onDestroy() {
    	super.onDestroy();
	}
    
    @Override
	public void onClick(View v) {
		int id = v.getId();
		
		switch (id) {
		case R.id.iv_send_msg:
			Intent intent = new Intent(BROADCAST_MESSAGE);
			intent.putExtra("value", mReiceivedNumber);
			mReiceivedNumber++;
			sendBroadcast(intent);
			break;
		default:
			break;
		}
	}
    
	private void registerReceiver() {
		
		if(mReceiver != null)
			return;

		final IntentFilter theFilter = new IntentFilter();
        theFilter.addAction(BROADCAST_MESSAGE);

        this.mReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
            	
				int strNewPostion = intent.getIntExtra("value", 0);
				if (intent.getAction().equals(BROADCAST_MESSAGE)) {
					Toast.makeText(BroadcastReceiverDemoActivity.this, "number=" + strNewPostion, Toast.LENGTH_SHORT).show();
				}
        	}
        };

        this.registerReceiver(this.mReceiver, theFilter);
	}
	
	private void unregisterReceiver() {
		if(mReceiver != null)
			this.unregisterReceiver(mReceiver);
	}
}
  
Line 02 : 메시지 명칭이다. 메시지를 수신할 때, onReceive(...)에서 받는 여러 메시지들을 분류할 때 사용할 Key값이다.
 

Line 18 : onResume()에서 준비해 놨던 registerReceiver() 함수를 호출한다.
 

Line 24 : onPause()에서 준비해 놨던  unregisterReceiver() 함수를 호출한다. onDestroy()함수가 호출이 되지 않는 경우도 있으니, onPause()가 안전하다. 리시버를 해제하지 않으면 오류가 발생하므로,  unregisterReceiver()의 위치는 상황에 맞춰서 잘 배치해야 한다.
 

Line 38~41 : 송신측 작업 ; "value" 키 값과, 데이터(숫자) 하나를 담아서 보낸다.
 

Line 54,67 : 리시버 등록 작업 ; 메시지 명칭을 필터에 적어논다.
 

Line 67 : 리시버 등록 작업 ;  준비해놓은 리시버와 필터 인스턴스로 현재의 액비티비에 리시버 등록을 완료한다.
 

Line 60 수신측 작업 ; 송신한 쪽에서 보낸, 데이터를 받는다.
 

Line 61 수신측 작업 ; onReceiver()에 들어온 메시지를 메시지명으로 비교해서, 수행할 작업을 구현한다.


[다운로드] 
   전체소스



Posted by 데브로망스
Tips2012. 1. 16. 15:49

이클립스에서 안드로이드 리소스를 정의할 때, 문자열을 하드코딩하면 아래와 같은 경고 메시지를 보게 된다.


 
얼마전까지만 해도 이런 메시지가 없었는데, 최근 업데이트한 이후에 지저분한(?) 메시지가 보이기 시작했다.
귀찮지만, 경각심을 일깨워주니 고맙기도 하다. (사실 고마워 해야한다. ^^)

또한, contentDescription 속성을 정의하지 않으면 "Missing contentDescription attribute" 경고도 보여주니 이래저래 string 리소스를 정의할 일도 많아졌다.

그런데, 코딩 중에 문자열을 string 리소스에 추가하고, 정의한 이름을 다시 Copy & Paste를 한 후에 android:text="@string/example"과 같이 적어주는 일은 최고로 귀찮은 일이다.

위와 같은 작업은 개발자 평균 30초에서 45초 걸리는 작업이라고 하니, 그냥 넘기기엔 결국 하드코딩된 문자열들이 우후죽순 발생하게 되고, 내내 찜찜한 상태로 커밋을 하게 된다.


[빠르게 문자열 리소스 추가하기]

android:text="Example" 에서 "Example"부분을 드래그한다.
"Alt + Shift + A, S"를 누르면 아래와 같은 팝업이 떠야한다. (이전 버전에서는 "Alt + Shift + A"만으로도 동작했었는데...)




그런데!!! 이런!! 오류가 발생한다. (예상치 못했음..)

"The attribute android:text does not accept a string reference"....



예전에는 잘 되었던 기능인데, 이런다.... TEXT속성에서는 불가능 하다는 건데...
(이유는 모르겠다. 일시적으로 이클립스 에디터의 버그인지.. 더 이상 지원안하기로 했는지. 버전 업을 기다려야 겠다.) 급한대로, "android:contentDescription="Example"에서 할 때는 잘 동작한다.

어쨌든, 팝업이 정상적으로 떴을 때, 리소스 명칭을 적절하게 넣은 후, [확인]을 누른다.
"Example"이 "@string/str_example"로 변경된 것을 확인할 수 있다.

실제로, res/values/strings.xml에는 str_example 이라는 문자열 리소스가 추가된 것을 확인할 수 있다.

<resources>

    <string name="hello">Hello World, TestAppActivity!</string>
    <string name="app_name">TransparentActivityDemo</string>
    <string name="str_example">Example</string>

</resources>

이 방법으로는 30초 넘게 걸리던 작업을 평균 15초로 단축시킨다고 하니, 꽤 쓸만한 단축키다. 

text 속성에서도 잘 동작하면 좋았을 껄, 글을 쓰다보니 문제가 발생해서, 별것도 아닌 글이 지저분 해졌다.
다음 버전 업데이트에서는 text 속성에서도 잘 동작하길 바라며...^^;;;;

 
Posted by 데브로망스