반응형

이전 포스트(Fragment 만들기Fragment 제어하기) 에서

레이아웃 xml 파일안에 프레그먼트(Fragment)를 만들어서

화면에 보여주고 제어하는 작업을 소개했습니다.


하지만 실제로 Fragment를 사용할 때는

XML에 프레그먼트를 작성하지 않고 자바코드로 추가합니다.

XML에 추가하면 고정적으로 화면에 추가되기 때문에 나중에

삭제가 되지 않습니다.


동적으로 추가/삭제 또는 다른 Fragment로 재배치 등의 작업을 수행하려면

자바코드에서 Fragment를 추가해야만 합니다.


이번 예제는

버튼 3개가 화면 상단에 있습니다.

처음에는 프레그먼트가 한개도 없는 상태입니다.


첫번째 'Analog'버튼을 누르면 AnalogClock를 보여주는 Fragment를 추가합니다.

두번째 'Digital'버튼을 누르면 DigitalClock을 보여주는 Fragment로 바뀝니다.

세번째 'Calendar'버튼을 누르면 CalendarView를 보여주는 Fragment로 바뀝니다.


   

 

실제 이런 동작의 형태를 보신적이 많이 있을 텐데요.

다른점은 버튼3개가 Tab으로 되어 있다는 거죠.

다음 포스트에서 Tab으로 Fragment를 바꾸는 예제도 할 겁니다.


이제 만들어보죠.


먼저 activity_main.xml 파일입니다.

Button 3개와 Fragment가 추가될 LinearLayout 1개로 구성되어 있습니다.

 

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical" >

    

    <LinearLayout 

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:orientation="horizontal">

        

        <Button android:id="@+id/btn01"

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text="ANALOG"

            android:textSize="12sp"

            android:onClick="mOnClick"/>

        

        <Button android:id="@+id/btn02"

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text="DIGITAL"

            android:textSize="12sp"

            android:onClick="mOnClick"/>

        

        <Button android:id="@+id/btn03"

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text="CALENDAR"

            android:textSize="12sp"

            android:onClick="mOnClick"/>

        

    </LinearLayout>

    

    <LinearLayout android:id="@+id/container"

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:orientation="vertical"

        android:padding="8dp" >        

        

    </LinearLayout>    


</LinearLayout>


이제 각 버튼을 누를때 마다 추가될 Fragment 객체가 보여줄

View의 설계을 위해 레이아웃 xml파일 3개를 추가하겠습니다.

res폴더>layout폴더> 안에

'fragment_analog', 'fragment_digital', 'fragment_calendar' 로 3개의 xml 파일을 만듭니다.

모두 LinearLayout으로 만들었습니다.




각 layout xml 파일안에 원하는 View들을 추가하면 되겠죠.


이 예제에서는 단순하게 'AnalogClock', 'DigitalClock', 'CalendarView'

각각의 layout xml파일안에 추가하겠습니다.

 

3개의 layout xml 파일입니다. 

 fragment_analog.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical" >

    

    <AnalogClock 

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"/>    


</LinearLayout>

 

 fragment_digital.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical" >

    

    <DigitalClock 

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"/>    


</LinearLayout>

 

 fragment_calendar.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical" 

    android:padding="16dp">

    

    <CalendarView 

        android:layout_width="match_parent"

        android:layout_height="match_parent"/>    


</LinearLayout>


이제

이 3개의 레이아웃 파일을 View로 만들어 보여줄 Fragment 자바파일을 3개 만들어보겠습니다.

자바 파일인 만큼 src폴더의 package에서 new메뉴를 실행시켜서

'AnalogFragment', 'DigitalFragment', 'CalendarFragment'

라는 이름의 새로운 class 자바 파일을 아래와 같이 생성하겠습니다.


당연히 Class를 만들때 Fragment를 상속받아 만드시면 됩니다.

android.app 패키지의 Fragment를 상속받으면 됩니다.


  




만들어진 3개의 Fragment 자바안에 Activity 자바파일처럼

lifecycle 콜백 메소드 중 보여줄 View를 만들어내는 onCreateView메소드를 오버라이드(Override)해서

위에 만든 3개의 레이아웃 xml파일을 View로 설정해 보겠습니다.


주석 빼면 코드가 몇 줄 안됩니다. 

 AnalogFragment.java

import android.app.Fragment;

import android.os.Bundle;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;


public class AnalogFragment extends Fragment {

//Fragment의 lifecycle 메소드 중에서 Fragment가 보여줄 View를 설정하는 메소드

//MainActivity.java 에서 onCreate() 메소드 안에 setContentView()하듯이

//Fragment에서는 이 메소드에서 Fragment가 보여줄 View 객체를 생성해서 return 시켜줘야 함.

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

// TODO Auto-generated method stub

View view=null;//Fragment가 보여줄 View 객체를 참조할 참조변수

//매개변수로 전달된 LayoutInflater객체를 통해 fragment_analog.xml 레이아웃 파일을

//View 객체로 생성

view= inflater.inflate(R.layout.fragment_analog, null);

//생성된 View 객체를 리턴

return view;

}

}

 

 DigitalFragment.java

import android.app.Fragment;

import android.os.Bundle;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;


public class DigitalFragment extends Fragment {

//Fragment의 lifecycle 메소드 중에서 Fragment가 보여줄 View를 설정하는 메소드

//MainActivity.java 에서 onCreate() 메소드 안에 setContentView()하듯이

//Fragment에서는 이 메소드에서 Fragment가 보여줄 View 객체를 생성해서 return 시켜줘야 함.

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

// TODO Auto-generated method stub

View view=null;//Fragment가 보여줄 View 객체를 참조할 참조변수

//매개변수로 전달된 LayoutInflater객체를 통해 fragment_digital.xml 레이아웃 파일을

//View 객체로 생성

view= inflater.inflate(R.layout.fragment_digital, null);

//생성된 View 객체를 리턴

return view;

}


}

 

 CalendarFragment.java

import android.app.Fragment;

import android.os.Bundle;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;


public class CalendarFragment extends Fragment {

//Fragment의 lifecycle 메소드 중에서 Fragment가 보여줄 View를 설정하는 메소드

//MainActivity.java 에서 onCreate() 메소드 안에 setContentView()하듯이

//Fragment에서는 이 메소드에서 Fragment가 보여줄 View 객체를 생성해서 return 시켜줘야 함.

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

// TODO Auto-generated method stub

View view=null;//Fragment가 보여줄 View 객체를 참조할 참조변수

//매개변수로 전달된 LayoutInflater객체를 통해 fragment_calendar.xml 레이아웃 파일을

//View 객체로 생성

view= inflater.inflate(R.layout.fragment_calendar, null);

//생성된 View 객체를 리턴

return view;

}


}


 이제

Fragment를 다 설계했으니 버튼을 누를때마다 Fragment가 추가되도록

MainActivity.java 파일을 작성해 보겠습니다.


주석을 참조하시기 바랍니다.

 

MainActivity.java

import android.app.Activity;

import android.app.Fragment;

import android.app.FragmentManager;

import android.app.FragmentTransaction;

import android.os.Bundle;

import android.view.View;


public class MainActivity extends Activity {

FragmentManager manager;  //Fragment를 관리하는 클래스의 참조변수

FragmentTransaction tran;  //실제로 Fragment를 추가/삭제/재배치 하는 클래스의 참조변수

Fragment frag1, frag2, frag3; //3개의 Fragment 참조변수


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//Fragment를 관리하는 FragmentManager 객체 덩어오기

manager= (FragmentManager)getFragmentManager();

//미리 3개의 Fragment 객체 생성

frag1= new AnalogFragment();

frag2= new DigitalFragment();

frag3= new CalendarFragment();

}

//Activity가 보여주는 activity_main.xml 파일에 태그문으로

//onClick속성이 설정된 View를 클릭했을 때 자동으로 호출되는 콜백 메소드

public void mOnClick(View v){

switch( v.getId() ){

case R.id.btn01:

//fragment_analog.xml을 보여주는 AnalogFragment 객체로 추가(add)

//Fragment 추가 작업을 수행하기 위해 먼저 FragmentManager에게 작업시작(beginTransaction) 요청

//Fragment를 자바에서 동적으로 추가/삭제/재배치 하려면 반드시 Transaction으로 실행

//FragmentTransction 클래스 객체를 생성 및 작업 시작

tran= manager.beginTransaction();

//Fragment 추가(add)

//첫번째 파라미터 : Fragment를 추가시킬 ViewGroup의 ID

  //                   (activity_main.xml안에 보면 버튼 아래에 있는 LinearLayout의 Id가 R.id.container)

//                  이 LinearLayout에 frag1(AnalogFragment) 객체 추가

//두번째 파라미터 : 추가시킬 Fragment 객체

tran.add(R.id.container, frag1); 

//Fragment 변경 작업을 완료했다는 메소드 호출

tran.commit();

break;

case R.id.btn02:

//fragment_digital.xml을 보여주는 DigitalFragment 객체로 추가(add)

//FragmentTransction 클래스 객체를 생성 및 작업 시작

tran= manager.beginTransaction();

//DigitalFragment 추가(add)

tran.add(R.id.container, frag2); 

//Fragment 변경 작업을 완료했다는 메소드 호출

tran.commit();

break;

case R.id.btn03:

//fragment_calendar.xml을 보여주는 CalendarFragment 객체로 추가(add)

//FragmentTransction 클래스 객체를 생성 및 작업 시작

tran= manager.beginTransaction();

//CalendarFragment 추가(add)

tran.add(R.id.container, frag3); 

//Fragment 변경 작업을 완료했다는 메소드 호출

tran.commit();

break;

}

}

}

 

위 코드에서 가장 눈여겨 볼 부분이 Fragment를 add 하는 부분이겠죠.

FragmentManager는 이전 포스트에서도 소개했으니 어떤 클래스인지는 알것이라고 간주하고

처음 보는 것이 FragmentTransaction 이라는 것이겠죠.


안드로이드는 Fragment를 추가/삭제/재배치 등의 작업을 수행하기 위해

FragmentTransaction을 사용합니다.


트랜잭션(Trasaction)이라는 용어를 알면 좀더 이해하기 쉬울텐데요.


짧게 설명하면

Transaction은 하나의 작업 흐름을 말합니다. 비슷하게 인지되는 프로세스(Process)가 있는데

다른 점은

트랜잭션은 프로세스와 다르게 롤백 기능이 있다는 것이죠.


이 롤백이 뭐냐?

예를 들어보겠습니다.

여러분이 인터넷 계좌이체 프로그램을 하나 만들고 있다고 생각해보죠.


A라는 계좌에서 100만원을 B계좌로 옮기는 부분을 눈여겨 보겠습니다.

다른 건 빼고

100만원을 이체하려면

A계좌의 잔액에서 100만원을 차감하도록 할 것이고

바로 이어서 B계좌의 잔액에 100만원을 증감하도록 해야겠죠.


물론 그런 식으로 코드를 작성했을 것이며 잘 된다고 가정해 보죠.

뺄셈 덧셈만 하면 되니 어려운 프로세스는 아니죠.


중요한 건 이 프로세스가 실행되는 도중 발생하는 예외상황에 대한 문제입니다.

무슨말이냐??


프로세서가 여러분이 작성한 코드를 한줄씩 읽어가며 실행하는 것을 아실테고

위 작업을 보면

프로세서가 먼저 A계좌에서 100만원을 빼는 코드를 실행하겠죠.

바로 이어서 B계좌에 100만원을 더하는 코드를 실행하려하겠죠.


바로 이때!!!

갑자기 퍽!! 에러가 나서 프로세서가 종료되었다면!!!!!!!!!


B계좌에 100만원 덧셈 작업은 실행이 안되고

A계좌만 100만원이 뺄셈된 상태겠죠...


그럼 이 100만원은????????

컴퓨터가 먹은(?) 것이죠.


이러면 안되잖아요. 그래서 이런 경우 하나의 작업( A에서 100빼고 B에 100추가 )이

종료되었다고 확인(commit)하기 전에 어떤 문제로 종료되면

처음 상태로 자동으로 되돌아 가도록 하는 기술이 있습니다.

이를 롤백이라고 합니다.


아주 기가막힌 기법이죠. 반드시 필요하기도 하고요.

이 롤백기능을 가진 것이 트랜잭션(Transaction) 입니다.


그래서 작업을 시작한다고 알리기 위해 begineTransaction을 실행하고 

원하는 작업을 수행한 후

마지막에 commit하면 적용되는 문법을 사용합니다.


안드로이드의 Fragment 추가/삭제/재배치는 이런 트랜잭션으로 수행하도록 설계되었습니다.

코드를 보시면 어렵지 않게 이해할 수 있을 것이라 기대합니다.


자 이제 완성했으니 실행해 볼까요?


실행결과는 아래와 같습니다.

각 버튼을 하나씩 눌러서 Fragment를 추가하는 겁니다.


  

 


버튼을 누를때 마다 Fragment가 추가(add)되는 것을 볼수 있습니다.


어! 근데 이건 제가 원하는 결과가 아니죠.

전 add가 아니라 버튼을 누를때마다 다른 Fragment를 보고 싶은 것이죠.

이럴 땐 새로운 Fragment를 추가(add)할 때 

기존 Fragment를 제거(remove)하고 추가 작업을 해야겠죠.

위 코드에서 추가 할때 tran.add() 메소드를 했듯이

제거할때는 tran.remove()메소드를 호출하면 됩니다.


생각해 보면 이 작업은 결국 재배치(replace)라는 용어로 부를 수도 있겠군요.

그래 FragmentTransaction 재배치(replace) 메소드도 가지고 있습니다.

재배치(replace)는 만약 기존에 Fragment가 없다면 자동으로 추가합니다.

편하네요.


위의 add() 코드를 replace() 코드로 변경하겠습니다.

주석은 빼고

변경된 부분은 굵은 글씨로 표시했습니다.

MainActivity.java

import android.app.Activity;

import android.app.Fragment;

import android.app.FragmentManager;

import android.app.FragmentTransaction;

import android.os.Bundle;

import android.view.View;


public class MainActivity extends Activity {

FragmentManager manager;  //Fragment를 관리하는 클래스의 참조변수

FragmentTransaction tran;  //실제로 Fragment를 추가/삭제/재배치 하는 클래스의 참조변수

Fragment frag1, frag2, frag3; //3개의 Fragment 참조변수


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//Fragment를 관리하는 FragmentManager 객체 덩어오기

manager= (FragmentManager)getFragmentManager();

//미리 3개의 Fragment 객체 생성

frag1= new AnalogFragment();

frag2= new DigitalFragment();

frag3= new CalendarFragment();

}

//Activity가 보여주는 activity_main.xml 파일에 태그문으로

//onClick속성이 설정된 View를 클릭했을 때 자동으로 호출되는 콜백 메소드

public void mOnClick(View v){

switch( v.getId() ){

case R.id.btn01:

//fragment_analog.xml을 보여주는 AnalogFragment 객체로 재배치(replace)

tran= manager.beginTransaction();

tran.replace(R.id.container, frag1); 

tran.commit();

break;

case R.id.btn02:

//fragment_digital.xml을 보여주는 DigitalFragment 객체로 재배치(replace)

tran= manager.beginTransaction();

tran.replace(R.id.container, frag2); 

tran.commit();

break;

case R.id.btn03:

//fragment_calendar.xml을 보여주는 CalendarFragment 객체로 재배치(replace)

tran= manager.beginTransaction();

tran.replace(R.id.container, frag3); 

tran.commit();

break;

}

}

}

 

동작 결과는 아래와 같습니다.

   

처음 소개했듯이 Fragment가 바뀌는 것을 확인할 수 있습니다.

마지막 CalendarView가 제대로 보이지 않으시면 CalendarView의 높이(layout_height)값을 '300dp'정도로 줄이세요.



참고로

Fragment는 Activity처럼 백스택(back stack)를 가지고 있지 않아서

현재 Fragment를 종료한다고 이전 Fragment가 보이지 않습니다.


하지만 그렇게 하고 싶을 수도 있겠죠.

만약 이전에 보여줬던 Fragment가 보이게 하려면 backstack에 Fragment를 추가해야 합니다.

코드만 한줄 더 쓰면 됩니다.

 tran= manager.beginTransaction();

 tran.replace(R.id.container, frag1);

 tran.addToBackStack(null); // 백스택에 지금 재배치한 Fragment를 추가

 tran.commit();

이렇게 하고 앱의 '뒤로가기' 버튼을 누르면

이전에 보여줬던 Fragment가 보이게 될 겁니다.



여기까지만 하고

다음 포스트에서는 이 예제를 기반으로 해서

Button이 아니라 Tab으로 Fragment가 변경되게 해보겠습니다.


반응형
반응형

이전 포스트(Fragment 만들기) 에서

레이아웃 xml 파일안에 프레그먼트(Fragment)를 만들어서

화면에 보여주는 작업을 소개했습니다.


이번에는

이렇게 만들어진 Fragment를 보여주는 Activity에서 Fragment 안에 있는 View들을 제어하는 예제입니다.


간단하게 소개하기 위해

activity_main안에 Button을 하나 만들고

이 버튼을 누르면 Fragment안에 있는 TextView의 글씨가 변경되는 예제입니다.


동작결과입니다. 간단합니다.

노란색 배경이 Fragment가 보여주는 View의 영역입니다.

MainActivity의 버튼을 눌렀더니 Fragment 안에 있는 TextView의 글씨가 Hello 변경되었습니다.


  



이전 예제를 그대로 활용하겠습니다.


먼저 activity_main.xml에 버튼하나를 추가했습니다.

굵은 글씨가 추가된 부분입니다.

 

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical">

    

    <TextView android:id="@+id/text"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text="The TextView into the MainActivity"/>

    

    <Button android:id="@+id/btn"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text="Change Text in the Fragment"

        android:onClick="mOnClick"/>

    

    <!-- TestFragment를 MainActivity가 보여줄 View로 고정시켜서 배치 -->

    <!-- 직접적으로 XML 레이아웃 파일에 태그문으로 생성된 Fragment는  동적으로 재배치/삭제 불가-->

    <fragment android:name="com.kitesoft.fragment.TestFragment"

        android:id="@+id/frag"

        android:layout_width="match_parent"

        android:layout_height="match_parent"/>    


</LinearLayout>

 

 


이제 제어하기 위한 자바 소스파일입니다.

가장 중요한 부분은 <fragment/>로 만들어진 Fragment

자바에서 참조하는 작업입니다.


xml에 만들어진 TextView나 ImageView 같은 View들

Java 소스코드 에서 참조하려면

findViewById()로 찾아옵니다.

메소드의 이름만 봐도 View객체를 찾으라는 것을 알수 있는 매우 직관적인 메소드명입니다.


우리가 하고자 하는 것은 Fragment를 찾는 것입니다.

Fragment는 View가 아닙니다.

이전에 사용했던 findViewById()는 좀 이상하지 않나요?

findFragmentById()가 맞지 않겠어요?

 맞습니다. 이 메소드를 사용해야 합니다. 하지만 애석하게도

Activity 클래스는 이런 메소드가 없습니다.


그래서 Activity 클래스 대신

Fragment를 제어하기 위한 별도의 클래스가 존재하게 됩니다.

Fragment를 add/remove/replace 하는 등의 작업들을 수행할 수 있도록 관리해주는

클래스라고 보면 됩니다.


이름도 참 직관적인 FragmentManager 입니다.

요놈이 findFragmentById() 메소드를 가지고 있습니다.

이 클래스 객체를 이용해서 Fragment가 보여주는 레이아웃(fragment_text.xml)안에 있는

View들을 제어합니다.


자바 소스파일입니다.

주석의 설명을 참고하시기 바랍니다.

 

 MainActivity.java

import android.app.Activity;

import android.app.FragmentManager;

import android.os.Bundle;

import android.view.View;

import android.widget.TextView;


public class MainActivity extends Activity {

TestFragment frag; //TestFragment 참조변수

FragmentManager manager; //FragmentManager 참조변수


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//Fragment는 View가 아닌만큼 다른 Widget을 찾을 때 처럼 

//findViewById()메소드를 사용할 수 없음

//대신 findFragmentById()메소드를 사용해서 찾아와야 하지만

//Activity 클래스는 이런 메소드가 없음.

//대신 Fragment를 추가/삭제/재배치 등 제어를 할 수 있는 FragmentManager 클래스가 존재함.

//Fragment를 제어하기 위한 매니져 객체

manager= getFragmentManager();

//매니져 객체에게 findFragmentById()를 요청하면서 id 전달

frag= (TestFragment)manager.findFragmentById(R.id.frag);

}

//Activity가 보여주는 activity_main.xml 파일에 태그문으로

//onClick속성이 설정된 View를 클릭했을 때 자동으로 호출되는 콜백 메소드

public void mOnClick(View v){

switch( v.getId() ){

case R.id.btn:

//TestFragment 객체가 보여주고 있는 View 객체( fragment_test.xml 로 만들어진 View) 안에 있는

//TextView 객체를 찾아오기

TextView text= (TextView)frag.getView().findViewById(R.id.text_fragment);

//Fragment가 보여 주는 레이아웃 안에 있는 TextView의 글씨를 변경

text.setText("Hello");

break;

}

}

}


여기까지 입니다.


참고로

위의 방법은 Activity에서 Fragment안에 있는 TextView를 직접 참조해와서

제어하는 방식입니다.



객체 지향적 프로그래밍 방식의 기본 개념은


"니껀 니가!!"


즉, 그 객체의 멤버 변수는 그 객체 스스로 제어하는 것이 좋다는 것이죠.

그래야 재사용성이 높아지고

클래스 간의 독립성을 유지 할 수 있기 때문이죠.

시간이 나면 강좌게시판을 통해 자세히 설명할 예정입니다.


그래서 이를 좀 더 반영하여

Activity에서 Fragment의 TextView를 직접 제어하지 않고

Fragment의 특정 메소드에 값을 전달하고 Fragment가 전달 된 값으로 TextView의 글씨를 변경하도록 해보겠습니다.


TestFragment.java 파일과 MainActivity.java 파일의 일부분만 추가 변경해 주면 됩니다.


변경된 부분만 굵은 글씨로 소개합니다.

 

 TestFragment.java

public class TestFragment extends Fragment {

TextView text;  //TextView 참조변수

public TestFragment() {

// TODO Auto-generated constructor stub

}

//Fragment의 lifecycle 메소드 중에서 Fragment가 보여줄 View를 설정하는 메소드

//MainActivity.java 에서 onCreate() 메소드 안에 setContentView()하듯이

//Fragment에서는 이 메소드에서 Fragment가 보여줄 View 객체를 생성해서 return 시켜줘야 함.

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

// TODO Auto-generated method stub

View view=null;//Fragment가 보여줄 View 객체를 참조할 참조변수

//매개변수로 전달된 LayoutInflater객체를 통해 fragment_test.xml 레이아웃 파일을

//View 객체로 생성

view= inflater.inflate(R.layout.fragment_test, null);

//위에 만들어진 view객체 안에 있는 TextView를 찾아오기

    text= (TextView)view.findViewById(R.id.text_fragment);

//생성된 View 객체를 리턴

return view;

}

//TextView의 글씨를 변경하기 위해 만든 메소드

public void setTextViewValue(String str){

text.setText(str); //전달 받은 문자열로 TextView의 글씨를 변경

}

}

 

 



 

MainActivity.java


      //... 위 생략


      //Activity가 보여주는 activity_main.xml 파일에 태그문으로

      //onClick속성이 설정된 View를 클릭했을 때 자동으로 호출되는 콜백 메소드

public void mOnClick(View v){

switch( v.getId() ){

case R.id.btn:

   /* 이 영역은 모두 주석 처리///////////////

//TestFragment 객체가 보여주고 있는 View 객체( fragment_test.xml 로 만들어진 View) 안에 있는

//TextView 객체를 찾아오기

TextView text= (TextView)frag.getView().findViewById(R.id.text_fragment);

//Fragment가 보여 주는 레이아웃 안에 있는 TextView의 글씨를 변경

text.setText("Hello");

*/

   //Fragment 객체안에 정의 된 TextView의 글씨를 변경하는 메소드를 호출면서 "Hello"문자열 전달.

frag.setTextViewValue("Hello");

   break;

}

}


동작의 결과는 같습니다.

필자의 경우는 이 방법을 더 선호합니다.



이번에 소개된 예제는 XML안에 Fragment를 태그문을 이용해 고정적으로 만들어낸 방식입니다.

이 방식은 Fragment의 값은 바꿀 수 있어도 Fragment 자체를 동적으로 재배치 하거나 삭제 하지 못하는 단점이 있습니다.


다음 포스트에서는 동적으로 Fragment를 추가하는 예제를 소개하겠습니다.


반응형
반응형

이번 소개될 예제소스는 Fragment에 관련된 것입니다.


많은 초심자들이 Fragment의 사용방법은 어느정도 학습을 통해 익혔음에도

언제. 왜. 사용하는지는 모호한가 보더군요.


기껏 열심히 학습해 놓고 잘 사용을 안하더군요.


간단하게 말해서 Fragment는 Activity 클래스의 Java 코드가 너무 복잡해질 것을

우려해서 만들어진 것으로 볼 수 있습니다.

물론 근본적인 이유가 있지만

실제 사용되는 경우는 Activity 자바파일의 복잡성

해소하기 위해서라고 보면될 겁니다.


예를 들어보죠.

여러분이 이전 예제에 소개되었던 Tab 예제(ViewPager ActionBar Tab 구현)

를 실제로 앱을 구현하느데 사용한다고 가정해보겠습니다.


Tab을 누를 때마다

보여지는 화면이 달라지도록 만들텐데요.

이전 예제에서는 샘플로 만든 예제여서 각 각의 화면(xml 레이아웃 파일)안에

AnalogClockDigitalClockCalendarView들을 각각

가지고 있도록 했습니다.


하지만

실제 개발될 앱들의 경우에는 각 Tab마다 보여질 Page View(xml 레이아웃 파일)들이

샘플예제처럼 간단히 하나의 View만 가지고 있지는 않을 겁니다.

실제로는 TextView여러개, Button, EditText, ImagView 등 많은 Widget들이

하나의 Page(Tab이 보여주는 View) 안에 존재하도록 설계할 경우가 많을 겁니다.


자.여기서 생각해보죠.

만약 첫번째 탭버튼을 누를때 보여질 xml 레이아웃 파일안에 Widget이 10개있고

두번째 탭버튼을 누를때 보여질 xml 레이아웃 파일안에 Widget이 20개있고

세번째 탭버튼을 누를때 보여질 xml 레이아웃 파일안에 Widget이 15개있다면..


각 탭을 누를 때 마다 보여질 Page안에 있는 Widget들을 제어하려면

Activity 자바파일안에 Widget별로 참조변수를 만들어야 할겁니다.

실제로 Tab별로 보여질 Page는 동시에 화면에 표시될 일이 없겠죠.

하지만 Activity 자바파일안에는 참조변수를 총 45개 만들어야 합니다.

이거 너무 짜증나지 않을까요?


이럴 경우

MainActivity에서는 3개의 Page만 관리하고

각 Page마다 별도로 자기가 보여줄 View들에 대한 관리를 해주면 좋지 않을까요.


ViewGroup은 View를 여러개 추가하고 관리하지만

별도의 Java코드 파일을 생성해서 사용하지는 않죠.


그렇다고 View를 보여주기 위해 3개의 Activity를 만들어서

MainActivity에 추가하는 작업도 불가능합니다.

안드로이드는 한 화면에 Activity가 하나밖에 존재할 수 없기 때문이죠.


그래서 Activity 클래스처럼 자신이 보여줄 View를 설정하고 제어할 수 있으며

일반 View처럼 Activity의 위에 놓여질 수 있는 개념이 생겨났죠.

이것이 Fragment입니다.

쉽게말해 Activity+View 의 특징을 가지고 있다고 보면 됩니다.


시간이 되면 강좌게시판을 통해 자세한 개념을 설명하고자 합니다.

여기서는 예제소스를 보는 게시판이니 만큼 단순히

Activity 자바파일에 모든 Widget들의 참조변수를 작성하지 않아도 된다는데 그 의미를 두시기 바랍니다.


너무 설명이 서론이 길었네요. 우선 Fragment의 개념 이해를 위해

MainActivity가 보여줄 activity_main.xml파일안에 일반 View처럼 Fragment를 추가하는

예제를 소개하도록 하겠습니다.


쉽게 결과를 이해하기 위해

MainActivity가 보여주는 activity_main.xml안에는

TextView 1개

Fragment 1개만 배치해보겠습니다.

Fragment가 보여주고 관리하는 View는 Activity와 구분하기 위해

배경을 노란색(#ffff00)으로 하고 TextView는 빨간색(#ff0000) 글씨로 나타나게 하겠습니다.


먼저 결과 화면은 매우 심플합니다.


 

색이 달라서 쉽게 이해할 수 있으리라 생각됩니다.


자. 그럼 이제 위처럼 TextView 한개와 Fragment 한깨짜리 예제를 만들어보겠습니다.


먼저 Fragment가 보여줄 xml 레이아웃 파일부터 만들어보겠습니다.


  



 

 fragment_text.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical"

    android:background="#ffff00" >

    

    <TextView android:id="@+id/text_fragment"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text="The TextView into the TestFragment"

        android:textColor="#ff0000"/>    


</LinearLayout>

배경(backgroud)가 노란색이고 빨간색 글씨의 TextView 하나를 가지고 있는 레이아웃 파일입니다.


이제

이 레이아웃 파일을 View로 만들어 보여줄 Fragment 자바파일을 만들어보겠습니다.

자바 파일인 만큼 src폴더의 package에서 new메뉴를 실행시켜서

TestFragment라는 이름의 새로운 class 자바 파일을 아래와 같이 생성하겠습니다.


당연히 Class를 만들때 Fragment를 상속받아 만드시면 됩니다.

android.app 패키지의 Fragment를 상속받으면 됩니다.

android.support.v4의 Fragment 는 FragmentPagerAdapter를 쓸 때 사용합니다. -추후 소개될 예정-

 



만들어진 TestFragment 자바안에 Activity 자바파일처럼

lifecycle 콜백 메소드 중 보여줄 View를 만들어내는 onCreateView메소드를 오버라이드(Override)해서

위에 만든 fragment_test.xml파일을 설정해 보겠습니다.

 

 TestFragment.java

public class TestFragment extends Fragment {

public TestFragment() {

// TODO Auto-generated constructor stub

}

//Fragment의 lifecycle 메소드 중에서 Fragment가 보여줄 View를 설정하는 메소드

//MainActivity.java 에서 onCreate() 메소드 안에 setContentView()하듯이

//Fragment에서는 이 메소드에서 Fragment가 보여줄 View 객체를 생성해서 return 시켜줘야 함.

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

// TODO Auto-generated method stub

View view=null; //Fragment가 보여줄 View 객체를 참조할 참조변수

//매개변수로 전달된 LayoutInflater객체를 통해 fragment_test.xml 레이아웃 파일을

//View 객체로 생성

view= inflater.inflate(R.layout.fragment_test, null);

//생성된 View 객체를 리턴

return view;

}


}

코드에 대한 설명은 주석을 참고하시기 바랍니다.


 이제

Fragment를 다 설계했으니

MainActivity에 보여지도록 activity_main.xml안에 태그문을 통해 포함시켜 보겠습니다.

TextView 1개와 Fragment 1개를 작성합니다.


 

 activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical">

    

    <TextView android:id="@+id/text"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text="The TextView into the MainActivity"/>

    

    <!-- TestFragment를 MainActivity가 보여줄 View로 고정시켜서 배치 -->

    <!-- 직접적으로 XML 레이아웃 파일에 태그문으로 생성된 Fragment는  동적으로 재배치/삭제 불가-->

    <fragment android:name="com.kitesoft.fragment.TestFragment"

        android:id="@+id/frag"

        android:layout_width="match_parent"

        android:layout_height="match_parent"/>    


</LinearLayout>

 

fragment 태그 문의 주요 속성인 name을 통해 어떤 Fragment 클래스인지 구분합니다.


그래픽 모드로 옮겨서 미리보기를 보면 아래처럼 보여질 겁니다.


 

MainActivity.java 파일은 activity_main.xml 보여주는 기본 코드

그대로 입니다.

하나도 바뀌지 않았습니다.

 

 MainActivity.java

public class MainActivity extends Activity {


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

}


이제 실행 시켜주면

MainActivity에서 보여주는 View에 Fragment가 포함되어서 보여질 겁니다.


다음 포스트에서는

이렇게 만들어진 Fragment의 TextView를 MainActivity에서 제어해 보겠습니다.


반응형
반응형

앞선 포스트에서

간단한 ActionBar Tab을 구현해봤습니다.

ActionBar에 Tab을 만드는 방법을 알아보기 위한 예제이기에

각 Tab마다 보이는 View의 변경을

setContentView()메소드를 이용했습니다.


하지만

실제 이런 류의 액션바 탭(ActionBar Tab)을 구현한 앱들을 보면

Tab의 선택으로 뷰가 바뀌기도 하지만


Tab에 따라 보여지는 View를 옆으로 드래그해서

넘기는 경우가 많습니다.

즉, 마치 Page를 넘기듯 화면상의 View를 변경하며

변경되는 View에 맞추어 Tab도 자동으로 바뀌도록 되어 있죠.


여러분이 스마트폰으로 인터넷을 보실 때

화면을 옆으로 드래그 하면 해당 View의 색션이 자동으로 바뀌는 것을 자주

보셨을 겁니다.


그래서

이번 포스트는 Tab의 선택으로도 뷰가 변경되고

View의 Page를 넘기듯 하여 Tab을 변경하도록도 하는 예제를 만들어보겠습니다.


이미 ActionBar에 Tab을 추가하는 예제도 소개했으며

View를 Page 넘기듯 변경하는 ViewPager 예제도 소개했었습니다.


이번 예제는 이 둘을 연결하는 예제입니다.

앞선 포스트의 내용을 참고하시면 이해하시는데 도움이 되시리라 봅니다.


먼저 실행화면부터 소개하겠습니다.


탭(Tab) 선택으로 View변경하기

  



View를 페이지 넘기듯 하여 현재 Tab의 선택 변경하기

   

   

   


각 Tab의 선택에 따라 다른 View가 보이기도 하며

View를 드래그하여 Tab이 변경되는 것도 가능합니다.


실행결과를 보시면 어렵지 않게 어떤 어플인지 인지하실겁니다.



자 그럼

위의 동작이 되도록 ViewPager를 이용한 ActionBar Tab을 만들어보겠습니다.


먼저 Tab은 ActionBar에 추가할 것이므로

레이아웃 파일(activity_main.xml)에는 특별히 작업할 내용이 없지만


Tab에 의해 보여지는 View를 만들어내는

ViewPager를 레이아웃 파일안에 추가하겠습니다.


우선 메인 레이아웃파일입니다.

 

 activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    tools:context="${relativePackage}.${activityClass}" >


    <android.support.v4.view.ViewPager

        android:id="@+id/pager"

        android:layout_width="match_parent"

        android:layout_height="match_parent">

        

    </android.support.v4.view.ViewPager>


</RelativeLayout>

보시다 시피 단순히 ViewPager 하나가 추가되어 있습니다.


ViewPager에 대한 설명은 ViewPager(뷰페이저) 포스트를 참고하시기 바랍니다.


이제 각 Tab이 선택했을 때 보여질 View의 레이아웃파일을 만들어보겠습니다.

이 레이아웃 xml파일들은 ViewPager가 차례로 View 객체로 만들게 됩니다.


res폴더 >> layout 폴더안에 

3개의 서로 다른 레이아웃 xml 파일을 만들겠습니다.


첫번째 탭(Tab)에 의해 보여질 View 레이아웃 파일입니다.

파일 이름은 'layout_tab_0.xml' 입니다.

 

 layout_tab_0.xml

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent" >

    

    <AnalogClock 

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_centerInParent="true"/>    


</RelativeLayout>


두번째 탭(Tab)에 의해 보여질 View 레이아웃 파일입니다.

파일 이름은 'layout_tab_1.xml' 입니다.

 

 layout_tab_1.xml

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent" >

    

    <DigitalClock 

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_centerInParent="true"/>    


</RelativeLayout>


세번째 탭(Tab)에 의해 보여질 View 레이아웃 파일입니다.

파일 이름은 'layout_tab_2.xml' 입니다.

 

 layout_tab_2.xml

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:padding="15dp" >

    

    <CalendarView 

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:layout_centerInParent="true"/>   


</RelativeLayout>


각 View 마다 서로 다른 Widget를 넣었기에

구별하는데 어렵지 않으리라 봅니다. 

이미 이전 포스트에서도 만들어봤던 파일들입니다.


 

다음으로

MainActivity 자바파일을 만들기 전에

ViewPager가 View를 옆으로 드래그(페이지를 넘기듯)했을 때

보여질 View들을 만들어 내는 CustomAdapter를 만들겠습니다.

(앞선 ViewPager 포스트를 참고하시기 바랍니다.)


코드의 의미는 주석으로 설명합니다.


 

CustomAdapter.java

public class CustomAdapter extends PagerAdapter {

LayoutInflater inflater;

public CustomAdapter(LayoutInflater inflater) {

// TODO Auto-generated constructor stub

//전달 받은 LayoutInflater를 멤버변수로 전달

this.inflater=inflater;

}


//PagerAdapter가 가지고 잇는 View의 개수를 리턴

//Tab에 따른 View를 보여줘야 하므로 Tab의 개수인 3을 리턴..

@Override

public int getCount() {

// TODO Auto-generated method stub

return 3; //보여줄 View의 개수 리턴(Tab이 3개라서 3을 리턴)

}

//ViewPager가 현재 보여질 Item(View객체)를 생성할 필요가 있는 때 자동으로 호출

//쉽게 말해, 스크롤을 통해 현재 보여져야 하는 View를 만들어냄.

//첫번째 파라미터 : ViewPager

//두번째 파라미터 : ViewPager가 보여줄 View의 위치(가장 처음부터 0,1,2,3...)

@Override

public Object instantiateItem(ViewGroup container, int position) {

// TODO Auto-generated method stub

View view=null;//현재 position에서 보여줘야할 View를 생성해서 리턴...

//새로운 View 객체를 Layoutinflater를 이용해서 생성

//position마다 다른 View를 생성

//만들어질 View의 설계는 res폴더>>layout폴더안에 3개의 레이아웃 파일 사용

switch( position ){

case 0: //첫번째 Tab을 선택했을때 보여질 뷰

view= inflater.inflate(R.layout.layout_tab_0, null);

break;

case 1: //두번째 Tab을 선택했을때 보여질 뷰

view= inflater.inflate(R.layout.layout_tab_1, null);

break;

case 2: //세번째 Tab을 선택했을때 보여질 뷰

view= inflater.inflate(R.layout.layout_tab_2, null);

break;

}

//ViewPager에 위에서 만들어 낸 View 추가

if(view != null) container.addView(view);

//세팅된 View를 리턴

return view;

}

//화면에 보이지 않은 View는파쾨를 해서 메모리를 관리함.

//첫번째 파라미터 : ViewPager

//두번째 파라미터 : 파괴될 View의 인덱스(가장 처음부터 0,1,2,3...)

//세번째 파라미터 : 파괴될 객체(더 이상 보이지 않은 View 객체)

@Override

public void destroyItem(ViewGroup container, int position, Object object) {

// TODO Auto-generated method stub

//ViewPager에서 보이지 않는 View는 제거

//세번째 파라미터가 View 객체 이지만 데이터 타입이 Object여서 형변환 실시

container.removeView((View)object);

}

//instantiateItem() 메소드에서 리턴된 Ojbect가 View가  맞는지 확인하는 메소드

@Override

public boolean isViewFromObject(View v, Object obj) {

// TODO Auto-generated method stub

return v==obj;

}


}



이제 이 CustomAdapter ViewPager에 적용하고

버튼으로 제어하는 작업을 하겠습니다.


메인 액티비티 소스파일입니다.

설명은 주석을 참고하세요.

다시 말하지만 주석을 제외하면 코드가 별로 길지 않습니다.

미리 겁먹지 않으시길 바랍니다.

 

 MainActivity.java

public class MainActivity extends Activity {

ViewPager pager; //ViewPager 참조변수

ActionBar actionBar;  //ActionBar 참조변수


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//Activity 객체는 이미 ActionBar를 가지고 있으므로

//이미 존해하는 ActionBar 객체를 얻어오기.(API 10버전 이상)

actionBar= getActionBar();

//ActionBar가 Tab를 보여줄 수 있는 모양이 되도록 설정

actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

//ViewPager 객체 참조

pager= (ViewPager)findViewById(R.id.pager);

//ViewPager에 설정할 Adapter 객체 생성

//ListView에서 사용하는 Adapter와 같은 역할.

//다만. ViewPager로 스크롤 될 수 있도록 되어 있다는 것이 다름

//PagerAdapter를 상속받은 CustomAdapter 객체 생성

//CustomAdapter에게 LayoutInflater 객체 전달

CustomAdapter adapter= new CustomAdapter(getLayoutInflater());

//ViewPager에 Adapter 설정

pager.setAdapter(adapter);

//ViewPager에게 Page의 변경을 인지하는 Listener 세팅.

//마치 클릭상황을 인지하는 OnClickListener와 같은 역할..

pager.setOnPageChangeListener(new OnPageChangeListener() {

//Page가 일정부분 넘겨져서 현재 Page가 바뀌었을 때 호출

//이전 Page의 80%가 이동했을때 다음 Page가 현재 Position으로 설정됨.

//파라미터 : 현재 변경된 Page의 위치

@Override

public void onPageSelected(int position) {

// TODO Auto-generated method stub

//ViewPager는 3개의 View를 가지고 있도록 설계하였으므로.

//Position도 역시 가장 왼쪽 처음부터(0,1,2 순으로 되어있음)

//현재 전면에 놓여지는 ViewPager의 Page에 해당하는 Position으로

//ActionBar의 Tab위치를 변경.

actionBar.setSelectedNavigationItem(position);

}

@Override

public void onPageScrolled(int arg0, float arg1, int arg2) {

// TODO Auto-generated method stub

}

@Override

public void onPageScrollStateChanged(int arg0) {

// TODO Auto-generated method stub

}

});

//ActionBar에 추가 될 Tab 참조변수

Tab tab=null;

//첫번째 Tab 객체 생성 및 ActionBar에 추가하기

tab= actionBar.newTab(); //ActionBar에 붇는 Tab객체 생성

tab.setText("Analog");    //Tab에 보여지는 글씨

//Tab의 선택이 변경되는 것을 인지하는 TabListener 설정(아래쪽 객체 생성 코드 참고)

tab.setTabListener(listener); 

//ActionBar에 Tab 추가

actionBar.addTab(tab);

//두번째 Tab 객체 생성 및 ActionBar에 추가하기

tab= actionBar.newTab(); //ActionBar에 붇는 Tab객체 생성

tab.setText("Digital");     //Tab에 보여지는 글씨

//Tab의 선택이 변경되는 것을 인지하는 TabListener 설정(아래쪽 객체 생성 코드 참고)

tab.setTabListener(listener);

//ActionBar에 Tab 추가

actionBar.addTab(tab);

//세번째 Tab 객체 생성 및 ActionBar에 추가하기

tab= actionBar.newTab(); //ActionBar에 붇는 Tab객체 생성

tab.setText("Calendar");   //Tab에 보여지는 글씨

//Tab의 선택이 변경되는 것을 인지하는 TabListener 설정(아래쪽 객체 생성 코드 참고)

tab.setTabListener(listener); 

//ActionBar에 Tab 추가

actionBar.addTab(tab);

}//onCreate Method...

//ActionBar의 Tab 선택에 변화가 생기는 것을 인지하는 리스너(Listener)

TabListener listener= new TabListener() {

//Tab의 선택이 벗어날 때 호출

//첫번째 파라미터 : 선택에서 벗어나는 Tab 객체

//두번째 파라미터 : Tab에 해당하는 View를 Fragment로 만들때 사용하는 트랜젝션.(여기서는 사용X)

@Override

public void onTabUnselected(Tab tab, FragmentTransaction ft) {

// TODO Auto-generated method stub

}

//Tab이 선택될 때 호출

//첫번째 파라미터 : 선택된 Tab 객체

//두번째 파라미터 : Tab에 해당하는 View를 Fragment로 만들때 사용하는 트랜젝션.(여기서는 사용X)

@Override

public void onTabSelected(Tab tab, FragmentTransaction ft) {

// TODO Auto-generated method stub

//선택된 Tab객체의 위치값(왼족 처음부터 0,1,2....순으로 됨)

int position = tab.getPosition();

//Tab의 선택 위치에 따라 ViewPager에서 보여질 Item(View)를 설정

//첫번째 파라미터: ViewPager가 현재 보여줄 View의 위치

//두번째 파라미터: 변경할 때 부드럽게 이동하는가? false면 팍팍 바뀜

pager.setCurrentItem(position,true);

}

//Tab이 재 선택될 때 호출

//첫번째 파라미터 : 재 선택된 Tab 객체

//두번째 파라미터 : Tab에 해당하는 View를 Fragment로 만들때 사용하는 트랜젝션.(여기서는 사용X)

@Override

public void onTabReselected(Tab tab, FragmentTransaction ft) {

// TODO Auto-generated method stub

}

};

}


여기까지 ViewPager를 이용한 ActionBarTab을 구현해보았습니다.


사실 이번 예제소스는 단순히 Tab에 의해 다른 레이아웃의 View가 보여지기만 할 뿐

각 레이아웃의 View들을 제어하고 있지는 않습니다.


하지만 실제 어플에서는 이 View들을 제어하는 경우가 많습니다.

이번에 소개한 예제는 그 작업을 하기에는 별로 효율적이지 않은 예제 소스입니다.


다음번 포스트에서 이를 해결하기 위해

Fragment를 사용한 ViewPager ActionBar Tab을 만들어보겠습니다.


반응형
반응형

카카오톡이나 시스템 어플처럼

안드로이드 폰을 부팅하면 자동으로 실행되는

앱들이 있을 겁니다.


즉, 별도의 앱목록에서 실행 선택없이

스마트폰이 시작되면 자동으로 여러분의 앱이 실행되도록 하는 거죠.


보통 채팅 프로그램에서 필요로 하는 기능이죠.

악용한다면 ....(-_-;;)



결과는 보여줄 것을 없네요.


그냥 이 앱을 설치하고

폰의 전원을 재 부팅하면

자동으로 앱이 실행되는 예제소스입니다.


다른 부분은 건드릴 것 없고요.

시스템의 Boot가 완료되었다는 신호(방송: Broadcast)

수신할 수 있는 BroadcastReceiver만 만들고

이를 메니페스트에 등록만 하면 완성입니다.


MainActivity는 건드릴 필요없이


BroadcastReceiver 클래스만 소스파일에 추가로 만들겠습니다.


본인 프로젝트의 src폴더>>패키지명

새로 Class파일을 만듭니다.

상속은 BroadcastReceiver를 받습니다.


완성된 Receiver 클래스 파일입니다.

설명은 주석 참조

 

 StartReceiver.java

public class StartReceiver extends BroadcastReceiver {


//BroadCast를 받앗을때 자동으로 호출되는 콜백 메소드

//첫번째 파라미터 : Context 컨텍스트

//두번째 파라미터 : BroadCast의 정보를 가지고 있는 Intent..

@Override

public void onReceive(Context context, Intent intent) {

// TODO Auto-generated method stub

//수신받은 방송(Broadcast)의 Action을 얻어오기

//메니페스트 파일안에 이 Receiver에게 적용된 필터(Filter)의 Action만 받아오게 되어 있음.

String action= intent.getAction();

//수신된 action값이 시스템의 '부팅 완료'가 맞는지 확인..

if( action.equals("android.intent.action.BOOT_COMPLETED") ){

//맞다면...MainActivity 실행을 위한 Intent 생성..

Intent i= new Intent(context, MainActivity.class);

//위에 만들어진 Intent에 의해 실행되는 Activity는 

//액티비티 스택에서 새로운 Task로 Activity를 실행하도록 하라는 설정.

i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

//Intent에 설정된 Component(여기서는 MainActivity)를 실행

context.startActivity(i);

}

}

}



 BroadcastReceiver 는

안드로이드의 주요 4대 Component 중에 하나죠.


이 Component 들은 반드시 메니페스트 파일에 등록을 해주어야

사용할 수 있다는 것은 다 알고 있을 거라 생각합니다.


위에 만든 StartReceiver를 메니페스트에 등록하고

수신 퍼미션도 받겠습니다.


메니페스트에서 추가한 부분은 굵은 글씨로 표기합니다.


 AndroidManifest.xml

 <?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android"

    package="com.kitesoft.boottingapp"

    android:versionCode="1"

    android:versionName="1.0" >


    <uses-sdk

        android:minSdkVersion="14"

        android:targetSdkVersion="16" />

    

    <!-- Boot 될때 Booting 완료 방송(브로드캐스트)을 듣도록 퍼미션 획득 -->    

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


    <application

        android:allowBackup="true"

        android:icon="@drawable/ic_launcher"

        android:label="@string/app_name"

        android:theme="@style/AppTheme" >

        <activity

            android:name=".MainActivity"

            android:label="@string/app_name" >

            <intent-filter>

                <action android:name="android.intent.action.MAIN" />


                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>

        </activity>

        

        <receiver

            android:name=".StartReceiver"

            android:enabled="true"

            android:exported="false"

            android:label="STARTReceiver">

            

            <intent-filter >

                <action android:name="android.intent.action.BOOT_COMPLETED"/>                

            </intent-filter>

            

        </receiver>

        

    </application>


</manifest>

 

이제 시스템은 재 부팅하면 'Boot 완료' 방송(Broadcast)를 수신해서 App의 액티비티가 실행됩니다.


실제 채팅프로그램은 액티비티가 아니라 Service를 실행하겠죠.

반응형
반응형

액션바(ActionBar) 탭(Tab)를 붙여서 사용하는 앱들이 많더군요.


그래서 이번 포스트는 

ActionBar에 Tab를 구현하는 예제소스입니다.


ActionBar에 Tab을 구현할때는 가급적

Fragment를 사용하시는 것이 코드관리 및 리소스관리에

용이합니다.


그런데 Fragment까지 한꺼번에 소개하면

다소 분량이 많아서

단계적으로

간단하게 ActionBar에 Tab을 구현하고

하나씩 기능을 추가하면서 예제를 소개하겠습니다.


먼저 Tab이 뭔지 모를 수도 있고

이 예제의 동작 결과도 볼겸.


미리 결과화면 부터 소개합니다.

  

각 Tab의 선택에 따라 다른 View가 보이도록 했습니다.


결과만 봐도 Tab이 뭔지 알수 있을 거라 봅니다.


이 에제는 ActionBar에 Tab을 구현하는데 초점이 있어서

Tab마다 보여지는 View

MainActivity의 setContentView()메소드를 사용했습니다.


Activity 클래스의 setContentView()는 언제나

onCreate()에서만 사용한다고 아시는 분도 있던데.


클래스 안에 어떤 순간에도 메소드 호출이 가능합니다.

항상 setContentView(R.layout.main) 으로만 사용할 수 있는 건 아닙니다.

이 메소드의 의미에 초점을 맞추세요.


즉, 각 Tab마다 보여지는 View의 모양을

별도의 layout 파일로 만들거라서

layout_main.xml은 이 예제에서 별로 중요하지 않습니다.



그럼


각 Tab을 선택했을 때 보여지는 View의 모양을 설계하는

레이아웃 리소스 파일을 만들겠습니다.


res폴더 >> layout 폴더안에 

3개의 서로 다른 레이아웃 xml 파일을 만들겠습니다.


첫번째 탭(Tab)에 의해 보여질 View 레이아웃 파일입니다.

파일 이름은 'layout_tab_0.xml' 입니다.

 

 layout_tab_0.xml

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent" >

    

    <AnalogClock 

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_centerInParent="true"/>    


</RelativeLayout>


두번째 탭(Tab)에 의해 보여질 View 레이아웃 파일입니다.

파일 이름은 'layout_tab_1.xml' 입니다.

 

 layout_tab_1.xml

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent" >

    

    <DigitalClock 

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_centerInParent="true"/>    


</RelativeLayout>


세번째 탭(Tab)에 의해 보여질 View 레이아웃 파일입니다.

파일 이름은 'layout_tab_2.xml' 입니다.

 

 layout_tab_2.xml

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:padding="15dp" >

    

    <CalendarView 

        android:layout_width="match_parent"

        android:layout_height="match_parent"

        android:layout_centerInParent="true"/>   


</RelativeLayout>


각 View 마다 서로 다른 Widget를 넣었기에

구별하는데 어렵지 않으리라 봅니다.



이제 가장 중요한 MainActivity.java 소스파일입니다.

주요 설명은 주석을 참고하시기 바랍니다.

주석을 빼면 코드는 매우 간단합니다.


반복문을 사용하면 보다 코드를 간략하게 만들 수 있지만

보시기 편하도록 일일히 Tab 3개를 추가했습니다.


 

 MainActivity.java

public class MainActivity extends Activity {

ActionBar actionBar;  //ActionBar 참조변수


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//Activity 객체는 이미 ActionBar를 가지고 있으므로

//이미 존해하는 ActionBar 객체를 얻어오기.(API 10버전 이상에서 사용가능)

actionBar= getActionBar();

//ActionBar가 Tab를 보여줄 수 있는 모양이 되도록 설정

actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

//ActionBar에 추가 될 Tab 참조변수

Tab tab=null;

//첫번째 Tab 객체 생성 및 ActionBar에 추가하기

tab= actionBar.newTab(); //ActionBar에 붇는 Tab객체 생성

tab.setText("Analog");    //Tab에 보여지는 글씨

//Tab의 선택이 변경되는 것을 인지하는 TabListener 설정(아래쪽 객체 생성 코드 참고)

tab.setTabListener(listener); 

//ActionBar에 Tab 추가

actionBar.addTab(tab);

//두번째 Tab 객체 생성 및 ActionBar에 추가하기

tab= actionBar.newTab();//ActionBar에 붇는 Tab객체 생성

tab.setText("Digital");   //Tab에 보여지는 글씨

//Tab의 선택이 변경되는 것을 인지하는 TabListener 설정(아래쪽 객체 생성 코드 참고)

tab.setTabListener(listener);

//ActionBar에 Tab 추가

actionBar.addTab(tab);

//세번째 Tab 객체 생성 및 ActionBar에 추가하기

tab= actionBar.newTab(); //ActionBar에 붇는 Tab객체 생성

tab.setText("Calendar");   //Tab에 보여지는 글씨

//Tab의 선택이 변경되는 것을 인지하는 TabListener 설정(아래쪽 객체 생성 코드 참고)

tab.setTabListener(listener); 

//ActionBar에 Tab 추가

actionBar.addTab(tab);

}

//Tab의 선택변화를 인지하는 Listener 객체 생성

//(Button의 onClickListner 처럼 생각하시면 됩니다.)

TabListener listener= new TabListener() {

//Tab의 선택이 벗어날 때 호출

//첫번째 파라미터 : 선택에서 벗어나는 Tab 객체

//두번째 파라미터 : Tab에 해당하는 View를 Fragment로 만들때 사용하는 트랜젝션.(여기서는 사용X)

@Override

public void onTabUnselected(Tab tab, FragmentTransaction ft) {

// TODO Auto-generated method stub

}

//Tab이 선택될 때 호출

//첫번째 파라미터 : 선택된 Tab 객체

//두번째 파라미터 : Tab에 해당하는 View를 Fragment로 만들때 사용하는 트랜젝션.(여기서는 사용X)

@Override

public void onTabSelected(Tab tab, FragmentTransaction ft) {

// TODO Auto-generated method stub

//선택된 Tab객체의 위치값(왼족 처음부터 0,1,2....순으로 됨)

int position = tab.getPosition();

switch( position ){

case 0: //가장 왼쪽 Tab 선택(Analog)

//MainActivity가 보여 줄 View를

//res폴더>>layout폴더>>layout_tab_0.xml 로 설정

setContentView(R.layout.layout_tab_0);

break;

case 1: //두번째 Tab 선택(Digital)

//MainActivity가 보여 줄 View를

//res폴더>>layout폴더>>layout_tab_1.xml 로 설정

setContentView(R.layout.layout_tab_1);

break;

case 2: //세번째 Tab 선택(Calendar)

//MainActivity가 보여 줄 View를

//res폴더>>layout폴더>>layout_tab_2.xml 로 설정

setContentView(R.layout.layout_tab_2);

break;

}

}

//Tab이 재 선택될 때 호출

//첫번째 파라미터 : 재 선택된 Tab 객체

//두번째 파라미터 : Tab에 해당하는 View를 Fragment로 만들때 사용하는 트랜젝션.(여기서는 사용X)

@Override

public void onTabReselected(Tab tab, FragmentTransaction ft) {

// TODO Auto-generated method stub

}

};

}


여기까지 간단하게 ActionBar에 Tab을 추가했습니다.

다음 포스트에서 Tab의 각 View들의 전환에 역동성을 주기위해

각 View들을 ViewPager를 이용해서 만들어 볼겁니다.


감사합니다.

반응형
반응형

ViewPager

사용자가 마치 Page를 넘기는 듯한 효과를 보여주는 앱입니다.

다만
그냥 옆으로 스르륵 미끄러지는 것은 다소 밋밋합니다.

그래서 ViewPager의 Page를 넘길때 마다 약간의 애니메이션
적용하여 보다 입체적으로 보이도록 해보겠습니다.

아래 결과를 보시면 이전 ViewPager와의 차이를 극명하게 보실 수 있을 겁니다.

좌우로 Page를 넘기는 모습입니다.

  


  


  


좀 더 넘어가는 모습이 입체적이죠?
넘어가거나 들어오는 page View에
크기(scale), 투명도(alpha), 회전(rotate)를 주어서 효과를 만들었습니다.

방법은 간단한데
각 애니메이션의 값을 이해하는 것은 다소 수학적(?)인
계산이 요구됩니다.

일단 방법은 ViewPager에 setPageTransformer()메소드를 설정해서
Page의 변화순간순간 애니메이션값을 적용하여 변화를 주는 방식입니다.

다른 부분은 이전 포스트( ViewPager ) 를 참고하시고
추가 된 부분만 굵은 글씨로 표시했습니다.

메인 액티비티 소스코드만 변경하면 됩니다.

 MainActivity.java

public class MainActivity extends Activity {

ViewPager pager;


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

pager= (ViewPager)findViewById(R.id.pager);

//ViewPager에 설정할 Adapter 객체 생성

//ListView에서 사용하는 Adapter와 같은 역할.

//다만. ViewPager로 스크롤 될 수 있도록 되어 있다는 것이 다름

//PagerAdapter를 상속받은 CustomAdapter 객체 생성

//CustomAdapter에게 LayoutInflater 객체 전달

CustomAdapter adapter= new CustomAdapter(getLayoutInflater());

//ViewPager에 Adapter 설정

pager.setAdapter(adapter);

//Pager가 Page를 변경할 때 특정 작업을 수행하고 싶을 때 PageTransformer 설정

//여기서는 ViewPager가 Page를 변경할 때 애니메이션이 되도록 설정

pager.setPageTransformer(false, new PageTransformer() {

//현재 Page의 위치가 조금이라도 바뀔때마다 호출되는 메소드

//첫번째 파라미터 : 현재 존재하는 View 객체들 중에서 위치가 변경되고 있는 View들

//두번째 파라미터 : 각 View 들의 상대적 위치( 0.0 ~ 1.0 : 화면 하나의 백분율)

//           1.현재 보여지는 Page의 위치가 0.0

//           Page가 왼쪽으로 이동하면 값이 -됨. (완전 왼쪽으로 빠지면 -1.0)

//           Page가 오른쪽으로 이동하면 값이 +됨. (완전 오른쪽으로 빠지면 1.0)

//주의할 것은 현재 Page가 이동하면 동시에 옆에 있는 Page(View)도 이동함.

//첫번째와 마지막 Page 일때는 총 2개의 View가 메모리에 만들어져 잇음.

//나머지 Page가 보여질 때는 앞뒤로 2개의 View가 메모리에 만들어져 총 3개의 View가 instance 되어 있음.

//ViewPager 한번에 1장의 Page를 보여준다면 최대 View는 3개까지만 만들어지며

//나머지는 메모리에서 삭제됨.-리소스관리 차원.

@Override

public void transformPage(View page, float position) {

// TODO Auto-generated method stub

//position 값이 왼쪽, 오른쪽 이동방향에 따라 음수와 양수가 나오므로 절대값 Math.abs()으로 계산

//position의 변동폭이 (-2.0 ~ +2.0) 사이이기에 부호 상관없이 (0.0~1.0)으로 변경폭 조절

//주석으로 수학적 연산을 설명하기에는 한계가 있으니 코드를 보고 잘 생각해 보시기 바랍니다.

float normalizedposition = Math.abs( 1 - Math.abs(position) );

page.setAlpha(normalizedposition);  //View의 투명도 조절

page.setScaleX(normalizedposition/2 + 0.5f); //View의 x축 크기조절

page.setScaleY(normalizedposition/2 + 0.5f); //View의 y축 크기조절

page.setRotationY(position * 80);   //View의 Y축(세로축) 회전 각도  

}

});

}

//onClick속성이 지정된 View를 클릭했을때 자동으로 호출되는 메소드

public void mOnClick(View v){

int position;

switch( v.getId() ){

case R.id.btn_previous://이전버튼 클릭

position=pager.getCurrentItem();//현재 보여지는 아이템의 위치를 리턴

//현재 위치(position)에서 -1 을 해서 이전 position으로 변경

//이전 Item으로 현재의 아이템 변경 설정(가장 처음이면 더이상 이동하지 않음)

//첫번째 파라미터: 설정할 현재 위치

//두번째 파라미터: 변경할 때 부드럽게 이동하는가? false면 팍팍 바뀜

pager.setCurrentItem(position-1,true);

break;

case R.id.btn_next://다음버튼 클릭

position=pager.getCurrentItem();//현재 보여지는 아이템의 위치를 리턴

//현재 위치(position)에서 +1 을 해서 다음 position으로 변경

//다음 Item으로 현재의 아이템 변경 설정(가장 마지막이면 더이상 이동하지 않음)

//첫번째 파라미터: 설정할 현재 위치

//두번째 파라미터: 변경할 때 부드럽게 이동하는가? false면 팍팍 바뀜

pager.setCurrentItem(position+1,true);

break;

}

}

}


다소 수학적 연산이 요구되지만
잘 생각해보시면 이해가 가능하실 겁니다.

적용값을 조절해 보시면 여러분만의 애니메이션 모양을
만들 수 있습니다.


반응형
반응형

이전 포스트에서 소개한

ViewFlipper

대량의 이미지나 텍스트를

하나씩 변경하면서 소개하는

'슬라이드 보기' 같은

기능에 적합합니다.


이번에 소개할 예제소스는

'만화보기'처럼

Page를 넘겨가면서

보는데 적합한 예제입니다.


마치 Page를 넘기듯이

손가락으로 드래그해서

좌우로 넘기는 UI를 보신적이 있을 겁니다.


안드로이드의 기본 api에는 제공되지 않지만

Support Library에 포함되어 있는 녀석입니다.


ViewPager 라는 이름을 가지고 있으며

이름으로도 연상이 되듯이 View들을 Page를 넘기듯

보여주는 AdapterView의 일종입니다.


AdapterView의 대표적인 종류인

ListView와 매우 비슷하게 제작됩니다.


ListView의 CustomAdapter를 구현할 수 있다면

어렵지 않게 이해하실 수 있습니다.


혹시 잘 모르시는 분은


[안드로이드 Android] 리스트 뷰(ListView) 6. Custom ListView(사용자 정의 리스트 뷰) - BaseAdapter


포스트를 참고하세요.



이번에 소개할 ViewPager예제는

많은 양의 이미지를 한장씩 Page를 넘기면서

볼 수있는 예제입니다.


이미지는 10장만 쓰겠습니다.


손가락으로 넘길 수도 있으며

위의 버튼을 이용해서 앞뒤장을 넘어갈 수도 있게 하겠습니다.


먼저 결과화면을 그림 여러장으로 보여드리겠습니다.


시작화면-첫번째 페이지



두번째 Page로 드래그해서 넘기기

  


세번째 Page로 드래그해서 넘기기

  


Next버튼으로 넘기기

  


Previous 버튼으로 앞장으로 넘기기

 


사진으로도 충분히 이해가 가능하리라 판단되어

gif 파일로 보여드리지는 않습니다.


자. 이제 결과화면을 봤으니

만들어 보겠습니다.


먼저 그림에 사용할 이미지 10장은

첨부파일에서 다운받으시기 바랍니다.



게임타이틀 이미지.zip



가장 먼저 Main 레이아웃 파일입니다.

ListView와 매우 흡사합니다.

가장 주의할 것은 ViewPager를 기본 api가 아니므로

android.support.v4.view  패키지명을 앞에 표기해야 한다는 겁니다.

자동으로 작성되지 않으므로 철자에 주의해서 작성 하셔야 합니다.

 

 activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical">

    

    <LinearLayout 

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:orientation="horizontal"

        android:padding="3dp">

        

        <Button android:id="@+id/btn_previous"

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text="PREVIOUS"

            android:textSize="12sp"

            android:onClick="mOnClick"/>

        

        <Button android:id="@+id/btn_next"

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text="Next"

            android:textSize="12sp"

            android:onClick="mOnClick"/>

        

    </LinearLayout>

    

    <android.support.v4.view.ViewPager

        android:id="@+id/pager"

        android:layout_width="match_parent"

        android:layout_height="match_parent">

        

    </android.support.v4.view.ViewPager>    


</LinearLayout>



굵은 글씨의 ViewPager는 AdapterView 입니다.


Adapter란,

대량의 Data(여기서는 Image들)들

보여주기 위해 View를 여러개 만들어서 ViewGroup에 추가해줘야 하는데

이 작업을 Data의 순서에 맞게 적절한 View로 만들어 주는 클래스 객체를 말합니다.

(Custom ListView 참조)


이 ViewPager에 붙일 Adapter는

위 설명처럼 Image를 보여주기 위한 ImageView를 만들어 내는 기능이 있어야 합니다.


그래서 우선 만들어낼 ImageView의 모양을 설정하는 Layout 파일부터 만들겠습니다.


 

 viewpager_childview.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical" >

    

    <ImageView 

        android:id="@+id/img_viewpager_childimage"

        android:layout_width="match_parent"

        android:layout_height="match_parent"/>    


</LinearLayout>


ViewPager가 만들어낼 한 Page의 모양이 이렇게 생길 것이다 라는 것이죠.

이 예제에서는 ImageView 하나를 화면에 꽉 차게 만들겠다는 거죠.


이제 이 레이아웃 파일을 실제

View 객체로 만들어서 해당하는 순번에 따라 적절한 Image를 보여주는

Adapter를 만들어 보겠습니다.


이전에 소개한 Custom ListView의 예제와 같은 작업인데

그때는 BaseAdapter를 상속받아 만들었습니다.


이번 Adapter는 PagerAdapter를 상속받아 만든다는 것이 다르며

안에 오버라이드해서 만들 메소드도 약간 차이가 있습니다.


세부 설명은 주석으로 표기했습니다.

지면상 간략한 설명이어서 이해하기 어려울 수도 있으니

코드를 보고 해석할 수 있는 부분까지 해석해서

활용하시고 좀더 깊은 이해는 다른 자료들을 서베이 하시기 바랍니다.


PagerAdapter를 상속받은 CustomAdapter 클래스를 설계합니다.


만드는 과정은 그림으로 보여드립니다.


  


이렇게 만드시면 자동으로 2개의 메소드가 오버라이드(override) 되어 있을 겁니다.

getCount(), isViewFromObject()


추가적으로 필요한 2개의 메소드를 오버라이드 합니다.

instantiateItem(), destroyItem()

파라미터 중 첫번째가 ViewGroup인것을 오버라이드합니다.

View인 것은 deprecated 되었습니다.


커스텀 아답터(Custom Adapter) 소스 파일입니다.

 

 CustomAdapter.java

public class CustomAdapter extends PagerAdapter {

LayoutInflater inflater;

public CustomAdapter(LayoutInflater inflater) {

// TODO Auto-generated constructor stub

//전달 받은 LayoutInflater를 멤버변수로 전달

this.inflater=inflater;

}


//PagerAdapter가 가지고 잇는 View의 개수를 리턴

//보통 보여줘야하는 이미지 배열 데이터의 길이를 리턴

@Override

public int getCount() {

// TODO Auto-generated method stub

return 10; //이미지 개수 리턴(그림이 10개라서 10을 리턴)

}

//ViewPager가 현재 보여질 Item(View객체)를 생성할 필요가 있는 때 자동으로 호출

//쉽게 말해, 스크롤을 통해 현재 보여져야 하는 View를 만들어냄.

//첫번째 파라미터 : ViewPager

//두번째 파라미터 : ViewPager가 보여줄 View의 위치(가장 처음부터 0,1,2,3...)

@Override

public Object instantiateItem(ViewGroup container, int position) {

// TODO Auto-generated method stub

View view=null;

//새로운 View 객체를 Layoutinflater를 이용해서 생성

//만들어질 View의 설계는 res폴더>>layout폴더>>viewpater_childview.xml 레이아웃 파일 사용

view= inflater.inflate(R.layout.viewpager_childview, null);

//만들어진 View안에 있는 ImageView 객체 참조

//위에서 inflated 되어 만들어진 view로부터 findViewById()를 해야 하는 것에 주의.

ImageView img= (ImageView)view.findViewById(R.id.img_viewpager_childimage);

//ImageView에 현재 position 번째에 해당하는 이미지를 보여주기 위한 작업

//현재 position에 해당하는 이미지를 setting

img.setImageResource(R.drawable.gametitle_01+position);

//ViewPager에 만들어 낸 View 추가

container.addView(view);

//Image가 세팅된 View를 리턴

return view;

}

//화면에 보이지 않은 View는파쾨를 해서 메모리를 관리함.

//첫번째 파라미터 : ViewPager

//두번째 파라미터 : 파괴될 View의 인덱스(가장 처음부터 0,1,2,3...)

//세번째 파라미터 : 파괴될 객체(더 이상 보이지 않은 View 객체)

@Override

public void destroyItem(ViewGroup container, int position, Object object) {

// TODO Auto-generated method stub

//ViewPager에서 보이지 않는 View는 제거

//세번째 파라미터가 View 객체 이지만 데이터 타입이 Object여서 형변환 실시

container.removeView((View)object);

}

//instantiateItem() 메소드에서 리턴된 Ojbect가 View가  맞는지 확인하는 메소드

@Override

public boolean isViewFromObject(View v, Object obj) {

// TODO Auto-generated method stub

return v==obj;

}


}


ListView의 CustomAdapter와 방법은 다소 다르지만

해야하는 작업은 같습니다.



이제 이 CustomAdapter를 ViewPager에 적용하고

버튼으로 제어하는 작업을 하겠습니다.


메인 액티비티 소스파일입니다.

설명은 주석을 참고하세요.


 

 MainActivity.java

public class MainActivity extends Activity {

ViewPager pager;


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

pager= (ViewPager)findViewById(R.id.pager);

//ViewPager에 설정할 Adapter 객체 생성

//ListView에서 사용하는 Adapter와 같은 역할.

//다만. ViewPager로 스크롤 될 수 있도록 되어 있다는 것이 다름

//PagerAdapter를 상속받은 CustomAdapter 객체 생성

//CustomAdapter에게 LayoutInflater 객체 전달

CustomAdapter adapter= new CustomAdapter(getLayoutInflater());

//ViewPager에 Adapter 설정

pager.setAdapter(adapter);

}

//onClick속성이 지정된 View를 클릭했을때 자동으로 호출되는 메소드

public void mOnClick(View v){

int position;

switch( v.getId() ){

case R.id.btn_previous://이전버튼 클릭

position=pager.getCurrentItem();//현재 보여지는 아이템의 위치를 리턴

//현재 위치(position)에서 -1 을 해서 이전 position으로 변경

//이전 Item으로 현재의 아이템 변경 설정(가장 처음이면 더이상 이동하지 않음)

//첫번째 파라미터: 설정할 현재 위치

//두번째 파라미터: 변경할 때 부드럽게 이동하는가? false면 팍팍 바뀜

pager.setCurrentItem(position-1,true);

break;

case R.id.btn_next://다음버튼 클릭

position=pager.getCurrentItem();//현재 보여지는 아이템의 위치를 리턴

//현재 위치(position)에서 +1 을 해서 다음 position으로 변경

//다음 Item으로 현재의 아이템 변경 설정(가장 마지막이면 더이상 이동하지 않음)

//첫번째 파라미터: 설정할 현재 위치

//두번째 파라미터: 변경할 때 부드럽게 이동하는가? false면 팍팍 바뀜

pager.setCurrentItem(position+1,true);

break;

}

}

}


코드는 엄처 간단하죠.


실제로 매우 사용성이 높은 View이니 만큼 여러분이

제작하고자 하는 앱에 적절히 활용할 수 있기를 바랍니다.

반응형
반응형

여러장의 이미지나 텍스트등을

한장씩 넘겨가면서 보고싶을 때

유용한 ViewFilpper 예제입니다.


View Animator의 일종으로서

FrameLayout을 상속받아 만들어졌습니다.


컴퓨터에서

여러장의 이미지를 하나씩 볼때 사용하는

뷰어를 만든다고 보시면 됩니다.


다음 장으로 넘어가는 'Next' 버튼,

이전 장으로 넘어가는 'Previous' 버튼,


자동으로 일정 간격으로 View가 변경되는 'Auto' 버튼이 있습니다.

'슬라이드 보기' 같은 기능이죠.

참고로 이 'Auto'버튼은 on, off가 되도록 ToggleButton

사용했습니다.


우선 결과 화면입니다.


먼저 버튼을 눌러서 이미지를 변경하는 모습입니다. 



자동으로 이미지가 변경되는 모습입니다.




동작에 대한 이해는 어렵지 않겠죠.


이제 만들어 보겠습니다.


사용된 이미지는 첨부파일을 다운받으시거나

다른 이미지를 사용하셔도 좋습니다.



게임타이틀 이미지.zip



총 10장의 이미지인데

xml 레이아웃파일에 10개의 ImageView를 만들자니

너무 길어서..

우선 3개반 xml에 만들고

나머지 7개는 Java에서 반복문을 이용해서

ViewFlipper에 추가(addView) 했습니다.


레이아웃 파일입니다.

 

 activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical"

    android:padding="5dp">

    

    <LinearLayout 

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:orientation="horizontal"

        android:weightSum="3">

        

        <Button android:id="@+id/btn_previous"

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text="PREVIOUS"

            android:textSize="14sp"

            android:onClick="mOnClick"/>

        

        <ToggleButton android:id="@+id/toggle_auto"

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:textOff="AUTO"

            android:textOn="STOP"

             android:textSize="14sp"/>

        

        <Button android:id="@+id/btn_next"

            android:layout_width="0dp"

            android:layout_height="wrap_content"

            android:layout_weight="1"

            android:text="NEXT"

             android:textSize="14sp"

            android:onClick="mOnClick"/>

        

    </LinearLayout>

    

    <ViewFlipper 

        android:id="@+id/flipper"

        android:layout_width="match_parent"

        android:layout_height="match_parent">

        

        <ImageView 

            android:id="@+id/img01"

            android:layout_width="match_parent"

            android:layout_height="match_parent"

            android:src="@drawable/gametitle_01"/>

        

        <ImageView 

            android:id="@+id/img02"

            android:layout_width="match_parent"

            android:layout_height="match_parent"

            android:src="@drawable/gametitle_02"/>

        

        <ImageView 

            android:id="@+id/img03"

            android:layout_width="match_parent"

            android:layout_height="match_parent"

            android:src="@drawable/gametitle_03"/> 

        

    </ViewFlipper>


</LinearLayout>


ViewFipper 안에 3개의 ImageView가 보이시죠?


이제 ViewFlipper를 제어하는 자바 소스파일입니다.

설명은 주석을 참고하세요.

 

 MainActivity.java

public class MainActivity extends Activity {

ViewFlipper flipper;

//자동 Flipping 선택 ToggleButton 참조변수

ToggleButton toggle_Flipping;


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//ViewFlipper 객체 참조

flipper= (ViewFlipper)findViewById(R.id.flipper);

//총 10개의 이미지 중 3개만 XML에서 ImageView로 만들었었음.

//소스코드에서 ViewFipper에게 나머지 7개의 이미지를 추가하기위해

//ImageView 7개를 만들어서 ViewFlipper에게 추가함

//layout_width와 layout_height에 대한 특별한 지정이 없다면

//기본적으로 'match_parent'로 설정됨. 

for(int i=0;i<7;i++){

ImageView img= new ImageView(this);

img.setImageResource(R.drawable.gametitle_04+i);

flipper.addView(img);

}

//ViewFlipper가 View를 교체할 때 애니메이션이 적용되도록 설정

//애니메이션은 안드로이드 시스템이 보유하고 있는  animation 리소스 파일 사용.

//ViewFlipper의 View가 교체될 때 새로 보여지는 View의 등장 애니메이션

//AnimationUtils 클래스 : 트윈(Tween) Animation 리소스 파일을 Animation 객체로 만들어 주는 클래스

//AnimationUtils.loadAnimaion() - 트윈(Tween) Animation 리소스 파일을 Animation 객체로 만들어 주는 메소드

//첫번째 파라미터 : Context

//두번재 파라미터 : 트윈(Tween) Animation 리소스 파일(여기서는 안드로이드 시스템의 리소스 파일을 사용

             //                    (왼쪽에서 슬라이딩되며 등장)

Animation showIn= AnimationUtils.loadAnimation(this, android.R.anim.slide_in_left);

//ViewFlipper에게 등장 애니메이션 적용

flipper.setInAnimation(showIn);

//ViewFlipper의 View가 교체될 때 퇴장하는 View의 애니메이션

//오른쪽으로 슬라이딩 되면 퇴장하는 애니메이션 리소스 파일 적용.

//위와 다른 방법으로 애니메이션을 적용해봅니다.

//첫번째 파라미터 : Context

//두번재 파라미터 : 트윈(Tween) Animation 리소스 파일(오른쪽으로 슬라이딩되며 퇴장)

flipper.setOutAnimation(this, android.R.anim.slide_out_right);

//자동 Flipping 선택 ToggleButton에 따른 작업

toggle_Flipping= (ToggleButton)findViewById(R.id.toggle_auto);

//ToggleButton에 Toggle상태 변경 리스너 세팅(OnCheckedChangedListener)

toggle_Flipping.setOnCheckedChangeListener(new OnCheckedChangeListener() {

//ToggleButton의 선택 상태가 변경되면 자동으로 호출되는 메소드

//첫번재 파라미터 : 선택의 변화가 생긴 CompoundButton(여기서는 toggle_Flipping)

//두번째 파라미터 : Compoundbutton(toggle_Flipping)의 ON(true),OFF(false) 상태

@Override

public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

// TODO Auto-generated method stub

if(isChecked){//On 으로 바뀌었으므로 ..자동 Flipping 시작..

//1초의 간격으로 ViewFlipper의 View 자동 교체

flipper.setFlipInterval(1000);//플리핑 간격(1000ms)

flipper.startFlipping();//자동 Flipping 시작

}else{//OFF로 변경되었으므로  Flipping 정지

flipper.stopFlipping();;//Flipping 정지

}

}

});

}

//onClick속성이 지정된 View가 클릭되었을 때 자동으로 호출되는 메소드.

public void mOnClick(View v){

switch( v.getId() ){

case R.id.btn_previous:

flipper.showPrevious();//이전 View로 교체

break;

case R.id.btn_next:

flipper.showNext();//다음 View로 교체

break;

}

}

}


여기까지 ViewFiipper 입니다.


여러 이미지의 교체를 매우 손쉽게 합니다.


그런데. 잠시

이렇게 여러 이미지를 교체하는 앱 중에서

만화 보기 처럼 손으로 드래그 해서

이미지를 좌우로 바꾸는 어플들을 보셨을 겁니다.


이런 경우는 ViewFipper 보다

만화책의 Page를 넘기는 듯한 모습이 되는

ViewPager 라는 것이 있습니다.

사용성이 좋은 View 중에 하나죠.


다음 포스트에서 ViewPager를 만들어보겠습니다.

반응형
반응형

이전 예제에서

하나의 ImageView에 Frame Animation 하나를

보여줬습니다.


이번 예제 소스는

하나의 ImageView에

여러 동작의 Frame Animation을 실행시켜 보겠습니다.


이전 예제에서는 jump만 했습니다.

이번에는 jump, run, attack 3가지의 동작입니다.


각 각의 애니메이션을 실행하는 버튼을 3개 만들었습니다.


실행 결과먼저 보겠습니다.




이전 포스트를 보셨다면

이번 예제도 별로 어렵지 않을 겁니다.


먼저

3가지 동작의 animation-list.xml 파일을 만들겠습니다.


사용되는 이미지는 첨부했으니 다운받아

본인의 프로젝트에 넣어주시기 바랍니다.



Pink_Run.zip


Pink_Jump.zip


Pink_Attack.zip



만드는 방법은 이전 포스트를 참고하시기 바랍니다.


첫번째 pink_jump.xml 파일입니다.


주의할 것은 이전 포스트에서는

소스코드(.java)에서 setOneShot을 했다는 것이고

이번 예제에서는 xml 파일에 oneShot 속성으로 부여했다는 겁니다.

 

 pink_jump.xml

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"

    android:oneshot="true">

    

    <item android:drawable="@drawable/pink_jump_001"

        android:duration="100"/>

    

    <item android:drawable="@drawable/pink_jump_002"

        android:duration="100"/>

    

    <item android:drawable="@drawable/pink_jump_003"

        android:duration="100"/>

    

    <item android:drawable="@drawable/pink_jump_004"

        android:duration="100"/>

    

    <item android:drawable="@drawable/pink_jump_005"

        android:duration="100"/>

    

    <item android:drawable="@drawable/pink_jump_006"

        android:duration="100"/>

    

    <item android:drawable="@drawable/pink_jump_007"

        android:duration="100"/>

    

    <item android:drawable="@drawable/pink_jump_008"

        android:duration="100"/>    


</animation-list>



다음은 pink_run.xml 입니다.

이 애니메이션은 무한 반복되도록 oneShot 속성을 부여하지 않았습니다.

 

 pink_run.xml

<animation-list xmlns:android="http://schemas.android.com/apk/res/android" >

    

    <item android:drawable="@drawable/pink_run_001"

        android:duration="100"/>

    

    <item android:drawable="@drawable/pink_run_002"

        android:duration="100"/>

    

    <item android:drawable="@drawable/pink_run_003"

        android:duration="100"/>

    

    <item android:drawable="@drawable/pink_run_004"

        android:duration="100"/>

    

    <item android:drawable="@drawable/pink_run_005"

        android:duration="100"/>

    

    <item android:drawable="@drawable/pink_run_006"

        android:duration="100"/>

    

    <item android:drawable="@drawable/pink_run_007"

        android:duration="100"/>

    

    <item android:drawable="@drawable/pink_run_008"

        android:duration="100"/>

    

    <item android:drawable="@drawable/pink_run_009"

        android:duration="100"/>

    

    <item android:drawable="@drawable/pink_run_010"

        android:duration="100"/>


</animation-list>


다음은 pink_attack.xml 입니다.

oneShot 속성을 주었습니다.

 

 pink_attack.xml

<animation-list xmlns:android="http://schemas.android.com/apk/res/android"

    android:oneshot="true">

    

    <item android:drawable="@drawable/pink_attack_001"

        android:duration="100"/>

    

    <item android:drawable="@drawable/pink_attack_002"

        android:duration="100"/>

    

    <item android:drawable="@drawable/pink_attack_003"

        android:duration="100"/>

    

    <item android:drawable="@drawable/pink_attack_004"

        android:duration="100"/>

    

    <item android:drawable="@drawable/pink_attack_005"

        android:duration="100"/>

    

    <item android:drawable="@drawable/pink_attack_006"

        android:duration="100"/>


</animation-list>




3가지 프레임 애니메이션 리소스를 만들었으니

메인 레이아웃을 만들겠습니다.


각 애니메이션을 실행하는 버튼(Button) 3개,

애니메이션이 실행될 ImageView 1개 입니다.


 

 activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:orientation="vertical"

    tools:context="${relativePackage}.${activityClass}" >

    

    <Button android:id="@+id/btn_run"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text="RUN START"

        android:onClick="mOnClick"/>

    

    <Button android:id="@+id/btn_jump"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text="JUMP START"

        android:onClick="mOnClick"/>

    

    <Button android:id="@+id/btn_attack"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text="ATTACK START"

        android:onClick="mOnClick"/>  


    <ImageView 

        android:id="@+id/img"

        android:layout_width="wrap_content"

        android:layout_height="match_parent"      

        android:src="@drawable/pink_run"

        android:layout_gravity="center"/>


</LinearLayout>



이제 소스파일입니다.

설명은 주석문을 참고하시기 바랍니다.


다시 말씀드리지만

주석문을 제외하면 얼마 안됩니다.

미리 코드의 길이에 겁먹지 않길 바랍니다.

 

 MainActivity.java

public class MainActivity extends Activity {

ImageView img;

//FrameAnimation에 의해 Animation을 실행할

//Animation Drawable 객체 참조변수

//즉, Frame 단위로 이미지를 바꿔서 그려주는 Drawable 객체

AnimationDrawable ani;


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//ImageView 객체 참조

img=(ImageView)findViewById(R.id.img);

//ImageView에 src 속성으로 설정된 pink_run이미지를 Drawable 객체로 얻어오기. 

//Frame Aniamtion은 Drawable의 일종이으로 형변환 가능

ani=(AnimationDrawable)img.getDrawable();

//처음 시작하면 Run 애니메이션 실행

//pink_run.xml 리소스는 oneshot이 아니므로 계속 반복됨.

ani.start();

}

//onClick 속성이 지정된 View가 클릭되었을 때 자동으로 호출되는 콜백메소드

public void mOnClick(View v){

switch(v.getId()){

case R.id.btn_run:

//'RUN START' 버튼을 눌렀을 때 이전 Frame Animation이 진행중이면 정지

//Frame Animation은 한번 start()해주면 계속 Running 상태임.

//Frame Animation을 OneShot으로 하고 다시 시작하게 하고 싶다면

//이전 Frame Animation을 'stop'시켜야 함.

if(ani.isRunning()) ani.stop();

//ImageView에 pink_run.xml Drawable 리소스파일 세팅

img.setImageResource(R.drawable.pink_run);

//ImageView는 설정된 xml 리소스 파일을 Drawable 객체로 가지고 있으므로

//이를 다시 얻어옴.

ani=(AnimationDrawable)img.getDrawable();

//AnimationDrawable 객체에게

//Frame 변경을 시작하도록 함.

ani.start();

break;

case R.id.btn_jump:

//이전 Frame Animation이 진행중일 수도 있으므로 'stop'시켜야 함.

if(ani.isRunning()) ani.stop();

//ImageView에 pink_jump.xml Drawable 리소스파일 세팅

img.setImageResource(R.drawable.pink_jump);

//ImageView는 설정된 xml 리소스 파일을 Drawable 객체로 가지고 있으므로

//이를 다시 얻어옴.

ani=(AnimationDrawable)img.getDrawable();

//AnimationDrawable 객체에게

//Frame 변경을 시작하도록 함.

ani.start();

break;

case R.id.btn_attack:

//이전 Frame Animation이 진행중일 수도 있으므로 'stop'시켜야 함.

if(ani.isRunning()) ani.stop();

//ImageView에 pink_attack.xml Drawable 리소스파일 세팅

img.setImageResource(R.drawable.pink_attack);

//ImageView는 설정된 xml 리소스 파일을 Drawable 객체로 가지고 있으므로

//이를 다시 얻어옴.

ani=(AnimationDrawable)img.getDrawable();

//AnimationDrawable 객체에게

//Frame 변경을 시작하도록 함.

ani.start();

break;

}

}

}


여기 까지가 Frame Animation 입니다.


다음에 시간 여유가 있을 때 Tween Animation도 소개 하도록 하겠습니다.

반응형

+ Recent posts