如何防止更改方向后活动重新启动

huangapple go评论78阅读模式
英文:

How to prevent activity to restart after changing its orientation

问题

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

我是新手使用KOTLIN进行Android开发,我有一个包含片段的活动,就像下面的图片(图像1)一样,问题是每当我尝试从竖屏更改为横屏时,活动就会返回到先前的活动(就像重新启动一样)。

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.

我尝试将 android:configChanges="orientation|screenSize|keyboardHidden" 添加到我的 AndroidManifest.xml,它可以完美工作,但有人说不推荐使用它。

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

你们能告诉我或者示范一下解决这个问题的最佳实践吗?

英文:

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

如何防止更改方向后活动重新启动

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

}

答案1

得分: 2

以下是已翻译的内容:

不建议在配置更改时阻止活动重新启动

建议的方法要么是保存和恢复UI状态,要么是使用ViewModel。其中任何一种方法都可以解决您的问题,但最好使用ViewModel方法。


保存和恢复UI状态

保存状态

在活动启动之前,但在配置更改被通知后立即重写Activity.onSaveInstanceState(Bundle)(如果您正在保存活动状态),或者Fragment.onSaveInstanceState(Bundle)(如果您正在保存片段状态)。

override fun onSaveInstanceState(outState: Bundle?) {
  // 将数据保存到outState数据包中。然后确保调用super方法。
  super.onSaveInstanceState(outState)
}

恢复状态

在由于配置更改而导致活动重新启动后,恢复以前保存的数据并将其应用于UI。

override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  // ... 膨胀布局等...
  if (savedInstanceState != null) {
    // 如果savedInstanceState不为null,这意味着此活动正在从配置更改中恢复。
    // onSaveInstanceState()中保存的所有数据都可以从savedInstanceState捆绑包中访问。
    // ... 从savedInstanceState中恢复值并应用于您的视图...
  } else {
    // 初始化视图
  }
}

使用ViewModel

这种方法是Google作为Android Jetpack库的一部分引入的相对较新的方法。
不要覆盖onSaveInstanceState(Bundle)并检查savedInstanceState是否为null,而是将数据持久化在ViewModel中,它将在配置更改时保留。

基本

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

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

  init {
    // 在这里初始化您的数据...
  }
}
class MyFragment : Fragment() {
  private val model by viewModels<MyViewModel>()

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // ... 膨胀布局等...
    viewPager.setCurrentItem(model.currentTabIndex, false)
    // 从`ViewModel`中获取值并应用于片段。
  }
}

更多关于ViewModel的信息

为了更好地使用ViewModel,建议从官方综合指南中学习。

英文:

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.

huangapple
  • 本文由 发表于 2020年8月11日 13:40:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/63352130.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定