Android Basic

ViewPager

hik14 2021. 2. 22. 17:18

swipe 뷰를 사용하면 손가락의 가로 동작이나 스와이프로 탭과 같은 동위 화면 간을 탐색할 수 있습니다.

이러한 탐색 패턴을 가로 페이징이라고도 합니다. 

구성요소 

 

1. Viewpager

- 각각의 페이지(Pagefragment)를 보여줄 view에 해당하며, 각 페이지들이 표시될 activity 또는 fragment 내에 선언된다.  

 

2. PagerAdapter

- 각 페이지들을 설정하고 순서 및 기본적인 셋팅(데이터)을 하여 viewPager에게 pageFragment를 제공한다.

 

3. PageFragment

- 실제 1개의 개별 화면을 구성하는 페이지이다.

 

 

FragmentPagerAdapter 

 

- 적고 고정된 수의 페이지 간을 탐색할 때 사용합니다.

- 모든 페이지(Fragment)를 메모리에 유지킨다(페이지가 많다면 메모리 소비가 심함)

- 페이지 수가 적어 모두 메모리에 생성되어 있다면 사용자가 이전 페이지와 앞 페이지를 지속적으로 탐색을 해야되는 경우, 유용하다.

 

 

FragmentStatePagerAdapter 

 

- 페이지의 수가 많거나 그 수가 불분명 하지만 많을것으로 예상되는 (예 전자책) 페이징할 때 사용합니다. 

- 사용자가 다른 곳을 탐색할 때 프래그먼트를 삭제하여 메모리 사용량을 최적화합니다

- 이전 페이지, 현재 페이지 ,다음 페이지 3개의 페이지를 생성하여 메모리유지한다. 

 

childFragmentManger( fragment - childFragment 관리)

developer.android.com/guide/fragments/fragmentmanager?hl=ko

 

프래그먼트 관리자  |  Android 개발자  |  Android Developers

참고: Navigation 라이브러리를 사용하여 앱의 탐색을 관리하는 것이 좋습니다. 프레임워크는 프래그먼트, 백 스택, 프래그먼트 관리자 사용에 관한 권장사항을 따릅니다. Navigation에 관한 자세한

developer.android.com

구현 

 

1.  페이저가 구현 될 Fragment

class NoteListFragment: Fragment() {

    private val TAG = "MainActivity"

    private lateinit var binding: FragmentNoteListBinding
    private val activityViewModel: NotesViewModel by viewModels()

    private lateinit var normalListFragment: NormalNoteListFragment
    private lateinit var workNoteListFragment: WorkNoteListFragment

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        binding = DataBindingUtil.inflate<FragmentNoteListBinding>(inflater, R.layout.fragment_note_list, container,false)
        binding.noteViewModel = activityViewModel

        normalListFragment = NormalNoteListFragment()
        workNoteListFragment = WorkNoteListFragment()

        Log.d(TAG, "NoteListFragment onCreateView: call")
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        // fragment view 생성이 된후 viewPager setting
        val pagerAdapter = NoteListPagerAdapter(childFragmentManager)
        pagerAdapter.addItem(normalListFragment)
        pagerAdapter.addItem(workNoteListFragment)
        binding.mViewPager.adapter = pagerAdapter
        
        //TapLayout 설정.
        binding.mTabLayout.setupWithViewPager(mViewPager)

        Log.d(TAG, "NoteListFragment onViewCreated: call")
    }
   }
<?xml version="1.0" encoding="utf-8"?>

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="noteViewModel"
            type="com.example.notesapp.NotesViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".NoteListFragment">

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/mTabLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <androidx.viewpager.widget.ViewPager
            android:id="@+id/mViewPager"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="1.0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/mTabLayout" />

        <com.google.android.material.floatingactionbutton.FloatingActionButton
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_margin="32dp"
            android:src="@drawable/ic_baseline_add_24"
            android:onClick="@{noteViewModel :: navigationToAddNotesFragment}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

 

2. Adapter

class NoteListPagerAdapter(val fm: FragmentManager): FragmentPagerAdapter(fm) {

    var items = ArrayList<Fragment>()

    // 페이지 개수
    override fun getCount(): Int = 2

    // 데이터 설정 및 페이지 반환
    override fun getItem(position: Int): Fragment = items[position]

    // 페이지 이름
    override fun getPageTitle(position: Int): CharSequence {
        return "Page${(position + 1)}"
    }

    // 페이지 1개 추가
    fun addItem(item: Fragment) {
        items.add(item)
    }

    // 페이지 list로 추가
    fun addItems(items: ArrayList<Fragment>){
        this.items =  items
    }
}

 

3. 각각 페이지가 될 Fragment

class NormalNoteListFragment : Fragment() {

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_private_note_list, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

    }
}

class WorkNoteListFragment : Fragment() {

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.fragment_normal_note_list, container, false)
    }
    
}

**LifeCycle

 

mainActivity 위에 NoteFragment 설정 후 NoteFragment안에 페이저를 구현하였다.

일반적인 NoteFragment가 활성상태(onResume) 이후 페이지(Fragment) 생명주기가 순차적으로 실행된다

페이저가 있는 프래그먼트에서 다른 프래그먼트로 이동시.

**각각의 페이지에 viewModel을 연결해보자

 

mainActivity

private val mViewModel: NotesViewModel by viewModels()

하위 및 손자 프래그먼트

private val activityViewModel: NotesViewModel by activityViewModels()

class MainActivity : AppCompatActivity() {

    private val TAG = "lifecycle"
    private val VTAG = "viewModel"

    private lateinit var mBinding: ActivityMainBinding
    private lateinit var navController: NavController
    private lateinit var appBarConfiguration: AppBarConfiguration
    private val mViewModel: NotesViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        mBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        mBinding.viewModel = mViewModel

        Log.d(TAG, "MainActivity onCreate: call")
        Log.d(VTAG, "MainActivity ${mViewModel.getRef()}")
    }
}

class NoteListFragment: Fragment() {

    private val TAG = "lifecycle"
    private val VTAG = "viewModel"

    private lateinit var binding: FragmentNoteListBinding
    private val activityViewModel: NotesViewModel by activityViewModels()

    private lateinit var normalListFragment: NormalNoteListFragment
    private lateinit var workNoteListFragment: WorkNoteListFragment

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {

        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_note_list, container,false)
        binding.noteViewModel = activityViewModel


        normalListFragment = NormalNoteListFragment()
        workNoteListFragment = WorkNoteListFragment()

        Log.d(TAG, "NoteListFragment onCreateView: call")
        Log.d(VTAG, "NoteListFragment ${ activityViewModel.getRef()}")
        return binding.root
    }
    
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        // fragment view 생성이 된후 viewPager setting
        val pagerAdapter = NoteListPagerAdapter(childFragmentManager)
        pagerAdapter.addItem(normalListFragment)
        pagerAdapter.addItem(workNoteListFragment)
        binding.mViewPager.adapter = pagerAdapter

        //TapLayout 설정.
        binding.mTabLayout.setupWithViewPager(mViewPager)

        Log.d(TAG, "NoteListFragment onViewCreated: call")
    }
}

class NormalNoteListFragment : Fragment() {

    private val TAG = "lifecycle"
    private val VTAG = "viewModel"

    private lateinit var binding: FragmentNormalNoteListBinding
    private val activityViewModel: NotesViewModel by activityViewModels()

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {

        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_normal_note_list, container, false)
        binding.noteViewModel = activityViewModel

        Log.d(TAG, "NormalNoteListFragment onCreateView: call")
        Log.d(VTAG, "NormalNoteListFragment ${activityViewModel.getRef()}")
        return binding.root
    }
}

class WorkNoteListFragment : Fragment() {

    private val TAG = "lifecycle"
    private val VTAG  = "viewModel"

    private lateinit var binding: FragmentWorkNoteListBinding
    private val activityViewModel: NotesViewModel by activityViewModels()



    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {

        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_work_note_list, container, false)
        binding.noteViewModel = activityViewModel

        Log.d(TAG, "WorkNoteListFragment onCreateView: call")
        Log.d(VTAG, "WorkNoteListFragment ${activityViewModel.getRef()}")
        return binding.root
    }
}

동일한 참조를 얻음.