我有两个 Activity -
A.产品 list
B.产品详细信息
每当用户单击产品项目时,他都会转到产品详细信息 Activity ,在该 Activity 中会调用一个API,该API显示该产品的详细信息。
现在的问题是,第一次,产品详细信息中的 View 最初没有数据,我正在借助数据绑定(bind)在viewmodel中使用livedata填充 View 中的数据。现在,当用户导航回到“产品列表”时,将在“产品详细信息 Activity ”上调用finish。
当用户再次单击另一产品时,这一次,当他被带到产品详细信息屏幕时,在从API提取新产品详细信息并将其绑定(bind)到 View 中之前,旧产品详细信息会短暂出现。我不知道是什么原因导致了这种现象。有人可以指出我正确的方向吗?
我已经尝试过调用finish和System.gc,但是以某种方式, View 会保留旧数据,然后再填充新数据。
产品 list 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)
}
}
产品详细信息 View 模型
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
}
}
最佳答案
我通过在 View 模型内实现onCleared()并清除 View 模型对象来找到解决方案。
关于android - 使用数据绑定(bind)和MVVM将新数据投入 Activity 之前,旧 View 数据会短暂出现在 Activity 中,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57976409/