如何在不跳帧的情况下更新 UI

如何解决如何在不跳帧的情况下更新 UI

所以我正在 Kotlin 中使用协程开发和 android 应用程序,无论我做出什么改变,我仍然不断收到消息:

I/Choreographer: Skipped 59 frames!  The application may be doing too much work on its main thread.

我怎样才能摆脱它。我的意思是我只显示九张照片......下面是我的代码

型号:

data class Food (
    val id: String,val name: String,val price: String,@Json(name = "img_url") val imgSrcUrl: String,val type: String,val description: String,val average_rating: String,val number_of_raters: String,val special_price: String
)
data class FoodCategory(
    val id: String,val title: String,val foods: List<Food>
)

视图模型:

enum class NetworkStatus {LOADING,DONE,FAILED}

enum class FontFamily (@FontRes val fontRes: Int) {
    POPPINS_BOLD(R.font.poppins_bold),POPPINS(R.font.poppins)
}

class FoodOverviewViewModel(private val foodRepository: FoodRepository): ViewModel() {

    private lateinit var foodProducts: List<Food>

    //This is the data that is gonna be exposed to the viewmodel
    //It will be submitted to a ListAdapter
    private val _foodCategory = MutableLiveData<List<FoodCategory>>()
    val foodCategory: LiveData<List<FoodCategory>>
        get() = _foodCategory
    
    //Used to display a progress bar for network status
    private val _status = MutableLiveData<NetworkStatus>()
    val status: LiveData<NetworkStatus>
        get() = _status

    init {
        getOverviewProducts()
    }

    private fun getOverviewProducts() {
        viewModelScope.launch(Dispatchers.Default) {
            _status.postValue(NetworkStatus.LOADING)
            try {
                getUpdatedFood()
                Log.i("getOverviewProducts","I am running on tread: $coroutineContext")
                _status.postValue(NetworkStatus.DONE)

            }catch (e: Exception) {
                _status.postValue(NetworkStatus.FAILED)
            }
        }
    }

    private suspend fun getUpdatedFood() {
        //withContext(Dispatchers.Default) {
            val limiter = 6 //Number of items I want to get from the server
            val foodCategory = arrayListOf<FoodCategory>()
            Log.i("getUpdatedFood","I am running on tread: $coroutineContext")

            val getRecommended = foodRepository.getRecommendedFood(limiter.toString())
            foodCategory += FoodCategory(id = 0.toString(),title = "Recommended for you",foods = getRecommended)
            
            val getSpecials = foodRepository.getSpecials(limiter.toString())
            foodCategory += FoodCategory(id = 1.toString(),title = "Specials",foods = getSpecials)

            _foodCategory.postValue(foodCategory)
        //}
    }
}

存储库:

class FoodRepository {

    suspend fun getRecommendedFood(limiter: String) = withContext(Dispatchers.IO) {
        Log.i("Resp-getRecommended","I am running on tread: $coroutineContext")
        return@withContext ProductApi.retrofitService.getRecommended(limiter)
    }
    suspend fun getSpecials(limiter: String) = withContext(Dispatchers.IO) {
        Log.i("Resp-getSpecials","I am running on tread: $coroutineContext")
        return@withContext ProductApi.retrofitService.getSpecials(limiter)
    }
}




绑定适配器:


//Load image using Glide (in Food item recycleview)
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView,imgUrl: String?) {
    imgUrl?.let {
        val imgUri = imgUrl.toUri().buildUpon().scheme("http").build()
        Glide.with(imgView.context)
            .load(imgUri)
            .apply(
                RequestOptions()
                .placeholder(R.drawable.loading_animation)
                .error(R.drawable.ic_broken_image))
            .into(imgView)
    }
}

//set raters count (in Food item recycleview)
@BindingAdapter("ratersCount")
fun bindText(txtView: TextView,number_of_raters: String?) {
    number_of_raters?.let {
        val ratersCount = "(${number_of_raters})"
        txtView.text = ratersCount
    }
}

//update the progressbar visibilty (in outer-parent recycleview) 
@BindingAdapter("updateStatus")
fun ProgressBar.updateStatus(status: NetworkStatus?) {
    visibility = when (status) {
        NetworkStatus.LOADING -> View.VISIBLE
        NetworkStatus.DONE -> View.GONE
        else -> View.GONE
    }
}

//Hide or view an imageview based in the network Status. When network Error,an error image
//will show (in outer-parent recycleview)
@BindingAdapter("setNoInternet")
fun ImageView.setNoInternet(status: NetworkStatus?) {
    when(status) {
        NetworkStatus.LOADING -> {
            visibility = View.GONE
        }
        NetworkStatus.DONE -> {
            visibility = View.GONE
        }
        NetworkStatus.FAILED -> {
            visibility = View.VISIBLE
            setImageResource(R.drawable.ic_connection_error)
        }
    }
}

//Submit the list of FoodCatergory item to the outer-parent recycleview
@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView,data: List<FoodCategory>?) {
    (recyclerView.adapter as FoodCategoryAdapter).submitList(data)
}

//Submit list the the Food item recyclew view (child recycleView)
@BindingAdapter("setProducts")
fun RecyclerView.setProducts(foods: List<Food>?) {
    if (foods != null) {
        val foodAdapter = FoodItemAdapter()
        foodAdapter.submitList(foods)

        adapter = foodAdapter
    }
}

我有食品项目的回收视图和食品类别的回收视图池。如果我注释掉 _foodCategory.postValue(foodCategory) ViewModel: getUpdatedFood() 中,我没有收到消息。但是,当我将列表提交到外部回收视图(持有视图池的那个)时,我得到了这个答案。请帮忙。我坚持了一段时间试图摆脱该消息。

谢谢..

更新 下面是适配器及其视图持有者

FoodItem 布局

<layout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>

        <import type="android.view.View"/>
        <variable
                name="foodItem"
                type="com.example.e_commerceapp.models.Food"/>
        <variable
                name="font"
                type="com.example.e_commerceapp.products.overview.FontFamily"/>
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/child_item_main_layout"
            android:background="@drawable/search_background"
            android:layout_marginTop="10dp"
            android:layout_marginStart="10dp"
            android:layout_marginBottom="10dp"
            android:layout_width="150dp"
            android:layout_height="250dp">

        <ImageView
                android:layout_width="120dp"
                android:layout_marginStart="10dp"
                android:layout_marginEnd="10dp"
                android:id="@+id/burger_image"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                android:layout_height="160dp"
                />
<!--        app:imageUrl="@{foodItem.imgSrcUrl}"-->

        <TextView
                android:layout_width="match_parent"
                android:layout_height="34dp"
                android:layout_marginStart="5dp"
                android:id="@+id/burger_title"
                app:layout_constraintStart_toStartOf="parent"
                android:layout_marginEnd="5dp"
                android:singleLine="true"
                android:textColor="#B4000000"
                app:layout_constraintTop_toBottomOf="@id/burger_image"
                android:text="@{foodItem.name}"
                android:textSize="12sp"
                android:fontFamily="@font/poppins"/>
        <TextView
                android:layout_width="wrap_content"
                android:layout_height="35dp"
                app:layout_constraintTop_toBottomOf="@id/burger_title"
                android:layout_marginEnd="5dp"
                android:gravity="center"
                android:id="@+id/burger_price"
                android:layout_marginStart="5dp"
                app:layout_constraintStart_toEndOf="@id/special_price"
                android:textColor="#D0000000"/>

<!--                app:price="@{foodItem.price}"-->
<!--                app:specialPrice="@{foodItem.special_price}"-->
<!--                app:fontRes="@{foodItem.special ? font.POPPINS : font.POPPINS_BOLD}"-->


        <TextView
                android:layout_width="wrap_content"
                android:layout_height="35dp"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@id/burger_title"
                android:layout_marginEnd="5dp"
                android:gravity="center_vertical"
                android:layout_marginStart="5dp"
                android:id="@+id/special_price"
                android:textColor="#D0000000"
                android:visibility="gone"
                android:textStyle="normal"
                android:fontFamily="@font/poppins_bold"/>
<!--        app:setSpecialPrice="@{foodItem.special_price}"-->


        <ImageView
                android:layout_width="15dp"
                android:layout_height="15dp"
                app:layout_constraintTop_toBottomOf="@id/burger_price"
                android:src="@drawable/ic_baseline_star_24"
                android:visibility="@{foodItem.hasRating ? View.GONE : View.VISIBLE}"
                android:id="@+id/rating_star"
                app:layout_constraintStart_toStartOf="parent"
                android:layout_marginStart="5dp"
                android:layout_marginBottom="10dp"
                app:layout_constraintBottom_toBottomOf="parent"/>
        <TextView
                android:layout_width="wrap_content"
                android:layout_height="15dp"
                android:layout_marginStart="5dp"
                android:gravity="center"
                android:textSize="12sp"
                android:visibility="@{foodItem.hasRating ? View.GONE : View.VISIBLE}"
                android:id="@+id/rating_count"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintTop_toBottomOf="@id/burger_price"
                android:text="@{foodItem.average_rating}"
                android:layout_marginBottom="10dp"
                app:layout_constraintStart_toEndOf="@id/rating_star"/>
        <TextView
                android:layout_width="wrap_content"
                android:layout_height="15dp"
                android:id="@+id/number_of_raters"
                android:textSize="12sp"
                android:visibility="@{foodItem.hasRating ? View.GONE : View.VISIBLE}"
                android:layout_marginStart="2dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toEndOf="@id/rating_count"
                app:ratersCount="@{foodItem.number_of_raters}"
                android:layout_marginBottom="10dp"
                app:layout_constraintTop_toBottomOf="@id/burger_price"/>

        <ImageView android:layout_width="20dp"
                   android:layout_height="20dp"
                   android:layout_marginEnd="10dp"
                   android:layout_marginTop="10dp"
                   app:layout_constraintEnd_toEndOf="parent"
                   app:layout_constraintTop_toTopOf="parent"/>


    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

食品适配器

class FoodItemAdapter: ListAdapter<Food,FoodItemAdapter.ItemFoodViewHolder>(DiffCallback) {

    override fun onCreateViewHolder(parent: ViewGroup,viewType: Int): ItemFoodViewHolder {
        return ItemFoodViewHolder(
            FoodItemBinding.inflate(LayoutInflater.from(parent.context),parent,false))
    }

    override fun onBindViewHolder(holder: ItemFoodViewHolder,position: Int) {
        val currentFood = getItem(position)
        holder.bind(currentFood)
    }

    class ItemFoodViewHolder(private var binding: FoodItemBinding): RecyclerView.ViewHolder(binding.root) {
        fun bind(food: Food) {
            binding.foodItem = food
            binding.executePendingBindings()
        }
    }

    object DiffCallback: DiffUtil.ItemCallback<Food>() {
        override fun areItemsTheSame(oldItem: Food,newItem: Food): Boolean {
            return oldItem === newItem
        }

        override fun areContentsTheSame(oldItem: Food,newItem: Food): Boolean {
            return oldItem.id == newItem.id
        }
    }
}

FoodCategory 布局

<?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="foodCategory"
                type="com.example.e_commerceapp.models.FoodCategory"/>
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
            android:background="#fff"
            android:layout_marginTop="5dp"
            android:layout_marginBottom="5dp"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

        <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginStart="16dp"
                android:id="@+id/category_title"
                android:layout_marginTop="16dp"
                android:text="@{foodCategory.title}"
                android:textColor="#2B2A2A"
                android:fontFamily="@font/poppins_bold"
                android:textSize="16sp"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"/>

        <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/nestedRecyclerView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="4dp"
                app:setProducts="@{foodCategory.foods}"
                android:orientation="horizontal"
                app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toBottomOf="@+id/category_title"
                tools:itemCount="4"
                tools:listitem="@layout/food_item"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

FoodCategory 适配器

class FoodCategoryAdapter: ListAdapter<FoodCategory,FoodCategoryAdapter.CategoryFoodViewHolder>(Companion) {

    private val viewPool = RecyclerView.RecycledViewPool()

    override fun onCreateViewHolder(parent: ViewGroup,viewType: Int): CategoryFoodViewHolder {
        return CategoryFoodViewHolder(FoodCategoryBinding.inflate(LayoutInflater.from(parent.context),false))
    }

    override fun onBindViewHolder(holder: CategoryFoodViewHolder,position: Int) {
        val currentFoodCategory = getItem(position)
        holder.bind(currentFoodCategory)
    }


    inner class CategoryFoodViewHolder(private var binding: FoodCategoryBinding): RecyclerView.ViewHolder(binding.root) {
        fun bind(currentFoodCategory: FoodCategory?) {
            binding.foodCategory = currentFoodCategory
            binding.nestedRecyclerView.setRecycledViewPool(viewPool)
            binding.executePendingBindings()
        }
    }

    companion object: DiffUtil.ItemCallback<FoodCategory>() {
        override fun areItemsTheSame(oldItem: FoodCategory,newItem: FoodCategory): Boolean {
            return oldItem === newItem
        }

        override fun areContentsTheSame(oldItem: FoodCategory,newItem: FoodCategory): Boolean {
            return oldItem.id == newItem.id
        }
    }
}

父回收视图

<?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"
        tools:context=".products.overview.FoodOverviewFragment">
    <data>
        <variable
                name="foodOverview"
                type="com.example.e_commerceapp.products.overview.FoodOverviewViewModel"/>

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
            android:background="@color/grey"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

        <RelativeLayout
                android:layout_width="match_parent"
                android:id="@+id/relative_layout"
                android:layout_height="105dp"
                android:elevation="8dp"
                android:layout_marginBottom="5dp"
                android:background="#fff"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintEnd_toEndOf="parent">

            <ImageView
                    android:layout_width="200dp"
                    android:layout_marginTop="10dp"
                    android:layout_height="35dp"
                    android:id="@+id/logo_and_name"
                    android:src="@drawable/compony_logo_and_name"
                    android:layout_alignParentStart="true"/>
            <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="35dp"
                    android:layout_marginTop="10dp"
                    android:id="@+id/notifications"
                    android:src="@drawable/ic_baseline_notifications_24"
                    android:layout_alignParentEnd="true"
                    android:paddingEnd="20dp"
                    android:paddingStart="20dp"/>
            <TextView
                    android:layout_width="match_parent"
                    android:id="@+id/search"
                    android:layout_marginTop="10dp"
                    android:layout_marginStart="20dp"
                    android:layout_marginEnd="20dp"
                    android:background="@drawable/search_background"
                    android:layout_below="@id/logo_and_name"
                    android:gravity="center_vertical"
                    android:layout_height="match_parent"
                    android:layout_marginBottom="10dp"
                    android:paddingEnd="10dp"
                    android:paddingStart="10dp"
                    android:text="@string/search_text"
                    tools:ignore="RtlSymmetry"
                    app:drawableEndCompat="@drawable/ic_baseline_search_24"/>
        </RelativeLayout>

        <ProgressBar
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:updateStatus="@{foodOverview.status}"
                app:layout_constraintTop_toBottomOf="@id/relative_layout"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                android:id="@+id/progressbar"/>
        <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                app:layout_constraintTop_toBottomOf="@id/relative_layout"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                android:id="@+id/noInternetImage"/>

        <androidx.recyclerview.widget.RecyclerView
                android:layout_width="0dp"
                android:id="@+id/foodCategory"
                android:clipToPadding="false"
                tools:itemCount="4"
                tools:listitem="@layout/food_category"
                app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
                android:layout_height="0dp"
                app:listData="@{foodOverview.foodCategory}"
                app:layout_constraintTop_toBottomOf="@id/relative_layout"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>

解决方法

跳帧可能与您发布的代码无关:对我来说,这听起来像是 RecyclerViews / Adapters 的错误配置。不过,您需要发布该代码以使其更加清晰。

然而,即使您发布的内容可能不是罪魁祸首,您仍然可以优化您拥有的协程代码:

class FoodOverviewViewModel(private val foodRepository: FoodRepository): ViewModel() {

    private lateinit var foodProducts: List<Food>

    private val _foodCategory = MutableLiveData<List<FoodCategory>>()
    val foodCategory: LiveData<List<FoodCategory>>
        get() = _foodCategory
    
    private val _status = MutableLiveData<NetworkStatus>()
    val status: LiveData<NetworkStatus>
        get() = _status

    init {
        getOverviewProducts()
    }

    private fun getOverviewProducts() {
        viewModelScope.launch { // <------- Don't apply a custom scope here
            _status.value = NetworkStatus.LOADING // <--- Don't call "postValue" here
            try {
                val food = getUpdatedFood() // <------ This is already using a background dispatcher
                _foodCategory.value = food // <------- Emit this value here
                _status.value = NetworkStatus.DONE
            } catch (e: Exception) {
                _status.value = NetworkStatus.FAILED
            }
        }
    }

    private suspend fun getUpdatedFood(): List<FoodCategory> { // <---- Return a value here
        val limiter = 6 //Number of items I want to get from the server
        val foodCategory = arrayListOf<FoodCategory>()
        Log.i("getUpdatedFood","I am running on tread: $coroutineContext")

        val getRecommended = foodRepository.getRecommendedFood(limiter.toString())
            foodCategory += FoodCategory(id = 0.toString(),title = "Recommended for you",foods = getRecommended)
            
        val getSpecials = foodRepository.getSpecials(limiter.toString())
        foodCategory += FoodCategory(id = 1.toString(),title = "Specials",foods = getSpecials)
        return foodCategories
    }
}

这里的关键思想:

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-