반응형

이번 소개될 예제소스는 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도 소개 하도록 하겠습니다.

반응형
반응형

안드로이드에서 Animation 을 구현하는 방법은 2가지 입니다.


1. Frame Animation : 여러장의 사진을 바꿔가며 애니메이션을 실행

2. Tween Animaion : 이미지의 위치, 크기, 각도, 투명도를 조절하며 애니메이션 실행


우선 첫번째로

특정 이미지 뷰(ImageView)에 보여지는 그림이 자동으로

변경돼서 애니메이션을 실행하는 Frame Animation을 만들어 보겠습니다.


'START'버튼을 누르면 Animation이 시작됩니다.


동작결과부터 보겠습니다.




위의 Pink 캐릭터가 점프 하는 애니메이션을 위해

여러장의 이미지가 필요합니다.



Pink_Jump.zip



첨부파일로 첨부했으니 다운받으셔서

본인 프로젝트의 drawable-hdpi 폴더에 추가하세요.

(다른 해상도에 넣어도 됩니다.)


프레임 애니메이션(Frame Animation)

만들기 위해 프로젝트의 res폴더

drawable 폴더를 하나 만들고

그 안에 여러장의 이미지를 리스트업 한 xml 파일을 만듭니다.


즉, 여러장의 그림을 보유하고 있는

Drawable(그림 관리 클래스) 리소스를 만든다고 보시면 됩니다.



먼저 이 작업부터 하죠.

혹시나 해서 res폴더>>drawable폴더 생성 과정부터 보여드립니다.

'drawable' 폴더 이름은 절대 틀리면 안됩니다.



  




이제 'drawable' 폴더안에 여러장의 이미지 하나 하나를

Frame으로 하여 변경시킬 수 있는

Animation-list 리소스 파일을 만듭니다.

파일 이름은 'pink_jump' 로 하겠습니다.


역시 그림으로 보겠습니다.


  



'Root element' 로 'animation-list'를 선택하셔야 합니다.(한번 클릭)


이렇게 만들어진

'pink_jump.xml' 파일 입니다.

특별한 설명없이도 Frame당 100ms(0.1초)씩 보여진다는 것을

아실 수 있을 겁니다.


 

 pink_jump.xml

<animation-list xmlns:android="http://schemas.android.com/apk/res/android">

    

    <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>



이제 이 Drawable 리소스 파일

설정하는 ImageView가 있는

activity_main.xml 파일입니다.


간단하게 애니메이션을 시작하는 버튼(Button) 1개,

애니메이션을 보여줄 ImageView 1개 입니다.

 

 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}" >

    

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

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text="FrameAnimation START"

        android:onClick="mOnClick"/>


    <ImageView 

        android:id="@+id/img"

        android:layout_width="wrap_content"

        android:layout_height="wrap_content"

        android:layout_centerInParent="true"        

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


</RelativeLayout>


ImageView의 src 로 위에서 만든 pink_jump.xml 파일이 적용되었다는 것이

중요합니다.



이제 Frame Animation이 가능한 상태는 되었습니다.

하지만 실행시켜도 애니메이션을 실행되지 않습니다.

src로 지정된 Drawable 리소스 파일인

pink_jump.xml 파일의 아이템들은

시작부터 자동으로 애니메이션이 되지는 않습니다.


그럼 이 여러장의 그림을 가지고 있는

Drawable 리소스에게 Frame 애니메이션이 실행되되록

소스코드를 만들어 보겠습니다.


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

'START' 버튼을 누를 때 마다

pink 캐릭터가 Jump 합니다.


 

 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 속성으로 설정된 이미지를 Drawable 객체로 얻어오기. 

//Frame Aniamtion은 Drawable의 일종이므로 형변환 가능

ani=(AnimationDrawable)img.getDrawable();

//Frame Animation을 한번반 실행

//이 코드를 지우면 자동으로 반복함(기본값)

ani.setOneShot(true);

}

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

public void mOnClick(View v){

switch(v.getId()){

case R.id.button:

//'START' 버튼을 눌렀을 때 이전 Frame Animation이 진행중이면 정지

//Frame Animation은 한번 start()해주면 계속 Running 상태임.

//Frame Animation을 OneShot으로 하고 다시 시작하게 하고 싶다면

//이전 Frame Animation을 'stop'시켜야 함.

if(ani.isRunning()) ani.stop();

//AnimationDrawable 객체에게

//Frame 변경을 시작하도록 함.

ani.start();

break;

}

}

}

 

여기까지 Frame Animation의 기본적인 실행을 보았습니다.


이미지만 많다면 좀더 부드럽게 애니메이션이 진행되겠죠.


다음 포스트에서는 하나의 이미지뷰

여러 종류의 Frame Animation이 실행되도록 하겠습니다.


pink 캐릭터 jump도 하고 run도 하고 attack도 하도록 해보겠습니다.

반응형
반응형


이전 포스트의 ListView 예제들은

Data로 단순한 String 문자열을 사용했습니다.


하지만 실제 어플을 개발할 때 사용하는 ListView는

나열할 항목의 Data가 String 하나로 단순하지 않은 경우가 많죠.


그래서 이전 예제에서 사용했던

ArrayAdapter를 사용하기에는 다소 부족합니다.


그래서 실제로는 AdapterView의 일종인 ListView의 항목으로

보여줄 Data들을 적절한 View로 만들어주는

Adapter 객체를 사용자정의(Customize)해야 하는 경우가 많습니다.


이번 예제는 이를 위한 예제로서

대량의 데이터로 사용할 Data를 문자열2개와 이미지 하나로 할 겁니다.


실행결과를 먼저 보시는 것이 이해가 빠르겠네요.


예제는 요즘 유명한 비정상 회담 멤버들의 국적을

표시하는 예제입니다.


간단한 ListView이지만

각 항목마다 문자열2개, 이미지 하나를 보여줍니다.



실행화면


스크롤 되는 ListView

  

각 항목마다 이름, 국적, 국기 이미지를 표시합니다.


딱 봐도 어떤 구조인지 느껴지시죠??




이제 예제 소스를 소개하겠습니다.


먼저 프로젝트를 만들어

기본 Activity에서 보여줄 xml파일을 설계합니다.


activity_main.xml 레이아웃 파일

단순히 ListView 하나와 제목 TextView 하나입니다.

 

 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">

    

    <TextView 

        android:layout_width="match_parent"

        android:layout_height="30dp"

        android:text="비정상회담 멤버"

        android:textStyle="bold"

        android:gravity="center"

        android:background="#dddddd"/>


    <ListView 

        android:id="@+id/listview"

        android:layout_width="match_parent"

        android:layout_height="300dp"

        android:layout_marginTop="5dp"/>


</LinearLayout>

  


Java 소스파일은 잠시뒤에 소개하고

먼저 ListView의 한 항목으로 사용할 데이터인

이름, 국적, 국기 이미지 리소스는 하나의 멤버에 대한 정보인 만큼

하나의 클래스 객체로 관리하는 것이 용이하므로

이 3개의 값(이름,국적,이미지 리소스 아이디)을 멤버변수로 보관하는

데이터 클래스를 만들어 보겠습니다.



비정상회담 국기.zip


그전에 국기 이미지는 첨부파일에서 받아 다운받셔서

압축을 풀고

이 프로젝트의 res폴더>>drawable폴더 중 타겟 해상도의 폴더에 넣으세요.

저는 drawable-hdpi에 넣었습니다.

(제 에뮬레이터geny-motion이 hdpi 이기 때문에..)



이미지를 모두 넣었다면


이제 비정상 회담 멤버 한명 한명의 이름, 국적, 국기이미지를 저장할

데이터 클래스를 만들어 보겠습니다.


MainActivity.java 파일 안에 클래스를 선언하면

복잡해 보이기에 별도의 .java 소스 파일로 만듭니다.

코딩이 편하도록 같은 패키지안에 소스파일을 만듭니다.


이름, 국적을 저장하기 위한 String 2개,

국기 이미지 리소스 아이디를 저장하기위한 int형 변수 1개

(R.drawable.korea 같은 int형 ID 값)


 

 MemberData.java

//Member의 정보를 저장하기 위한 클래스.....

public class MemberData {

String name;    //이름 저장

String nation;   //국적 저장

int imgId;      //국기 이미지의 리소스 아이디

public MemberData(String name, String nation, int imgId) {

// TODO Auto-generated constructor stub

//생성자함수로 전달받은 Member의 정보를 멤버변수에 저장..

this.name= name;

this.nation=nation;

this.imgId=imgId;

}

//이 아래는 getter , setter 메소드듭입니다.

//OOP(객체 지향 프로그래밍)은 클래스 객체 외부에서 직접 엠머변수에 접근하는 것을 지양합니다.

//객체의 멤버변수 제어는 객체 스스로 하도록 해서 재사용성을 높인 방법이죠.

//getter, setter 멤버 메소드들은 그 목적으로 만들어 진 것이죠.

public void setName(String name) {

this.name = name;

}

public void setNation(String nation) {

this.nation = nation;

}

public void setImgId(int imgId) {

this.imgId = imgId;

}

public String getName() {

return name;

}

public String getNation() {

return nation;

}

public int getImgId() {

return imgId;

}


}


특별할 것 없는 데이터 클래스 입니다.



MainActivity.java 소스파일안에 이 데이터 객체들을 만드는 작업과

ListView 참조변수 작업만 먼저 수행하겠습니니다.

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

 MainActivity.java

public class MainActivity extends Activity {

//이름,국적,이미지리소스아이디를 가지고 있는 MemberData 클래스의 객체를 

//배열로 보관하기 위한 ArrayList 객체 생성

//MemberData[] 이렇게 선언하는 일반배열은 배열 개수가 정해져 있어서 나중에 추가,삭제가 불편하죠.

//배열 요소의 개수를 유동적으로 조절할 수 있는 ArrayList 객체로 data 보관

ArrayList<MemberData> datas= new ArrayList<MemberData>();

//ListView 참조변수

ListView listview;


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//이번 예제에서는 우선 직접 코딩으로 멤버정보를 입력하고

//다른 예제에서 추가/삭제 관련 작업을 해보겠습니다.

//비정상 회담(요즘 유명하기에 허락없이 도용합니다.-_-)멤버 정보 생성

datas.add( new MemberData("유세윤", "대한민국", R.drawable.korea));

datas.add( new MemberData("블레어", "호주", R.drawable.australia));

datas.add( new MemberData("기욤 패트리", "캐나다", R.drawable.canada));

datas.add( new MemberData("장위안", "중국", R.drawable.china));

datas.add( new MemberData("로빈", "프랑스", R.drawable.france));

datas.add( new MemberData("다니엘", "대한민국", R.drawable.germany));

datas.add( new MemberData("알베르토", "대한민국", R.drawable.italy));

datas.add( new MemberData("샘오취리", "대한민국", R.drawable.ghana));

//귀찮아서 다 못넣겠네요...-_-

//ListView 객체 찾아와서 참조

listview= (ListView)findViewById(R.id.listview);

}

}

 


이제 가장 중요한 작업입니다.


ListView는 AdapterView의 일종입니다.


즉, 대량의 데이터를 적절한 View 객체로 만들어주는

Adapter를 보유한 ViewGroup으로서 Adapter 객체가

만들어주는 View를 나열시켜주는 View Group입니다.


 이전 포스트들에서 소개한 ArrayAdapter는 단순하게

문자열을 나열할 때 손쉽게 사용했습니다.


하지만 지금 우리의 예제처럼

문자열 2개를 위아래로..

이미지 하나를 오른쪽에..

보여주도록 하는 View는 만들 수 없습니다.


그래서 이름, 국적, 국기이미지 아이디를 저장하고 있는

데이터를 가지고 앞에 보여드렸던

실행화면의 항목처럼 배치되도록 하는

View를 만들어내는 Adapter를 만들어야 겠습니다.

즉, 커스텀 Adapter(Custom Adapter)를 만들겠다는 겁니다.



먼저 커스텀 Adapter가 만들어낼 View(List의 항목하나)의

배치(레이아웃)을 설계하는 xml 레이아웃 파일을 만들겠습니다.



res폴더>>layout폴더>>list_row.xml 파일 생성

다들 알겠지만

노파심(?)에서 만드는 과정을 소개합니다.



  

 

마지막 사진이 우리가 만들 레이아웃 xml 파일의 Graphical Layout 입니다.

커스텀 Adapter가 만들어낼 View의 모양으로

ListView의 한 항목에 해당하는 모습니다.


xml파일입니다.

 list_row.xml

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

    android:layout_width="match_parent"

    android:layout_height="70dp"

    android:orientation="horizontal"

    android:padding="3dp"

    android:background="#dddddd" 

    android:weightSum="10">

    

    <LinearLayout 

        android:layout_width="0dp"

        android:layout_height="match_parent"

        android:layout_weight="7"

        android:orientation="vertical"

        android:weightSum="5">

        

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

            android:layout_width="match_parent"

            android:layout_height="0dp"

            android:layout_weight="3"

            android:text="NAME"

            android:textSize="18sp"

            android:textStyle="bold"

            android:gravity="center_vertical"

            android:background="#ffffff"

            android:padding="3dp"/>

        

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

            android:layout_width="match_parent"

            android:layout_height="0dp"

            android:layout_weight="2"

            android:text="NATION"

            android:gravity="center_vertical"

            android:layout_marginTop="2dp"

            android:background="#ffffff"

            android:padding="3dp"/>

        

    </LinearLayout>

    

    <ImageView android:id="@+id/img_flag"

        android:layout_width="0dp"

        android:layout_height="64dp"

        android:layout_weight="3"

        android:src="@drawable/korea"

        android:background="#ffffff"

        android:layout_marginLeft="3dp"/>    


</LinearLayout>

  

이제 Adapter가 만들어낼 View의 모양을 만들었으니

실제로 대량의 Data인

비정상 회담 멤버의 이름,국적,국기이미지 데이터들을 저장하고 있는

ArrayList인 'datas'

지금 만든 list_row.xml 의 모양으로 View 객체를

만들어내는 Adapter를 만들겠습니다.


MainActivity.java 파일이 너무 복잡하니

별도의 .java 소스파일로 커스텀 Adapter class를 설계하겠습니다.


여기서 가장 중요한 것은

우리가 만들려는 커스텀 Adapter class

'BaseAdapter'를 상속받아 만든다는 겁니다.

이 클래스가 기본적인 Adapter의 기능들을 수행하도록 되어 있습니다.

이 기본 기능들을 우리의 목적에 맞게

오버라이딩(overriding)해서 원하는 동작이 되도록 할 겁니다.


혹시나 해서 또 노파심(?)에

상속받아 클래스를 만드는 과정을 보겠습니다.


   


  

마지막 그림처럼 Class 소스파일이 만들어지면 성공입니다.


이제

비정상 회담 멤버들의 정보를 View 객체로 만들어주는

MemberDataAdapter 클래스 파일을 만들어 냅니다.

주석으로 설명을 많이 해놔서

코드가 엄청 길어 보이지만..

주석을 제외한 검정 글씨만 보면 많지 않습니다.


 

 MemberDataAdapter.java

//클래스의 이름은 다른 이름이어도 상관없지만 상속만 BaseAdapter를 잘 받으면 됩니다.

public class MemberDataAdapter extends BaseAdapter {

ArrayList<MemberData> datas;

LayoutInflater inflater;

public MemberDataAdapter(LayoutInflater inflater, ArrayList<MemberData> datas) {

// TODO Auto-generated constructor stub

//객체를 생성하면서 전달받은 datas(MemberData 객체배열)를 멤버변수로 전달

//아래의 다른 멤버 메소드에서 사용하기 위해서.멤버변수로 참조값 전달

this.datas= datas;

//list_row.xml 파일을 View 객체로 생성(inflating) 해주는 역할의 객체

//이 MemberDataAdapter 클래스를 객체로 만들어내는 클래스에서 LayoutInflater 객체 전달해주야 함.

//이번 예제어세는 MainActivity.java에서 전달.

//xml 종이의 글씨를 부풀려서 객체로 메모리에 만들어 낸다고 해서 '부풀리다(inflate)'라는 표현 사용

this.inflater= inflater;

}


//ListView에서 이 MemberDataAdapter 객체에게 요청하는 값으로서

//MemberDataAdapter 객체가 만들어낼 View의 개수를 리턴하는 메소드

//ListView에 나열되는 목록의 개수를 의미함.

//이 예제에서는 datas라는 ArrayList의 개수를 지칭함.

//특별한 경우가 아니라면 보통 datas의 size 를 리턴함.

@Override

public int getCount() {

// TODO Auto-generated method stub

return datas.size(); //datas의 개수를 리턴

}

//MemberDataAdapter의 특정 위치(position)에 해당하는 Data를 리턴하는 메소드

//이 예제에서는 datas의 특정위치에 해당하는  MemberData 객체를 리턴.

//ListView의 position은 가장 위에 목록부터(0,1,2,3...)으로 지정됨.

@Override

public Object getItem(int position) {

// TODO Auto-generated method stub

return datas.get(position);//datas의 특정 인덱스 위치 객체 리턴.

}

//특정 위치(position)의 data(MemberData)을 지칭하는 아이디를 리턴하는 메소드

//특별한 경우가 아니라면 보통은 data의 위치를 아이디로 사용하므로

//전달받은 position를 그대로 리턴함.

@Override

public long getItemId(int position) {

// TODO Auto-generated method stub

return position;

}

//이 커스텀 Adapter 클래스 설게에서 가장 중요한 메소드로서 

//ListView에서 한 항목의 View 모양과 값을 설정하는 메소드

//AdapterView의 일종인 ListView는 설정된 Adapter(이 예제에서는 MemberDataAdapter)에게

//대량의 데이터들(datas : MemberData객체 배열)을 보여주기에 적절한 View로 만들고 

//해당 데이터의 값으로 만들어 내는 핵심 메소드로서 ListView를 위에 만든 getCount()메소드의 리턴값만큼

//getView를 요구하여  목록에 나열함.

//즉, 이 메소드의 리턴값인 View 가 ListView의 한 항목을 의미합니다.

//우리는 이 리턴될 View의 모양을 res폴더>>layout폴더>>list_row.xml 파일을 만들어 설계합니다.

//첫번째 파라미터 position : ListView에 놓여질 목록의 위치.

//두번째 파라미터 convertview : 리턴될 View로서 List의 한 함목의 View 객체(자세한 건 아래에 소개)

//세번째 파라미터 parent : 이 Adapter 객체가 설정된 AdapterView객체(이 예제에서는 ListView)

@Override

public View getView(int position, View convertView, ViewGroup parent) {

// TODO Auto-generated method stub

//크게 getView의 안에서는 2가지 작업이 이루어 집니다.

//1. ListView의 목록 하나의 모양을 담당하는 View 객체를 만들어 내는 'New View'

//2. 만들어진 View에 해당 Data를 연결하는 'Bind View'

//1.New View

//지문의 한계상 자세히는 설명하기 어려우니 세부사항은 다른 포스트를 참고하시기 바랍니다.

//가장 먼저 new View를 하기 위해서 convertView 객체가 null인지 확인합니다.

//null을 확인하는 이유는 자원 재활용때문인데..

//짧게 설명하자면.. ListView에서 보여줄 목록이 만약 100개라면... 

//데이터의 양이 많아 분명 동시에 보여질 수 있는 목록의 개수를 정해져 있을 겁니다.

//그래서 이전 예제에서 보았듯이 ListView는 개수가 많으면 자동으로 Scroll되도록 되어 있지요.

//여기서 조금 생각해보시면 간단한데요.. 한번에 만약 5개 밖에 못보여진다면...

//굳이 나머지 95개의 View를 미리 만들 필요가 없겠죠.. 어차피 한번에 보여지는게 5~6개 수준이라면..

//그 정보의 View만 만들어서 안에 데이터만 바꾸면 메모리를 아낄 수 있다고 생각한 겁니다.

//여기 전달되어 체크되고 있는 converView 객체가 재활용할 View가 있으면 null이 아닌값을 가지고 

//있다고 보시면 되고 converView가 null이면 새로 만들어야 한다고 보시면 됩니다.

if( convertView==null){

//null이라면 재활용할 View가 없으므로 새로운 객체 생성

//View의 모양은 res폴더>>layout폴더>>list.xml 파일로 객체를 생성

//xml로 선언된 레이아웃(layout)파일을 객체로 부풀려주는 LayoutInflater 객체 활용.

convertView= inflater.inflate(R.layout.list_row, null);

}

//2. Bind View

//재활용을 하든 새로 만들었든 이제 converView는 View객체 상태임.

//이 convertView로부터 데이터를 입력할 위젯들 참조하기.

//이름을 표시하는 TextView, 국적을 표시하는 TextView, 국기이미지를 표시하는 ImageView

//convertView로 부터 findViewById()를 하시는 것에 주의하세요.

TextView text_name= (TextView)convertView.findViewById(R.id.text_name);

TextView text_nation= (TextView)convertView.findViewById(R.id.text_nation);

ImageView img_flag= (ImageView)convertView.findViewById(R.id.img_flag);

//현재 position( getView()메소드의 첫번재 파라미터 )번째의 Data를 위 해당 View들에 연결..

text_name.setText( datas.get(position).getName() );

text_nation.setText( datas.get(position).getNation() );

img_flag.setImageResource( datas.get(position).getImgId() );

//설정이 끝난 convertView객체 리턴(ListView에서 목록 하나.)

return convertView;

}


}



이제

MainActivity 소스파일에서 이 Adapter를 객체로 만들고

ListView에 설정하면 끝입니다.


완성된 MainActivity.java 소스파일입니다.

굵은 글씨가 새로 추가한 내용입니다.


 

 MainActivity.java

public class MainActivity extends Activity {

//이름,국적,이미지리소스아이디를 가지고 있는 MemberData 클래스의 객체를 

//배열로 보관하기 위한 ArrayList 객체 생성

//MemberData[] 이렇게 선언하는 일반배열은 배열 개수가 정해져 있어서 나중에 추가,삭제가 불편하죠.

//배열 요소의 개수를 유동적으로 조절할 수 있는 ArrayList 객체로 data 보관

ArrayList<MemberData> datas= new ArrayList<MemberData>();

//ListView 참조변수

ListView listview;


@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//이번 예제에서는 우선 직접 코딩으로 멤버정보를 입력하고

//다른 예제에서 추가/삭제 관련 작업을 해보겠습니다.

//비정상 회담(요즘 유명하기에 허락없이 도용합니다.-_-)멤버 정보 생성

datas.add( new MemberData("유세윤", "대한민국", R.drawable.korea));

datas.add( new MemberData("블레어", "호주", R.drawable.australia));

datas.add( new MemberData("기욤 패트리", "캐나다", R.drawable.canada));

datas.add( new MemberData("장위안", "중국", R.drawable.china));

datas.add( new MemberData("로빈", "프랑스", R.drawable.france));

datas.add( new MemberData("다니엘", "대한민국", R.drawable.germany));

datas.add( new MemberData("알베르토", "대한민국", R.drawable.italy));

datas.add( new MemberData("샘오취리", "대한민국", R.drawable.ghana));

//귀찮아서 다 못넣겠네요...-_-

//ListView 객체 찾아와서 참조

listview= (ListView)findViewById(R.id.listview);

//AdapterView의 일종인 ListView에 적용할 Adapter 객체 생성

//MemberData 객체의 정보들(이름, 국적, 이미지)를 적절하게 보여줄 뷰로 만들어주는 Adapter클래스 객체생성

//이 예제에서는 MemberDataAdapter.java 파일로 클래스를 설계하였음.

//첫번재 파라미터로 xml 레이아웃 파일을 객체로 만들어 주는 LayoutInflater 객체 얻어와서 전달..

//두번째 파라미터는 우리가 나열한 Data 배열..

MemberDataAdapter adapter= new MemberDataAdapter( getLayoutInflater() , datas);

//위에 만든 Adapter 객체를 AdapterView의 일종인 ListView에 설정.

listview.setAdapter(adapter);

}

}

 

여기까지 입니다.

다음 포스트에서는 ListView의 멤버를 추가하거나 삭제하는 예제를 소개하겠습니다.


반응형

+ Recent posts