IT_Programming/Android_Java

ViewFlipper

JJun ™ 2010. 3. 10. 13:04



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



 

 

 

1. ViewFlipper의 용도


ViewFlipper는 Tab과 마찬가지로 화면에 표시 가능한 여러 view가 준비되어 있을 때 화면에 현재 표시된 view를 다른 view로 전환하기 위해 사용된다.

Tab은 일반적으로 표현하려는 view간의 구분(TabWidget의 이름으로) 이 필요할 때 사용되며, ViewFlipper는 view간의 구분이 필요 없을 경우 많이 사용된다.

 

ViewFlipper의 사용 예로는 안드로이드 폰의 초기화면과 같은 경우가 있다.

예를 들면, 많은 어플리케이션을 인스톨한 유저는 사용하는 모든 어플리케이션에 대한 icon을 하나의 view에 표시할 수 없고, 여러 개의 view를 사용해야만 모든 icon을 표시 할 수 있다. Tab을 사용해서 여러 view를 하나씩 보여줄 수도 있지만, 모든 view는 단순한 icon의 나열임으로 굳이 view 와 view를 이름으로 구분할 필요가 없는 상황에서 Tab이름을 위한 Label을 배치하는 것은 공간의 효율적 사용이 아니다.

 

이럴 때 ViewFlipper를 사용하면 화면 전체를 원하는 view로 채워 넣을 수 있어 공간의 효율적인 사용이 가능하다.

 

하지만, 여러 view가 있는데 그 중 특정 view로 비 순차적으로 건너뛰어야 할 경우 Tab은 해당 Tab을 클릭하면 되지만, ViewFlipper는 원하는 view까지 순차적으로 flip해야 한다는 단점이 있기는 하다. (ViewFlipper도 구현에 따라 특정 view로 건너뛰게 만들 수 있으나, 그렇게 사용하려면 굳이 ViewFlipper를 사용할 이유가 적어지는 것 같다)

 

결국, view간의 전환을 구현하는데 Tab을 사용할지 아니면 ViewFlipper를 사용할 지는 전적으로 개발자의 몫임으로, 사용자의 입장에서 판단해서 사용자에게 최대한 편리한 UI를 제공하는 것이 중요할 것이다.

 

 

 

 

2. ViewFlipper의 구현


다음은 ViewFlipper의 클래스 상속 구조이다.



 

ViewFlipper를 사용했을때 앞/뒤 view로 전환을 위해서는 버튼, trackball, 방향키, 화면 터치, 흔들기 등이 사용되는데,
여기서는 일반 스마트폰의 초기 화면 같은 터치 방식을 이용해 구현해 보려고 한다.

 

먼저 예제를 보기 전에 몇가지 살펴볼 것이 있다.

 

 

 

FrameLayout의 특징


ViewFlipper가 상속하는 FrameLayout은 다른 layout과는 조금 다른 점이 있다.

바로 FrameLayout에서는 child view들이 모두 왼쪽 상단을 기준으로 겹쳐 쌓이도록 디자인 되었다는 것이다.

즉, FrameLayout에 어떤 layout을 적용 하던 child view는 항상 FrameLayout의 좌측 상단 코너를 기준으로 쌓여버린다.
(layout_gravity등의 layout 관련 속성이 FrameLayout에서는 작동 안 함)

만약 하나 이상의 child view가 FrameLayout에 할당될 경우 모든 child view는 FreameLayout의 좌측 상단에 쌓이게 되고,
가장 마지막에 쌓인 child view의 크기만큼 뒤에 쌓여있는 다른 child view의 영역을 가려버린다.

 

그림으로 표현하면 다음과 같다. 











위의 그림은 Child View 1, 2, 3 순으로 FrameLayout에 더했을 때 일어 나는 일이다.

FrameLayout에 더해지는 모든 view는 외쪽 상단 코너를 기준점으로 정렬되고 가장 나중에 더해진 Child View 3이 전에 더해진 child view들을
모두 가려 버리는 것을 볼 수 있다.

 

Tab과 ViewFlipper에서는 이렇게 child view를 한곳에 포개어 놓고 보여줄 child view를 가장 상단으로 올리는 방법으로
여러 개의 view를 한 화면에 번갈아 가면서 표시한다.

 

그렇기 때문에 FrameLayout 내부에서 복잡한 구성의 child view를 표현하려면 다음과 같은 구조를 사용해야 한다.

(Tab, ViewFlipper 모두 FrameLayout에서 상속함으로 참고)





FrameLayout은 한번에 한 view만 표현 가능하기 때문에 child view의 layout을 "fill_parent"로

설정하면 FrameLayout을 꽉 채운 child view로 표현 할 수 있다.

 

 

 

OnTouchListener 인터페이스의 구현


화면에 일어나는 touch를 감지하기 위해서는 android.view.View.OnTouchListener interface를 구현하여야 한다.

OnTouchListener 인터페이스는 다음과 같은 abstract callback method를 포함 함으로 구현해 주어야 한다.


abstract boolean onTouch(View v, MotionEvent event)


parameter:

    v - touch가 일어나고 있는 view object

    event - 현재 일어나고 있는 event


return:

     callback이 event를 처리 했으면 true, 그렇지 못했으면 false.

 

 

MotionEvent객체에는 여러 모션을 나타내는 constant가 미리 define되어 있다.

예제에서는 MotionEvent:ACTION_DOWN MotionEvent:ACTION_UP을 사용하는데 이는 각각 터치 시작 터치 끝을 나타낸다.

 

또, MotionEvent객체는 현재 일어나는 이벤트에 대한 좌표 정보를 리턴하는 여러 메소드도 제공하는데

예제에서는 float MotionEvent:GetX()라는 이벤트가 일어나는 x좌표를 float형으로 리턴하는 메소드를 이용해 발생 이벤트를 인식한다.

 

 

 

Animation 리소스의 사용


안드로이드에서는 animation도 resource로 등록이 된다.

 

프로젝트 폴더\res\anim\ 폴더 밑에 xml형식의 문서로 저장되며, java code 에서는 AnimationUtil:loadAnimation(context, int)을 이용해
컴파일된 animation 리소스에 접근한다. (
'R.anim.animation_xml_file_이름' 이용)


이번 예제를 위해서는 다음과 같이 4개의 animation xml 문서가 사용되었다.

  • push_left_in.xml: 새로운 view가 왼쪽 방향으로 밀려 들어옴.
  • push_left_out.xml: 기존 view가 왼쪽 방향으로 밀려 나감.
  • push_right_in.xml: 새로운 view가 오른쪽 방향으로 밀려 들어옴.
  • push_right_out.xml: 기존 view가 오른쪽 방향으로 밀려 나감.

 

 

위의 xml 문서에서 사용된 주요 xml element와 attribute는 다음과 같다. 


<translate>

상하좌우 이동 animation을 지정하며. TranslateAnimation 클래스에 대응한다.


다음과 같은 4가지의 xml attribute에

  • android:fromXDelta: animation시작하는 X 좌표
  • android:toXDelta: 끝나는 X 좌표
  • android:fromYDelta: Starting Y location.
  • android:toYDelta: Ending Y location.


다음과 같은 3가지 종류의 값(value) 중 하나를 사용한다.

  • -100% ~ 100%: 자기자신을 기준으로 한 위치에 대한 비율. 0%가 현재 자기 위치.
  • -100%p ~ 100%p: 부모 컨테이너를 기준으로 한 위치에 대한 비율. 0%p가 현재 자기 위치.
  • float값: 절대 위치를 나타냄.

  


<alpha>

투명함을 조정하며, AlphaAnimation 클래스에 대응한다.


다음과 같은 xml attribute를

  • android:fromAlpha: animation 시작 시 투명도
  • android:toAlpha: animation 완료 시 투명도


다음과 같은 값으로 설정한다.

  • 0.0 ~ 1.0 (0.0이 투명, 1.0이 완전 불 투명)

  


추가로 위의 두 element(translate, alpha)에서 공통으로 사용되는 animation이 지속되는 시간을 나타내는 attribute는 다음과 같다.

  • android:duration: Animnation이 시작돼서 끝나는 시간을 밀리 초(1/1000초) 단위로 설정함.

  




Java code에서 ViewFlipper 객체의 사용

Java code에서 ViewFlipper 객체 생성은 findViewById() 메소드를 사용하던가 새로운 view를 inflate 시킬 수 있다.

예제에서는 layout xml 파일 내부에 선언된 ViewFlipper element와 findViewById() 메소드를 이용해 객체를 생성한다. 

예제 코드 내부에서 ViewFlipper객체가 사용할 메소드는 다음과 같다.


  • View:addView(View): layout xml 문서 내부의 ViewFlipper element에 nest된 view 이외에 새로운 view를 동적으로 추가한다.
  • ViewAnimator:setInAnimation(Context, int): 새로운 view가 화면에 진입시의 animation 설정
  • ViewAnimator:setOutAnimation(Context, int): 기존 view가 화면에서 퇴장시의 animation 설정
  • ViewAnimator:showNext(): ViewFlipper에 등록된 view중 현재 view의 다음 view를 화면에 보이게 함
  • ViewAnimator:showPrevious(): ViewFlipper에 등록된 view중 현재 view의 전 view를 화면에 보이게 함
  • ViewFlipper:setFlipInterval(int): 자동 flipping시 적용할 시간간격 (1/1000초 단위 사용)
  • ViewFlipper:startFlipping(): 설정된 시간간격을 적용해 자동 flipping 시작 함 
  • ViewFlipper:stopFlipping(): 자동 flipping을 정지 함

 

  



위에서 설명한 것들을 바탕으로 예제 소스를 분석해 보자.



push_left_in.xml


<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
	<translate android:fromXDelta="100%p" android:toXDelta="0%p" android:duration="300"/>
	<alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="300" />
</set>


 

push_left_out.xml


<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
	<translate android:fromXDelta="0%p" android:toXDelta="-100%p" android:duration="300"/>
	<alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="300" />
</set>


 

push_right_in.xml


<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
	<translate android:fromXDelta="-100%p" android:toXDelta="0%p" android:duration="300"/>
	<alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="300" />
</set>


 

push_right_out.xml


<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
	<translate android:fromXDelta="0%p" android:toXDelta="100%p" android:duration="300"/>
	<alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="300" />
</set>


 

main.xml


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <CheckBox
    	android:id="@+id/chkAuto"
    	android:layout_width="wrap_content"
    	android:layout_height="wrap_content"
    	android:text="Check for Auto Flip" />
	<ViewFlipper
		android:id="@+id/viewFlipper"  
	    android:layout_width="fill_parent" 
	    android:layout_height="fill_parent"
	    >  
	    <TextView
		    android:id="@+id/view1"
		    android:layout_width="wrap_content"
		    android:layout_height="wrap_content"
		    android:layout_gravity="center"
		    android:text="View 1"
		    android:textSize="30px"
		    android:textColor="#FF0000" />
	    <TextView
		    android:id="@+id/view2"
		    android:layout_width="wrap_content"
		    android:layout_height="wrap_content"
		    android:layout_gravity="center"
		    android:text="View 2"
		    android:textSize="30px"
		    android:textColor="#00FF00" />
		<TextView
		    android:id="@+id/view3"
		    android:layout_width="wrap_content"
		    android:layout_height="wrap_content"
		    android:layout_gravity="center"
		    android:text="View 3"
		    android:textSize="30px"
		    android:textColor="#0000FF" />	    
	</ViewFlipper>
</LinearLayout>


 

MyViewFlipper.java


package com.holim.test;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AnimationUtils;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.ViewFlipper;
// View.OnTouchListener 인터페이스와
// CompoundButton.OnCheckedChangeListener 인터페이스 구현 함
public class MyViewFlipper extends Activity
						implements View.OnTouchListener,
						CompoundButton.OnCheckedChangeListener {
	CheckBox checkBox; 
    ViewFlipper flipper;
    // 터치 이벤트 발생 지점의 x좌표 저장
    float xAtDown;
    float xAtUp;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        checkBox = (CheckBox)findViewById(R.id.chkAuto);
        checkBox.setOnCheckedChangeListener(this);
        flipper = (ViewFlipper)findViewById(R.id.viewFlipper);
        flipper.setOnTouchListener(this);
        // ViewFlipper에 동적으로 child view 추가
        TextView tv = new TextView(this);
        tv.setText("View 4\nDynamically added");
        tv.setTextColor(Color.CYAN);
        flipper.addView(tv);       
    }
    // View.OnTouchListener의 abstract method
    // flipper 터지 이벤트 리스너
	@Override
	public boolean onTouch(View v, MotionEvent event) {
		// 터치 이벤트가 일어난 뷰가 ViewFlipper가 아니면 return
		if(v != flipper) return false;		
		if(event.getAction() == MotionEvent.ACTION_DOWN) {
			xAtDown = event.getX(); // 터치 시작지점 x좌표 저장			
		}
		else if(event.getAction() == MotionEvent.ACTION_UP){
			xAtUp = event.getX(); 	// 터치 끝난지점 x좌표 저장
			if( xAtUp < xAtDown ) {
				// 왼쪽 방향 에니메이션 지정
				flipper.setInAnimation(AnimationUtils.loadAnimation(this,
		        		R.anim.push_left_in));
		        flipper.setOutAnimation(AnimationUtils.loadAnimation(this,
		        		R.anim.push_left_out));
		        // 다음 view 보여줌
				flipper.showNext();
			}
			else if (xAtUp > xAtDown){
				// 오른쪽 방향 에니메이션 지정
				flipper.setInAnimation(AnimationUtils.loadAnimation(this,
		        		R.anim.push_right_in));
		        flipper.setOutAnimation(AnimationUtils.loadAnimation(this,
		        		R.anim.push_right_out));
		        // 전 view 보여줌
				flipper.showPrevious();			
			}
		}		
		return true;
	}
	// CompoundButton.OnCheckedChangeListener의 abstract method
	// 책크박스 책크 이벤트 리스너
	@Override
	public void onCheckedChanged(CompoundButton view, boolean isChecked) {
		Log.w("checked", Boolean.toString(isChecked));
		if(isChecked == true) {
			// 왼쪽 에니메이션 설정
			flipper.setInAnimation(AnimationUtils.loadAnimation(this,
	        		R.anim.push_left_in));
	        flipper.setOutAnimation(AnimationUtils.loadAnimation(this,
	        		R.anim.push_left_out));
	        // 자동 Flipping 시작 (간격 3초)
			flipper.setFlipInterval(3000);
			flipper.startFlipping();
		}
		else 
		{
			// 자동 Flipping 해지
			flipper.stopFlipping();
		}		
	}
}


 


다음은 실행 결과이다. 에뮬레이터에서는 마우스 드레그를 통해 터치 이벤트를 test가능하며, 애니메이션이 적용되어 view간의 전환이
부드럽게 이뤄짐을 볼 수 있다. 또, CheckBox를 첵크 하면 2초 간격으로 자동으로 view 가 flip 되는 것을 볼 수 있다.











  


                                                       프로젝트 소스 다운로드


018_ViewFlipper Demo.zip


018_ViewFlipper Demo.zip
0.05MB