본문 바로가기

Programming/Android

[Android] ViewPager & Fragment

ViewPager & Fragment.md

ViewPager & Fragment

Fragment를 사용하는 것 중에 하나가 ViewPager와 같이 사용하는 것이다.

(보통 TabLayout과 같이 사용하는데 이번에는 TabLayout는 다루지 않는다.)

기본 예제(Basic)

  • activity_basic.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">
    <android.support.v4.view.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

  • fragment_basic.xml
 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <android.support.v7.widget.AppCompatTextView
        android:id="@+id/history"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="1"
        android:textColor="@android:color/black"
        android:gravity="center"
        style="@style/TextAppearance.AppCompat.Small"/>
    
</LinearLayout>

  • BasicActivity
 
public class BasicActivity extends FragmentActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_basic);
        ViewPager viewPager = (ViewPager) findViewById(R.id.view_pager);
        // getSupportFragmentManager를 넘겨줘야 한다
        viewPager.setAdapter(new BasicFragmentAdapter(getSupportFragmentManager()));
    }
    class BasicFragmentAdapter extends FragmentPagerAdapter {
        
        private ArrayList<Fragment> mFragment = new ArrayList<>();
        // fragment의 background에 색을 주기 위한 것이므로 없어도 상관없음.
        private String[] colors = getResources().getStringArray(R.array.default_preview);
        public BasicFragmentAdapter(FragmentManager fm) {
            super(fm);
            for(int i=0; i<5; i++) {
                mFragment.add(BasicFragment.newInstance("Fragment " + String.valueOf(i+1), colors[i]));
            }
        }
        @Override
        public Fragment getItem(int position) {
            return mFragment.get(position);
        }
        @Override
        public int getCount() {
            return mFragment.size();
        }
    }
}

  • BasicFragment
 
public class BasicFragment extends Fragment {
    private final static String PARAMS_TITLE = "params_title";
    private final static String PARAMS_BACKGROUND_COLOR = "params_background_color";
    private String title = "";
    private String colorResId;
    public static Fragment newInstance(String title, String color) {
        Bundle args = new Bundle();
        args.putString(PARAMS_TITLE, title);
        args.putString(PARAMS_BACKGROUND_COLOR, color);
        Fragment fragment = new BasicFragment();
        fragment.setArguments(args);
        return fragment;
    }
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Bundle bundle = getArguments();
        title = bundle.getString(PARAMS_TITLE, "");
        colorResId = bundle.getString(PARAMS_BACKGROUND_COLOR, "");
        Log.i(title, "onCreate()");
    }
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        Log.i(title, "onCreateView()");
        return inflater.inflate(R.layout.fragment_basic, container, false);
    }
    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        Log.i(title, "onViewCreated()");
        view.setBackgroundColor(Color.parseColor(colorResId));
        TextView tvHistory = (TextView) view.findViewById(R.id.history);
        tvHistory.setText(title);
    }
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        Log.i(title, "setUserVisibleHint(" + (isVisibleToUser ? "true" : "false") + ")");
        super.setUserVisibleHint(isVisibleToUser);
    }
}

위 예제를 실행하면 아래와 같이 나오게 된다.

결과 화면


setOffscreenPageLimit

    • 위 예제의 로그를 살펴보면 swipe 될 때마다 미리 만들어지지 않는 fragment들은 onCreate()부터 다시 호출되는 것이 보인다.


이유는 예를 들어 5개 페이지가 있다고 가정 했을 때 현재 페이지가 3 페이지이면 아래와 같이 나타나게 된다.

    1 -> |offset| 2 -> (3) -> 4 -> |offset| 5

    기본 미리 호출되는 limit이 좌우 1개씩이기 때문에 2와 4만 미리 세팅된다. 따라서 1과 5는 만들어 지게 된다.

    5 페이지를 모두 다 처음에 부르고 싶다면 java 코드에서

     
    mViewPager.setOffscreenPageLimit(5)

    로 설정하면 처음에 다 settting 되고 다음에는 setUserVisibleHint가 호출되면서 user에 visible 여부를 알 수 있다.

Clip page

다음 페이지를 미리 보여주는 UI도 구성할 수 있다.

  • Adapter에서 getPageWidth를 Override해서 수정하면 된다.
 
class BasicFragmentAdapter extends FragmentPagerAdapter {
        private ArrayList<Fragment> mFragment = new ArrayList<>();
        private String[] colors = getResources().getStringArray(R.array.default_preview);
        @Override
        public float getPageWidth(int position) {
            return getCount() == 1 ? super.getPageWidth(position) : 0.9f;
        }
        public BasicFragmentAdapter(FragmentManager fm) {
            super(fm);
            for(int i=0; i<5; i++) {
                mFragment.add(BasicFragment.newInstance("Fragment " + String.valueOf(i+1), colors[i]));
            }
        }
        @Override
        public Fragment getItem(int position) {
            return mFragment.get(position);
        }
        @Override
        public int getCount() {
            return mFragment.size();
        }
    }

결과 화면



참고