안드로이드 뷰페이저 기본 사용법. (Android ViewPager)

2019. 1. 23. 19:47


1. 화면에 표시될 컨텐츠를 전환하는 방법.

안드로이드를 탑재한 스마트폰이 처음 만들어지던 시기에는, 앱 화면을 구성할 때 UI(User Interface)의 "직관성"이 가장 중요한 이슈 중 하나였습니다. 사용자가 특정 기능을 사용하기 위해 화면의 어떤 요소를 터치해야 하는지, 앱에 표시되는 컨텐츠를 전환하기 위해 어떤 버튼을 선택해야 하는지 등을 쉽게 알 수 있도록 만드는 것이 주 관심 대상이었죠.


그런데 하드웨어 기술 발전과 더불어 스마트폰 사용이 보편화되고 관련 시장이 확장, 성숙됨에 따라, 앱 사용성의 중요 포인트가 "직관성"에서 "편의성"으로 바뀌게 됩니다. 오직 탐색 기능을 위해 화면을 차지하는 여러 종류의 버튼들 또는 점점 커져버린 디스플레이 크기로 인해 한손만으로는 터치가 힘들어진 공간들처럼, 단순히 직관적인 UI에 대한 고민만으로는 사용자의 사용 편의성을 만족시킬 수 없는 상황이 만들어진 것이죠.


이에 스마트폰 제조사들은 여러 가지 새로운 기술들을 앱 개별이 아닌 시스템 차원에서 도입하게 됩니다. 이를테면 스와이프(swipe) 또는 멀티 터치(multi-touch)와 같은 제스쳐(gesture)를 조금 더 적극적으로 활용하게 만든 것이죠.


제스쳐(gesture)는 특정 영역 또는 요소에 한정된 단순 터치의 불편함을 개선할 수 있게 만들어주는 사용자 입력 방법입니다. 특히 스와이프(swipe)의 경우, 특정 공간에 구애받지 않고 화면을 쓸어 넘기는 액션만으로 다양한 기능을 동작할 수 있게 만들 수 있는데요, 스와이프(swipe)가 가장 잘 활용되는 방법 중 하나가, 화면을 좌/우 가로 방향으로 쓸어 넘겨 화면에 표시될 컨텐츠를 전환하는 것입니다. 마치, 책을 볼 때 손가락으로 현재 페이지를 쓸어 넘겨 다음 페이지로 넘어가듯이, 화면에 표시되는 뷰를 전환하기 위해 화면을 쓸어 넘기는 것입니다.


그리고 이러한 변화에 맞게, 앱 개발에 사용되는 UI 요소들도 새롭게 만들어지게 되었는데요. 그 중, 앞에서 설명한, 스와이프를 통해 컨텐츠 전환을 할 수 있도록 만들어주는 요소가 추가되었는데, 뷰페이저(ViewPager)가 바로 그것입니다.

2. 뷰페이저(ViewPager)

뷰페이저(ViewPager)는 데이터를 페이지 단위로 표시하고, 좌/우 뒤집기(flip)을 통해 페이지를 전환할 수 있도록 만들어주는 컨테이너입니다. 자체적으로 화면을 그리는 기능을 가지지는 않고, 여러 종류의 뷰(View) 위젯을 사용하여 각 뷰페이저의 페이지를 구성합니다. 뷰페이저가 뷰그룹(ViewGroup)으로부터 상속된 것을 보면, 컨테이너(Container) 역할을 수행한다는 것을 쉽게 유추할 수 있죠.

ViewPager 클래스


뷰페이저는 데이터를 "페이지 단위"로 화면에 표시합니다. 여기서 "페이지"라는 말이, 뷰페이저의 구현 방식을 요약하는 단어인데요. 알다시피, 안드로이드에서는 데이터 리스트를 아이템 단위의 뷰 또는 뷰 집합으로 표시할 때, 어댑터(Adapter)를 사용합니다. (어댑터를 사용하여 데이터를 아이템 뷰로 표시하는 대표적인 컴포넌트는 리스트뷰입니다. 관련 예제는 [안드로이드 리스트뷰 기본 사용법]에서 확인할 수 있습니다.)

2.1 안드로이드 어댑터(Android Adapter)

안드로이드 어댑터(Adapter)의 역할을 한 문장으로 표현하자면, "사용자가 정의한 데이터 리스트를 입력으로 받아들여 화면에 표시할 뷰(View)들을 생성"하는 것입니다. 일반적으로 데이터 리스트에 포함된 데이터 한개 당 하나의 아이템 뷰로 매핑되며, 아이템 뷰는 표시할 데이터에 따라 여러 개의 뷰가 조합된 형태로 구성될 수 있습니다.

안드로이드 어댑터 역할


안드로이드 SDK에서 제공하는 어댑터들이 데이터로부터 뷰를 만들어낸다는 공통된 역할을 수행한다고 해도, 모든 어댑터 클래스를 위해 동일한 메서드를 작성해야 하거나, 동일한 구현 과정을 거쳐야 하는 것은 아닙니다. 각 어댑터는 자신만의 메서드 실행 흐름을 가지며, 이에 따라 각각의 메서드가 오버라이드(override)되어야 합니다.


뷰페이저의 경우, 페이저어댑터(PagerAdapter)를 사용하여 각 페이지를 위한 뷰를 생성합니다.

2.2 페이저어댑터(PagerAdapter)

페이저어댑터(PagerAdapter)는 뷰페이저(ViewPager)의 페이지뷰를 생성하는데 사용되는 어댑터 클래스입니다.

안드로이드 페이저어댑터 역할


PagerAdapter는 abstract 키워드로 정의된 추상 클래스입니다. 즉, PagerAdapter 객체를 바로 만들어서 사용할 수는 없고, PagerAdapter로부터 상속받은 자식 어댑터 클래스 객체를 구현해야 한다는 것을 의미합니다.

PagerAdapter 클래스


PagerAdapter 클래스를 상속한 자식 어댑터 클래스를 구현할 때, 오버라이드 해야 할 메서드는 아래와 같습니다.


메서드 설명
instantiateItem(ViewGroup container, int position) position에 해당하는 페이지 생성.
destroyItem(ViewGroup container, int position, Object object) position 위치의 페이지 제거.
getCount() 사용 가능한 뷰의 갯수를 리턴.
isViewFromObject(View view, Object object) 페이지뷰가 특정 키 객체(key object)와 연관되는지 여부.

표에 나열된 설명만으로는 각 메서드가 언제, 어떻게 동작하는지, 각 메서드의 코드를 어떻게 작성해야 하는지 파악하기가 쉽지 않죠? 좀 더 깊은 이해를 위해서 뷰페이저의 페이지 관리와 동작 방식을 설명할 필요가 있겠네요.

3. 뷰페이저(ViewPager) 동작 방식 이해하기.

뷰페이저는 페이지 단위로 뷰를 만들어 화면에 표시한다고 설명했습니다. 하나의 페이지가 뷰페이저 전체 화면에 표시되는데요, 그렇다면 뷰페이저에 포함된 페이지가 몇 개 있는지는 어떻게 알려줄 수 있을까요?

3.1 getCount() 메서드 : 뷰페이저의 전체 페이지 수 결정.

뷰페이저에 포함된 전체 페이지 수는 getCount() 메서드의 리턴 값으로 결정됩니다. 앞에서 언급했듯이, getCount()는 개발자가 직접 오버라이드하여 작성하는 메서드이므로 전체 페이지 수는 개발자가 결정합니다. 페이지를 표시할 데이터를 어떻게 관리하는지에 따라, 상수를 바로 리턴할 수도 있고, 배열이나 리스트 객체의 길이를 리턴할 수도 있습니다.


당연하게도, 스와이프 제스쳐 또는 ViewPager.setCurrentItem()을 통해 페이지를 전환하더라도 getCount() 메서드로 리턴되는 값 이상의 페이지로는 페이지 전환이 이루어지지 않습니다.


자, 전체 페이지 수가 어떻게 결정되는지 확인했으니, 이제 각 페이지가 언제 어떻게 만들어지는지 확인해볼까요?

3.2 뷰페이저(ViewPager)의 페이지 생성과 관리

뷰페이저에 페이지가 열 개 있다고 가정해 봅시다. 뷰페이저가 처음 화면에 표시될 때 첫 번째 페이지가 표시될텐데요, 그 첫 번째 페이지는 언제 만들어지는 걸까요? 그리고 스와이프에 의해 두 번째 페이지로 전환이 된다면, 이 두 번째 페이지는 또 언제 생성이 되는 걸까요? 각 페이지가 화면에 표시되는 매 순간 생성될까요? 아니면 뷰페이저가 화면에 표시되기 전에 모든 페이지를 미리 생성해놓고, 현재 페이지만 전환하는 걸까요?


정답은, 뷰페이저는 항상 현재 페이지를 기준으로 좌/우 하나 씩, 즉, 현재 페이지를 포함하여 "최대 세 개의 페이지를 생성 및 관리"한다 입니다. 이는 "페이지가 표시되는 매 순간 생성"하는 방법과 "미리 모든 페이지를 생성"하는 방법을 절충한 방법이라고 볼 수 있죠. 만약 페이지를 표시하는 순간에 뷰를 생성하게 되면, 생성 과정에 소요되는 지연으로 인해 페이지가 늦게 표시되는 문제가 생길 것입니다. 그리고 만약 앱의 시작과 동시에 모든 페이지를 생성하게 되면, 페이지 수가 많거나 뷰가 복잡한 경우 많은 메모리가 필요하게 되죠. 그래서 그 중간 정도의 해결책인, 현재 페이지 기준으로 세 개의 페이지를 생성하는 방법을 택한 것입니다. 글로만 설명하니 이해가 쉽지 않죠? 아래 그림을 통해 뷰페이저가 페이지를 생성하고 유지하는 방법을 살펴보시길 바랍니다.

뷰페이저의 페이지뷰 관리 방법


그리고 뷰페이저는 내부적으로 각 페이지를 뷰(View)로써 직접 관리하지 않고, 각 페이지와 연관되는 키 객체(key object)로 관리합니다. 여기서 키 객체(key object)는 특별한 타입의 클래스 객체를 말하는 것은 아니고, 페이지 참조 및 식별을 위해 사용되는 Object 타입 객체를 말합니다. 키(key)는 ArrayList로 관리되는데, 이는 페이지의 위치와 관계없이 지정된 페이지를 추적하고 고유하게 식별하게 해줍니다.


그런데 사실, 일반적으로 뷰페이저가 내부적으로 페이지를 관리하는 방법에 대해 앱 개발자가 자세하게 알 필요는 없습니다. 개발자의 주 관심은 "페이지뷰를 어디서, 어떻게 만들면 되는가?"이죠. 음, 페이지뷰는 어디서 만들면 될까요?

3.3 instantiateItem() 메서드 : 화면에 표시할 페이지뷰 생성.

화면에 표시할 페이지뷰를 만드는 작업은 페이저어댑터의 instantiateItem() 메서드에서 수행합니다. 파라미터로 전달된 position에 해당하는 페이지를 생성한 다음, 또 다른 파라미터로 전달된 컨테이너(=뷰페이저 객체)에 생성된 페이지뷰를 추가하면 됩니다.

그런 다음, 페이지 식별을 위한 Object 객체를 리턴합니다. 물론, 생성된 페이지뷰의 참조를 리턴하는 게 일반적이지만, 반드시 페이지뷰의 참조를 직접 리턴해야 하는 것은 아닙니다. 페이지를 식별할 수 있는 키 객체(key object)를 리턴하면 됩니다.

설명에 대한 이해가 쉽지 않을 수 있겠는데요, 아래 예제 코드를 보면 어떤 내용인지 이해가 되실거라 생각합니다.

3.4 isViewFromObject() 메서드 : 페이지뷰가 키 객체와 연관되는지 확인.

isViewFromObject() 메서드는 뷰페이저 내부적으로 관리되는 키 객체(key object)와 페이지뷰가 연관되는지 여부를 확인하는 역할을 수행합니다. 어떤 정보를 키 객체(key object)로 사용할 것인가, 즉, instantiateItem()에서 리턴하는 객체의 종류에 따라 이 메서드의 구현 코드가 결정될텐데요, 일반적으로 instantiateItem()에서 페이지뷰의 View 객체를 리턴하고, isViewFromObject()에서는 해당 View와 Object가 동일한지 여부를 검사하여 그 결과를 리턴하도록 구현합니다.

    @Override
    public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
        return (view == (View)object) ;
    }

4. 뷰페이저(ViewPager) 사용하기.

앞서 설명한 내용만으로 뷰페이저(PagerView)를 어떻게 사용해야 하는지 쉽게 이해가 되시나요? 좀 더 쉬운 이해를 돕기위해, 예제를 통해 뷰페이저(PagerView)를 사용하는 방법에 대해 알아보겠습니다.


예제는 아래 그림과 같이, 전체 화면을 차지하는 뷰페이저에 열 개의 페이지를 만듭니다. 최대한 간단한 설명을 위해, 각 페이지에는 하나의 텍스트뷰만을 표시하도록 만들겠습니다.

뷰페이저 예제 화면 구성


4.1 워크 플로우

뷰페이저 사용하기 예제 구현은 아래와 같은 순서로 수행됩니다.

뷰페이저 예제 구현 워크 플로우


(참고로 아래의 예제는, 안드로이드 스튜디오 프로젝트 생성 단계에서 "Basic Activity"를 선택하여 생성된 코드를 기반으로 작성되었습니다.)

4.2 메인액티비티 레이아웃에 뷰페이저 추가.

예제에서 뷰페이저는 메인액티비티에 표시합니다. 이를 위해 메인액티비티 레이아웃 XML 파일에 PagerView를 추가합니다. 예제에서는, 메인액티비티의 루트 레이아웃이 ConstraintLayout인 것에 주의하세요.

[STEP-1] "content_main.xml" - 메인액티비티 레이아웃 리소스 XML 파일
    <android.support.v4.view.ViewPager
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:id="@+id/viewPager"/>

4.3 뷰페이저 페이지 레이아웃 리소스 XML 작성.

다음 단계로, 뷰페이저의 페이지로 표시될 페이지뷰 레이아웃을 작성합니다. 여기서 작성한 레이아웃 리소스는, 이후 단계에서 페이저어댑터를 구현할 때 instantiateItem() 메서드에서 LayoutInflater를 통해 뷰로 변환합니다.


그런데 한 가지 알아둘 것은, 뷰페이저의 페이지뷰 화면을 만들 때 반드시 본문의 예제처럼 LayoutInflater를 사용해야 하는 것은 아니라는 것입니다. 예제와는 다르게 new 키워드를 사용하여 뷰 위젯 등을 직접 생성하거나, 프래그먼트를 사용하여 페이지 화면을 만드는 등 다양한 방법으로 페이지뷰를 구성할 수 있습니다. 그러므로 예제에서의 페이지뷰 작성 방법을 무조건 따라야 한다는 오해는 하지 마시길 바랍니다.


레이아웃 리소스("/res/layout/")에 "page.xml"을 추가한 다음, 텍스트뷰를 하나 추가합니다.

[STEP-2] "/res/layout/page.xml" - 페이지 레이아웃 리소스 작성.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:textSize="48sp"
        android:id="@+id/title"/>

</android.support.constraint.ConstraintLayout>

4.4 페이저어댑터(PagerAdapter) 상속 및 구현

앞서 뷰페이저를 추가하고 페이지에 대한 레이아웃도 만들었으므로, 이제 페이저어댑터를 구현하여 데이터 리스트로부터 페이지뷰를 생성하는 코드를 작성해야 합니다. 이를 위해 PagerAdapter 클래스를 상속한 클래스를 만들고, [2.2 페이저어댑터(PagerAdapter)]에서 소개한 몇 가지 메서드를 오버라이드합니다.

[STEP-3] "TextViewPagerAdapter.java" - PagerAdapter 상속 및 구현.
public class TextViewPagerAdapter extends PagerAdapter {

    // LayoutInflater 서비스 사용을 위한 Context 참조 저장.
    private Context mContext = null ;

    public TextViewPagerAdapter() {

    }

    // Context를 전달받아 mContext에 저장하는 생성자 추가.
    public TextViewPagerAdapter(Context context) {
        mContext = context ;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        View view = null ;

        if (mContext != null) {
            // LayoutInflater를 통해 "/res/layout/page.xml"을 뷰로 생성.
            LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            view = inflater.inflate(R.layout.page, container, false);

            TextView textView = (TextView) view.findViewById(R.id.title) ;
            textView.setText("TEXT " + position) ;
        }

        // 뷰페이저에 추가.
        container.addView(view) ;

        return view ;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        // 뷰페이저에서 삭제.
        container.removeView((View) object);
    }

    @Override
    public int getCount() {
        // 전체 페이지 수는 10개로 고정.
        return 10;
    }

    @Override
    public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
        return (view == (View)object);
    }
}

4.5 뷰페이저에 페이저어댑터 지정하기.

마지막으로, 메인액티비티에서 뷰페이저의 참조를 가져온 다음, 앞서 구현한 TextViewPagerAdapter 인스턴스를 뷰페이저에 지정합니다.

[STEP-4] "MainActivity.java" - 뷰페이저에 페이저어댑터 지정.
public class MainActivity extends AppCompatActivity {

    private ViewPager viewPager ;
    private TextViewPagerAdapter pagerAdapter ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ,,, 코드 계속.

        viewPager = (ViewPager) findViewById(R.id.viewPager) ;
        pagerAdapter = new TextViewPagerAdapter(this) ;
        viewPager.setAdapter(pagerAdapter) ;
    }
}

5. 실행 화면.

예제를 실행하면, 아래와 같은 화면이 표시됩니다. 첫 번째 페이지뷰(TEXT 0)가 표시되는 것을 확인할 수 있습니다.

예제 실행 화면 1


그리고 오른쪽 엣지 스와이프를 통해 다음 페이지로 전환, 왼쪽 엣지 스와이프를 통해 이전 페이지로 전환할 수 있습니다.

예제 실행 화면 2


6. 참고

.END.


ANDROID 프로그래밍/VIEWPAGER , , , , , , , , , , , , ,

  1. 이전 댓글 더보기
  2. Blog Icon
    dunkey2615@naver.com

    textView에 text를 입력해도 그게 나오는게 아니라 text 0~ 10까지 나오는데 이 원리가 뭘까요? ㅠㅠ

  3. 원리를 떠나서, 소스에 그렇게 동작하도록 만들어져 있습니다.

    본문의 내용이 허접해서 한번에 이해하시기 힘드시겠지만, 다른 블로그 내용들도 참고하시고, 잘 이해안되는 내용은 구글링을 통해 찾아보시면 금방 익숙해지실거라 생각이 듭니다.

    질문에 대한 해답은 본문에 있습니다. 무작정 예제만 따라한다고 끝은 아니고, 내용에 대한 이해를 하셔야 해요.

    감사합니다.

  4. textView.setText("TEXT " + position); 과 getCount() 리턴값이 10인점을 참고하면 좋을 것 같네요

  5. 추가적인 답변 달아주셔서 감사드려요!

  6. Blog Icon
    지나가는나그네

    진짜 포스팅 퀄리티 환상이네요...

    초보 개발자 먹여살리는 티스토리...

    항상 리스펙트하고 감사합니다

  7. 에고, 환상이라니요.. 과찬의 말씀을. ㅜㅜ
    누군가 만들어놓은 티스토리 스킨 이쁜 거 입혀서 내용만 살짝 얹은 것 뿐인데요.

    그래도 누군가에게 조금은 도움이 된 것 같아서 뿌듯합니다.

    방문해 주셔서 감사합니다.

  8. Blog Icon
    학생

    궁금한 것이 있습니다.

    뷰페이저에서 텍스트 말고 이미지를 넣을려면 어떻게 수정해야 되나요?

  9. Blog Icon
    학생

    텝뷰로 해서 프래그먼트가 123 으로 나눠져 있는데 프래그먼트 안에 뷰 페이저 이미를 하고 싶습니다.

  10. 음, 일단 대답은 "그냥 넣으시면 됩니다."로 드려야 할 것 같은데요.
    그냥 텍스트 대신 이미지뷰를 사용하시면 됩니다.

    그런데 페이지뷰와 프래그먼트를 결합하는 문제라면 여기 답글 만으로는 내용이 좀 부족할 듯 하구요.

    안드로이드 개발자 사이트의 가이드를 따라서 작성하시면 어렵지 않게 만드실 수 있을 것 같아요.

    https://developer.android.com/guide/navigation/navigation-swipe-view

    감사합니다.

  11. Blog Icon
    학생

    감사합니다~~

  12. 방문해 주셔서 감사합니다~~

  13. Blog Icon
    임주영

    안녕하세요 질문이 있어서 댓글을 남깁니다
    저는 프래그먼트 안에서 viewpager를 만들고 FragmentStatePagerAdapter를 통해 adapter를 구현하였는데요
    adapter 안에는 image 5개를 넣어놨고 Override 함수 getItem를 불러 ITemFragment.newInstance(images[position])을 리턴 한 상태입니다.

    물론 각페이지마다 이미지는 잘 나옵니다

    제가 구현하고 싶은것은 각각 image가 클릭되었을 때 프래그먼트 A를 호출하고 싶은데요 ,즉 이동입니다.
    viewpager에서 클릭 이벤트가 작동하지 않더군요 어떻게 할 수 있을까요?

  14. 뷰 페이저에서 클릭 이벤트가 작동하지 않는다라는 의미가, 뷰페이저의 각 페이지로 구성된 프래그먼트에 표시되는 이미지 클릭 이벤트가 리스너가 실행되지 않는다는 의미인가요?

  15. Blog Icon
    임주영

    네 이미지 클릭 이벤트 리스너가 안되서 이미지를 클릭하면 팅깁니다. adapter에서 따로 구현해야되는건지 아니면 touch event를 이용해서 구현해야되는건지 잘모르겠어서 질문 올립니다

    참고로 https://www.youtube.com/watch?v=W_WCPDto3QM 이것을 보고 테스트 했고 동영상과 차이점이라면 제 뷰페이저는 프래그먼트에 있습니다.

  16. 음, 이미지 클릭 이벤트 리스너가 안되는 게 좀 이상한데요.
    아마 리소스에서 이미지 뷰에 대한 참조를 제대로 못 가져오는 것 같은데, 관련 코드를 살펴봐야 정확한 원인을 알 수 있을 것 같습니다.

    관련 코드를 조금 올려주시거나, 문제가 발생하는 부분의 에러 메시지를 같이 올려주시면 도움을 드리기가 더 수월할 것 같습니다.

    감사합니다.

  17. Blog Icon
    감동쓰

    늘 꽉꽉 찬 포스팅으로 도움 많이 받고 갑니다.

  18. 내용만 많을 뿐, 그다지 실속은.....
    ^^

    방문해 주셔서 감사합니다.

  19. Blog Icon
    히히

    도움 많이 되었습니다. 감사해요 :)

  20. 도움이 되셨다니 다행입니다.

    방문해 주셔서 감사합니다.

  21. Blog Icon
    드디어

    내용 너무 꼼꼼하고 좋아요! 많이 헷갈렸는데 여러번 읽으면서 좀 더 이해해야 겠어요.
    혹시 뷰페이저만들어서 엑셀 파싱한 다음 배경은 그대로 이되, 각각의 셀 내용이 컨텐츠로 보여지게 할때에는 어떤식으로 찾아봐야할까요? 뽀따님께서는 어떤식으로 풀어나가실 것 같으신가요? 여러 뷰페이저를 찾아봤지만 그런 부분은 딱히 나와있지가 않아서 초보라서 많이 어렵네요 ㅜㅜ

  22. 답글이 너무 늦었네요. 죄송합니다.
    최근에 이래 저래 일이 많아서 블로그에 신경쓸 여유가 없어서...

    음, 방법을 결정하셨는지 모르겠네요.
    일단 질문 내용으로만 보면, 굳이 뷰페이저를 사용해야 하는 상황인가.. 라는 생각이 들고요. (질문을 제대로 이해한건지 모르겠네요. ㅜㅜ)

    한 화면에 컨텐츠만 업데이트하는 방법으로 쉽게 구현할 수 있을 것 같습니다.

    다시 한번, 답변이 늦어져서 죄송합니다.

    감사합니다.

  23. Blog Icon
    개발자

    안녕하세요 항상 글 잘 보여 배워가는 학생입니다. :)
    저는 프래그먼트 위에 뷰페이저를 이용해서 배너를 만들어 보고 있는데요,
    container.addView(view)부분에서
    'Cannot add a null child view to a ViewGroup' 라는 에러가 발생하네요..
    '빈 자식을 뷰그룹에 넣을 수 없다' 라는 말인 것 같은데 inflate를 통해서 view를 채워 넣은 상태인데 왜 자꾸 이렇게 뜰까요..? ㅜㅜ
    상위 프래그먼트에 문제가 있는 것일까요..?
    항상 글을 보고 배우며 개발해가고 있습니다. 감사합니다!

    ___________

    죄송합니다 해결했네요 ㅜㅜ context를 안담아서 어댑터를 호출해버리니 그런 오류가 발생했네요 .. ㅜㅜ 감사합니다!

  24. 네~ 해결하셨군요!
    바로바로 댓글을 확인하면 좀 더 빠른 답변을 드릴 수 있을텐데, 여유가 너무 없네요.

    질문글 올리시는 것에 대해 죄송해하실 필요는 없습니다. 제가 도와드릴 수 있는 내용이라면 언제든 도와드릴테고, 제가 도움을 드릴 수 없는 문제라면 언제든 모른 체 할 준비가 되ㅇ.... 아.. 아니.. 최대한 도움 드릴 수 있도록 노력하겠습니다!! ^^;;

    답을 찾으셔서 다행입니다.

    또 다른 문의 사항 있으시면 언제든 질문글 남겨주세요.

    감사합니다.

  25. Blog Icon
    euzl

    감사합니다.
    이 글을 보고 ViewPager에 대한 개념도 제대로 생기고 예제 만들다가 생긴 오류도 해결했습니다!
    새해 복 많이받으세요😍

  26. 내용 이해와 더불어 오류 수정에도 도움이 되셨다니, 정말 뿌듯하네요. ^^

    방문해 주셔서 감사합니다.

  27. 웹뷰를 이용한 하이브리드 앱 위주로만 개발하다 순수 네이티브 앱을 처음 만들어보게 되었는데 설명과 예제 둘다 큰 도움이 되었습니다.
    좋은 블로그 만들어주시고 피드백도 열심히 해주시니 정말 감사합니다.

  28. 내용도 별로 없는 블로그에 방문해주시고, 격려의 댓글까지 남겨주셔서, 오히려 제가 감사드립니다.

    앞으로 더 좋은 내용들 남길 수 있도록 노력하겠습니다.

    감사합니다.

  29. Blog Icon
    비타맥스

    어려운 개념 쉽게 설명해주셔서 감사합니다~ 제가 context에 대한 배경 지식이 부족해서
    관련 정보 한번 읽어 본 뒤에 다시보니 이해가 되네요 ^^

  30. 쉬운 설명으로 받아들여져서 다행입니다. 그래도 조금 더 많은 설명과 참고 자료가 정리되었다면 좋았을텐데... 정리하는 입장에서도 아쉬움이 많이 남네요.

    방문해 주셔서 감사드립니다.

  31. Blog Icon
    공극어

    안녕하세요. 뷰페이저 예제를 보통 프레그먼트, 탭레이아웃이랑 연동해서 3~4개 프레그먼트만 예제들만 있어서 무한으로 증식할 수 있는 뷰페이저 예제를 찾았는데 제 가려운 부분을 긁어주셔서 정말 감사합합니다 ㅠㅠㅠ 이 예제로 정말 도움 많이 받았습니다. 따라서하면서 질문 한가지만 드리고 싶은데 위의 예제에서는 settext에 text+position으로 화면 넘어갈때마다 숫자만 붙잖아요. 그럼 화면 넘어갈때마다 다른 문구를 보여주려면 어떤 코드를 추가해야 될까요?

  32. 본문의 예제에서는,
    단순하게 하나의 페이지 XML을 사용해 뷰페이저의 각 페이지를 생성했습니다.
    그리고 각 페이지가 정상적으로 표시되는지 확인하기 위해 페이지가 만들어질 때 setText를 통해 페이지 번호를 화면에 표시한 것이죠.

    화면 넘어갈 때 다른 문구를 보여주려면, 간단하게 setText() 하는 부분에서 표시하고자 하는 문자열을 표시해주면 됩니다.

    하지만, 아마 그런 것을 원하시는 건 아닌 것 같고, 페이지에 아예 다른 내용을 출력하고 싶어 하시는 것 같은데요.

    그런 경우 어댑터의 instantiateItem() 메서드 내부에서 여러 조건에 따라(position ?) 다른 페이지 XML로 페이지를 생성하게 만드시면 됩니다.

    예제 코드의 동작에만 국한해서 생각하지 마시고, 본문의 전체 내용을 이해하고 예제 코드를 수정하여 동작을 확장시켜보세요.

    이것 저것 바꾸다보면, 원하는 기능을 충분히 구현하실 수 있을 것입니다.

    감사합니다.

  33. Blog Icon
    헬로월드

    안녕하세요!

    해당 포스팅에서 좋은 정보 얻어서 충실히 구현하고 있습니다.

    해당 예제에서page.xml을 view로 생성하여 pager에 등록하도록 구현하셨는데
    이러한 경우에 애당 레이아웃을 startActivity와 같은 메서드를 사용하지 않고
    바로 view로 생성하기 때문에 레이아웃과 연결된 java class가 작동하지 않는점을 확인했는데
    (ex. 해당 calss의 onCreate호출)

    이를 해결하기 위해서
    참고 할 부분좀 부탁드려도 될까요??

  34. 흠, 질문하신 내용을 경험한 적이 없어서 정확한 방법을 가르쳐드릴 순 없을 것 같고요.

    대신, 방법을 고민해본다면...

    OnPageChangeListener를 통해 뷰의 초기화 메서드를 호출하면 될 것 같습니다.
    물론, 뷰페이저에 OnPageChangeListener를 add하고 뷰의 메서드를 직접 호출하는게 조금 번거롭게 느껴질 수도 있지만, 이게 제일 확실한 방법인 것 같네요.
    아니면 어댑터 클래스에서 PagerAdapter의 finishUpdate() 메서드를 통해 뷰의 메서드를 호출할 수도 있을텐데, 위의 OnPageChangeListener 조금 더 깔끔할 것 같네요.

    흠.. 또 한 가지, 뷰에 OnFocusChangeListener를 구현하여 Focus를 가질 때 초기화 코드 등을 수행하는 방법도 생각해볼 수 있는데, 아무래도 뷰페이저가 Focus를 가지지 않으면 이 방법은 사용하지 못할 것 같네요.

    에고, 명확한 답변을 드리지 못해 죄송합니다.

    감사합니다.

  35. Blog Icon
    헬로월드

    답변 해주셔서 감사합니다.
    말씀해주신 방법 중 하나인 뷰페이저에 리스너를 붙혀 엑티비티를 띄울 순 있었지만
    원하는 방식이 아니였고 또한

    baseActivity -> viewPager View -> nomalArtivity

    층층이 쌓는 방식이 장기적으로 적합하지 않다고 판단되어 프래드먼트로 구현했습니다.

    취미로 안드로이드를 개발하며 좋은 글 잘 읽고 있습니다.
    특히 기초부터 하나하나 짚어 주시는게 참 좋네요!

  36. 답글을 적으면서도, 좀 애매했는데... 역시 원하시는 방법은 아니었군요.
    그래도 다른 해결 방법을 찾으셔서 다행입니다.

    또 다시 궁금한 내용이 있으면 언제든 질문글 남겨주세요.
    최대한 도움드릴 수 있도록 노력하겠습니다.

    감사합니다.

  37. 많은 것을 배워갑니다

  38. 방문해 주셔서 감사합니다.

  39. 뽀따님 따뽕.......

  40. 자주 찾아주시고, 댓글 남겨주셔서 감사합니다. ^^

  41. 계좌라도 어디 넣어놓으세요.... 후원 계좌 아니면 buy me a coffee라도...
    고마워서 돈 주고싶은사람들 많을겁니다

  42. 후원 계좌나 "buy me a coffee"는 전혀 생각을 안 해봤는데요.
    누구에게 후원을 받을만큼 좋은 내용들이 담겨져 있는지 스스로 확신도 안서고요.

    흠... 그런데 후원 기능을 탑재하면, 후원이 들어오긴 할까요? ^^;;
    잘 모르겠네요...

    어쨌든 관심가져주셔서 감사합니다!

  43. Blog Icon
    후후

    안녕하세요 VIEWPAGER를 실습하고 있는 학생입니다 !
    다른곳에서도 계속 오류뜨던걸 뽀따님 게시글 보고 바로 해결했습니다 ㅎㅎ 감사합니다

    하나 궁금한것이 있습니다.
    viewpager의 getcount()가 10 일때 10을 넘어서 슬라이드를 하면 해당 view가 꺼지는 것을
    구현할 수 있는 부분이 어딘지 알고싶습니다ㅜㅜ

  44. 음.. 질문하신 내용을 제가 직접 구현해본적이 없어서 정확한 방법은 알지 못하고요.

    방향을 잡아본다면,
    ViewPager의 addOnAdapterChangeListener를 통해 페이지가 변경되었을 때 현재 페이지를 확인하여 view가 꺼지는 것을 구현하면 어떨까.. 생각이 드네요.

    약간 꼼수를 적용하여,
    전체 페이지가 10개라면, getCount()에 11을 리턴하도록 만들고요.
    페이지가 변경되었을 때 현재 페이지가 11번째 페이지라면 view가 꺼지도록 만드는 것이죠.

    일단 대략 상상해본 것인데..
    직접 구현해보시면 금방 결과를 확인할 수 있을 것 같습니다.

    감사합니다.

  45. 안녕하세요 독학하고 있는 학생인데 평소 블로그보면서 도움을 많이 받고 있습니다...!
    ViewPager를 구현하는데, Drawable과 String 1개씩 포함하고 있는 클래스를 만들어서
    ImageView와 TextView를 같이 포함하는 ViewPager를 만들려 하는데,
    제가 설명하고 있는 방식이 잘못된 방식인가요??
    코드상으론 문제가 없어보이는데 실행이 안되네요.... ㅠㅠ

  46. 질문하신 내용만으로 보자면, 큰 문제가 될만한 건 없어보입니다.
    하지만 실제 코드가 어떻게 작성되었는지 제가 확인할 수가 없어서, 정확한 해답을 드리기 어렵네요.

    단순히 실행이 안된다... 라는 것만으로는 도움드리기가 힘들고요.
    로그캣으로 확인할 수 있는 에러 메시지를 같이 올려주시면 도움드리기가 더 수월할 것 같습니다.

    감사합니다.

  47. 답장 감사합니다.... 엉뚱하게 ArrayList를 선언하고 초기화하지않아서 생긴 문제였네요.... 현재는 잘 구현했습니다!! 그리고 항상 블로그보면서 새로운거 많이 배우고 도움 많이받고 있습니다. 좋은 게시물 많이 올려주셔서 감사해요:)

  48. 네. 잘 해결하셨군요.
    개발하시다가 잘 안풀리는 게 있으면, 언제든 질문글 남겨주세요.
    도움이 될지는 모르겠지만, 같이 고민 해드리겠습니다.

    감사합니다.