Viewpager2, tablayout doesn't work with lazyloader - android

I have ViewPager2, Tablayout with Lazyloder.
At the first getting in the fragment it works, dots move while loading but after I swipe from one fragment to another and back to my first fragment dots don't move anymore.
.xml
<com.agrawalsuneet.dotsloader.loaders.LazyLoader
android:id="#+id/dots_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_margin="20dp"
android:layout_marginTop="30dp"
app:lazyloader_animDur="400"
app:lazyloader_dotsDist="10dp"
app:lazyloader_dotsRadius="7dp"
app:lazyloader_firstDelayDur="150"
app:lazyloader_firstDotColor="#fff"
app:lazyloader_interpolator="#android:anim/decelerate_interpolator"
app:lazyloader_secondDelayDur="300"
app:lazyloader_secondDotColor="#fff"
app:lazyloader_thirdDotColor="#fff"
tools:ignore="MissingClass" />
fragment
#AndroidEntryPoint
class InspirationQuotesFragment :
BaseFragment<FragmentInspirationQuotesBinding, InspirationQuotesViewModel>
(FragmentInspirationQuotesBinding::inflate) {
override val vm: InspirationQuotesViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
backgroundAnimation()
getRandomPicture()
onFloatingActionClick()
}
override fun onResume() {
super.onResume()
}
private fun getRandomPicture() {
layout.floatingActionBtn.setOnClickListener {
onFloatingActionClick()
}
}
private fun onFloatingActionClick() {
layout.floatingActionBtn.animate().apply {
rotationBy(360f)
duration = 1000
}.start()
layout.ivRandomPicture.visibility = View.GONE
makeApiRequest()
}
private fun backgroundAnimation() {
val animationDrawable: AnimationDrawable = layout.rlLayout.background as AnimationDrawable
animationDrawable.apply {
setEnterFadeDuration(1000)
setExitFadeDuration(3600)
start()
}
}
private fun makeApiRequest() = lifecycleScope.launch {
vm.randomPicture
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.collect { response ->
if (response.fileSizeBytes < 600000) {
Log.d("fragment", "itGetsValue")
Glide.with(requireContext()).load(response.url)
.into(layout.ivRandomPicture)
layout.ivRandomPicture.visibility = View.VISIBLE
} else {
onFloatingActionClick()
}
}
}
}
viewpagerfragment
package com.example.learningmanager.fragments
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.View
import androidx.fragment.app.viewModels
import com.example.learningmanager.base.ui.BaseFragment
import com.example.learningmanager.databinding.FragmentViewPagerBinding
import com.example.learningmanager.fragments.inspirationquotes.ui.InspirationQuotesFragment
import com.example.learningmanager.fragments.notesmanager.ui.NotesManagerFragment
import com.example.learningmanager.fragments.setgoals.ui.SetGoalsFragment
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
#AndroidEntryPoint
class ViewPagerFragment #Inject constructor() : BaseFragment<FragmentViewPagerBinding, ViewPagerViewModel>(
FragmentViewPagerBinding::inflate
) {
override val vm: ViewPagerViewModel by viewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// viewpager with list of fragments
val fragmentList = arrayListOf<Fragment>(
InspirationQuotesFragment(),
NotesManagerFragment(),
SetGoalsFragment()
)
val adapter = ViewPagerAdapter(
fragmentList,
childFragmentManager,
lifecycle
)
layout.viewPager.adapter = adapter
// initialize tablayout with names
TabLayoutMediator(layout.tabLayout, layout.viewPager) {tab, position ->
when (position) {
0 -> tab.text = "Inspiration"
1 -> tab.text = "Notes"
2 -> tab.text = "Set Goals"
}
}.attach()
listener if tab changes and it is possible to make specific action
layout.tabLayout.addOnTabSelectedListener(object: TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
}
override fun onTabReselected(tab: TabLayout.Tab?) {
}
})
}
}
viewpageradapter
class ViewPagerAdapter(list: ArrayList<Fragment>, fm: FragmentManager, lifecycle: Lifecycle) :
FragmentStateAdapter(fm, lifecycle) {
val fragmentList = list
override fun getItemCount(): Int {
return fragmentList.size
}
override fun createFragment(position: Int): Fragment {
return fragmentList[position]
}
}
Have you got an information how to fix it or maybe change to another library?
Thank you

I ran the same sample at my end. I found out that the trick is to use initView() on onResume of a fragment. Which internally calls startLoading.
Sample code:
lateinit var dotsLoader: LazyLoader
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_test1, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
dotsLoader = view.findViewById(R.id.dots_loading)
}
override fun onResume() {
super.onResume()
dotsLoader.initView()
}

Related

How to add/remove tabs in ViewPager2 at runtime? Using only one fragment

In the app I'm testing this concept, the app starts with one tab and the user can click a button inside the fragment and add a new tab with the name of an item inside a pre-defined list of movie titles. Imagine the list has 100 titles, how can I re-use the same fragment for each tab created so I don't have to make 100 fragments (one for each title)?
This link shows how to add/remove tabs using several fragments and a sample app.
class DynamicViewPagerAdapter(
fragmentActivity: FragmentActivity,
private val titleId: Int
) : FragmentStateAdapter(fragmentActivity) {
override fun createFragment(position: Int): Fragment {
return DynamicFragment.getInstance(titleId)
}
override fun getItemCount(): Int {
return titles.size
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun containsItem(itemId: Long): Boolean {
return titles.contains(titles[itemId.toInt()])
}
fun addTab(title: String) {
titles.add(title)
notifyDataSetChanged()
}
fun addTab(index: Int, title: String) {
titles.add(index, title)
notifyDataSetChanged()
}
fun removeTab(name: String) {
titles.remove(name)
notifyDataSetChanged()
}
fun removeTab(index: Int) {
titles.removeAt(index)
notifyDataSetChanged()
}
}
You could achieve this by using one Fragment that you pass the id of the item to. The Fragment will then need to retrieve the Item from the List and display the content of that item. Here is an example pager adapter.
private inner class SamplePagerAdapter(activity: AppCompatActivity, private val itemId: Int): FragmentStateAdapter(activity) {
override fun getItemCount(): Int {
return 100
}
override fun createFragment(position: Int): Fragment {
return ItemFragment.newInstance(itemId)
}
}
I figured it out, a very important part was to keep ordinals. In the process of removing tabs, the adapter makes multiple calls to getItemId() and containsItem(). In this process the adapter uses the position of the tab and the ordinal of the item in the tab. In the example I found which uses different fragments and a predefined number of them, they use enum to get the ordinals, while I used a MutableMap (titles and keys; ordinals as values). Here's the link to the finished test app.
I used these global variable (only as a test):
// pre-defined list of titles
val testMovieTitles = listOf(
"Hulk", "Chucky", "Cruella", "Nobody", "Scar Face",
"Avatar", "Joker", "Profile", "Saw", "Ladies")
val titles = mutableListOf("All Movies")
val titlesOrdinals: MutableMap<String, Int> = mutableMapOf("All Movies" to 0)
const val MY_LOG = "MY_LOG"
The activity:
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import com.example.testmyviewpager2.*
import com.example.testmyviewpager2.databinding.ActivityDynamicViewPagerBinding
import com.google.android.material.tabs.TabLayoutMediator
class DynamicViewPagerActivity : AppCompatActivity() {
private var binding: ActivityDynamicViewPagerBinding? = null
var titleIncrementer = 0 // to use the next tile until it doesn't match one of the tabs
val activityViewPagerAdapter: DynamicViewPagerAdapter by lazy {
DynamicViewPagerAdapter(this)}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityDynamicViewPagerBinding.inflate(layoutInflater)
setContentView(binding!!.root)
setUpTabs()
addTabFabOnClick()
}
private fun setUpTabs() {
binding!!.dynamicViewPager.offscreenPageLimit = 4
binding!!.dynamicViewPager.adapter = activityViewPagerAdapter
// Set the title of the tabs
TabLayoutMediator(binding!!.dynamicTabLayout, binding!!.dynamicViewPager) { tab, position ->
tab.text = titles[position]
}.attach()
}
private fun addTabFabOnClick() {
binding!!.addTabFab.setOnClickListener {
val nextTitlePosition = titles.size - 1
val nextOrdinalId = titlesOrdinals.size - 1
var nextTitle = testMovieTitles[nextTitlePosition]
// if a title has been added before, don't add it
// new tabs cannot have the same name as old tabs
while(titles.contains(nextTitle)) {
titleIncrementer++
nextTitle = testMovieTitles[nextTitlePosition + titleIncrementer]
}
if (titleIncrementer > 0) { Log.d("${MY_LOG}Activity", "incrementer: $titleIncrementer") }
if(!titles.contains(nextTitle)) {
activityViewPagerAdapter.addTab(nextOrdinalId+1, nextTitle)
} else {
Log.d("${MY_LOG}Activity", "\t\t titles contains next title \t\t $titles $nextTitle")}
}
}
}
The re-usable fragment:
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.example.testmyviewpager2.*
import kotlinx.android.synthetic.main.fragment_dynamic.*
class DynamicFragment : Fragment() {
private var fragmentViewPagerAdapter: DynamicViewPagerAdapter? = null
private var titleToDisplay = "All Movies"
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_dynamic, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// get the adapter instance from the main activity
fragmentViewPagerAdapter = (activity as? DynamicViewPagerActivity)!!.activityViewPagerAdapter
removeButtonOnClick()
dynamic_fragment_text.text = titleToDisplay
Log.d("${MY_LOG}fragCreated", "name: ${titleToDisplay}")
super.onViewCreated(view, savedInstanceState)
}
override fun onDestroy() {
Log.d("${MY_LOG}destroyed", "\t\t\t $titles")
Log.d("${MY_LOG}destroyed", "\t\t\t $titlesOrdinals")
super.onDestroy()
}
private fun removeButtonOnClick() {
removeButton.setOnClickListener {
val numOfTabs = titles.size
if (numOfTabs > 1 && titleToDisplay != "All Movies") {
fragmentViewPagerAdapter!!.removeTab(titleToDisplay)
}
}
}
fun setTitleText(title: String) {
titleToDisplay = title
}
companion object{
//The Fragment retrieves the Item from the List and display the content of that item.
fun getInstance(titleId: Int): DynamicFragment {
val thisDynamicFragment = DynamicFragment()
val titleToDisplay = titles[titleId]
thisDynamicFragment.setTitleText(titleToDisplay)
return thisDynamicFragment
}
}
}
The ViewPager2 Adapter:
import android.util.Log
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.example.testmyviewpager2.*
class DynamicViewPagerAdapter(fragmentActivity: FragmentActivity) : FragmentStateAdapter(fragmentActivity) {
private val theActivity = DynamicViewPagerActivity()
override fun createFragment(position: Int): Fragment {
// Used this to change the text inside each fragment
return DynamicFragment.getInstance(titles.size-1)
}
override fun getItemCount(): Int {
return titles.size
}
override fun getItemId(position: Int): Long {
return titlesOrdinals[titles[position]]!!.toLong()
}
// called when a tab is removed
override fun containsItem(itemId: Long): Boolean {
var thisTitle = "No Title"
titlesOrdinals.forEach{ (k, v) ->
if(v == itemId.toInt()) {
thisTitle = k
}
}
return titles.contains(thisTitle)
}
fun addTab(ordinal: Int, title: String) {
titles.add(title)
// don't rewrite an ordinal
if(!titlesOrdinals.containsKey(title)) {
titlesOrdinals[title] = ordinal
}
notifyDataSetChanged()
Log.d("${MY_LOG}created", "\t\t\t $titles")
Log.d("${MY_LOG}created", "\t\t\t $titlesOrdinals")
}
fun removeTab(name: String) {
titles.remove(name)
notifyDataSetChanged()
Log.d("${MY_LOG}removeTab", "----------------")
}
fun removeTab(index: Int) {
titles.removeAt(index)
notifyDataSetChanged()
}
}

Swip ViewPager2 is slow

I have 7 fragments, I swip between them but it's slow, I did a test adding a fragment that just contains a textView, the swip to this fragment is fast, the difference between my fragments and this one is the amount of components inside.
class ViewPagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
FragmentStateAdapter(fragmentManager, lifecycle) {
val fragments:ArrayList<Fragment> = arrayListOf(
Fragment_Onboarding_page1(),
Fragment_Onboarding_page2(),
Fragment_Onboarding_page3(),
Fragment_Onboarding_page4(),
Fragment_Onboarding_page5(),
Fragment_Onboarding_page6(),
Fragment_page6_be_yourself()
)
override fun getItemCount(): Int {
return fragments.size
}
override fun createFragment(position: Int): Fragment {
return fragments[position]
}
}
class EcranAuthentification : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.fragment_view_pager)
supportActionBar?.hide()
initViewPager2WithFragments()
}
private fun initViewPager2WithFragments()
{
var viewPager2: ViewPager2 = findViewById(R.id.viewpager)
var adapter = ViewPagerAdapter(supportFragmentManager,lifecycle)
viewPager2.adapter = adapter
}
}
class Fragment_Onboarding_page2 : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView=inflater.inflate(R.layout.fragment__onboarding_page2, container, false)
val viewpager=activity?.findViewById<ViewPager2>(R.id.viewpager)
rootView.skip2.setOnClickListener {
viewpager?.currentItem=6
}
rootView.boutonRetour.setOnClickListener {
viewpager?.currentItem=0
}
return rootView
}
}
Please can you help me

When I press the back button or make the gesture to go back, it does not reload the ViewPager properly

I'm using Navigation Component, although I don't think that's the problem. The thing is that when I'm in a fragment that contains a ViewPager and I navigate to another one, when I go back using the back button or the gesture of the mobile phone, it returns to the previous fragment but it stops showing the ViewPager. I'll leave you my code for that fragment:
class HomeFragment : Fragment() {
private lateinit var homeFragmentViewModel: HomeFragmentViewModel
private var listAdapter: FlagsListAdapter? = null
private var regionName: String? = null
private val hashtagLabel: TextView by lazy { home_fragment__label__hashtag }
private val flagViewPager: ViewPager by lazy { home_fragment__viewpager__countries }
private val countryLabel: TextView by lazy { home_fragment__label__country_name }
private val showCasesButton: Button by lazy { home_fragment__button__country_cases }
companion object {
fun newInstance(): HomeFragment {
return HomeFragment()
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_home, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
homeFragmentViewModel = ViewModelProvider(this).get(HomeFragmentViewModel::class.java)
homeFragmentViewModel.getCountriesFlagLiveData().observeOnce(viewLifecycleOwner, Observer {
setFlagsAdapter(it)
})
showCasesButton.setOnClickListener {
val actionNavigateToShowCasesFragment = HomeFragmentDirections.navigateHomeFragmentToShowCasesFragment()
regionName?.let { regionName -> actionNavigateToShowCasesFragment.regionName = regionName }
it.findNavController().navigate(actionNavigateToShowCasesFragment)
}
setFormatHashtag()
}
private fun setFlagsAdapter(flagModelList: List<FlagModel>) {
listAdapter = context?.let {
FlagsListAdapter(
flagModelList,
it
)
}
flagViewPager.adapter = listAdapter
flagViewPager.setPadding(130, 0, 130, 0)
flagViewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) {
Toast.makeText(GlobalApplication.getContextFromApplication, "Hola", Toast.LENGTH_SHORT).show()
}
override fun onPageScrolled(
position: Int,
positionOffset: Float,
positionOffsetPixels: Int
) {
countryLabel.text = ""
countryLabel.text = flagModelList[position].regionName
regionName = flagModelList[position].regionName
}
override fun onPageSelected(position: Int) {
countryLabel.text = flagModelList[position].regionName }
})
}
private fun setFormatHashtag() {
val text = getString(R.string.home_fragment_hashtag)
val spannableString = SpannableString(text)
val foregroundColorSpan = context?.let {
ForegroundColorSpan(ContextCompat.getColor(it, R.color.hashtagColor))
}
spannableString.setSpan(foregroundColorSpan, 0, 8, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
hashtagLabel.text = spannableString
}
}
This is my activity:
class MainActivity : AppCompatActivity() {
private val navigationBottomBar by lazy { activity_main__navigation_view__bottom_bar }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setUpNavigation()
}
private fun setUpNavigation() {
val navController = Navigation.findNavController(this, R.id.activity_main__graph__nav_host)
NavigationUI.setupWithNavController(navigationBottomBar, navController)
}
}
When you load the fragment the first time it is shown like this, which is how it should be shown, and if I use the Bottom Navigation View it does well too:
But when I use the back button on my phone, here's what happens:
The problem is in your HomeFragment,setFlagsAdapter(it)` doesn't get called when you come back to this fragment. Either
change observeOnce to observe
or
Move
homeFragmentViewModel.getCountriesFlagLiveData().observeOnce(viewLifecycleOwner, Observer {
setFlagsAdapter(it)
})
from onCreateView to
override fun onResume(){
super.onResume()
homeFragmentViewModel.getCountriesFlagLiveData().observeOnce(viewLifecycleOwner, Observer {
setFlagsAdapter(it)
})
}

Null Pointer within Fragment using Synthetic

I am getting null pointers (sometimes) on views within fragments using synthetic.
I do not know what is wrong because I am declaring the fragments in XML and I think that when I call the populate method from the Activity, the root view is already created
Anyway, I do not believe is correct check this each time I call the populate...
I write an example code of what happens (the null pointer would be on tv):
The fragment:
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import kotlinx.android.synthetic.main.fragment_foo.*
class FooFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_foo, container, false)
}
fun populate(fooName: String) {
tv.text = fooName
}
}
And the XML related within the XML of the Activity related
<fragment
android:id="#+id/fFoo"
android:name="com.foo.FooFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:layout="#layout/fragment_foo"/>
This would the Activity related:
class FooActivity : AppCompatActivity() {
private lateinit var fooFragment: FooFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_foo)
fooFragment = fFoo as FooFragment
}
private fun loadDataProductionInfo(productionId: Long) {
FooAPIParser().getFoo {
initFoo(it.fooName)
}
}
private fun initFoo(fooName: String) {
fooFragment.populate(fooName)
}
}
And finally the XML of the specific fragment:
<androidx.constraintlayout.widget.ConstraintLayout
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:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/tvFoo"
style="#style/Tv"/>
</androidx.constraintlayout.widget.ConstraintLayout>
Kotlin synthetic properties are not magic and work in a very simple way. When you access btn_K, it calls for getView().findViewById(R.id.tv)
The problem is that you are accessing it too soon getView() returns null in onCreateView. Try doing it in the onViewCreated method:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
//Here
}
}
Using views outside onViewCreated.
tv?.text = "kotlin safe call"
If you are going to use Fragments you need to extend FragmentActivity
Thinking in your answers I have implemented a patch. I do not like very much, but I think it should work better than now. What do you think?
I would extend each Fragment I want to check this from this class:
import android.os.Bundle
import android.os.Handler
import android.view.View
import androidx.fragment.app.Fragment
open class BaseFooFragment : Fragment() {
private var viewCreated = false
private var attempt = 0
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewCreated = true
}
fun checkViewCreated(success: () -> Unit) {
if (viewCreated) {
success()
} else {
initLoop(success)
}
}
private fun initLoop(success: () -> Unit) {
attempt++
Handler().postDelayed({
if (viewCreated) {
success()
} else {
if (attempt > 3) {
return#postDelayed
}
checkViewCreated(success)
}
}, 500)
}
}
The call within the Fragment would be more or less clean:
fun populate(fooName: String) {
checkViewCreated {
tv.text = fooName
}
}
Finally, I have received help to find out a better answer.
open class BaseFooFragment : Fragment() {
private var listener: VoidListener? = null
private var viewCreated = false
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewCreated = true
listener?.let {
it.invoke()
listener = null
}
}
override fun onDestroyView() {
viewCreated = false
super.onDestroyView()
}
fun doWhenViewCreated(success: VoidListener) {
if (viewCreated) {
success()
} else {
listener = success
}
}
}
The VoidListener is simply this:
typealias VoidListener = () -> Unit
One way to do this more generic (for example, when you want to use more than one listener) could be like this:
open class BaseFooFragment : Fragment() {
private val listeners: MutableList<VoidListener> = mutableListOf()
private var viewCreated = false
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewCreated = true
listeners.forEach { it() }
listeners.clear()
}
override fun onDestroyView() {
viewCreated = false
super.onDestroyView()
}
fun doWhenViewCreated(success: VoidListener) {
if (viewCreated) {
success()
} else {
listeners.add(success)
}
}
}

Getting view Null in fragment when onPageSelected() of ViewPager called Kotlin

Using Kotlin Android Extensions for views initialization
Activity Code for PageSelected:
introPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
}
override fun onPageSelected(position: Int) {
when(position) {
0 -> (pagerAdapter.getItem(position) as SearchFragment).startViewAnimations()
}
}
override fun onPageScrollStateChanged(state: Int) {
}
})
Fragment Method:
fun startViewAnimations() {
rippleBack?.startRippleAnimation()
centerImage?.startAnimation(AnimationUtils.loadAnimation(activity, R.anim.rotate_indefinitely))
val handler = Handler()
handler.postDelayed({
relAllViews?.visibility = View.VISIBLE
relAllViews?.animate()?.alpha(1.0f)
rippleBack?.animate()?.alpha(0.0f)
rippleBack?.visibility = View.GONE
rippleBack?.stopRippleAnimation()
centerImage?.clearAnimation()
}, 3500)
}
If I use the same function in the Fragment's onViewCreated() it works fine code sample:
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
startViewAnimations()
}
Full Fragment Code:
package com.beeland.consumer.fragment.appintro
import android.os.Bundle
import android.os.Handler
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.animation.AnimationUtils
import com.beeland.consumer.R
import com.beeland.consumer.utils.Utils
import kotlinx.android.synthetic.main.fragment_search.*
/**
* #author Yash
* #since 23-04-2018.
*/
class SearchFragment : Fragment() {
/**
* Method to replace layouts in App Intro Section
*
* #return IntroFragment's new instance
*/
fun newInstance(): SearchFragment {
val intro = SearchFragment()
val args = Bundle()
args.putString("", "")
intro.arguments = args
return intro
}
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater!!.inflate(R.layout.fragment_search, container, false)
}
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
startViewAnimations()
}
fun startViewAnimations() {
rippleBack?.startRippleAnimation()
centerImage?.startAnimation(AnimationUtils.loadAnimation(activity, R.anim.rotate_indefinitely))
val handler = Handler()
handler.postDelayed({
relAllViews?.visibility = View.VISIBLE
relAllViews?.animate()?.alpha(1.0f)
rippleBack?.animate()?.alpha(0.0f)
rippleBack?.visibility = View.GONE
rippleBack?.stopRippleAnimation()
centerImage?.clearAnimation()
handler.postDelayed({
Utils.bounceAnimationVisible(txt5Km)
}, 300)
handler.postDelayed({
Utils.bounceAnimationVisible(txtPostalDelivery)
}, 600)
handler.postDelayed({
Utils.bounceAnimationVisible(img1)
}, 900)
handler.postDelayed({
Utils.bounceAnimationVisible(img2)
}, 1200)
handler.postDelayed({
Utils.bounceAnimationVisible(img3)
}, 1500)
handler.postDelayed({
Utils.bounceAnimationVisible(imgRes1)
Utils.bounceAnimationVisible(imgRes2)
Utils.bounceAnimationVisible(imgRes3)
Utils.bounceAnimationVisible(imgRes4)
}, 1800)
}, 3500)
}
}
View pager by default caches only 3 fragments. Current, previous and next.
When you move to some page all pages which are out of this range call onDestroyView() and the view is destroyed i.e. it becomes null. When this page gets into the range again it must create the view again thus calling onCreateView(). The view is null while the it is creating
Range in which fragments (actually views) will are cached can be customized with setOffscreenPageLimit(int limit).
Hope this solves your problem.

Categories

Resources