Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ViewPager2について調べてみた

 ViewPager2について調べてみた

Kazuki Nishida

March 14, 2019
Tweet

Other Decks in Programming

Transcript

  1. レシピ名 public class ViewPager2 extends ViewGroup { private RecyclerView mRecyclerView;

    … private void initialize(Context context, AttributeSet attrs) { mRecyclerView = new RecyclerView(context) { … }; mRecyclerView.setId(ViewCompat.generateViewId()); mLayoutManager = new LinearLayoutManager(context); mRecyclerView.setLayoutManager(mLayoutManager); setOrientation(context, attrs); … attachViewToParent(mRecyclerView, 0, mRecyclerView.getLayoutParams()); } public final void setAdapter(@Nullable Adapter adapter) { mRecyclerView.setAdapter(adapter); } } 内部にRecyclerViewを持っている • ViewGroupを継承しており、子Viewとして RecyclerView生成する • LayoutManagerはLinerLayoutManager固定 • ViewPager2にセットしたAdapterは単純に内 部のRecyclerViewにセットされるだけ • RecyclerViewは(いまのところ)外部からは取 得する方法なし
  2. レシピ名 public class ViewPager2 extends ViewGroup { private RecyclerView mRecyclerView;

    … private void initialize(Context context, AttributeSet attrs) { … new PagerSnapHelper().attachToRecyclerView(mRecyclerView); … } } ページスナップ(1ページごとにスクロールが 止まるやつ) • PagerSnapHelperクラスで実現されているお り、PagerSnapHelper内部でRecyclerViewに 対してScrollListenerなどのコールバックを設 定している • attachToRecyclerView()のパラメータは RecyclerViewなのでViewPager2とは関係な いRecyclerViewにも適用は可能?
  3. レシピ名 viewPager.adapter = object : RecyclerView.Adapter<ViewHolder>() { override fun onCreateViewHolder(parent:

    ViewGroup, viewType: Int): ViewHolder { return ViewHolder( LayoutInflater.from(this@ViewPagerActivity1).inflate( R.layout.pager_item_text, parent, false ) ) } override fun getItemCount() = 5 override fun onBindViewHolder(holder: ViewHolder, position: Int) { val binding = DataBindingUtil.bind<PagerItemTextBinding>(holder.root) ?.apply { text = "page$position" } } } RecyclerView.Adapterを使用する例 • RecyclerView使用時と同じようなAdapterを 作ればOK • 各ページはRecyclerView.ViewHolderによっ て提供されるView
  4. レシピ名 <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="text" type="String"/>

    </data> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:text="@{text}" android:gravity="center"/> </layout> ページ側Layoutを作成する際の注意点 • ページ用に指定するLayoutを指定する場合、 ルートViewのサイズはwidth, heightともに match_parentを指定する • そうしないとIllegalStateExceptionで死ぬ
  5. レシピ名 viewPager.orientation = ViewPager2.ORIENTATION_VERTICAL <androidx.viewpager2.widget.ViewPager2 … android:orientation="vertical" … /> 縦方向にスクロールする

    • ページ用に指定するLayoutを指定する場合、 ルートViewのサイズはwidth, heightともに match_parentを指定する コードで指定 XMLで指定
  6. レシピ名 viewPager.adapter = object : FragmentStateAdapter(supportFragmentManager) { override fun getItem(position:

    Int) = PageFragment.newInstance("page$position") override fun getItemCount() = 5 } … class PageFragment : Fragment() { … override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) = DataBindingUtil.inflate<PagerItemTextBinding>(inflater, R.layout.pager_item_text, container, false).apply { text = arguments?.getString(ARG_TEXT) }.root } } FragmentStateAdapterを使用する例 • Fragmentを使用してページを作成する場合は FragmentStateAdapterを使用する • 現時点ではOffscreenPageLimitの仕組みが 存在しないため、隣接するページのFragment の生成は実際のスクロール開始時に行われる • 生成済みのページに関してはRecyclerViewで いくつかキャッシュされるが、 RecyclerView.setItemViewCacheSize()や、 LinearLayoutManager.getExtraLayoutSpace ()のOverrideでキャッシュ数を調整したりは出 来ない
  7. いまのところ未実装 • no offscreen limit control • needs better TabLayout

    integration • no pageWidth setter (forced 100%/100%) • page transformer: no hardware/software layer choice; no reverse drawing order https://developer.android.com/jetpack/androidx/releases/viewpager2 ※もうちょい色々書いてあります。 これ
  8. レシピ名 viewPager.adapter = GroupAdapter<ViewHolder>().apply { add(TextItem("page1")) add(TextItem("page2")) add(TextItem("page3")) add(TextItem("page4")) add(TextItem("page5"))

    } } private class TextItem( private val text: String ) : BindableItem<PagerItemTextBinding>() { override fun getLayout() = R.layout.pager_item_text override fun bind(viewBinding: PagerItemTextBinding, position: Int) { viewBinding.text = text } } 単純な使用例 • GroupieのGroupAdapterは RecyclerView.Adapterを継承しているので普 通に使える • 自前でAdapterを実装するよりは少し楽かもし れない。
  9. レシピ名 viewPager.adapter = GroupAdapter<ViewHolder>().apply { add(TextItem("page1")) add(ImageItem("world_press17.jpg")) add(TextItem("page3")) add(ImageItem("181126jpower.jpg")) add(TextItem("page5"))

    add(ImageItem("img_473_1488875864.jpg")) } 単純な使用例2 • 当然、異なる形のページをGroupieのItemとし て表現する事も可能 • ViewTypeが複数ある場合は自前でAdapter 作るよりもだいぶ便利かもしれない
  10. レシピ名 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) … groupLayoutManager

    = GridLayoutManager(this, groupAdapter.spanCount).apply { spanSizeLookup = groupAdapter.spanSizeLookup } ) … recyclerView.apply { layoutManager = groupLayoutManager } グリッド表示 • LayoutManagerがLinerLayoutManagerなの で実現できません!!!! Groupieのサンプルコードから これ
  11. レシピ名 viewPager.adapter = GroupAdapter<ViewHolder>().apply { add(Section().apply { setHeader(TextItem("header")) setFooter(TextItem("footer")) add(TextItem("page1"))

    add(TextItem("page2")) add(TextItem("page3")) }) } セクションヘッダー、フッター付き • 使えるけども、 Header→Item→Item→Item→Footerと表示 されるだけなので、まぁ、なんか使えるかもしれ ない…
  12. 感想 • 基本的には実績のあるRecyclerViewの実装に依存するので、alphaだけど安定性 は高いと思う • 旧ViewPagerに実装されていたpageWidthなどの設定が出来ないので、ちょっと凝っ たレイアウトの場合はまだ旧ViewPagerを使うほうがいい • Offscreen Page

    Limitの仕組み、キャッシュなどの動きを考えるとページの構成物 がImageViewだけみたいな場合にはViewPager2、Fragment内に複雑なレイアウトを 必要とする場合はViewPagerが良いのではないかな。いまのところ。