处理 activity 中多个片段的方向变化
Handle orientation change for multiple fragments in an activity
我有一个 activity,它有一个片段容器。
我在那个 activity 中有三个片段。当我旋转屏幕时,它会出现第一个片段。如果它是当前屏幕,我如何让它显示第二个片段。
我尝试添加这个方法Handle Fragment On Screen Orientation Changes?。
但我有错误:
java.lang.IllegalStateException: Fragment VerifyOtp{d123b5f} (79fc7cef-725d-48cb-9591-3fa9da9f864f) is not currently in the FragmentManager
MainActivity 代码:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
var binding: ActivityMainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val sharedPref = getSharedPreferences("loggedUser", Context.MODE_PRIVATE)
val userPhone = sharedPref.getString("userPhone", null)
val location = sharedPref.getString("location", null)
if(userPhone == null){
val loginFragment = LoginFragment()
supportFragmentManager.beginTransaction().replace(R.id.loginFrameLayout, loginFragment).commit()
}
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
instantiateFragment(savedInstanceState)
}
private fun instantiateFragment(inState: Bundle){
val manager = supportFragmentManager
val transaction = manager.beginTransaction()
if(inState != null){
manager.getFragment(inState, "VERIFY_OTP")
}
}
}
登录片段:
class LoginFragment : Fragment() {
private var _binding: FragmentLoginBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
_binding = FragmentLoginBinding.inflate(inflater, container, false)
val view = binding.root
ArrayAdapter.createFromResource(
view.context,
R.array.country_code,
android.R.layout.simple_spinner_item
).also { adapter ->
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
binding.countryCode.adapter = adapter
}
val fragmentVerifyOTP = VerifyOtp()
binding.btnLogin.setOnClickListener {
var userPhoneNumber = binding.userPhoneNumber.text.toString()
if(userPhoneNumber.isEmpty() || userPhoneNumber.length < 10) {
binding.userPhoneNumber.error = "Phone Number Required"
}else{
val arguments = Bundle()
arguments.putString("Phone", userPhoneNumber)
fragmentVerifyOTP.arguments = arguments
val transaction = activity?.supportFragmentManager?.beginTransaction()
if (transaction != null) {
transaction.replace(R.id.loginFrameLayout, fragmentVerifyOTP, "VERIFY_OTP")
transaction.addToBackStack("")
transaction.commit()
}
}
}
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
验证OTP:
class VerifyOtp : Fragment() {
private var _binding: FragmentVerifyOtpBinding? = null
private val binding get() = _binding!!
private lateinit var phone: String
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentVerifyOtpBinding.inflate(inflater, container, false)
val view = binding.root
val bundle = this.arguments
if(bundle != null){
phone = bundle.getString("Phone").toString()
}
val application = requireNotNull(this.activity).application
val dataSource = AppDatabase.getInstance(application)
val viewModelFactory = LoginSharedViewModelFactory(phone, dataSource)
val viewModel = ViewModelProvider(requireActivity(), viewModelFactory).get(LoginSharedViewModel::class.java)
viewModel.setPhoneNumber(phone)
viewModel.phoneNumber.observe(viewLifecycleOwner, { phone ->
binding.tvOTPPhoneNumber.text = phone
})
binding.btnVerify.setOnClickListener {
val isFieldsSet = checkAllFields()
if(isFieldsSet && viewModel.newUser()){
val transaction = fragmentManager?.beginTransaction()
if (transaction != null) {
transaction.replace(R.id.loginFrameLayout, UserDetails())
transaction.addToBackStack("")
transaction.commit()
}
}else if(isFieldsSet && !viewModel.newUser()){
insertToSharedPreference(viewModel.phoneNumber.value,
viewModel.currentUser.value?.location
)
val user = viewModel.phoneNumber.value
val intent = Intent(view.context, Home::class.java).apply {
putExtra("USER_PHONE", user)
}
startActivity(intent)
}
}
return view
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
val manager = activity?.supportFragmentManager
manager?.putFragment(outState, "VERIFY_OTP", VerifyOtp())
}
您必须在屏幕开始旋转时使用 onSaveInstanceState
保存当前屏幕,并在屏幕旋转后使用 onRestoreInstanceState
恢复片段。
首先确保将其添加到 activity。这将允许我们使用状态更改。
android:configChanges="orientation|screenSize"
然后,
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the current fragment tag or id
savedInstanceState.putString("current_fragment", SCREEN_TAG_OR_ID);
super.onSaveInstanceState(savedInstanceState);
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
current_fragment = savedInstanceState.getString("current_fragment");
// use this to show the current screen
}
我有一个 activity,它有一个片段容器。
我在那个 activity 中有三个片段。当我旋转屏幕时,它会出现第一个片段。如果它是当前屏幕,我如何让它显示第二个片段。
我尝试添加这个方法Handle Fragment On Screen Orientation Changes?。
但我有错误:
java.lang.IllegalStateException: Fragment VerifyOtp{d123b5f} (79fc7cef-725d-48cb-9591-3fa9da9f864f) is not currently in the FragmentManager
MainActivity 代码:
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
var binding: ActivityMainBinding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val sharedPref = getSharedPreferences("loggedUser", Context.MODE_PRIVATE)
val userPhone = sharedPref.getString("userPhone", null)
val location = sharedPref.getString("location", null)
if(userPhone == null){
val loginFragment = LoginFragment()
supportFragmentManager.beginTransaction().replace(R.id.loginFrameLayout, loginFragment).commit()
}
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
instantiateFragment(savedInstanceState)
}
private fun instantiateFragment(inState: Bundle){
val manager = supportFragmentManager
val transaction = manager.beginTransaction()
if(inState != null){
manager.getFragment(inState, "VERIFY_OTP")
}
}
}
登录片段:
class LoginFragment : Fragment() {
private var _binding: FragmentLoginBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
_binding = FragmentLoginBinding.inflate(inflater, container, false)
val view = binding.root
ArrayAdapter.createFromResource(
view.context,
R.array.country_code,
android.R.layout.simple_spinner_item
).also { adapter ->
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
binding.countryCode.adapter = adapter
}
val fragmentVerifyOTP = VerifyOtp()
binding.btnLogin.setOnClickListener {
var userPhoneNumber = binding.userPhoneNumber.text.toString()
if(userPhoneNumber.isEmpty() || userPhoneNumber.length < 10) {
binding.userPhoneNumber.error = "Phone Number Required"
}else{
val arguments = Bundle()
arguments.putString("Phone", userPhoneNumber)
fragmentVerifyOTP.arguments = arguments
val transaction = activity?.supportFragmentManager?.beginTransaction()
if (transaction != null) {
transaction.replace(R.id.loginFrameLayout, fragmentVerifyOTP, "VERIFY_OTP")
transaction.addToBackStack("")
transaction.commit()
}
}
}
return view
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
验证OTP:
class VerifyOtp : Fragment() {
private var _binding: FragmentVerifyOtpBinding? = null
private val binding get() = _binding!!
private lateinit var phone: String
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentVerifyOtpBinding.inflate(inflater, container, false)
val view = binding.root
val bundle = this.arguments
if(bundle != null){
phone = bundle.getString("Phone").toString()
}
val application = requireNotNull(this.activity).application
val dataSource = AppDatabase.getInstance(application)
val viewModelFactory = LoginSharedViewModelFactory(phone, dataSource)
val viewModel = ViewModelProvider(requireActivity(), viewModelFactory).get(LoginSharedViewModel::class.java)
viewModel.setPhoneNumber(phone)
viewModel.phoneNumber.observe(viewLifecycleOwner, { phone ->
binding.tvOTPPhoneNumber.text = phone
})
binding.btnVerify.setOnClickListener {
val isFieldsSet = checkAllFields()
if(isFieldsSet && viewModel.newUser()){
val transaction = fragmentManager?.beginTransaction()
if (transaction != null) {
transaction.replace(R.id.loginFrameLayout, UserDetails())
transaction.addToBackStack("")
transaction.commit()
}
}else if(isFieldsSet && !viewModel.newUser()){
insertToSharedPreference(viewModel.phoneNumber.value,
viewModel.currentUser.value?.location
)
val user = viewModel.phoneNumber.value
val intent = Intent(view.context, Home::class.java).apply {
putExtra("USER_PHONE", user)
}
startActivity(intent)
}
}
return view
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
val manager = activity?.supportFragmentManager
manager?.putFragment(outState, "VERIFY_OTP", VerifyOtp())
}
您必须在屏幕开始旋转时使用 onSaveInstanceState
保存当前屏幕,并在屏幕旋转后使用 onRestoreInstanceState
恢复片段。
首先确保将其添加到 activity。这将允许我们使用状态更改。
android:configChanges="orientation|screenSize"
然后,
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the current fragment tag or id
savedInstanceState.putString("current_fragment", SCREEN_TAG_OR_ID);
super.onSaveInstanceState(savedInstanceState);
}
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
current_fragment = savedInstanceState.getString("current_fragment");
// use this to show the current screen
}