Recyclerview android error

The e/recyclerview: no adapter attached: skipping layout is an error that occurs due to a fault in the layout manager or in adapter function. Read more here!

“E/recyclerview: no adapter attached; skipping layout” is a programming error that occurs mainly due to missing Layout Manager or when the setAdapter(adapter) function is called before the layout manager.

This article explains the causes and fixes of the error in detail, so make sure to read it thoroughly for better understanding. Let’s begin by discussing why this problem occurs in the first place!

Why Is E/Recyclerview: No Adapter Attached; Skipping Layout Error occurring?

The e/recyclerview: no adapter attached; skipping layout error occurs due to the absence of Layout manager or syntax errors, which is most common in coding. As the name of the error describes, “attached skipping layout” means the layout manager is not attached to the recycler view.

Some of the other common mistakes that bring forward this error are:

  • Syntax error.
  • Wrong setup of the RecyclerView.
  • Wrong layout format.
  • Default RecyclerView.Adapter.
  • RecyclerView is put into a ScrollView.
  • Programming logical issues.
  • A layout manager is not set.
  • Called setAdapter(adapter) before layout manager.
  • The inflated layout is different from the one being called.

– Syntax Errors

Sometimes, a syntax error can cause a “no adapter attached skipping layout” error. Many things are included in syntax errors, from putting the wrong functions to the wrong order of the functions.

Given below is an example of a program that demonstrates a syntax error.

Program:

class UsersActivity : AppCompatActivity() {

override fun onCreate(saveInstanceState: Bundle?) {

super.onCreate(saveInstanceState)

setContentView(R.layout.activity_users)

RetrofitClient.instance.getUsers().enqueue(object: Callback>{

override fun onResponse(call: Call>, responses: Responses>) {

showData(responses.body()!!)

}

override fun onFailure(call: Call>, e: Throwable) {

g(“Fail”,”onFailure”)

}

})

}

private fun showData(usersLists: Lists) {

listUsersRecycle.apply {

layoutManager = LinearLayoutManager(this@UsersActivity)

adapter = UsersAdapter(usersList)

}

}

Here, the syntax error lies in the showData() function. Thus, it becomes the cause of the recyclerview adapter error.

– Wrong Setup of the Recyclerview

The wrong setup of recyclerview can cause errors, although, in some programming languages, it may work perfectly fine. If the error occurs due to this particular reason, do watch some tutorials on the setup of recyclerview. However, don’t forget to mention the name of the programming language.

Given below is an example that shows the wrong setup of a RecyclerView. Such errors can cause RecyclerView errors.

<view

android:id=”@+id/RecyclerView”

class=”android.support.v6.widget.RecyclerView”

android:layout_width=”match_parent”

android:layout_height=”match_parent” />

There are multiple syntax errors here, like the placement of /> in the same line.

– Wrong Layout Format

Understanding this format is very important as well. If the programmer is using the command: CardView layout_width=” match parent”, and the layout format is wrong, it will not match the parent RecyclerView width and thus cause an issue.

Therefore, below is an example of a program with the wrong layout format. Do not repeat this mistake if the user doesn’t want errors.

LayoutInflater.from(parent.getContext())

.inflate(R.layout.card_itemlist,null);

– Default Recyclerview.adapter

The program’s default setting of RecyclerView.adapter is another reason why the error occurs. This is because the functions the programmer uses are not compatible with the default setting of the Recycleview.adapter.

By default, when the users implement methods in the RecyclerView.adapter, it generates the following:

@Override

public int getItemCount() {

return 0;

}

– Recyclerview Is Put Into a Scrollview

Once the error occurs, run a check on the program and try to identify common mistakes and implementation methods. The error message will pop up if the RecyclerView is put into a ScrollView. This is because the height is unspecified during the measuring step (because ScrollView allows any height).

Therefore, as a result, it gets equal to the minimum height (as per the implementation done by the user). If that height is zero, the width will become zero too, and in this way, the error will occur.

– Programming Logical Issues

Program logical errors occur when the programmer uses a command that is not compatible with the coding software. Since the software cannot identify the command, it shows a recycler review error.

For instance, doing programming in kotlin, the following command is not accepted by the program:

mBinding.serviceProviderCertificates.apply {

adapter = adapter

layoutManager = LinearLayoutManager(activity)

}

– Layout Manager Is Not Set

Turns out layout is the most important factor to consider when trying to remove the recycler view error. If the layout manager is skipped or not set in the appropriate way, the error message will be shown. Following are the times when the layout manager is not set:

  1. Certain heights, dimensions and instructions are not properly given.
  2. The scrollview.fillviewport is set to “false”.
  3. The RecyclerView is inside of ScrollView.

For example:

<androidx.recyclerview.widget.RecyclerView

android:id=”@+id/reccylerview”

android:layout_width=”0dp”

android:layout_height=”0dp”

android:layout_marginTop=”8dp”

android:scrollbars=”vertical”

app:layoutManager=”androidx.recyclerview.widget.LinearLayoutManager”/>

– Called Setadapter(Adapter) Before Layout Manager

This is a type of error where the adapter function is placed before calling out the layout manager. Remember, when setting an adapter, the layout manager must get called out first by the programmer.

Let’s take the same example that we discussed earlier for a better understanding:

mBinding.serviceProviderCertificates.apply {

adapter = adapter

layoutManager = LinearLayoutManager(activity)

}

As it can be seen here, the adapter is called before the layout manager.

– Inflated Layout Is Different From the One Being Called

This error occurs for those who usually use the RecyclerView within a fragment and inflate it from other views. It is recommended that when inflating the whole fragment view, make sure that the RecyclerView is bind to its root view.

The error message is that the actual inflated layout is different from the one referred to by the programmer while finding the recyclerView. When the fragment is created, the onCreateView method appears as given below, which causes a recyclerview error.

Program error line:

Return inflater.inflate(R.layout.,container.false);

How To Fix the “E/Recyclerview: No Adapter Attached; Skipping Layout” Error?

To fix the e/recyclerview: no adapter attached; skipping layout error, a very common method is to run a check on the syntax of the program. Correcting the syntax will remove the error. You can also fix it by running a check on the layout manager and its positioning.



However, various other methods are discussed in this article. They are given below:

– Use the Correct Syntax

Finding syntax errors and correcting them can be pretty hectic. Therefore, make a list of the common syntax used before coding.

Doing so will save a bit of time by not making syntax errors. Here’s an example of correct syntax in a program:

private fun showData(usersList: List) {

listUsersRecycle.layoutManager = LinearLayoutManager(this@UsersActivity)

//Apply layout manager here.

listUsersRecycle.apply {

adapter = UsersAdapter(usersList)

}

}

– Use the Correct Setup Recyclerview

Check the recyclerView setup and update or correct it according to the type of code a programmer is coding.

Here’s an example of the correct setup of RecyclerView:

android:id=”@+id/RecyclerView”

android:layout_width=”match_parent”

android:layout_height=”match_parent”

/>

Ensure that this “/>” function comes at the end after the last program line.

– Use the Right Layout Format

Layout manager and layout format are both important to check in order to remove the error message. Thus, coding with the correct layout format with the correct functions and syntax is extremely important.

Here’s a quick example of the correct layout format:

LayoutInflater.from(parent.getContext())

.inflate(R.layout.card_listitem, parent, false);

– Change the Default Recyclerview.adapter

As said above, the RecyclerView.adapter is sometimes not compatible with the functions or syntax that are being used in the program. Thus, updating or changing the code can remove the recyclerview error.

Also, if there are zero items in the adapter, then the user will get zero things displayed in the output of the program.

Here’s an example of the correct codes used in RecyclerView.Adapter:

@Override

public int getItemCount() {

return artists.size();

}

– Don’t Put Recyclerview Into a Scrollview

If the programmer puts the RecyclerView into a ScrollView, then the RecyclerView error is a must. Therefore, be careful to make separate identities for these two instead of putting them into each other.

Some various ways to fix this issue. They are given below:

  • Set a specific height for the RecyclerView.
  • Set the ScrollView.fillViewport to “True”.
  • Keep RecyclerView out from ScrollView.

Programmers recommend the last option. If the RecyclerView height is not limited (which is the case when it is put into ScrollView), then all adapter’s views have enough space vertically and get created all at once. This means there will be no view recycling anymore, which kind of breaks the purpose of RecyclerView.

– Fix the Programming Logical Issues

Before starting the coding, note down the logical errors a program can possibly make; a bit of research will be required. Doing so will save a lot of time and unnecessary error messages, such as recyclerview errors.

Moreover, it will also make the coder avoid using commands that are not recognized by the program.

Here’s the correct way of typing a program and avoiding errors:

mBinding.serviceProviderCertificates.adapter = adapter

mBinding.serviceProviderCertificates.layoutManager = LinearLayoutManager(activity)

– Fix Layout Manager Issue

By fixing the layout manager setup, the error message will be resolved. Remember to check and reset the layout manager before calling out the recyclerview.adapter.

Here’s an example of the correct layout manager for Kotlin and Java programming languages:

Kotlin:

val linearLayoutManager:LinearLayoutManager = LinearLayoutManager(this)

linearLayoutManager.orientation = LinearLayoutManager.HORIZONTAL{or VERTICAL}

recycleView.layoutManager = linearLayoutManager

Java:

LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);

linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL{or VERTICAL});

list.setLayoutManager(linearLayoutManager);

– Call Layout Manager Before Setadapter(Adapter)

The error will occur if the programmer calls the adapter before the layout manager. Therefore, the solution to this issue is to stop calling the setAdapter(adapter) before the layout manager.

Following is an example of a program with the correct layout manager position and setAdapter(adapter) function.

ArtistArrayAdapter adapter = new ArtistArrayAdapter(this, artists);

recyclerView = (RecyclerView) findViewById(R.id.cardList);

recyclerView.setHasFixedSize(true);

recyclerView.setLayoutManager(new LinearLayoutManager(this));

recyclerView.setAdapter(adapter);

– Separately Create the View To Refer to the Recyclerview

The last method one can use to remove the error message is by separately creating the view to call out or refer to the recyclerView.

Create the view separately and use that to refer to recyclerView. Here’s a quick example for better understanding:

view= inflater.inflate(R.layout.,container.false);

recyclerview=view.findViewById(R.id.);

return view;

Conclusion

After reading this guide, the reader will be able to identify the causes behind the recyclerview error and how to deal with them. The key points of this guide are:

  • Run a syntax check on the program to see if there is any error in the syntax.
  • While programming, make sure to update the setup of the layout manager as well as the default recyclerview.adapter.
  • Do not use functions or syntax that are not recognized by the program that is being used. Otherwise, errors will pop up.

After reading this guide, you can start typing your own program and avoid conflict with e/recyclerview: no adapter attached; skipping layout error message. Happy Coding To You!

  • Author
  • Recent Posts

Position is Everything

Position Is Everything: Your Go-To Resource for Learn & Build: CSS,JavaScript,HTML,PHP,C++ and MYSQL.

Position is Everything

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and
privacy statement. We’ll occasionally send you account related emails.

Already on GitHub?
Sign in
to your account


Closed

thincal opened this issue

Jan 6, 2015

· 8 comments

Comments

@thincal

test env
android studio:1.0.1
robolectric:2.4
sdk-17

build.gradle
dependencies {
// needed libraries
compile ‘com.android.support:support-v4:+’
compile (‘com.android.support:recyclerview-v7:+’)

// Test libraries
androidTestCompile 'junit:junit:4.11'
androidTestCompile 'org.robolectric:robolectric:2.4'

}

error info
build is ok, but when running the test, it reports bellow error:

android.view.InflateException: XML file /home/zhaoliang/git/test-glass-apps/launcher/build/intermediates/res/debug/layout/card_launcher.xml line #-1 (sorry, not yet implemented): Error inflating class android.support.v7.widget.RecyclerView
at android.view.LayoutInflater.createView(LayoutInflater.java:613)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:687)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:746)
at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
at android.view.LayoutInflater.inflate(LayoutInflater.java:352)
at cn.test.glasswidget.cardview.GlassCardListView.init(GlassCardListView.java:103)
at cn.test.glasswidget.cardview.GlassCardListView.(GlassCardListView.java:59)
at cn.test.glasslauncher.launcher.GlassLauncherActivity.onCreate(GlassLauncherActivity.java:34)
at android.app.Activity.performCreate(Activity.java:5104)
at org.robolectric.internal.ReflectionHelpers$3.run(ReflectionHelpers.java:64)
at org.robolectric.internal.ReflectionHelpers.traverseClassHierarchy(ReflectionHelpers.java:114)
at org.robolectric.internal.ReflectionHelpers.callInstanceMethodReflectively(ReflectionHelpers.java:59)
at org.robolectric.util.ActivityController$1.run(ActivityController.java:115)
at org.robolectric.shadows.ShadowLooper.runPaused(ShadowLooper.java:268)
at org.robolectric.util.ActivityController.create(ActivityController.java:111)
at org.robolectric.util.ActivityController.create(ActivityController.java:122)
at org.robolectric.util.ActivityController.setup(ActivityController.java:202)
at org.robolectric.Robolectric.setupActivity(Robolectric.java:1388)
at cn.test.glasslauncher.launcher.GlassLauncherActivityTest.testActivityFound(GlassLauncherActivityTest.java:27)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.robolectric.RobolectricTestRunner$2.evaluate(RobolectricTestRunner.java:236)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:158)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:86)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:49)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:69)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:48)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:105)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:360)
at org.gradle.internal.concurrent.DefaultExecutorFactory$StoppableExecutorImpl$1.run(DefaultExecutorFactory.java:64)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
at java.lang.Thread.run(Thread.java:662)

@thincal

Btw the card_launcher.xml is:

< LinearLayout xmlns:android=»http://schemas.android.com/apk/res/android»
xmlns:tools=»http://schemas.android.com/tools»
android:layout_width=»match_parent»
android:layout_height=»match_parent»
tools:context=»cn.test.glasslauncher.launcher.GlassLauncherActivity» >

<android.support.v7.widget.RecyclerView
    android:id="@+id/recyclerview_horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="horizontal" />

< /LinearLayout>

@thincal

I test with another TestView created by myself, and inflate it from a related xml file, it still reports the error:
line #-1 (sorry, not yet implemented): Error inflating class cn.test.TestView.

And the layout inflate code is:
View contentView = LayoutInflater.from(mContext).inflate(R.layout.test_view, this)»

so what’s wrong here ? it works in a normal build but failed in the test.

@erd, if possible could you help have a quick check ? many thanks :)

@thincal

this issue is always present if using the jdk6, but will be gone when run test in the second time if using jdk7 or jdk8.

anyway, since that I still can’t find a final solution, but it is fine when using jdk7/8 if I build the project first then run test.

Hope it will help other people if meet same issue.

@SNeugber

What a timely coincidence. I’ve just come across the same error, but for me using a newer JDK didn’t help (I upgraded from 7 to 8).

@SNeugber

Update: my temporary workaround is to use Dagger to inject the R.id… for inflating the CardView. I can then inject a different R.id… for my tests which mirrors the layout with the sole exception of using a LinearLayout instead of a CardView.

@erd

This should be fixed with the RobolectricGradleTestRunner support added in 3.0.

@marcelpinto

I am having problems with this still in the 3.0 version.

android.view.InflateException: XML file .srctest..mainreslayoutsearch_activity.xml line #-1 (sorry, not yet implemented): Error inflating class fragment
    at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:713)

And the search_activity has a fragment inside the layout that this fragment has a RecyclerView if I remove the recylcerview works, so I guess is because of that.

Gradle file:

    testCompile('org.robolectric:robolectric:3.0') {
        exclude module: 'commons-logging'
        exclude module: 'httpclient'
    }
    testCompile 'org.robolectric:shadows-multidex:3.0'
    testCompile 'org.robolectric:shadows-support-v4:3.0'

Any solution?

@bes1002t

I’m using a simple implementation of RecyclerView taken from the Android website using a StaggeredGridLayoutManager and I keep getting this error that crashes my app:

java.lang.IllegalArgumentException: Scrapped or attached views may not be recycled. isScrap:false isAttached:true at android.support.v7.widget.RecyclerView$Recycler.recycleViewHolderInternal(RecyclerView.java:3501) at android.support.v7.widget.RecyclerView$LayoutManager.scrapOrRecycleView(RecyclerView.java:5355) at android.support.v7.widget.RecyclerView$LayoutManager.detachAndScrapAttachedViews(RecyclerView.java:5340) at android.support.v7.widget.StaggeredGridLayoutManager.onLayoutChildren(StaggeredGridLayoutManager.java:572) at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:1918) at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:2155) at android.view.View.layout(View.java:14008) at android.view.ViewGroup.layout(ViewGroup.java:4373) at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1021) at android.view.View.layout(View.java:14008) at android.view.ViewGroup.layout(ViewGroup.java:4373) at android.widget.FrameLayout.onLayout(FrameLayout.java:448) at android.view.View.layout(View.java:14008) at android.view.ViewGroup.layout(ViewGroup.java:4373) at android.widget.FrameLayout.onLayout(FrameLayout.java:448) at android.view.View.layout(View.java:14008) at android.view.ViewGroup.layout(ViewGroup.java:4373) at android.support.v7.internal.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:502) at android.view.View.layout(View.java:14008) at android.view.ViewGroup.layout(ViewGroup.java:4373) at android.widget.FrameLayout.onLayout(FrameLayout.java:448) at android.view.View.layout(View.java:14008) at android.view.ViewGroup.layout(ViewGroup.java:4373) at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1663) at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1521) at android.widget.LinearLayout.onLayout(LinearLayout.java:1434) at android.view.View.layout(View.java:14008) at android.view.ViewGroup.layout(ViewGroup.java:4373) at android.widget.FrameLayout.onLayout(FrameLayout.java:448) at android.view.View.layout(View.java:14008) at android.view.ViewGroup.layout(ViewGroup.java:4373) at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:1892) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1711) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:989) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4351) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:749) at android.view.Choreographer.doCallbacks(Choreographer.java:562) at android.view.Choreographer.doFrame(Choreographer.java:532) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:735) at android.os.Handler.handleCallback(Handler.java:725) at android.os.Handler.dispatchMessage(Handler.java:92) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:5041) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:511) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560) at dalvik.system.NativeStart.main(Native Method)

By simple, I literally mean it’s the same implementation taken from this page on their website, the only difference being is that my grid item’s layout is an ImageView and a couple of TextViews, so I won’t bother reposting my code.
Anyone else getting this error and know how to deal with it?

In this article, we will see how we can monitor internet connection changes. When the phone is online, we will fetch data from an API, otherwise, we will display an error page. Here’s what we want to create:

App Demo

Many applications require an internet connection to either fetch or save data. However, it’s normal for internet connection to be lost every now and then. As a developer, it falls to you to make sure the user has a smooth experience when using our applications and the internet connection is lost.

Prerequisites

In other to follow the tutorial, you need the following:

  • Android Studio (version >= 3.x recommended). Download here.
  • Basic knowledge of Kotlin.

Building our application

Launch Android Studio and create a ‘New Project…’ using the wizard. You should choose the “Basic Activity” template and select your preferred target, we are using (API 26: Android 8.0). You should also enable Kotlin support for the project.

Create project dialog

Asides the usual dependencies that come with a new project, we need to add some dependencies. One of the dependencies we need to add is Retrofit. Retrofit is a client for making HTTP calls.

Open your build.gradle file and add the following dependencies:

    implementation 'com.android.support:design:27.1.1'
    implementation 'com.squareup.retrofit2:retrofit:2.3.0'
    implementation 'com.squareup.retrofit2:converter-scalars:2.3.0'

Sync the gradle file so that the dependencies will be downloaded.

Next, we will request two permissions:

  • Internet permission – to gain access to make network calls
  • Network state – to check the network state of a device so that we know when we have an internet connection or not.

Open your AndroidManifest.xml file and add the permissions like so:

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.android.internetconnectivity">

        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
        <uses-permission android:name="android.permission.INTERNET"/>

        [...]

    </manifest>

When there is a network connection, we will fetch data from an API. Let’s set up an interface to hold the endpoints we will access. Create a new Kotlin file named ApiService and paste this:

    import retrofit2.Call
    import retrofit2.http.GET

    interface ApiService {
        @GET(".")
        fun getFeeds(): Call<String>
    }

For this demo, we are only going to access one endpoint, which is equivalent to our base URL. It’s for this reason we used a dot instead of the usual /some-url in the @GET annotation.

When these items are fetched, we will display the items in a list. We, therefore, need a RecyclerView in the layout and a matching adapter. Create a new Kotlin file named RecyclerAdapter and paste this:

    import android.support.v7.widget.RecyclerView
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import android.widget.TextView

    class RecyclerAdapter : RecyclerView.Adapter<RecyclerAdapter.ViewHolder>() {

        private var list = ArrayList<String>()

        fun setItems(newList: ArrayList<String>){
            this.list = newList
            this.notifyDataSetChanged()
        }

        override fun getItemCount() = list.size

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
            val view = LayoutInflater.from(parent.context)
                    .inflate(android.R.layout.simple_list_item_1, parent, false)

            return ViewHolder(view)
        }

        override fun onBindViewHolder(holder: ViewHolder, position: Int) {
            holder.textView.text = list[position]
        }

        inner class ViewHolder(itemView: View?): RecyclerView.ViewHolder(itemView) {
            var textView: TextView = itemView!!.findViewById(android.R.id.text1)
        }

    }

The adapter handles the display of items on a list. It has some overridden methods like:

  • getItemCount – to tell the size of the list to be populated.
  • onCreateViewHolder – used to choose a layout for a list row.
  • onBindViewHolder – to bind data to each row depending on the position, etc.

Next, we will update the layout of our MainActivity‘s activity_main.xml file like so:

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout
        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"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <android.support.v7.widget.RecyclerView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/recyclerView"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <ImageView
            android:id="@+id/imageView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:src="@drawable/no_internet_connection" />

    </android.support.constraint.ConstraintLayout>

The layout contains a RecyclerView for our list items and an ImageView to show an error message.

We also need an error message image. Once you have an image, rename the file to no_internet_connection and save it to your drawable folder: NameOfProject/app/src/main/res/drawable.

For us to monitor when the connectivity changes, we need broadcast receivers. Broadcast receivers are components that allow you to register and listen to Android system and application events. Usually, the Android system sends broadcast events when various system events occur and your app needs to register to get these events.

Let’s register a listener to be triggered when the internet connection is online or offline. Open your MainActivity file and paste the following code:

    import android.content.BroadcastReceiver
    import android.content.Context
    import android.content.Intent
    import android.content.IntentFilter
    import android.net.ConnectivityManager
    import android.support.v7.app.AppCompatActivity
    import android.os.Bundle
    import android.support.v7.widget.LinearLayoutManager
    import android.util.Log
    import android.view.View
    import kotlinx.android.synthetic.main.activity_main.*
    import okhttp3.OkHttpClient
    import org.json.JSONObject
    import retrofit2.Call
    import retrofit2.Callback
    import retrofit2.Response
    import retrofit2.Retrofit
    import retrofit2.converter.scalars.ScalarsConverterFactory


    class MainActivity : AppCompatActivity() {

        private val arrayList = ArrayList<String>()
        private val adapter = RecyclerAdapter()
        private val retrofit = Retrofit.Builder()
                .baseUrl("https://api.reddit.com/")
                .addConverterFactory(ScalarsConverterFactory.create())
                .client(OkHttpClient.Builder().build())
                .build()

        private var broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context, intent: Intent) {
                val notConnected = intent.getBooleanExtra(ConnectivityManager
                        .EXTRA_NO_CONNECTIVITY, false)
                if (notConnected) {
                    disconnected()
                } else {
                    connected()
                }
            }
        }

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
            setupRecyclerView()
        }

    }

Above, we initialized some variables:

  • arrayList – we will add fetched items to this list.
  • adapter – this is the instance of the adapter class.
  • retrofit – a Retrofit instance.
  • broadcastReciever – this instance implements the onRecieve callback. This callback method is called when the system has notified us of a change in the network connection. In the callback, we then check to know the connectivity status thereby calling either a private connected or disconnected function.

After creating the broadcast receiver, we have to register it to get updates and unregister if there are no more activities. To do this, add the following functions to the code above in the MainActivity:

    override fun onStart() {
        super.onStart()
        registerReceiver(broadcastReceiver, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))
    }

    override fun onStop() {
        super.onStop()
        unregisterReceiver(broadcastReceiver)
    }

In the onCreate function, we set up our RecyclerView by calling the setupRecyclerView. Create a private function in the MainActivity class and set it up like this:

    private fun setupRecyclerView(){
        with(recyclerView){
            layoutManager = LinearLayoutManager([email protected])
            adapter = [email protected]
        }
    }

Remember we mentioned the connected and disconnected functions earlier in this post. We will now add them to the class. Add them to the MainActivity file like so:

    private fun disconnected() {
        recyclerView.visibility = View.INVISIBLE
        imageView.visibility = View.VISIBLE
    }

    private fun connected() {
        recyclerView.visibility = View.VISIBLE
        imageView.visibility = View.INVISIBLE
        fetchFeeds()
    }

The disconnected function is called when there is no network connection. It hides the RecyclerView and shows the ImageView. The connected function is called when there is an active internet connection. It shows the RecyclerView, hides the ImageView, and finally calls the fetchFeeds function.

Next, in the same file, paste the following code:

    private fun fetchFeeds() {
        retrofit.create(ApiService::class.java)
                .getFeeds()
                .enqueue(object : Callback<String> {
                    override fun onFailure(call: Call<String>, t: Throwable) {
                        Log.e("MainActivityTag", t.message)
                    }

                    override fun onResponse(call: Call<String>?, response: Response<String>) {
                        addTitleToList(response.body()!!)
                    }

                })
    }

This function calls the API to get data. When the call is successful, we have another function that helps us add the title of the posts gotten from the endpoint to our list and then to our adapter. Create a function named addTitleToList and set it up like so:

    private fun addTitleToList(response: String) {
        val jsonObject = JSONObject(response).getJSONObject("data")
        val children = jsonObject.getJSONArray("children")

        for (i in 0..(children.length()-1)) {
            val item = children.getJSONObject(i).getJSONObject("data").getString("title")
            arrayList.add(item)
            adapter.setItems(arrayList)
        }
    }

We manually parsed the JSON here to get the title. When you ‘Run’ the app, you should have this:

App Demo

Conclusion

In this post, we have learned how to monitor network changes using broadcast receivers. We saw how we register and unregister for broadcasts sent by the Android system. You have gained some valuable knowledge on handling network errors in your app. You can play around the repo here and feel free to drop your feedback.

The source code to the GitHub repo is here.

Endless or Infinite scrolling is called pagination. You do this in Android using RecyclerView. However, there are a few critical error scenarios to handle.

This is the third post in the Android Pagination article series.

In the previous article, (Android Pagination: Using APIs with Retrofit and Gson), we fetched real API data in Pagination. We used themoviedb.org for our data source. Additionally, Retrofit was used for networking, Glide for image loading and Gson for JSON parsing.

However, when networking is involved, one simply cannot ignore the various errors that occur. A good app intelligently handles all errors. This article aimed towards that.

This article has been updated for AndroidX!

Pagination Series Overview

  1. Android Pagination Tutorial: Getting Started
  2. Using APIs with Retrofit and Gson
  3. Error Handling
  4. Using Multiple RecyclerView Types
  5. Adding Swipe-to-Refresh Support

Unlike one-off API calls, which involve one request to display data on-screen, Pagination is different.

Those who’ve followed my Pagination post series, know that the first-page call happens separately. This means error handling for page 1 will be different than that for page 2 and beyond.

Pagination involves 2 crucial error handling scenarios. Let’s deal with both.

Pagination Error Handling for Page 1

For pagination, page 1 is our one-off API call.

Now if the API call fails here, the screen will be empty with nothing to display. So unless you have cached data as a backup, you have nothing to show.

One-off loading content

So what can we do? Use the entire screen to tell people what went wrong.

Ideally, stick to a crisp one or two liner about the error. Nothing too technical. Also, don’t forget to include a ‘Call-to-Action’ (CTA) button.

Mostly, the CTA would and should be a retry button.

Here’s how I’ve modified my existing screen to accommodate an error layout:

activity_main.xml layout skeleton

<FrameLayout>
    <androidx.recyclerview.widget.RecyclerView />
    <ProgressBar/>
    <include layout="@layout/error_layout"/>
</FrameLayout>

error_layout.xml

<merge
       android:layout_width="match_parent"
       android:layout_height="match_parent">

    <LinearLayout
        android:id="@+id/error_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:orientation="vertical"
        android:visibility="gone"
        tools:visibility="visible">

        <!--Displays a generic error message-->
        <TextView
            style="@style/TextAppearance.AppCompat.Subhead"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/error_msg"/>

  <!--Displays a reason for the error—>
        <TextView
            android:id="@+id/error_txt_cause"
            style="@style/TextAppearance.AppCompat.Caption"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginTop="@dimen/activity_margin_quarter"
            tools:text="The server took too long to respond."/>

        <!—CTA— prompting user to retry failed request>
        <Button
            android:id="@+id/error_btn_retry"
            style="@style/Widget.AppCompat.Button.Borderless"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="@dimen/activity_margin_content"
            android:text="@string/btn_retry"
            android:textColor="@color/colorPrimary"/>

    </LinearLayout>
</merge>
One-off API call error layout

Use humor to reduce the  seriousness of the error

While this is the bare minimum, use the available screen estate effectively. Use this opportunity to display an image associated with the error. Even better if you can use a short GIF to go with it.

Some examples from material.uplabs.com:

Remember, adding a bit of humor goes a long way. But this may offend a few, so use it sparingly.

Handling Failure

Once the call fails, we must do three things:

  1. Hide the ProgressBar (loading indicator)
  2. Display error layout
  3. Show the appropriate error message

We can handle all of this by writing a convenient method showErrorView(Throwable t) called in onFailure() of our API call.

callTopRatedMoviesApi().enqueue(new Callback < TopRatedMovies > () {
 @Override
 public void onResponse(Call < TopRatedMovies > call, Response < TopRatedMovies > response) {
  hideErrorView();
  // TODO: handle data
 }

 @Override
 public void onFailure(Call < TopRatedMovies > call, Throwable t) {
  t.printStackTrace();
  showErrorView(t);
 }
});

private void showErrorView(Throwable throwable) {
 if (errorLayout.getVisibility() == View.GONE) {
  errorLayout.setVisibility(View.VISIBLE);
  progressBar.setVisibility(View.GONE);

  // display appropriate error message
  // Handling 3 generic fail cases.
  if (!isNetworkConnected()) {
   txtError.setText(R.string.error_msg_no_internet);
  } else {
   if (throwable instanceof TimeoutException) {
    txtError.setText(R.string.error_msg_timeout);
   } else {
    txtError.setText(R.string.error_msg_unknown);
   }
  }
 }
}

showErrorView() takes a Throwable parameter, that can be used to know what the error is.

NOTE
Don’t forget to hide the error view! A good place to do this is BEFORE executing your API call.

Notice our error layout has a retry button. This is our CTA which effectively allows retrying the failed request. So hopefully our screen will have something to show now.

Next, let’s add a click listener.

btnRetry.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View view {
     loadFirstPage();
   }
});

The button allows users to retry the first request if it failed.

Let’s run through a quick recap of what we just did. We:

  1. modified our activity_main.xml layout to include an error view
  2. displayed error view whenever the one-off API call failed
  3. used onFailure() to display an appropriate error message
  4. allowed users to retry the request after failure, via a CTA (Retry Button)

Pagination Error Handling for Page 2 and above

As mentioned earlier, errors for Page 2 and above will be handled differently. Initially, when expecting a page to load, a ProgressBar (loading indicator) is display at the footer.

If the API successfully returns a response, then we remove our ProgressBar and append the new data. However, for some reason, if that fails, we need to handle it.

Similar to page 1, we need to do two things:

  1. Hide the footer ProgressBar and show an error (in the footer) instead
  2. Tell the user what went wrong
  3. Allow them to retry

While the steps to perform remain the same, the key difference lies in executing this and displaying it to the user.

Open item_progress.xml. We need to add an error layout to this.

<FrameLayout android:layout_width="match_parent"
             android:layout_height="wrap_content">

    <ProgressBar
        android:id="@+id/loadmore_progress"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"/>

    <LinearLayout
        android:id="@+id/loadmore_errorlayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="?attr/selectableItemBackground"
        android:clickable="true"
        android:orientation="horizontal"
        android:paddingBottom="@dimen/activity_margin"
        android:paddingTop="@dimen/activity_margin"
        android:visibility="gone">

        <ImageButton
            android:id="@+id/loadmore_retry"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginLeft="@dimen/activity_margin_content"
            android:layout_marginStart="@dimen/activity_margin_content"
            android:background="@drawable/rety_selector"
            android:padding="@dimen/activity_margin_half"
            android:src="@drawable/ic_refresh_black_24dp"
            android:tint="@color/placeholder_grey"
            android:tintMode="src_in"
            tools:targetApi="lollipop"/>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginEnd="@dimen/activity_margin_content"
            android:layout_marginLeft="@dimen/activity_margin"
            android:layout_marginRight="@dimen/activity_margin_content"
            android:layout_marginStart="@dimen/activity_margin"
            android:gravity="center_vertical"
            android:orientation="vertical">

            <TextView
                android:id="@+id/loadmore_errortxt"
                style="@style/Base.TextAppearance.AppCompat.Body1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />

            <TextView
                style="@style/Base.TextAppearance.AppCompat.Caption"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="@string/tap_to_reload"/>

        </LinearLayout>
    </LinearLayout>
</FrameLayout>
(Footer) Progress layout with retry UI

Notice that our design now does two things. It:

  1. allows a user to retry
  2. tells them what went wrong

Listening to Retry button clicks

Go to PaginationAdapter.java. Let’s add the click action for the retry button.

protected class LoadingVH extends RecyclerView.ViewHolder implements View.OnClickListener {
 private ProgressBar mProgressBar;
 private ImageButton mRetryBtn;
 private TextView mErrorTxt;
 private LinearLayout mErrorLayout;

 public LoadingVH(View itemView) {
  super(itemView);

  …
  mErrorTxt = (TextView) itemView.findViewById(R.id.loadmore_errortxt);
  mErrorLayout = (LinearLayout) itemView.findViewById(R.id.loadmore_errorlayout);

  mRetryBtn.setOnClickListener(this);
  mErrorLayout.setOnClickListener(this);
 }

 @Override
 public void onClick(View view) {
  switch (view.getId()) {
   case R.id.loadmore_retry:
   case R.id.loadmore_errorlayout:
    showRetry(false, null);
    mCallback.retryPageLoad();
    break;
  }
 }
}

Notice that mCallback.retryPageLoad() is a listener which MainActivity will implement.

public interface PaginationAdapterCallback {
 void retryPageLoad();
}

Head back to MainActivity.java and implement the PaginationAdapterCallback interface. Then, go to the loadNextPage() method and make the following changes:

private void loadNextPage() {
 callTopRatedMoviesApi().enqueue(new Callback < TopRatedMovies > () {
  @Override
  public void onResponse(Call < TopRatedMovies > call,
   Response < TopRatedMovies > response) {
   adapter.removeLoadingFooter();
   isLoading = false;…
  }

  @Override
  public void onFailure(Call < TopRatedMovies > call, Throwable t) {
   adapter.showRetry(true, fetchErrorMessage(t));
  }
 });
}

Add the showRetry() method in PaginationAdapter.java.

public void showRetry(boolean show, @Nullable String errorMsg) {
 retryPageLoad = show;
 notifyItemChanged(movieResults.size() - 1);

 if (errorMsg != null) this.errorMsg = errorMsg;
}

adapter.showRetry() is responsible for changing the footer ProgressBar into a retry button. It additionally displays an error message saying what went wrong.

Displaying an error message matters

Typically, apps tend to show a retry button on failure. They miss out on telling us what exactly went wrong. The reason for failure could be anything. Does the user have a network issue? Or did the server fail to respond?

Most importantly, the error message tells us on whose side the issue is.

Here is how Instagram and DailyHunt (NewsHunt) handle pagination errors:

Take a good look at both these examples. Which one do you think did a better job of telling you what went wrong? That’s right, Dailyhunt. The app clearly tells us what the issue is. Also, it tells us what to do, to rectify it. Finally, the error footer is clickable, allowing us to retry that failed request.

While Instagram certainly does have a clean design, simply showing a retry option doesn’t really tell us the problem.

Finally, with everything done, let’s go ahead and run our app.


Final Results

We’ve now successfully handled both use cases. Page 1 is a separate call compared to the rest of the pages.

When page 1 fails, we don’t have any content to show the user. This leaves the entire screen blank. We handled this by smartly using the available screen estate. An appropriate error message was displayed. Additionally, we allowed users to retry the failed request.

We then followed a similar approach to handle page 2 and beyond. In addition, to display a retry button in the footer, we displayed the reason for failure as well. Finally, we inferred from 2 famous apps about how they handled a similar scenario.

Some key takeaways:

  • Pagination primarily has 2 error cases to be handled
  • Errors can be unavoidable, but we can take steps to handle them
  • Tell users what exactly went wrong, but don’t get too technical
  • Be forgiving with errors (consider using humor) and allow users to retry requests

I hope this post helped make your paginated apps error-prepared.

Read next – Part 4: Handling multiple View Types with Pagination.

As always, I’d like to hear what you think. Tell me if I covered all the use cases. Did I miss out on anything? Drop ’em in the comments below.

I’m happy you took the time to read this. All I need for you is to help me spread the word!

Product Designer who occasionally writes code.

Have you been experiencing some irregularities in your recyclerview? Issues like:

  • The recyclerview pre-setting the (wrong) value of some of the views in the list.
  • Some views being visible when they shouldn’t be and vice versa.

and so on…

RecyclerView recycling

99% of the time, this is a problem with recyclerview recycling the views. The 1% percent left could come from human error.

Let’s quickly revisit how RecyclerView works.

How does RecyclerView work?

RecyclerView can easily be called the better ListView. It works just like a ListView — displays a set of data on the screen but uses a different approach for the purpose. RecyclerView, as its name suggests, recycles views once they get out of scope (screen) with the help of ViewHolder pattern.

So how does this affect your app?

Just like I wrote up there, RecyclerView recycles the views once they get out of the screen. While trying to reuse the cache view, binding with the new data sometimes goes wrong. The RecyclerView either retains the old data for the view or fail to reset the view.

Solution

There is an okay solution and there a boom! solution.

1. Reset your views

This may seem trivial but really, a simple thing like resetting your view to their default state during binding could save you a lot of time.

fun bind(question: Question) {
    itemView.rb1.isSelected = false
    itemView.rb2.isSelected = false
    itemView.rb3.isSelected = false
    itemView.rb4.isSelected = false
    
    //Other code goes here
}

What we are basically doing is making sure that we have a default state for the views when our actual data is not being set. This implementation would be different for your different use cases.

2. Use the god-method – Disable recycling

For a situation whereby the first solution above doesn’t work, there is a solution that works like boom!

Add the following line of code in your bindViewHolder() method:

holder.setIsRecyclable(false) 

override fun onBindViewHolder(holder: QuestionsViewHolder, position: Int) {
    holder.setIsRecyclable(false) //Disable recycling

    val question = questions[holder.adapterPosition]
    holder.bind(question)
}

What this line of code does is to basically disable recycling for the recyclerview. That way, new views are created for each list item.

RecyclerView no recycling

Works like charm!

But isn’t this against what RecyclerView is made to do?

You may want to ask the question, isn’t this against what the recyclerview is made to do?

Google deliberately created RecyclerView.ViewHolder.setIsRecyclable() for cases where RecyclerView should not recycle views or recycling views have adverse effects.

NB:

  • A way you can make it more efficient is to take note of which views to recycle and which not to recycle, by handling that based on the number of items in the list.
  • If you are working on an interactive list like mine in the example above, you can find a way to save and keep track of user selection (or any other form of interaction with the views). Like the options chosen by the users as it is in my example above. Reason being that the recyclerview uses entirely new views when scrolling through the list.

Voila!


Thanks for reading. Also, let me know if the solution works for you. And you can give suggestions of other solutions that might have worked for you.

Peace out ✌

Just implemented RecyclerView in my code, replacing ListView.

Everything works fine. The data is displayed.

But error messages are being logged:

15:25:53.476 E/RecyclerView: No adapter attached; skipping layout

15:25:53.655 E/RecyclerView: No adapter attached; skipping layout

for the following code:

ArtistArrayAdapter adapter = new ArtistArrayAdapter(this, artists);
recyclerView = (RecyclerView) findViewById(R.id.cardList);
recyclerView.setHasFixedSize(true);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(this));

As you can see I have attached an adapter for RecyclerView.
So why do I keep getting this error?

I have read other questions related to the same problem but none of them help.

30 Answers

Can you make sure that you are calling these statements from the «main» thread outside of a delayed asynchronous callback (for example inside the onCreate() method).
As soon as I call the same statements from a «delayed» method. In my case a ResultCallback, I get the same message.

In my Fragment, calling the code below from inside a ResultCallback method produces the same message. After moving the code to the onConnected() method within my app, the message was gone…

LinearLayoutManager llm = new LinearLayoutManager(this);
llm.setOrientation(LinearLayoutManager.VERTICAL);
list.setLayoutManager(llm);
list.setAdapter( adapter );

I was getting the same two error messages until I fixed two things in my code:

(1) By default, when you implement methods in the RecyclerView.Adapter it generates:

@Override
public int getItemCount() {
    return 0;
}

Make sure you update your code so it says:

@Override
public int getItemCount() {
    return artists.size();
}

Obviously if you have zero items in your items then you will get zero things displayed on the screen.

(2) I was not doing this as shown in the top answer: CardView layout_width=»match_parent» does not match parent RecyclerView width

//correct
LayoutInflater.from(parent.getContext())
            .inflate(R.layout.card_listitem, parent, false);

//incorrect (what I had)
LayoutInflater.from(parent.getContext())
        .inflate(R.layout.card_listitem,null);

(3) EDIT: BONUS:
Also make sure you set up your RecyclerView like this:

<android.support.v7.widget.RecyclerView
    android:id="@+id/RecyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    />

NOT like this:

<view
    android:id="@+id/RecyclerView"
    class="android.support.v7.widget.RecyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

I have seen some tutorials using the latter method. While it works I think it generates this error too.

I have the same situation with you, display is ok, but error appear in the locat.
That’s my solution:
(1) Initialize the RecyclerView & bind adapter ON CREATE()

RecyclerView mRecycler = (RecyclerView) this.findViewById(R.id.yourid);
mRecycler.setAdapter(adapter);

(2) call notifyDataStateChanged when you get the data

adapter.notifyDataStateChanged();

In the recyclerView’s source code, there is other thread to check the state of data.

public RecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    this.mObserver = new RecyclerView.RecyclerViewDataObserver(null);
    this.mRecycler = new RecyclerView.Recycler();
    this.mUpdateChildViewsRunnable = new Runnable() {
        public void run() {
            if(RecyclerView.this.mFirstLayoutComplete) {
                if(RecyclerView.this.mDataSetHasChangedAfterLayout) {
                    TraceCompat.beginSection("RV FullInvalidate");
                    RecyclerView.this.dispatchLayout();
                    TraceCompat.endSection();
                } else if(RecyclerView.this.mAdapterHelper.hasPendingUpdates()) {
                    TraceCompat.beginSection("RV PartialInvalidate");
                    RecyclerView.this.eatRequestLayout();
                    RecyclerView.this.mAdapterHelper.preProcess();
                    if(!RecyclerView.this.mLayoutRequestEaten) {
                        RecyclerView.this.rebindUpdatedViewHolders();
                    }

                    RecyclerView.this.resumeRequestLayout(true);
                    TraceCompat.endSection();
                }

            }
        }
    };

In the dispatchLayout(), we can find there is the error in it:

void dispatchLayout() {
    if(this.mAdapter == null) {
        Log.e("RecyclerView", "No adapter attached; skipping layout");
    } else if(this.mLayout == null) {
        Log.e("RecyclerView", "No layout manager attached; skipping layout");
    } else {

i have this problem , a few time problem is recycleView put in ScrollView object

After checking implementation, the reason appears to be the following. If RecyclerView gets put into a ScrollView, then during measure step its height is unspecified (because ScrollView allows any height) and, as a result, gets equal to minimum height (as per implementation) which is apparently zero.

You have couple of options for fixing this:

  1. Set a certain height to RecyclerView
  2. Set ScrollView.fillViewport to true
  3. Or keep RecyclerView outside of ScrollView. In my opinion, this is the best option by far. If RecyclerView height is not limited — which is the case when it’s put into ScrollView — then all Adapter’s views have enough place vertically and get created all at once. There is no view recycling anymore which kinda breaks the purpose of RecyclerView .

(Can be followed for android.support.v4.widget.NestedScrollView as well)

It happens when you are not setting the adapter during the creation phase:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity);
    mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
    ....
}

public void onResume() {
    super.onResume();
    mRecyclerView.setAdapter(mAdapter);
    ....
}

Just move setting the adapter into onCreate with an empty data and when you have the data call:

mAdapter.notifyDataSetChanged();

1) Create ViewHolder that does nothing :)

// SampleHolder.java
public class SampleHolder extends RecyclerView.ViewHolder {
    public SampleHolder(View itemView) {
        super(itemView);
    }
}

2) Again create RecyclerView that does nothing :)

// SampleRecycler.java
public class SampleRecycler extends RecyclerView.Adapter<SampleHolder> {
    @Override
    public SampleHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    public void onBindViewHolder(SampleHolder holder, int position) {

    }

    @Override
    public int getItemCount() {
        return 0;
    }
}

3) Now when your real recycler is not ready just use the sample one like below.

RecyclerView myRecycler = (RecyclerView) findViewById(R.id.recycler_id);
myRecycler.setLayoutManager(new LinearLayoutManager(this));
myRecycler.setAdapter(new SampleRecycler());

This is not best solution though but it works! Hope this is helpful.

Make sure you set the layout manager for your RecyclerView by:

mRecyclerView.setLayoutManager(new LinearLayoutManager(context));

Instead of LinearLayoutManager, you can use other layout managers too.

Check if you have missed to call this method in your adapter

@Override
public int getItemCount() {
    return list.size();
}

I had the same error I fixed it doing this if you are waiting for data like me using retrofit or something like that

Put before Oncreate

private ArtistArrayAdapter adapter;
private RecyclerView recyclerView;

Put them in your Oncreate

 recyclerView = (RecyclerView) findViewById(R.id.cardList);
 recyclerView.setHasFixedSize(true);
 recyclerView.setLayoutManager(new LinearLayoutManager(this));
 adapter = new ArtistArrayAdapter( artists , R.layout.list_item ,getApplicationContext());
 recyclerView.setAdapter(adapter);

When you receive data put

adapter = new ArtistArrayAdapter( artists , R.layout.list_item ,getApplicationContext());
recyclerView.setAdapter(adapter);

Now go in your ArtistArrayAdapter class and do this what it will do is if your array is empty or is null it will make GetItemCount return 0 if not it will make it the size of artists array

@Override
public int getItemCount() {

    int a ;

    if(artists != null && !artists.isEmpty()) {

        a = artists.size();
    }
    else {

        a = 0;

     }

   return a;
}
ArtistArrayAdapter adapter = new ArtistArrayAdapter(this, artists);
recyclerView = (RecyclerView) findViewById(R.id.cardList);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(adapter);

Just replace above code with this and it should work. What you did wrong is you called setAdapter(adapter) before calling layout manager.

These Lines must be in OnCreate:

mmAdapter = new Adapter(msgList);
mrecyclerView.setAdapter(mmAdapter);

For those who use the RecyclerView within a fragment and inflate it from other views: when inflating the whole fragment view, make sure that you bind the RecyclerView to its root view.

I was connecting and doing everything for the adapter correctly, but I never did the binding.
This answer by @Prateek Agarwal has it all for me, but here is more elaboration.

Kotlin

    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {

    val rootView =  inflater?.inflate(R.layout.fragment_layout, container, false)
    recyclerView = rootView?.findViewById(R.id.recycler_view_id)
    // rest of my stuff
    recyclerView?.setHasFixedSize(true)
    recyclerView?.layoutManager = viewManager
    recyclerView?.adapter = viewAdapter
    // return the root view
    return rootView
}

Java

  @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    View rootView= inflater.inflate(R.layout.fragment_layout,container,false);
    recyclerview= rootView.findViewById(R.id.recycler_view_id);
    return rootView;
}

In Kotlin we had this weird illogical issue.

This didn’t work:

 mBinding.serviceProviderCertificates.apply {
            adapter = adapter
            layoutManager =  LinearLayoutManager(activity)
        }

While this worked:

mBinding.serviceProviderCertificates.adapter = adapter
mBinding.serviceProviderCertificates.layoutManager = LinearLayoutManager(activity)

Once I get more after work hours, I will share more insights.

This happens because the actual inflated layout is different from that which is being referred by you while finding the recyclerView. By default when you create the fragment, the onCreateView method appears as follows:
return inflater.inflate(R.layout.<related layout>,container.false);

Instead of that, separately create the view and use that to refer to recyclerView
View view= inflater.inflate(R.layout.<related layout>,container.false);
recyclerview=view.findViewById(R.id.<recyclerView ID>);
return view;

First initialize the adapter

public void initializeComments(){
    comments = new ArrayList<>();

    comments_myRecyclerView = (RecyclerView) findViewById(R.id.comments_recycler);
    comments_mLayoutManager = new LinearLayoutManager(myContext);
    comments_myRecyclerView.setLayoutManager(comments_mLayoutManager);

    updateComments();
    getCommentsData();

}

public void updateComments(){

    comments_mAdapter = new CommentsAdapter(comments, myContext);
    comments_myRecyclerView.setAdapter(comments_mAdapter);
}

When ever there is a change in the dataset set, just call the updateComments method.

I had this error, and I tried to fix for a while until I found the solution.

I made a private method buildRecyclerView, and I called it twice, first on onCreateView and then after my callback (in which I fetch data from an API). This is my method buildRecyclerView in my Fragment:

private void buildRecyclerView(View v) {
        mRecyclerView = v.findViewById(R.id.recycler_view_loan);
        mLayoutManager = new LinearLayoutManager(getActivity());
        ((LinearLayoutManager) mLayoutManager).setOrientation(LinearLayoutManager.VERTICAL);
        mRecyclerView.setLayoutManager(mLayoutManager);
        mAdapter = new LoanAdapter(mExampleList);
        mRecyclerView.setLayoutManager(mLayoutManager);
        mRecyclerView.setAdapter(mAdapter);
}

Besides, I have to modify the method get-Item-Count in my adapter, because On on-Create-View the list is null and it through an error. So, my get-Item-Count is the following:

@Override
    public int getItemCount() {
        try {
            return mLoanList.size();
        } catch (Exception ex){return 0;}

    }

This is really a simple error you are getting, there in no need of doing any codes in this.
This error occurs due to the wrong layout file used by the activity. By IDE i automatically created a layout v21 of a layout which became a default layout of the activity.
all codes I did in the old layout file and new one was only having few xml codes, which led to that error.

Solution: Copy all codes of old layout and paste in layout v 21

In my case, I was setting the adapter inside onLocationChanged() callback AND debugging in the emulator. Since it didn’t detected a location change it never fired. When I set them manually in the Extended controls of the emulator it worked as expected.

I have solved this error. You just need to add layout manager
and add the empty adapter.

Like this code:

myRecyclerView.setLayoutManager(...//your layout manager);
        myRecyclerView.setAdapter(new RecyclerView.Adapter() {
            @Override
            public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                return null;
            }

            @Override
            public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

            }

            @Override
            public int getItemCount() {
                return 0;
            }
        });
//other code's 
// and for change you can use if(mrecyclerview.getadapter != speacialadapter){
//replice your adapter
//}

In my layout xml file, the bottom line with layoutManager was missing. The error disappeared after I added it.

   <androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recycler_view_chat"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1"
    app:layoutManager="LinearLayoutManager"/>

In my situation it was a forgotten component which locates in ViewHolder class but it wasn’t located in layout file

I had the same problem and realized I was setting both the LayoutManager and adapter after retrieving the data from my source instead of setting the two in the onCreate method.

salesAdapter = new SalesAdapter(this,ordersList);
        salesView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
        salesView.setAdapter(salesAdapter);

Then notified the adapter on data change

               //get the Orders
                Orders orders;
                JSONArray ordersArray = jObj.getJSONArray("orders");
                    for (int i = 0; i < ordersArray.length() ; i++) {
                        JSONObject orderItem = ordersArray.getJSONObject(i);
                        //populate the Order model

                        orders = new Orders(
                                orderItem.getString("order_id"),
                                orderItem.getString("customer"),
                                orderItem.getString("date_added"),
                                orderItem.getString("total"));
                        ordersList.add(i,orders);
                        salesAdapter.notifyDataSetChanged();
                    }

This issue is because you are not adding any LayoutManager for your RecyclerView.

Another reason is because you are calling this code in a NonUIThread. Make sure to call this call in the UIThread.

The solution is only you have to add a LayoutManager for the RecyclerView before you setAdapter in the UI Thread.

recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));

My problem was my recycler view looked like this

        <android.support.v7.widget.RecyclerView
        android:id="@+id/chatview"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent">
    </android.support.v7.widget.RecyclerView>

When it should have looked like this

       <android.support.v7.widget.RecyclerView
        android:id="@+id/chatview"
        android:layout_width="395dp"
        android:layout_height="525dp"
        android:layout_marginTop="52dp"
        app:layout_constraintTop_toTopOf="parent"
        tools:layout_editor_absoluteX="8dp"></android.support.v7.widget.RecyclerView>
</android.support.constraint.ConstraintLayout>

In your RecyclerView adapter class, for example MyRecyclerViewAdapter, make a constructor with the following params.

MyRecyclerViewAdapter(Context context, List<String> data) {
    this.mInflater = LayoutInflater.from(context); // <-- This is the line most people include me miss
    this.mData = data;
}

mData is the data that you’ll pass to the adapter. It is optional if you have no data to be passed.
mInflater is the LayoutInflater object that you have created and you use in the OnCreateViewHolder function of the adapter.

After this, you attach the adapter in the MainActivity or wherever you want to on the main/UI thread properly like

MyRecyclerViewAdapter recyclerAdapter;
OurDataStuff mData;

    ....
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //Like this:

        RecyclerView recyclerView = findViewById(R.id.recyclerView);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        recyclerAdapter = new RecyclerAdapter(this, mData); //this, is the context. mData is the data you want to pass if you have any
        recyclerView.setAdapter(recyclerAdapter);
    }
   ....

Solved by setting the initialized empty list and adapter at the bottom and calling notifyDataSetChanged when results are fetched.

    LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
    recyclerviewItems.setLayoutManager(linearLayoutManager);
    someAdapter = new SomeAdapter(getContext(),feedList);
    recyclerviewItems.setAdapter(someAdapter);

I lost 16 minutes of my life with this issue, so I’ll just admit to this incredibly embarrassing mistake that I was making- I’m using Butterknife and I bind the view in onCreateView in this fragment.

It took a long time to figure out why I had no layoutmanager — but obviously the views are injected so they won’t actually be null, so the the recycler will never be null .. whoops!

@BindView(R.id.recycler_view)
RecyclerView recyclerView;

    @Override
public View onCreateView(......) {
    View v = ...;
    ButterKnife.bind(this, v);
    setUpRecycler()
 }

public void setUpRecycler(Data data)
   if (recyclerView == null) {
 /*very silly because this will never happen*/
       LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
       //more setup
       //...
    }
    recyclerView.setAdapter(new XAdapter(data));
}

If you are getting an issue like this trace your view and use something like uiautomatorviewer

In my case it happened cause i embedded a RecyclerView in a LinearLayout.

I previously had a layout file only containing one root RecyclerView as follows

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView
    android:id="@+id/list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:listitem="@layout/fragment_products"

    android:name="Products.ProductsFragment"
    app:layoutManager="LinearLayoutManager"
    tools:context=".Products.ProductsFragment"

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

I believe the problem is within the 3 lines separated. Anyway, I think its a simple problem, ill be working on it tomorrow; thought i should write what i found before forgetting about this thread.

Just add the following to RecyclerView

app:layoutManager="android.support.v7.widget.LinearLayoutManager"

Example:

   <android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical"
        app:layoutManager="android.support.v7.widget.LinearLayoutManager"
        app:layout_constraintBottom_toTopOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

    </android.support.v7.widget.RecyclerView>

Adding yet another answer since I came across this thread googling the error. I was trying to initialize a PreferenceFragmentCompat but I forgot to inflate the preference XML in onCreatePreferences like this:

class SettingsFragment : PreferenceFragmentCompat() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val inflater = LayoutInflater.from(context)
        inflater.inflate(R.layout.fragment_settings, null)
    }

    override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
        // Missing this line here:
        //   setPreferencesFromResource(R.xml.settings, rootKey)
    }
}

The error was a mystery until I realized that PreferenceFragmentCompat must be using a RecyclerView internally.

Понравилась статья? Поделить с друзьями:
  • Recv error end of file trassir
  • Recv error 10054
  • Recursive error after couldn t load font verdana
  • Recursionerror maximum recursion depth exceeded ошибка
  • Recursion error питон