如何防止活动在更改方向后重新启动 保存状态恢复状态基本有关ViewModel

问题描述

我是使用KOTLIN进行android开发的新手,我的活动包含如下图所示的片段(图像1),问题是每当我尝试将方向从potrait更改为landscape时,活动就会返回到上一个活动(就像重新启动它一样。)

我尝试将android:configChanges="orientation|screenSize|keyboardHidden"添加到我的AndroidManifest.xml中,它可以正常工作,但是有人说不建议使用它。

你们能告诉我还是举例说明解决此问题的最佳做法?

图像1

Image 1

代码

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,savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragment_latest_visit,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
        }
    }

}

解决方法

实际上不建议防止活动在配置更改时重新启动

推荐的方法将通过saving and restoring UI stateusing ViewModel。其中任何一种都可以解决您的问题,但是最好使用ViewModel方法。


保存和恢复UI状态

保存状态

在活动开始之前,但在发出配置更改信号之后,如果要保存活动状态,则覆盖Activity.onSaveInstanceState(Bundle),如果要保存片段状态,则覆盖`Fragment.onSaveInstanceState(Bundle)。

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

恢复状态

由于配置更改而重新启动活动后,请还原先前保存的数据 并将其应用于用户界面。

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
  }
}

使用ViewModel

这种方法是Google作为Android Jetpack库的一部分引入的相对较新的方法。 而不是覆盖onSaveInstanceState(Bundle) and checking savedInstanceState for null,your data is persisted inside a ViewModel`,并且可以从配置更改中保留下来。

基本

ViewModel中初始化数据,然后从您的活动或片段中访问它们。

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.
  }
}

有关ViewModel

的更多信息

为了更好地使用ViewModel,最好向official comprehensive guide学习。

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...