How to prevent activity to restart after changing its orientation

Tags: , , , ,



I’m new to android development using KOTLIN, i have activity that contain fragments like the image below (Image 1), the problem is whenever i try to change the orientation from potrait to landscape, the activity return to the previous activity (Like restart it).

I’ve tried to add android:configChanges="orientation|screenSize|keyboardHidden" to my AndroidManifest.xml, it works perfectly but some says that it’s not recommended to use it.

Could you guys tell me or show me example the best practice to solve this problem ?

IMAGE 1

Image 1

CODE

MainActivity.kt

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        title = resources.getString(R.string.title_visit)
        loadFragment(VisitFragment())
        navigation_menu.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
    }

    private val mOnNavigationItemSelectedListener =
        BottomNavigationView.OnNavigationItemSelectedListener { item ->
            when (item.itemId) {
                R.id.menu_visit -> {
                    loadFragment(VisitFragment())
                    title = resources.getString(R.string.title_visit)
                    return@OnNavigationItemSelectedListener true
                }

                R.id.menu_customer -> {
                    loadFragment(CustomerFragment())
                    title = resources.getString(R.string.title_customer)
                    return@OnNavigationItemSelectedListener true
                }

                R.id.menu_new_merchant -> {
                    loadFragment(NewMerchantFragment())
                    title = resources.getString(R.string.title_new_merchant)
                    return@OnNavigationItemSelectedListener true
                }

                R.id.menu_history -> {
                    loadFragment(HistoryFragment())
                    title = resources.getString(R.string.title_history)
                    return@OnNavigationItemSelectedListener true
                }

                R.id.menu_profile -> {
                    loadFragment(ProfileFragment())
                    title = resources.getString(R.string.title_profile)
                    return@OnNavigationItemSelectedListener true
                }
            }
            false
    }

    private fun loadFragment(fragment: Fragment) {
        val transaction = supportFragmentManager.beginTransaction()
        transaction.replace(R.id.container, fragment)
        transaction.addToBackStack(null)
        transaction.commit()
    }
}

VisitFragment.kt

class VisitFragment : Fragment() {
    private lateinit var viewPager: ViewPager
    private lateinit var tabs: TabLayout

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_visit, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val fragmentAdapter = PagerAdapter(childFragmentManager)
        pager.adapter = fragmentAdapter
        tabs_main.setupWithViewPager(pager)
    }

    fun setNumber() {
        val tabs = tabs_main.getTabAt(0)
        val badge = tabs?.orCreateBadge
        // Customize badge
        badge?.number = 1
    }
}

LatestVisitFragment.kt

class LatestVisitFragment : Fragment() {
    lateinit var latestVisitAdapter: LatestVisitAdapter
    lateinit var recyclerView: RecyclerView
    private val testinstance: ArrayList<TestResponseItem> = ArrayList()
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_latest_visit, container, false)
    }

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

    private fun loadDataLatestVisit(){
        ApiConfig().getService()
            .getUsers()
            .enqueue(object : Callback<List<TestResponseItem>> {
                override fun onFailure(call: Call<List<TestResponseItem>>, t: Throwable) {
                    Toast.makeText(context, t.localizedMessage, Toast.LENGTH_SHORT).show()
                }

                override fun onResponse(
                    call: Call<List<TestResponseItem>>,
                    response: Response<List<TestResponseItem>>
                ) {
                    rv_latest_visit.adapter = LatestVisitAdapter(response.body())
                }

            })
    }
}

LatestVisitAdapter.kt

class LatestVisitAdapter(val data: List<TestResponseItem>?) :
    RecyclerView.Adapter<LatestVisitAdapter.MyHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyHolder {
        val v =
            LayoutInflater.from(parent.context).inflate(R.layout.item_latest_visit, parent, false)
        return MyHolder(v)
    }

    override fun getItemCount(): Int = data?.size ?: 0

    override fun onBindViewHolder(holder: LatestVisitAdapter.MyHolder, position: Int) {
        holder.bind(data?.get(position))
    }

    class MyHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun bind(get: TestResponseItem?) {
            itemView.txt_merchant_name.text = get?.name
            itemView.txt_owner_name.text = get?.email
            val address =
                "${get?.address?.street},${get?.address?.city}, ${get?.address?.suite}, ${get?.address?.zipcode}"
            itemView.txt_address.text = address
        }
    }

}

Answer

It really is not recommended to prevent activity restart on config changes.

The recommended ways would either by saving and restoring UI state or by using ViewModel. Either one of them can solve your problem but it’s better to use ViewModel approach.


Saving and restoring UI state

Saving state

Right before activity start, but right after config changes have been signaled, override Activity.onSaveInstanceState(Bundle) if you’re saving activity state, or `Fragment.onSaveInstanceState(Bundle) if you’re saving fragment state.

override fun onSaveInstanceState(outState: Bundle?) {
  // Save your data into outState data bundle. And then make sure to
  // call the super method.
  super.onSaveInstanceState(outState)
}

Restoring state

After activity restart due to config changes, restore the previously saved data and apply it to the UI.

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  // ... inflate layout, etc...
  if (savedInstanceState != null) {
    // If savedInstanceState is not null, that means this activity is restoring from
    // config changes. All your saved data in onSaveInstanceState() should be accessible
    // from savedInstanceState bundle.
    // ... restore values from savedInstanceState and apply to your views ...
  } else {
    // Initialize vie
  }
}

Using ViewModel

This approach is relatively new introduced by Google as part of Android Jetpack library. Instead of overriding onSaveInstanceState(Bundle) and checking savedInstanceStatefor null, your data is persisted inside aViewModel` and will survive from configuration changes.

Basic

Initialize your data inside the ViewModel and access them from your activity or fragment.

class MyViewModel : ViewModel() {
  var myList: List<User> = emptyList()
  var currentTabIndex: Int = 0

  init {
    // Initialize your data here...
  }
}
class MyFragment : Fragment() {
  private val model by viewModels<MyViewModel>()

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // ... inflate layout, etc...
    viewPager.setCurrentItem(model.currentTabIndex, false)
    // Fetch the values from `ViewModel` and apply to your fragment.
  }
}

More about ViewModel

For better usage of ViewModel, it’s better to learn from the official comprehensive guide.



Source: stackoverflow