1

我有一个应用程序,它有一个主要活动和 3 个片段。让我们将它们命名为 A、B 和 C,如下所示:

碎片

  • 片段 A 有一个 RecyclerView。当我单击 RecyclerView 中的项目时,它会将安全参数中的 Account 对象传递给显示数据的 Fragment B。

  • 在 Fragment B 中,用户可以按下编辑按钮来编辑 Account 对象,然后将他/她导航到 Fragment C。

  • 在 Fragment C 中,用户既可以按返回键取消修改并返回 Fragment B,也可以修改并按保存返回 Fragment A。

这里的问题是,如果在按下编辑按钮后,用户进行了一些修改,然后按下后退按钮而不保存,它仍然会临时修改对象(暂时因为如果我关闭应用程序然后再次打开它,对象会重置恢复到原来的状态。)。

下面是我的代码(这是一个简单的代码,只是为了重现问题):

片段 A.kt

<androidx.constraintlayout.widget.ConstraintLayout
    android:id="@+id/Layout_Fragment_Account"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

<androidx.recyclerview.widget.RecyclerView
        android:id="@+id/RecyclerView_Account"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:visibility="@{ViewModel.loadingStatus==List.LIST ? View.VISIBLE : View.GONE}"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:visibility="gone" />

...

</androidx.constraintlayout.widget.ConstraintLayout>

片段 A.kt

private fun subscribeAccounts(accounts: List<Account>) {
    val adapter = AccountAdapter(
        /* The click listener to handle account on clicks */
        AccountClickListener {
            navigateTo(AccountFragmentDirections.actionFragmentAccountToFragmentViewAccount(it))
        },
        /* The click listener to handle popup menu for each accounts */
        AccountOptionsClickListener { view, Account ->
            //View Popup
        }
    )

    binding.RecyclerViewAccount.apply {
        /*
        * State that layout size will not change for better performance
        */
        setHasFixedSize(true)

        /* Bind the layout manager */
        layoutManager = LinearLayoutManager(requireContext())

        /* Bind the adapter */
        this.adapter = adapter
    }

    /* Submits the list for displaying */
    adapter.submitList(accounts)
}

片段 B.xml

<layout 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">

<data>
    <variable
        name="Account"
        type="...Account" />
</data>

<androidx.constraintlayout.widget.ConstraintLayout
    android:id="@+id/Layout_Credential_View"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/Account_Logo"
        errorResource="@{@drawable/ic_account_placeholder}"
        imageUrl="@{Account.logoUrl}"
        loadingResource="@{@drawable/ic_image_loading}"
        android:layout_width="100dp"
        android:layout_height="100dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:src="@drawable/ic_account_placeholder" />

    <TextView
        android:id="@+id/Account_Name"
        style="@style/Locky.Text.Title5.Name"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="32dp"
        android:text="@{Account.entryName}"
        android:textAlignment="center"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/Account_Logo"
        tools:text="This can be a very very very long title toooooo" />

...

 </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

片段 B.kt

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    /* Binds the UI */
    _binding = FragmentViewAccountBinding.inflate(inflater, container, false)
    /* Instantiate the view model */
    _viewModel = ViewModelProvider(this).get(ViewAccountViewModel::class.java)
    /* Bind lifecycle owner to this */
    binding.lifecycleOwner = this

    /*
    * Fetch the account object from argument
    * Then bind account object to layout
    */
    val account = ViewAccountFragmentArgs.fromBundle(requireArguments()).accountToVIEW
    binding.account = account
    _account = account

    /* Returns the root view */
    return binding.root
}

override fun onDestroyView() {
    super.onDestroyView()
    _binding = null
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setHasOptionsMenu(true)
}

override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
    inflater.inflate(R.menu.menu_credentials_actions, menu)
    super.onCreateOptionsMenu(menu, inflater)
}

override fun onOptionsItemSelected(item: MenuItem) =
    when (item.itemId) {
        R.id.Action_Duplicate -> {
            navigateTo(
                ViewAccountFragmentDirections.actionFragmentViewAccountToFragmentAddAccount(
                    _account.apply {
                        this.id = 0
                    })
            )
            true
        }
        R.id.Action_Edit -> {
            navigateTo(
                ViewAccountFragmentDirections.actionFragmentViewAccountToFragmentAddAccount(
                    _account
                )
            )
            true
        }
        else -> false
    }

}

片段 C.xml

<layout 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">

<data>

    <import type="...Constants" />

    <variable
        name="ViewModel"
        type="...AddAccountViewModel" />
</data>

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingStart="16dp"
    android:paddingEnd="16dp"
    android:paddingBottom="16dp">

...

    <!--
    **************** Require Text Fields ****************
    -->
    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/Account_Name"
        style="@style/Locky.TextBox.Default"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:hint="@string/field_account_name"
        app:endIconMode="clear_text"
        app:helperText="@string/label_required"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/Barrier_Logo"
        app:startIconDrawable="@drawable/ic_profile">

        <com.google.android.material.textfield.TextInputEditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="textCapWords"
            android:text="@={ViewModel.entryName}" />
    </com.google.android.material.textfield.TextInputLayout>

...

</LinearLayout>

</layout>

片段 C.kt

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    _binding = FragmentAddAccountBinding.inflate(inflater, container, false)
    /* Binds the UI */
    _binding = FragmentAddAccountBinding.inflate(inflater, container, false)
    /* Instantiate the view model */
    _viewModel = ViewModelProvider(this).get(AddAccountViewModel::class.java)
    /* Bind view model to layout */
    binding.viewModel = viewModel
    /* Bind lifecycle owner to this */
    binding.lifecycleOwner = this

    /*
    * Fetch the account object from argument
    * And set it to view model for two way binding
    */
    val account = AddAccountFragmentArgs.fromBundle(requireArguments()).accountToADD
    viewModel.setAccount(account)
    /* Returns the root view */
    return binding.root
}

片段 C 视图模型

class AddAccountViewModel(application: Application) : ObservableViewModel(application) {

/**
 * Bindable two-way binding
 **/
private lateinit var _account: Account

var entryName: String
    @Bindable get() {
        return _account.entryName
    }
    set(value) {
        _account.entryName = value
        notifyPropertyChanged(BR.entryName)
    }

var logoUrl: String?
    @Bindable get() {
        return _account.logoUrl
    }
    set(value) {
        _account.logoUrl = value ?: ""
        notifyPropertyChanged(BR.logoUrl)
    }


internal fun setAccount(account: Account?) {
    this._account = account ?: Account()
}
}

导航.xml

<fragment
    android:id="@+id/Fragment_A"
    android:name="...AccountFragment"
    android:label="Accounts"
    tools:layout="@layout/fragment_account">
    <action
        android:id="@+id/action_Fragment_Account_to_BottomSheet_Fragment_Account_Filter"
        app:destination="@id/BottomSheet_Fragment_Account_Filter" />
    <action
        android:id="@+id/action_Fragment_Account_to_Fragment_View_Account"
        app:destination="@id/Fragment_View_Account" />
</fragment>

<fragment
    android:id="@+id/Fragment_B"
    android:name="...ViewAccountFragment"
    android:label="View Account"
    tools:layout="@layout/fragment_view_account">
    <action
        android:id="@+id/action_Fragment_View_Account_to_Fragment_Add_Account"
        app:destination="@id/Fragment_Add_Account" />
    <argument
        android:name="ACCOUNT_toVIEW"
        app:argType="....Account" />
</fragment>

<fragment
    android:id="@+id/Fragment_C"
    android:name="...AddAccountFragment"
    android:label="Add Account"
    tools:layout="@layout/fragment_add_account">
    <action
        android:id="@+id/action_Fragment_Add_Account_to_BottomSheet_Fragment_Account_Logo"
        app:destination="@id/BottomSheet_Fragment_Account_Logo" />
    <argument
        android:name="ACCOUNT_ToADD"
        app:argType="...Account" />
</fragment>

下面是该问题的演示:

问题演示

我尝试了一种快速破解方法,我保存了一个未修改的帐户对象的实例,在用户离开片段之前,我通过分配每个变量来重置更改,但这并不高效。我认为我在这里使用安全参数做错了什么?

有人可以帮我吗。我实在想不通这个。谢谢

4

0 回答 0