在使用数据绑定和 MVVM 将新数据放入 activity 之前,旧视图数据会短暂出现在 activity 中

Old view data appears in activity briefly, before new data is put into activity using databinding and MVVM

我有两个活动- 一、产品清单 B. 产品详情

每当用户单击产品项目时,他都会转到产品详细信息 activity,其中会调用 API 以显示该产品的详细信息。

现在的问题是,第一次,Product Detail 中的视图最初没有数据,我在数据绑定的帮助下使用 viewmodel 中的 livedata 膨胀视图中的数据。现在,当用户导航回 Product List 时,将在 Product Detail Activity 上调用 finish。 Mow 当用户再次点击另一个产品时,这一次当他被带到产品详细信息屏幕时,在从 API 获取新产品详细信息并绑定到视图之前,旧产品详细信息会短暂出现。我不知道是什么导致了这种行为。有人能给我指出正确的方向吗?

我已经尝试调用 finish 和 System.gc,但不知何故,视图会在新数据膨胀之前保留旧数据。

产品列表Activity



import android.content.Intent
import android.os.Bundle
import android.os.Parcelable
import androidx.core.view.GravityCompat
import androidx.appcompat.app.ActionBarDrawerToggle
import android.view.MenuItem
import android.view.View
import androidx.drawerlayout.widget.DrawerLayout
import com.google.android.material.navigation.NavigationView
import android.widget.TextView
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import com.app.thingle.R
import com.app.thingle.databinding.ActivitySideMenuBinding

import com.app.thingle.data.model.dashBoardDataPackage.DashBoardStatus
import com.app.thingle.data.model.dashBoardDataPackage.Product
import com.app.thingle.utility.Constants
import com.app.thingle.viewModel.DashboardViewModel
import kotlinx.android.synthetic.main.activity_side_menu.view.*
import kotlinx.android.synthetic.main.app_bar_side_menu.view.*
import java.util.ArrayList

class SideMenuActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedListener {
    lateinit var sideMenuBinding: ActivitySideMenuBinding
    lateinit var dashboardViewModel: DashboardViewModel
    private lateinit var cartTextView: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        sideMenuBinding = DataBindingUtil.setContentView(this, R.layout.activity_side_menu)
        dashboardViewModel = ViewModelProviders.of(this).get(DashboardViewModel::class.java)
        sideMenuBinding.viewModel = dashboardViewModel
        dashboardViewModel.getProductsFromServer()
        sideMenuBinding.navView.setNavigationItemSelectedListener(this)
        val toggle = ActionBarDrawerToggle(
            this,
            sideMenuBinding.drawerLayout,
            sideMenuBinding.appBarSideMenu.mainToolbar,
            R.string.navigation_drawer_open,
            R.string.navigation_drawer_close
        )
        toggle.isDrawerIndicatorEnabled = false
        sideMenuBinding.drawerLayout.addDrawerListener(toggle)
        toggle.syncState()
        if (!dashboardViewModel.isUser()) {
            sideMenuBinding.drawerLayout.nav_view.menu.findItem(R.id.nav_log_out).isVisible = false
        }
        sideMenuBinding.appBarSideMenu.mainToolbar.toolbar_action_holder.menu_icon_id.setOnClickListener {
            sideMenuBinding.drawerLayout.openDrawer(GravityCompat.START)
        }
        cartTextView =
            sideMenuBinding.appBarSideMenu.mainToolbar.toolbar_action_holder.cart_holder.cart_amount
        setUpBindings()

    }

    private fun setUpBindings() {
        handleActivityStatus()
        handleLoaderState()
        handleApiResponse()
        setUpListUpdate()
        setUpCartStatus()
        setUpListClicks()

    }


    private fun setUpListUpdate() {
        dashboardViewModel.getProductsByType().observe(this, Observer {
            dashboardViewModel.setItemsInAdapter(it)
        })
    }

    private fun setUpCartStatus() {
        dashboardViewModel.getCartCount().observe(this, Observer {
            if (it > 0) {
                cartTextView.visibility = View.VISIBLE
                cartTextView.text = it.toString()

            }
        })

    }

    private fun handleApiResponse() {
        dashboardViewModel.getApiResponse().observe(this, Observer { handleResponse(it) })
    }

    private fun handleLoaderState() {
        dashboardViewModel.getUiState().observe(this, Observer { handleState(it) })

    }

    private fun handleActivityStatus() {
        dashboardViewModel.getDashboardStatus().observe(this, Observer {
            when (it) {
                DashBoardStatus.WRONG_PRODUCT_TYPE -> {
                    sideMenuBinding.appBarSideMenu.mainContent.noResultFoundLayout.visibility =
                        View.VISIBLE
                    sideMenuBinding.appBarSideMenu.mainContent.recyclerText.visibility = View.GONE
                }
                DashBoardStatus.GO_TO_CART_SCREEN -> {
                    showToast("Go to Cart")
                }
                DashBoardStatus.GO_TO_SEARCH_SCREEN -> {
                    this.startActivity(
                        Intent(this, SearchScreen::class.java)
                    )
                }
                DashBoardStatus.EMPTY_LIST -> {
                    sideMenuBinding.appBarSideMenu.mainContent.noResultFoundLayout.visibility =
                        View.VISIBLE
                    sideMenuBinding.appBarSideMenu.mainContent.recyclerText.visibility = View.GONE
                }
                DashBoardStatus.FETCH_API_SUCCESS -> {
                    sideMenuBinding.appBarSideMenu.mainContent.recyclerText.visibility =
                        View.VISIBLE
                }
                DashBoardStatus.FETCH_API_ERROR -> {
                    sideMenuBinding.appBarSideMenu.mainContent.noResultFoundLayout.visibility =
                        View.VISIBLE
                    sideMenuBinding.appBarSideMenu.mainContent.recyclerText.visibility = View.GONE
                }
                DashBoardStatus.NO_CONNECTION -> {
                    showToast(getString(R.string.no_network))
                }
                else -> showToast(getString(R.string.something_went_wrong))

            }
        })
    }

    private fun setUpListClicks() {
        dashboardViewModel.getSelectedProductType().observe(this, Observer {
            this.startActivity(
                Intent(this, BooksCategoryActivity::class.java)
                    .putParcelableArrayListExtra(
                        Constants.PRODUCT_LIST,
                        it.Products as ArrayList<Product>
                    )
                    .putExtra(Constants.CATEGORY_NAME, it.name)
                    .putExtra(Constants.ID, it.id)
            )
        })

        dashboardViewModel.getSelectedProduct().observe(this, Observer {
            this.startActivity(
                Intent(this, BookDetailActivity::class.java)
                    .putExtra(Constants.PRODUCT_ID, it.id)
            )
        })

    }


    override fun onNavigationItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            R.id.nav_borrows -> {
                if (dashboardViewModel.isUser()) {
                    this.startActivity(Intent(this, BorrowHistoryActivity::class.java))
                } else {
                    goToLogin()
                }
            }
            R.id.nav_contributions -> {
                if (dashboardViewModel.isUser()) {
                    this.startActivity(Intent(this, ContributionHistoryActivity::class.java))
                } else {
                    goToLogin()
                }
            }
            R.id.nav_profile -> {
                if (dashboardViewModel.isUser()) {
                    this.startActivity(Intent(this, EditProfileActivity::class.java))
                } else {
                    goToLogin()
                }

            }
            R.id.nav_help_contact -> {
                if (dashboardViewModel.isUser()) {
                    this.startActivity(Intent(this, ContactUsActivity::class.java))
                } else {
                    goToLogin()
                }

            }
            R.id.nav_log_out -> {
                logOut()
            }

        }

        val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
        drawerLayout.closeDrawer(GravityCompat.START)
        return true
    }

    private fun goToLogin() {
        this.startActivity(Intent(this, LoginActivity::class.java))
        finishAffinity()
    }

    private fun logOut() {
        dashboardViewModel.clearUserData()
        goToLogin()
    }

    override fun onBackPressed() {
        val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
        if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
            drawerLayout.closeDrawer(GravityCompat.START)
        } else {
            this.startActivity(Intent(this, ChooseTypeActivity::class.java))
            killActivity()
        }
    }

    private fun killActivity() {
        this.finishAffinity()
    }


}

布局- 产品详细信息

<?xml version="1.0" encoding="utf-8"?>
<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="viewModel"
            type="com.app.thingle.viewModel.ProductDetailViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/main_layout_holder"
        tools:context=".ui.activities.BookDetailActivity">

        <include
            android:id="@+id/about_book_toolbar"
            layout="@layout/thingle_search_and_cart_toolbar" />

        <FrameLayout
            android:id="@+id/product_not_available_holder"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:elevation="3dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/about_book_toolbar">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@color/lightPink"
                android:fontFamily="@font/avenirltstd_book"
                android:gravity="center"
                android:padding="5dp"
                android:visibility="gone"
                android:text="@string/this_product_is_currently_unavailable"
                android:textColor="@color/white"
                android:textSize="14sp" />

        </FrameLayout>

        <ScrollView
            android:id="@+id/about_product_scrollView"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/product_not_available_holder">

            <androidx.constraintlayout.widget.ConstraintLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content">


                <ImageView
                    android:id="@+id/book_image_id"
                    android:layout_width="100dp"
                    android:layout_height="140dp"
                    android:layout_marginStart="20dp"
                    android:layout_marginTop="20dp"
                    android:scaleType="centerCrop"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toTopOf="parent" />

                <TextView
                    android:id="@+id/book_name_id"
                    app:layout_constraintVertical_chainStyle="packed"
                    app:layout_constraintVertical_bias="0.1"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="20dp"
                    android:layout_marginEnd="20dp"
                    android:fontFamily="@font/avenirltstd_black"
                    android:textColor="@color/black"
                    android:textSize="22sp"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toEndOf="@+id/book_image_id"
                    app:layout_constraintTop_toTopOf="@id/book_image_id"
                    app:layout_constraintBottom_toTopOf="@id/book_intro"
                    />

                <TextView
                    android:id="@+id/book_intro"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="20dp"
                    android:layout_marginTop="10dp"
                    android:layout_marginEnd="20dp"
                    android:fontFamily="@font/avenirltstd_book"
                    android:textColor="@color/black"
                    android:textSize="18sp"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toEndOf="@+id/book_image_id"
                    app:layout_constraintTop_toBottomOf="@id/book_name_id"
                    app:layout_constraintBottom_toBottomOf="@id/book_image_id"
                    />

                <Button
                    android:id="@+id/add_cart_id"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="20dp"
                    android:layout_marginTop="20dp"
                    android:background="@drawable/pink_button_border"
                    android:fontFamily="@font/avenirltstd_book"
                    android:onClick="@{viewModel::onAddCartButtonClicked}"
                    android:text="@string/add_to_cart"
                    android:textAllCaps="false"
                    android:textColor="@color/white"
                    android:textSize="18sp"
                    app:layout_constraintEnd_toStartOf="@id/share_id"
                    app:layout_constraintHorizontal_weight="2"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toBottomOf="@id/book_image_id" />

                <Button
                    android:id="@+id/share_id"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="20dp"
                    android:layout_marginTop="20dp"
                    android:layout_marginEnd="20dp"
                    android:background="@drawable/pink_button_border"
                    android:fontFamily="@font/avenirltstd_book"
                    android:onClick="@{viewModel::onShareButtonClicked}"
                    android:text="@string/share"
                    android:textAllCaps="false"
                    android:textColor="@color/white"
                    android:textSize="18sp"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintHorizontal_weight="1"
                    app:layout_constraintStart_toEndOf="@id/add_cart_id"
                    app:layout_constraintTop_toBottomOf="@+id/book_image_id" />

                <View
                    android:id="@+id/line_id"
                    android:layout_width="0dp"
                    android:layout_height="0.1dp"
                    android:layout_marginTop="20dp"
                    android:background="@color/grey"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toBottomOf="@id/add_cart_id" />

                <TextView
                    android:id="@+id/about_book_info"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="20dp"
                    android:layout_marginTop="15dp"
                    android:fontFamily="@font/avenirltstd_book"
                    android:text="@string/about"
                    android:textColor="@color/black"
                    android:textSize="22sp"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toBottomOf="@id/line_id" />

                <TextView
                    android:id="@+id/text_info_book"
                    android:layout_width="0dp"
                    android:layout_height="wrap_content"
                    android:layout_marginStart="20dp"
                    android:layout_marginTop="10dp"
                    android:layout_marginEnd="20dp"
                    android:fontFamily="@font/avenirltstd_book"
                    android:lineSpacingExtra="@dimen/line_spacing"
                    android:textColor="@color/lightBlack"
                    android:textSize="16sp"
                    app:layout_constraintEnd_toEndOf="parent"
                    app:layout_constraintStart_toStartOf="parent"
                    app:layout_constraintTop_toBottomOf="@id/about_book_info" />


            </androidx.constraintlayout.widget.ConstraintLayout>
        </ScrollView>
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

产品详情Activity



import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.TextView
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProviders
import com.app.thingle.R
import com.app.thingle.data.model.bookDetailDataPackage.ProductDetailStatus
import com.app.thingle.databinding.ActivityBookDetailBinding
import com.app.thingle.utility.Constants
import com.app.thingle.viewModel.ProductDetailViewModel
import kotlinx.android.synthetic.main.thingle_search_and_cart_toolbar.view.*
import android.view.KeyEvent.KEYCODE_BACK
import android.view.KeyEvent
import androidx.lifecycle.ViewModel


class BookDetailActivity : BaseActivity() {
    private lateinit var bookDetailBinding: ActivityBookDetailBinding
    private lateinit var productDetailViewModel: ProductDetailViewModel
    private lateinit var cartTextView: TextView
    private lateinit var toolBarTitle: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        bookDetailBinding = DataBindingUtil.setContentView(this, R.layout.activity_book_detail)
        productDetailViewModel = ViewModelProviders.of(this).get(ProductDetailViewModel::class.java)
        bookDetailBinding.viewModel = productDetailViewModel
        productDetailViewModel.getProductById(intent.getIntExtra(Constants.PRODUCT_ID, -1))
        setUpBindings()
        setUpToolbar()

    }

    private fun setUpToolbar() {
        toolBarTitle = bookDetailBinding.aboutBookToolbar.toolbar_action_holder.thingle_toolbar_text
        toolBarTitle.text = getString(R.string.about_Book)
        bookDetailBinding.aboutBookToolbar.toolbar_action_holder.thingle_logo_toolbar_back.setOnClickListener { onBackPressed() }
        bookDetailBinding.aboutBookToolbar.toolbar_action_holder.toolbar_search_id.setOnClickListener {
            this.startActivity(
                Intent(this, SearchScreen::class.java)
            )
            finish()
        }
        bookDetailBinding.aboutBookToolbar.toolbar_action_holder.cart_id.setOnClickListener {
            showToast(
                "Go To Cart"
            )
        }
        cartTextView =
            bookDetailBinding.aboutBookToolbar.toolbar_action_holder.cart_holder.cart_amount
    }

    private fun setUpBindings() {
        setUpValues()
        handleLoaderState()
        handleApiResponse()
        setUpCartStatus()
        handleActivityStatus()
        setUpButtonClick()


    }

    private fun setUpValues() {
        productDetailViewModel.getProduct().observe(this, Observer {
            if (!it.isAvailable) {
                bookDetailBinding.productNotAvailableHolder.visibility = View.VISIBLE
                bookDetailBinding.addCartId.visibility = View.GONE
            }
            bookDetailBinding.bookNameId.text = it.title
            bookDetailBinding.bookIntro.text = it.shortDescription
            bookDetailBinding.textInfoBook.text = it.description
            if (it.ProductImages!!.isNotEmpty()) {
                if (it.ProductImages[0].imageUrl != null) {
                    setImageUrlOnImageView(
                        bookDetailBinding.bookImageId,
                        it.ProductImages[0].imageUrl
                    )
                }

            }

        })
    }

    private fun setUpButtonClick() {
        productDetailViewModel.getProductId().observe(this, Observer {
            showToast(it.toString())
        })
    }

    private fun handleActivityStatus() {
        productDetailViewModel.getActivityStatus().observe(this, Observer {
            when (it) {
                ProductDetailStatus.FETCH_API_SUCCESS -> {
                }
                ProductDetailStatus.FETCH_API_ERROR -> {
                    showToast(getString(R.string.something_went_wrong))
                    onBackPressed()
                }
                ProductDetailStatus.NO_CONNECTION -> {
                    showToast(getString(R.string.no_network))
                }
                ProductDetailStatus.EMPTY_DATA -> {
                    showToast(getString(R.string.no_data))
                    onBackPressed()
                }
                ProductDetailStatus.SHARE_BUTTON_CLICKED -> {
                    showToast("Share")
                }
                //ProductDetailStatus.ADD_CART_BUTTON_CLICKED->{showToast("Add To Cart")}
                else -> {
                    showToast(getString(R.string.something_went_wrong))
                }
            }

        })
    }

    private fun setUpCartStatus() {
        productDetailViewModel.getCartCount().observe(this, Observer {
            if (it > 0) {
                cartTextView.visibility = View.VISIBLE
                cartTextView.text = it.toString()

            }
        })

    }


    private fun handleApiResponse() {
        productDetailViewModel.getApiResponse().observe(this, Observer { handleResponse(it) })
    }

    private fun handleLoaderState() {
        productDetailViewModel.getUiState().observe(this, Observer { handleState(it) })

    }

    override fun onBackPressed() {
        super.onBackPressed()
        killActivity()

    }

    private fun killActivity() {
        finish()
    }

    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
        if (keyCode == KEYCODE_BACK) {
            killActivity()
        }
        return super.onKeyDown(keyCode, event)
    }



}

产品详情视图模型



import android.app.Application
import android.view.View
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.app.thingle.data.model.bookDetailDataPackage.ProductDetailStatus
import com.app.thingle.data.model.bookDetailDataPackage.ResponseObject
import com.app.thingle.data.repository.CartRepository
import com.app.thingle.data.repository.ProductDetailRepository
import com.app.thingle.utility.ApiResponseCallback
import com.app.thingle.utility.ConnectionDetector
import com.app.thingle.utility.SingleLiveEvent

class ProductDetailViewModel(application: Application) : BaseViewModel(application) {
    private var product: MutableLiveData<ResponseObject> = MutableLiveData()
    private var productId: MutableLiveData<Int> = MutableLiveData()
    private var userCartItems =
        MutableLiveData<List<com.app.thingle.data.model.cartDataPackage.ResponseObject>>()
    var cartCount: MutableLiveData<Int> = MutableLiveData()
    private val productDetailStatus = SingleLiveEvent<ProductDetailStatus>()


    fun getProductById(productId: Int) {

        if (product.value == null && productId != -1) {
            fetchProductsByType(productId)
        } else {
            productDetailStatus.value = ProductDetailStatus.EMPTY_DATA
        }
    }

    private fun fetchProductsByType(id: Int) {
        if (ConnectionDetector.getInstance(context).isConnectionAvailable()) {
            setLoaderState(true)
            product = ProductDetailRepository.instance.fetchProductById(
                id,
                context,
                object : ApiResponseCallback {
                    override fun provideResponse(
                        isApiError: Boolean,
                        isResponseError: Boolean,
                        response: String
                    ) {
                        if (isApiError || isResponseError) {
                            setLoaderState(false)
                            productDetailStatus.value = ProductDetailStatus.FETCH_API_ERROR
                        } else {
                            productDetailStatus.value = ProductDetailStatus.FETCH_API_SUCCESS
                            if (product.value == null) {
                                productDetailStatus.value = ProductDetailStatus.EMPTY_DATA
                            }
                            if (isUser()) {
                                fetchCartForUser()
                            } else {
                                setLoaderState(false)
                                cartCount.value = 0
                            }

                        }
                    }

                })


        } else {
            productDetailStatus.value = ProductDetailStatus.NO_CONNECTION
        }


    }

    private fun fetchCartForUser() {
        userCartItems =
            CartRepository.instance.fetchCartForUser(context, object : ApiResponseCallback {
                override fun provideResponse(
                    isApiError: Boolean,
                    isResponseError: Boolean,
                    response: String
                ) {
                    setLoaderState(false)
                    if (isApiError || isResponseError) {
                        cartCount.value = 0
                    } else {
                        if (userCartItems.value!!.isNotEmpty()) {
                            cartCount.value = userCartItems.value!!.size
                        }
                    }
                }

            })


    }


    fun getProductId(): LiveData<Int> {
        return productId
    }

    fun getProduct(): LiveData<ResponseObject> {
        return product
    }

    fun getCartCount(): LiveData<Int> {
        return cartCount
    }

    fun getActivityStatus(): SingleLiveEvent<ProductDetailStatus> {
        return productDetailStatus
    }

    fun onAddCartButtonClicked(v: View) {
        productDetailStatus.value = ProductDetailStatus.ADD_CART_BUTTON_CLICKED
        productId.value = product.value!!.id
    }

    fun onShareButtonClicked(v: View) {
        productDetailStatus.value = ProductDetailStatus.SHARE_BUTTON_CLICKED
    }


}

super.onCreate(null) 而不是 super.onCreate(savedInstanceState) 应该可以解决问题。 您可能想输入一些代码来检查您是否希望发生这种情况(即最后一个实例已完成)或不(即您导航离开并且系统清理了它必须重新启动的 activity)

我通过在我的视图模型中实现 onCleared() 并清除我的视图模型对象找到了解决方案。