Tuesday, 21 May 2019

Kotlin for Android development essentials: ViewPager and Fragment

One of the common features in Android applications is swiping to change texts, images, buttons or complex combinations of those. In today's tutorial, I will show you how to do that in Kotlin programming language and Android studio 3.4.1.

Firstly, create a blank Android project (remember to choose Kotlin as the language) with an empty main activity; there will be two files created, MainActivity.kt for the activity and activity_main.xml for the layout of the activity.

To be able to swipe to change fragment (you can put texts, images, buttons, etc into a fragment), we need add view android.support.v4.view.ViewPager as a place holder of the fragment. We then can pass the fragment that we want to render to the ViewPager adapter which is linked to the ViewPager view.

Let's start by creating the layout for the main activity with ViewPager view on top of a simple text view. The layout code for the main activity is shown below.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<android.support.v4.view.ViewPager
android:id="@+id/vpImages"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@id/txtIntro"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<TextView
android:id="@+id/txtIntro"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="Swipe the green area above to change fragment"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/vpImages"
/>
</android.support.constraint.ConstraintLayout>

The next step is to create the image fragment that shows the name of an image. There will be two files created, ImagesFragment.kt for the fragment and fragment_images.xml for the layout of the fragment. The image fragment class can be used to return a new image fragment instance with a new image name as the input parameter; this input parameter is then used to change the content of the only text view inside the image fragment. It is important to know that a fragment needs to be inflated to a view in onCreateView function before its children views (e.g., a text view in our case) can be accessed in onViewCreated function. Let's have a look at the image fragment class and layout.
<?xml version="1.0" encoding="utf-8"?>
<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=".ImagesFragment"
android:background="@color/colorPrimaryDark">
<TextView
android:id="@+id/txt_frag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_blank_fragment"
android:layout_gravity="center"
android:textColor="@color/colorWhite"
/>
</FrameLayout>
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private const val ARG_IMAGE = "param1"
/**
* A simple [Fragment] subclass.
* Use the [ImagesFragment.newInstance] factory method to
* create an instance of this fragment.
*/
class ImagesFragment : Fragment() {
private var image: String? = null
//private var listener: OnFragmentInteractionListener? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
image = it.getString(ARG_IMAGE)
}
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_images, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
var txt_frag:TextView = view.findViewById(R.id.txt_frag)
txt_frag.text = this.image.toString()
}
companion object {
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param ARG_IMAGE Image name for the fragment.
* @return A new instance of fragment ImagesFragment.
*/
@JvmStatic
fun newInstance(imageName: String) =
ImagesFragment().apply {
arguments = Bundle().apply {
putString(ARG_IMAGE, imageName)
}
}
}
}

The next step is to create the ViewPager adapter to control which fragment will be rendered in the ViewPager view. It is important to know that there are two types of adapters for different situations, FragmentPagerAdapter (fragments are stored in memory until the activity shuts down) and FragmentStatePagerAdapter (fragments that user does not see are destroyed). The source code for the adapter is shown below.
import android.support.v4.app.Fragment
import android.support.v4.app.FragmentManager
import android.support.v4.app.FragmentStatePagerAdapter
class ViewPagerAdapter(supportFragmentManager: FragmentManager) : FragmentStatePagerAdapter(supportFragmentManager) {
override fun getItem(position: Int): Fragment {
if(position == 0)
return ImagesFragment.newInstance("I am the image of fragment 1")
else if(position == 1)
return ImagesFragment.newInstance("I am the image of fragment 2")
return ImagesFragment.newInstance("I am the image of fragment 3")
}
override fun getCount(): Int {
return 3
}
}

The final step is to go back to the main activity and create the link between the ViewPager view and the ViewPager adapter. The source code for this step is shown below.
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.v4.view.ViewPager
class MainActivity : AppCompatActivity() {
private lateinit var viewPager: ViewPager
private lateinit var pagerAdapter: ViewPagerAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewPager = findViewById(R.id.vpImages)
pagerAdapter = ViewPagerAdapter(supportFragmentManager)
viewPager.adapter = pagerAdapter
}
}
view raw MainActivity.kt hosted with ❤ by GitHub

Our app is ready to run now!!!

No comments:

Post a Comment