【Kotlin】スワイプしてタブを切り替え(ViewPager)を最速で理解する方法(Android)

【Kotlin】スワイプしてタブを切り替え(ViewPager)を最速で理解する方法(Android)

横にスワイプして切り替えるアプリをよく見ますよね???

横スクロールをしてタブを切り替える画面の作り方を説明していきます!

メルカリとかLINE LIVEとかでも使われています。

  

おそらく、使ったことがない人はいないでしょう。

今回は、こちらを最速で理解する方法を説明していきます!

複雑にすると理解することが多くなるので、余計なものを削ぎ落とし、スクロールさせて画面を切り替えることのみをターゲットにして、説明していきます。

作るアプリのイメージ

    

ざっとですがアプリの仕様はこちら

  • アプリを起動したら3つのタブに分かれている画面を表示
  • 横スクロール、スワイプすることで、タブが切り替わります。
  • それだけだと味気ないので、切替時にステータスバー、アクションバー、タブの色を変更します。
  • 各タブの画面はFragmentにて実装します。今回の作りは1つのFragmentでも対応できますが、複数のFragmentでの対応が主になると思うので、Fragmentは3つ作ります。

Layoutファイルの定義

まずは画面のレイアウト定義の説明です。

使うものは TabLayout と ViewPager になります。ソースはこちら。

activity_main.xml

<?xml version="1.0"encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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"
   android:id="@+id/main_content"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:fitsSystemWindows="true"
    tools:context="apps.test.marketableskill.biz.viewpager.MainActivity">

    <android.support.design.widget.AppBarLayout
       android:id="@+id/appbar"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
           android:id="@+id/toolbar"
           android:layout_width="match_parent"
           android:layout_height="?attr/actionBarSize"
           android:layout_weight="1"
           android:background="?attr/colorPrimary"
           app:layout_scrollFlags="scroll|enterAlways"
           app:popupTheme="@style/AppTheme.PopupOverlay"
            app:title="@string/app_name">
        </android.support.v7.widget.Toolbar>

        <android.support.design.widget.TabLayout
           android:id="@+id/tabs"
           android:layout_width="match_parent"
            android:layout_height="wrap_content">
            <android.support.design.widget.TabItem
               android:id="@+id/tabItem"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
                android:text="@string/tab_text_1" />
            <android.support.design.widget.TabItem
               android:id="@+id/tabItem2"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
                android:text="@string/tab_text_2" />

            <android.support.design.widget.TabItem
               android:id="@+id/tabItem3"
               android:layout_width="wrap_content"
               android:layout_height="wrap_content"
                android:text="@string/tab_text_3" />
        </android.support.design.widget.TabLayout>
    </android.support.design.widget.AppBarLayout>
    <android.support.v4.view.ViewPager
       android:id="@+id/container"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>

android.support.design.widget.TabLayout がタブのレイアウトを行うViewになり、

android.support.v4.view.ViewPager がタブの切替を制御するViewになります。

次に、タブの切り替え後に表示される画面です。

Fragment_tab1.xml

<FrameLayout 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="apps.test.marketableskill.biz.viewpager.Tab1Fragment">
    <TextView
       android:layout_width="match_parent"
       android:layout_height="match_parent"
        android:text="TAB1です" />
</FrameLayout>

 

TAB2、3はTextViewのtextが違うのみです。

android:textの部分を android:text=”TAB2です”  、android:text=”TAB3です” を変更してもらえればOKです。

こちらを参考にfragment_tab2.xmlとfragment_tab3.xmlを作成してください

Advertisement

登場人物の整理

登場人物ってわけじゃないですが、登場するもののまとめです。

Fragment

タブ毎の画面はFragmentにて表示を行います。必要なタブ分のFragmentを作成する必要があります。

ViewPager

スワイプをしてタブの横スクロールを制御します。android-support-v4されているものです。

FragmentPagerAdapter

ViewPagerに渡すAdapterになります。

スクロールを制御するViewPagerはFragmentPagerAdapterにあるFragmentの情報を使って各タブの表示を行います。

実装

では、早速作ってみましょう!

Tab1Fragment

package apps.test.marketableskill.biz.viewpager
import android.content.Context
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup

class Tab1Fragment : Fragment() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }
    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        return inflater!!.inflate(R.layout.fragment_tab1, container, false)
    }
    override fun onAttach(context: Context?) {
        super.onAttach(context)
    }
    override fun onDetach() {
        super.onDetach()
    }
    companion object {
        fun newInstance(): Tab1Fragment {
            val fragment = Tab1Fragment()
            return fragment
        }
    }
}

 

特段難しいことはしていません。タブごとに文字を表示しているのみです。

こちらを参考にTab2FragmentとTab3Fragmentを作成してください。

TabsPagerAdapter

package apps.test.marketableskill.biz.viewpager
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager
import android.support.v4.app.FragmentPagerAdapter

class TabsPagerAdapter(fm: FragmentManager,
                       tabsFragments : ArrayList<Class<out Fragment>>) : FragmentPagerAdapter(fm) {

    val tabsFragments: ArrayList<Class<out Fragment>> = tabsFragments

    override fun getItem(position: Int): Fragment {
        return tabsFragments[position].newInstance()
    }

    override fun getCount(): Int {
        returntabsFragments.size
    }
}

FragmentPagerAdapterを継承する必要があります。

タブ(Fragment)情報をプロパティに保持

PagerAdapterを生成時にタブ毎のFragmentを渡してもらい、それをプロパティに保持します。

1つであれば固定でも対応できますが、複数のタブを持ち、それぞれ違うFragmentを使う際は、必要な対応です。

 

classTabsPagerAdapter(fm: FragmentManager,
                       tabsFragments : ArrayList<Class<outFragment>>) : FragmentPagerAdapter(fm) {

   valtabsFragments: ArrayList<Class<outFragment>> = tabsFragments

getItem

fragmentの切り替えを行います。渡されたPositionのFragmentを作成します。

 

override fungetItem(position: Int): Fragment {
   returntabsFragments[position].newInstance()
}

getCount

タブの最大値を設定します。保持しているリストのサイズを戻します。

override fungetCount(): Int {
   returntabsFragments.size
}

MainActivity

package apps.test.marketableskill.biz.viewpager
import android.graphics.Color
import android.os.Build
import android.support.design.widget.TabLayout
import android.support.v7.app.AppCompatActivity
import android.support.v4.view.ViewPager
import android.os.Bundle
import android.view.*
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    private varmTabsPagerAdapter: TabsPagerAdapter? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)

        //タブで利用するFragmentのリストを作成
        val tabsFragments = arrayListOf(
                Tab1Fragment::class.java,
                Tab2Fragment::class.java,
                Tab3Fragment::class.java
        )

        //Adapterの生成
        mTabsPagerAdapter = TabsPagerAdapter(supportFragmentManager, tabsFragments)

        // ViewPagerにAdapterを設定
        container.adapter = mTabsPagerAdapter

       //ViwePagerのリスナを設定
        container.addOnPageChangeListener(object : ViewPager.OnPageChangeListener{

            override fun onPageScrollStateChanged(state: Int) {
            }

            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
            }

            override fun onPageSelected(position: Int) {
                when (position) {
                    0 -> changeColor(Color.RED)
                    1 -> changeColor(Color.BLUE)
                    2 -> changeColor(Color.DKGRAY)
                }
            }
        })
        tabs.addOnTabSelectedListener(TabLayout.ViewPagerOnTabSelectedListener(container))
    }

    //ステータスバー、アクションバー、タブの色変更
    private fun changeColor(color : Int) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            window.setStatusBarColor(color)
        }
        tabs.setBackgroundColor(color)
        toolbar.setBackgroundColor(color)
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menuInflater.inflate(R.menu.menu_main, menu)
        return true
    }
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        val id = item.itemId
        if (id == R.id.action_settings) {
            return true
        }
        return super.onOptionsItemSelected(item)
    }
}

 

少し補足していきます。

Advertisement
複数のFragmentを使うため、それぞれのFragmetの情報をリストに格納
    //タブで利用するFragmentのリストを作成
    val tabsFragments = arrayListOf(
            Tab1Fragment::class.java,
            Tab2Fragment::class.java,
            Tab3Fragment::class.java
    )

ここで入れた順番でタブが、左から表示されます。

Adapterの生成
//Adapterの生成
mTabsPagerAdapter = TabsPagerAdapter(supportFragmentManager, tabsFragments)

先程作成したリスト(Fragment)も引数として渡します。

ViewPagerにAdapterの設定
    container.adapter = mTabsPagerAdapter
リスナの登録
   //ViwePagerのリスナを設定
    container.addOnPageChangeListener(object : ViewPager.OnPageChangeListener{

        override fun onPageScrollStateChanged(state: Int) {
        }

        override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
        }

        override fun onPageSelected(position: Int) {
            when (position) {
                0 -> changeColor(Color.RED)
                1 -> changeColor(Color.BLUE)
                2 -> changeColor(Color.DKGRAY)
            }
        }
    })
    tabs.addOnTabSelectedListener(TabLayout.ViewPagerOnTabSelectedListener(container))
}

リスナとしては、

  • onPageScrollStateChanged:スクロールされたときに呼び出される
  • onPageScrolled:スクロール中(特定の位置などに達した場合など)に呼び出される
  • onPageSelected:ページが切り替わり終わったときに呼び出される

があります。今回は、素直に終わった時とするため、onPageSelectedで処理を行います。

ステータスバーなどの色の変更

補足ですが、色を変更する処理はこちらです。

 

//ステータスバー、アクションバー、タブの色変更
private fun changeColor(color : Int) { 
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
        window.setStatusBarColor(color)
     }
    tabs.setBackgroundColor(color) 
    toolbar.setBackgroundColor(color)
}

はい。以上でタブの切替が実装できました。

チューニングすべきこともたくさんありますが、これでスワイプにてタブ切替ができるようになります!