【问题标题】:RecyclerView skipping layout and laggingRecyclerView 跳过布局和滞后
【发布时间】:2021-03-18 17:20:09
【问题描述】:

我很抱歉提出重复回答的问题,但我无法解决与我的具体案例相关的问题,也许我遗漏了一些东西。错误是E/RecyclerView: No adapter attached; skipping layout,我不确定,是我设置的适配器还是 RecyclerView 本身的问题?另外,我正在关注一个教程,这是提供的代码。

(我尝试将initRecyclerView() 引入主onCreateView,但没有运气。一些答案说先设置一个空适配器,然后用更改通知它,但我不知道该怎么做。) 这是我的 HomeFragment:

open class HomeFragment() : Fragment() {
    private lateinit var homeViewModel: HomeViewModel
    private lateinit var binding: FragmentHomeBinding

    private val language = arrayOf("English", "German", "Arabic", "Spanish", "Chinese", "French")


    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_home, container, false)

        val dao = VocabData.getInstance(this).VocabDao
        val repository = VocabRepository(dao)
        val factory = HomeViewModelFactory(repository, application = activity?.applicationContext as Application)
        homeViewModel = ViewModelProvider(this, factory).get(HomeViewModel::class.java)
        binding.myViewModel = homeViewModel
        binding.lifecycleOwner = this
        initRecyclerView()


        homeViewModel.message.observe(viewLifecycleOwner, Observer {
            it.getContentIfNotHandled()?.let {
                Toast.makeText(requireContext(), it, Toast.LENGTH_LONG).show()
            }
        })

        // create an adapter
        val arrayAdapter =
            ArrayAdapter(requireContext(), android.R.layout.simple_spinner_dropdown_item, language)
        binding.spinner.adapter = arrayAdapter
        // Set layout to use when the list of choices appear
        arrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
        // Set Adapter to Spinner
        binding.spinner.setAdapter(arrayAdapter)

        val button = binding.tagButton

        button.setOnClickListener{
        GlobalScope.launch(Dispatchers.IO) {
            homeViewModel.tagger(binding.spinner)
              }
        }

        return binding.root
    }

    private fun initRecyclerView(){
        binding.vocabRecyclerView.layoutManager = LinearLayoutManager(requireContext())
        displayVocabsList()
    }

    private fun displayVocabsList() {
    homeViewModel.vocabs.observe(viewLifecycleOwner, Observer {
        Log.i("MYTAG", it.toString())
        binding.vocabRecyclerView.adapter = MyRecyclerViewAdapter(it, { selectedItem: Vocab -> listItemClicked(selectedItem) })
    })

    }
    
    private fun listItemClicked(vocab: Vocab){
        Toast.makeText(requireContext(), "selected sentence is ${vocab.sentString}", Toast.LENGTH_LONG).show()
    }
}

这是我的 RecyclerViewAdapter,实际上只是样板代码:

class MyRecyclerViewAdapter(private val vocabsList: List<Vocab>, private val clickListener:(Vocab)->Unit) :
    RecyclerView.Adapter<MyViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        val binding : ListItemBinding =
            DataBindingUtil.inflate(layoutInflater, R.layout.list_item, parent, false)
        return MyViewHolder(binding)
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.bind(vocabsList[position], clickListener)
    }

    override fun getItemCount(): Int {
        return vocabsList.size
    }
}

class MyViewHolder(private val binding: ListItemBinding):RecyclerView.ViewHolder(binding.root){
    fun bind(vocab: Vocab, clickListener:(Vocab)->Unit){
        binding.sentenceTextView.text = vocab.sentString
        binding.listItemLayout.setOnClickListener{
            clickListener(vocab)
        }
    }
}

我认为这就是所有相关代码。如果您有任何想法,请告诉我如何避免此错误/滞后,谢谢!

更新

activity_main.xml

<layout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.bottomnavigation.BottomNavigationView
        android:id="@+id/nav_view"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="0dp"
        android:layout_marginEnd="0dp"
        android:background="?android:attr/windowBackground"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:menu="@menu/bottom_nav_menu" >

        <androidx.viewpager2.widget.ViewPager2
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </com.google.android.material.bottomnavigation.BottomNavigationView>

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment"
        class = "androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toTopOf="@id/nav_view"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/mobile_navigation" />

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

fragment_home.xml

<layout xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
            name="myViewModel"
            type="com.jwanhsulaiman.talktag.ui.home.HomeViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="8dp"
        tools:context=".ui.home.HomeFragment">

        <EditText
            android:id="@+id/editTextTextMultiLine"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:ems="10"
            android:gravity="start|top"
            android:hint="Input Sentence(s)\n"
            android:inputType="textMultiLine"
            android:text="@={myViewModel.inputVocab}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/spinner" />

        <Button
            android:id="@+id/tag_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:enabled="@{myViewModel.enabled}"
            android:text="@={myViewModel.tagAll}"
            android:textSize="14sp"
            android:textStyle="bold"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/editTextTextMultiLine" />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/vocab_recycler_view"
            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/progressBar" />

        <Button
            android:id="@+id/clear"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginLeft="8dp"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginRight="8dp"
            android:onClick="@{()->myViewModel.deleteAll()}"
            android:text="CLEAR"
            app:layout_constraintEnd_toStartOf="@+id/progressBar"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/editTextTextMultiLine" />

        <Spinner
            android:id="@+id/spinner"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginRight="8dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <ProgressBar
            android:id="@+id/progressBar"
            style="?android:attr/progressBarStyle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:layout_marginEnd="8dp"
            android:layout_marginRight="8dp"
            android:visibility="@{!myViewModel.barProgress}"
            app:layout_constraintEnd_toStartOf="@+id/tag_button"
            app:layout_constraintTop_toBottomOf="@+id/editTextTextMultiLine" />

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

HomeViewModel.kt

import android.annotation.SuppressLint
import android.app.Application
import android.view.View
import android.widget.Spinner
import androidx.databinding.Bindable
import androidx.databinding.BindingAdapter
import androidx.databinding.Observable
import androidx.databinding.ObservableField
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import com.jwanhsulaiman.talktag.Event
import com.jwanhsulaiman.talktag.R
import com.jwanhsulaiman.talktag.database.Vocab
import com.jwanhsulaiman.talktag.database.VocabRepository
import edu.stanford.nlp.tagger.maxent.MaxentTagger
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import opennlp.tools.sentdetect.SentenceDetectorME
import opennlp.tools.sentdetect.SentenceModel


@BindingAdapter("android:visibility")
fun setVisibility(view: View, visible: Boolean) {
    view.visibility = if (visible) View.INVISIBLE else View.VISIBLE
}

@BindingAdapter("android:enabled")
fun setEnabled(view: View, enabled: Boolean) {
    view.isEnabled = !enabled
}

@SuppressLint("StaticFieldLeak")
class HomeViewModel(private val repository: VocabRepository, application: Application) : AndroidViewModel(
    application
), Observable {

    private val context = getApplication<Application>().applicationContext
    private val model: SentenceModel = SentenceModel(context.resources.openRawResource(R.raw.en))
    private val sDetector = SentenceDetectorME(model)

    private var senlist = mutableListOf<String?>()

    val vocabs = repository.vocabs


    @Bindable
    var barProgress = ObservableField<Boolean>()


    @Bindable
    var enabled = ObservableField<Boolean>()

    private fun makeVisible(){
        this.barProgress.set(true) }
    private fun makeInvisible(){
        this.barProgress.set(false) }

    private fun makeEnabled(){
        this.enabled.set(true) }
    private fun makeDisabled(){
        this.enabled.set(false) }


    @Bindable
    val inputVocab = MutableLiveData<String>()

    @Bindable
    val tagAll = MutableLiveData<String>()


    private val statusMessage = MutableLiveData<Event<String>>()

    val message : LiveData<Event<String>>
        get() = statusMessage

    init {
        tagAll.postValue("Tag!")
    }



    suspend fun tagger(spinner: Spinner){
        if (inputVocab.value.isNullOrBlank()) {
            statusMessage.postValue(Event("Please enter sentence"))
        } else {
       withContext(Dispatchers.IO) {
           //tag words
           makeEnabled()
           makeVisible()
           }
       }
    }

    private suspend fun splitSens(vocab: String): MutableList<String?> {
        withContext(Dispatchers.IO) {

            //split sentences
        }
        return senlist
    }


    private suspend fun tagAll(vocab: String){
       withContext(Dispatchers.IO){
           insert(Vocab(0, vocab))
           inputVocab.postValue(null)
       }
        makeInvisible()
        makeDisabled()
    }

    fun insert(vocab: Vocab) : Job = viewModelScope.launch {
        repository.insert(vocab)
        statusMessage.value = Event("Vocab inserted successfuly")
    }

    fun update(vocab: Vocab) : Job = viewModelScope.launch {
        repository.update(vocab)
    }

    fun delete(vocab: Vocab) : Job = viewModelScope.launch {
        repository.delete(vocab)
    }

    fun deleteAll() = viewModelScope.launch {
        repository.deleteAll()
    }

    override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
        //TODO("Not yet implemented")
    }

    override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) {
        //TODO("Not yet implemented")
    }
}

build.gradle

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'

/**
plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt'
}
**/

android {
    compileSdkVersion 29
    buildToolsVersion "30.0.3"

    defaultConfig {
        applicationId "com.jwanhsulaiman.talktag"
        minSdkVersion 16
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        multiDexEnabled true

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
    buildFeatures {
        dataBinding true
    }
}

dependencies {
    implementation files('libs/postagger.jar')
    implementation files('libs/nlp/opennlp-tools-1.9.3.jar')
    def lifecycle_version = "2.2.0"
    def room_version = "2.2.3"
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.3.2'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation 'androidx.vectordrawable:vectordrawable:1.1.0'
    implementation 'androidx.navigation:navigation-fragment:2.3.3'
    implementation 'androidx.navigation:navigation-ui:2.3.3'
    implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.0'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0'
    implementation 'androidx.navigation:navigation-fragment-ktx:2.3.3'
    implementation 'androidx.navigation:navigation-ui-ktx:2.3.3'
    implementation 'androidx.room:room-runtime:2.2.6'
    testImplementation 'junit:junit:4.13.2'

    kapt "com.android.databinding:compiler:3.5.0"


    implementation 'androidx.fragment:fragment-ktx:1.3.0'

    // ViewModel
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    // LiveData
    implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
    // Annotation processor
    //noinspection LifecycleAnnotationProcessorWithJava8
    kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"

    implementation "androidx.room:room-runtime:$room_version"
    kapt "androidx.room:room-compiler:$room_version"

    // optional - Kotlin Extensions and Coroutines support for Room
    implementation "androidx.room:room-ktx:$room_version"
    //coroutines
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1'


    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
    kapt 'androidx.room:room-compiler:2.2.6'
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0"

// Kotlin Extensions and Coroutines support for Room
    implementation "androidx.room:room-ktx:2.2.6"

    implementation 'com.android.support:multidex:1.0.3'
}

【问题讨论】:

  • 尝试在 onViewCreated() 方法中调用 initRecyclerView(),以及所有与适配器初始化和 ViewModel API 调用相关的代码。附加:不要与 ViewModel 共享任何 UI 组件。
  • 能否提供更多代码?像相关的布局,homeViewModel,build.gradle 这样我们就可以在我们这边运行相同的东西并检查。例如最小的可运行演示。
  • 你能提供你的 layout.xml 吗?
  • 嗨@JouanH.Sulaiman,你能提供你的 HomeViewModel 代码吗?
  • homeViewModel.vocabs.observeobserver 是否从 Room 数据库接收数据?

标签: android android-layout kotlin android-recyclerview android-adapter


【解决方案1】:

好的,你有这个消息是正常的,因为在你的代码中,你会这样做:

homeViewModel.vocabs.observe(viewLifecycleOwner, Observer {
        Log.i("MYTAG", it.toString())
        binding.vocabRecyclerView.adapter = MyRecyclerViewAdapter(it, { selectedItem: Vocab -> listItemClicked(selectedItem) })
    })

没有此消息的最佳解决方案是在 createView 中设置适配器:

myRecyclerViewAdapter = MyRecyclerViewAdapter({ selectedItem: Vocab -> listItemClicked(selectedItem) })
binding.vocabRecyclerView.adapter = myRecyclerViewAdapter

和:

homeViewModel.vocabs.observe(viewLifecycleOwner, Observer {
   myRecyclerViewAdapter.addData(it)
}

对于 recyclerView :

class MyRecyclerViewAdapter : .... {

private var values = arrayListOf<....>()

fun addData(values : List<>){
   values.addAll(values)
}
}

注意:我放了 addAll 但我不知道你在做什么,可能你必须做其他事情。 您收到此消息是因为没有适配器。只有当您的观察数据有数据时,您才有一个适配器。如果您初始化适配器并在侦听新数据之后会更好。

}

【讨论】:

  • 这引发了几个问题......首先我应该在分配 myRecyclerViewAdapter 之前输入“val”,对吗?我正在使用科特林。然后在同一行上出现类型不匹配,需要 List 并找到 (Vocab) -> Unit。即使我更改了参数类型,它也会显示“没有为参数 clickListener 传递值”,最后我看不出 recyclerView 代码如何适合我的……不过我很感激!谢谢
  • @JouanH.Sulaiman,是的,因为您只需要传递 clickListener。我认为您不会更改适配器构造函数。类 MyRecyclerViewAdapter( 私有 val clickListener:(Vocab)->Unit) 。对 val 来说是的,不客气
  • 好的,所以错误消失了,但滞后仍然存在...... :(
  • 该错误至少在首次启动应用程序时就存在,否则显然不存在
  • 什么错误?因为如果是消息“E/RecyclerView:未连接适配器;跳过布局”,我在您的代码中看不到它何时可能发生(recyclerview 和微调器似乎已初始化)并且很难看到滞后(可能是视频)。对我来说,你在 viewModel 中放了一些 bindingview 很奇怪。使用您的 2 @BindingAdapter 创建另一个类 BindingView ...
猜你喜欢
  • 2020-10-14
  • 1970-01-01
  • 1970-01-01
  • 2016-07-15
  • 1970-01-01
  • 2021-05-04
  • 1970-01-01
  • 2021-02-24
  • 1970-01-01
相关资源
最近更新 更多