Android: crash from IndexOutOfBoundsException: length=36; index=-1 - android-recyclerview

I have a RecyclerView list of CardViews. A click on the CardView loads a DetailsActivity for that CardView, using its position in the RecyclerView list. Recently a click on CardView crashed the app with an ArrayIndexOutOfBoundsException. I did click on the CardView quickly after returning to the Recycler list UI, so might this be some type of asynchronous error. Any thoughts on how to fix?
Code for the item click in the RecyclerView's CardsAdapter:
public List<Card> mListItems;
...
public Card getItem(int position) {
**return mListItems.get(position); // line 102**
}
ClickListener code in the CardsAdapter to handle the item click:
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = layoutInflater.inflate(R.layout.item_todo, parent,false);
final ItemHolder itemHolder = new ItemHolder(itemView);
itemHolder.cardView.setOnClickListener(view -> {
**Card adapterItem = CardsAdapter.this.getItem(itemHolder.getBindingAdapterPosition()); // line 639**
int adapPos = itemHolder.getBindingAdapterPosition();
if (adapPos == RecyclerView.NO_POSITION || recyclerItemClickListener == null) {
return;
}
recyclerItemClickListener.onItemClick(itemHolder.cardView, adapterItem, adapPos);
});
...
}
Logcat:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.todo.quickcards, PID: 10677
java.lang.ArrayIndexOutOfBoundsException: length=36; index=-1
at java.util.ArrayList.get(ArrayList.java:439)
at com.todo.quickcards.adapter.CardsAdapter.getItem(CardsAdapter.java:102)
at com.todo.quickcards.adapter.CardsAdapter.lambda$onCreateViewHolder$0$com-todo-quickcards-adapter-CardsAdapter(CardsAdapter.java:639)
at com.todo.quickcards.adapter.CardsAdapter$$ExternalSyntheticLambda0.onClick(Unknown Source:4)
at android.view.View.performClick(View.java:6897)
at android.view.View$PerformClick.run(View.java:26101)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

If you are gonna call getItem(itemHolder.getBindingAdapterPosition()) then better call it after/inside the Condition check . Which checked for invalid position .
int adapPosition = itemHolder.getBindingAdapterPosition();
if (adapPos == RecyclerView.NO_POSITION || recyclerItemClickListener == null) {
return;
}
Card adapterItem = CardsAdapter.this.getItem(adapPosition);
recyclerItemClickListener.onItemClick(itemHolder.cardView, adapterItem, adapPos);

Related

Recylerview in fragment doesnt populate with items

Sorry for this noob questons, but I am starting to learn Firebase UI and 2 days in a row I am stuck with populating a RecyclerView in a fragment.
I tried to move my code around in onViewCreated , onCreateView methods but I think that is not the problem, in log it just throws one error:
2020-02-18 15:08:26.730 4467-4467/com.social.voteup E/RecyclerView: No adapter attached; skipping layout
Double checked everything is linking with the xml file.
Here is my code:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_home, container, false);
return view;
}
On View Created method
mRecyclerView = view.findViewById(R.id.posts_recyclerview);
mSwipeRefreshLayout = view.findViewById(R.id.refreshLayout);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
setupAdapter();
// Refresh Action on Swipe Refresh Layout
mSwipeRefreshLayout.setOnRefreshListener(() -> mAdapter.refresh());
I use the adapter inside the fragment as shown here:
private void setupAdapter() {
// Init Paging Configuration
PagedList.Config config = new PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPrefetchDistance(2)
.setPageSize(10)
.build();
// Init Adapter Configuration
FirestorePagingOptions<Post> options = new FirestorePagingOptions.Builder<Post>()
.setLifecycleOwner(this)
.setQuery(mQuery, config, Post.class)
.build();
// Instantiate Paging Adapter
mAdapter = new FirestorePagingAdapter<Post, PostViewHolder>(options) {
#NonNull
#Override
public PostViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = getLayoutInflater().inflate(R.layout.item_post, parent, false);
return new PostViewHolder(view);
}
#Override
protected void onBindViewHolder(#NonNull PostViewHolder viewHolder, int i, #NonNull Post post) {
// Bind to ViewHolder
viewHolder.bind(post);
}
#Override
protected void onError(#NonNull Exception e) {
super.onError(e);
Log.e("MainActivity", Objects.requireNonNull(e.getMessage()));
}
protected void onLoadingStateChanged(#NonNull LoadingState state) {
switch (state) {
case LOADING_INITIAL:
case LOADING_MORE:
mSwipeRefreshLayout.setRefreshing(true);
break;
case LOADED:
mSwipeRefreshLayout.setRefreshing(false);
break;
case ERROR:
mSwipeRefreshLayout.setRefreshing(false);
break;
case FINISHED:
mSwipeRefreshLayout.setRefreshing(false);
break;
}
}
};
// Finally Set the Adapter to mRecyclerView
mRecyclerView.setAdapter(mAdapter);
}
Here is the full source file on pastebin:https://pastebin.com/YkzKRuUD
Add this line of code in the On View Created method.
You have created an adapter but you haven't attached the adapter to the recyclerView
mRecyclerView = view.findViewById(R.id.posts_recyclerview);
mSwipeRefreshLayout = view.findViewById(R.id.refreshLayout);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mRecyclerView.setAdapter(new setupAdapter());
// Refresh Action on Swipe Refresh Layout
mSwipeRefreshLayout.setOnRefreshListener(() -> mAdapter.refresh());

RecyclerView.Adapter get ItemView through position

In my Adapter, I call LayoutManager.ChildAt(position) to get the itemview, but the view I get is not the matched itemview, and when i call notifyItemChanged(position), app crashes:
E/AndroidRuntime: FATAL EXCEPTION: mainProcess: com.paicaifu.riches, PID: 8502
Java.lang.IllegalArgumentException: Called attach on a child which is not detached: ViewHolder{adb21588 position=0 id=-1, oldPos=-1, pLpos:-1}
at Android.support.v7.widget.RecyclerView$5.attachViewToParent(RecyclerView.java:654)
at android.support.v7.widget.ChildHelper.attachViewToParent(ChildHelper.java:239)
at android.support.v7.widget.RecyclerView.addAnimatingView(RecyclerView.java:1107)
at android.support.v7.widget.RecyclerView.animateChange(RecyclerView.java:3270)
at android.support.v7.widget.RecyclerView.dispatchLayoutStep3(RecyclerView.java:3088)
at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2917)
at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:3283)
…
at android.view.Choreographer.doCallbacks(Choreographer.java:574)
Thanks,this is my code in the adapter:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder,
final int position) {
if (holder instanceof CardHolder) {
final CardHolder cardHolder = (CardHolder) holder;
cardHolder.rootview.setOnClickListener(this);
cardHolder.rootview.setTag(position);
cardHolder.rootview.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//get last itemview in adapter,but preView isnot match the
//position,it is the visibable views on screen
View preView = mLinearLayoutManager.getChildAt(preIndex);
//change the datasource
mData.getResult().getCardList().get(preIndex).setOpen(false);
//update item
notifyItemChanged(preIndex);//when the item is out of screen,
//this line will cause crashes
preIndex = position;
}
});
}
I just want find the last item and change it's state(open or close),when the item is out of screen,notifyItemChanged(int position) will appear problem.
This is probably a bit too late, but the crash "Called attach on a child which is not detached" typically happens when you do NOT create a new view in onCreateViewHolder and instead return a cached view that's already inflated and attached.

Application crash while using spinner

This method is to get the inputs that includes three EditText boxes and one spinner which is populated from string-array.The click of the button is the trigger for undertaking the calculations. While the OnClick method is pressed the application crashes. The code is as follows
package biz.avksons.siddharth.hydro_basic_calc;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;
public class UoE extends AppCompatActivity {
// Define variables for Spinner here
Spinner spinner_1;
String spin_select;
boolean dbug=true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_uoe);
}
// ************************************************************************************
// Start the method to calculate the units of electricity here
public void onUoECalc_ButtonClick(View v) {
/* Define Custom method for calculating the Units of electricity on clicking the Calculate button*/
//Processing variable for count
boolean flag = false; // Used to check if the inputs done in the EditText are valid or not, else there will an exception which would lead to application crash
/* Initiate the Variables here*/
//-----------------------------------------------------
//Allocate the ids of EditText to the variables for further use
EditText et_capacity = (EditText) findViewById(R.id.et_uoe_capacity);
EditText et_plf = (EditText) findViewById(R.id.et_uoe_plf);
EditText et_availability = (EditText) findViewById(R.id.et_uoe_availability);
EditText et_energy = (EditText) findViewById(R.id.et_uoe_mus);
//Variable for deciding Timeperiod
Double time_period = 365.00;
//---DEBUG <code></code>
if (dbug)
{
Toast.makeText(this, "Spiner selection : " + spin_select, Toast.LENGTH_SHORT).show();
}
//Set flag to true only if the inputs in the two edittext, discharge and head are sensible inputs
if (checkedittext(et_capacity) && checkedittext(et_plf) && checkedittext(et_availability)) {
flag = true;
} else {
//If the inputs in discharge are not sensible then display the message to input a sensible input before moving further
Toast.makeText(this, "Please enter valid inputs", Toast.LENGTH_LONG).show();
// Set the EditText box of the output to blank if the inputs are not valid as tested in the above statements
et_capacity.setText("");
}
// do this operation only if the flag is set to true which are tested above
if (flag == true) {
//-------Initiating Spinner here
addListenerOnSpinnerItemSelection();
// Set selection default for Spinner here
// spinner_1.setSelection(1);
//--------Allocation of Spin Button Here-------------
if (dbug)
{
Toast.makeText(this, "Spin_select"+ spin_select, Toast.LENGTH_SHORT).show();
}
switch (spin_select) {
case "1 yr":
time_period = 365.00;
case "6 mon":
time_period = 180.00;
case "3 mon":
time_period = 90.00;
}
//-------End of Spin Button--------------
//Assign the values here
Double d_capacity = Double.parseDouble(et_capacity.getText().toString());
Double d_plf = Double.parseDouble(et_plf.getText().toString());
Double d_availability = Double.parseDouble(et_availability.getText().toString());
//-DEBUG Code----
if(dbug)
{
Toast.makeText(this, "Time Period selected" + time_period.toString(), Toast.LENGTH_SHORT).show();
}
Double d_energy = Double.valueOf((d_capacity * 1000 * 24 * time_period * d_plf * d_availability) / 1000000);
et_energy.setText(Double.toString(Math.round(d_energy))); // In MW
// capacity.setText(String.format(".2%f",Double.toString(C)));
}
}
//Method to check for validity of contents in edittext
public boolean checkedittext(EditText edtxt) {
if (edtxt.getText().toString().matches("")) {
return false;
} else {
return true;
}
}
//--------Method to get the selected option from Spinner
public void addListenerOnSpinnerItemSelection() {
//------Declare a variable on Spinner and allocate the id here
spinner_1 = (Spinner) findViewById(R.id.spin_uoe_period);
spinner_1.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
{
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
{
spin_select = parent.getItemAtPosition(position).toString();
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
public void onHelp_UoE(View v)
{
Toast.makeText(this,"Please enter valid figures in the boxes mentioned and select the period you are interested in; from the dropdown menu before pressing calculate",Toast.LENGTH_LONG).show();
}
}
The error in Logcat is as follows
E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.IllegalStateException: Could not execute method for android:onClick
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:293)
at android.view.View.performClick(View.java:4240)
at android.view.View$PerformClick.run(View.java:17721)
at android.os.Handler.handleCallback(Handler.java:730)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:5103)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288)
at android.view.View.performClick(View.java:4240) 
at android.view.View$PerformClick.run(View.java:17721) 
at android.os.Handler.handleCallback(Handler.java:730) 
at android.os.Handler.dispatchMessage(Handler.java:92) 
at android.os.Looper.loop(Looper.java:137) 
at android.app.ActivityThread.main(ActivityThread.java:5103) 
at java.lang.reflect.Method.invokeNative(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:525) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) 
at dalvik.system.NativeStart.main(Native Method) 
Caused by: java.lang.NullPointerException
at biz.avksons.siddharth.hydro_basic_calc.UoE.onUoECalc_ButtonClick(UoE.java:84)
at java.lang.reflect.Method.invokeNative(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:525) 
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:288) 
at android.view.View.performClick(View.java:4240) 
at android.view.View$PerformClick.run(View.java:17721) 
at android.os.Handler.handleCallback(Handler.java:730) 
at android.os.Handler.dispatchMessage(Handler.java:92) 
at android.os.Looper.loop(Looper.java:137) 
at android.app.ActivityThread.main(ActivityThread.java:5103) 
at java.lang.reflect.Method.invokeNative(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:525) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) 
at dalvik.system.NativeStart.main(Native Method) 
I tried to find the NullPointerException error which as per the code suggests that the spinner_select variable is not initialized. My understanding tells me that the spinner_select variable should be initialized every time the method is called.
Request for assistance..Thank You
I hope you have missed
break;
in switch case statement

Recycler View: Inconsistency detected. Invalid view holder adapter positionViewHolder

Recycler View Inconsistency Detected error, coming while scrolling fast or scrolling while loading more items..
FATAL EXCEPTION: main
Process: com.pratap.endlessrecyclerview, PID: 21997
java.lang.IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder{56a082c position=40 id=-1, oldPos=39, pLpos:39 scrap [attachedScrap] tmpDetached no parent}
at android.support.v7.widget.RecyclerView$Recycler.validateViewHolderForOffsetPosition(RecyclerView.java:4251)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4382)
at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:4363)
at android.support.v7.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:1961)
at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1370)
at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1333)
at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:562)
at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2864)
at android.support.v7.widget.RecyclerView.consumePendingUpdateOperations(RecyclerView.java:1445)
at android.support.v7.widget.RecyclerView.access$400(RecyclerView.java:144)
at android.support.v7.widget.RecyclerView$1.run(RecyclerView.java:282)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858)
at android.view.Choreographer.doCallbacks(Choreographer.java:670)
at android.view.Choreographer.doFrame(Choreographer.java:603)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)
at android.os.Handler.handleCallback(Handler.java:746)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5443)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
Adapter
public class DataAdapter extends RecyclerView.Adapter {
private final int VIEW_ITEM = 1;
private final int VIEW_PROG = 0;
private List<Feed> mFeed;
// The minimum amount of items to have below your current scroll position
// before loading more.
private int visibleThreshold = 5;
private int lastVisibleItem, totalItemCount;
private boolean loading;
private OnLoadMoreListener onLoadMoreListener;
public DataAdapter(List<Feed> feeds, RecyclerView recyclerView) {
mFeed = feeds;
if (recyclerView.getLayoutManager() instanceof LinearLayoutManager) {
final LinearLayoutManager linearLayoutManager = (LinearLayoutManager) recyclerView
.getLayoutManager();
recyclerView
.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView,
int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager
.findLastVisibleItemPosition();
if (!loading
&& totalItemCount <= (lastVisibleItem + visibleThreshold)) {
// End has been reached
// Do something
if (onLoadMoreListener != null) {
onLoadMoreListener.onLoadMore();
}
loading = true;
}
}
});
}
}
#Override
public int getItemViewType(int position) {
return mFeed.get(position) == null ? VIEW_PROG : VIEW_ITEM;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
RecyclerView.ViewHolder vh;
if (viewType == VIEW_ITEM) {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.list_row, parent, false);
vh = new StudentViewHolder(v);
}
else {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.progress_item, parent, false);
vh = new ProgressViewHolder(v);
}
return vh;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof StudentViewHolder) {
Feed singleStudent= (Feed) mFeed.get(position);
((StudentViewHolder) holder).tvName.setText(singleStudent.getTitle());
((StudentViewHolder) holder).student= singleStudent;
} else {
ProgressViewHolder.PROGRESS_BAR.setIndeterminate(true);
}
}
public void setLoaded() {
loading = false;
}
public void addFeed(Feed feed) {
mFeed.add(feed);
//mFeed.addAll(0, (Collection<? extends Feed>) feed);
notifyItemInserted(mFeed.size());
//notifyItemRangeInserted(0,mFeed.size());
notifyDataSetChanged();
//notifyItemInserted(mFeed.size());
//setLoaded();
//notifyItemInserted(mFeed.size());
}
public void removeAll(){
mFeed.clear();
notifyDataSetChanged();
}
#Override
public int getItemCount() {
return mFeed.size();
}
public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
this.onLoadMoreListener = onLoadMoreListener;
}
public static class StudentViewHolder extends RecyclerView.ViewHolder {
public TextView tvName;
public Feed student;
public StudentViewHolder(View v) {
super(v);
tvName = (TextView) v.findViewById(R.id.tvName);
//tvEmailId = (TextView) v.findViewById(R.id.tvEmailId);
}
}
public static class ProgressViewHolder extends RecyclerView.ViewHolder {
//public ProgressBar progressBar;
public static ProgressBar PROGRESS_BAR;
public ProgressViewHolder(View v) {
super(v);
PROGRESS_BAR = (ProgressBar) v.findViewById(R.id.progressBar1);
// progressBar = (ProgressBar) v.findViewById(R.id.progressBar1);
}
}
}
Activity
public class MainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener {
private Toolbar toolbar;
private TextView tvEmptyView;
private RecyclerView mRecyclerView;
private DataAdapter mAdapter;
private LinearLayoutManager mLayoutManager;
private RestManager mManager;
private List<Feed> mFeed;
SwipeRefreshLayout mSwipeRefreshLayout;
protected Handler handler;
private int currentPage=1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.toolbar);
tvEmptyView = (TextView) findViewById(R.id.empty_view);
mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
mSwipeRefreshLayout= (SwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);
mSwipeRefreshLayout.setOnRefreshListener(this);
//studentList = new ArrayList<Student>();
mFeed = new ArrayList<Feed>();
handler = new Handler();
if (toolbar != null) {
setSupportActionBar(toolbar);
getSupportActionBar().setTitle("Android Students");
}
mManager = new RestManager();
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
mRecyclerView.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this);
// use a linear layout manager
mRecyclerView.setLayoutManager(mLayoutManager);
// create an Object for Adapter
mAdapter = new DataAdapter(mFeed,mRecyclerView);
// set the adapter object to the Recyclerview
mRecyclerView.setAdapter(mAdapter);
// mAdapter.notifyDataSetChanged();
loadData(false);
// if (mFeed.isEmpty()) {
// mRecyclerView.setVisibility(View.GONE);
// tvEmptyView.setVisibility(View.VISIBLE);
//
// } else {
// mRecyclerView.setVisibility(View.VISIBLE);
// tvEmptyView.setVisibility(View.GONE);
// }
mAdapter.setOnLoadMoreListener(new OnLoadMoreListener() {
#Override
public void onLoadMore() {
//add null , so the adapter will check view_type and show progress bar at bottom
mFeed.add(null);
mAdapter.notifyItemInserted(mFeed.size() - 1);
handler.postDelayed(new Runnable() {
#Override
public void run() {
// remove progress item
mFeed.remove(mFeed.size() - 1);
// mAdapter.notifyItemRemoved(mFeed.size());
//add items one by one
int start = mFeed.size();
currentPage++;
Log.d("CurrentPage", String.valueOf(currentPage));
Call<Results> listCall = mManager.getFeedApi().getAllFeeds(1);
listCall.enqueue(new Callback<Results>() {
#Override
public void onResponse(Call<Results> call, Response<Results> response) {
mSwipeRefreshLayout.setRefreshing(false);
if (response.isSuccess()) {
if (response.body() != null) {
Results feedList = response.body();
// List<Results> newUsers = response.body();
Log.d("Retrofut", String.valueOf(feedList));
for (int i = 0; i < feedList.results.size(); i++) {
Feed feed = feedList.results.get(i);
// mFeed.add(feed);
mAdapter.addFeed(feed);
// mAdapter.notifyDataSetChanged();
//mAdapter.notifyItemInserted(mFeed.size());
}
// mAdapter.notifyDataSetChanged();
}
}
}
#Override
public void onFailure(Call<Results> call, Throwable t) {
Log.d("Retrofut", "Error");
mFeed.remove(mFeed.size() - 1);
mAdapter.notifyItemRemoved(mFeed.size());
mAdapter.setLoaded();
mSwipeRefreshLayout.setRefreshing(false);
}
});
// for (int i = 1; i <= 20; i++) {
// studentList.add(new Student("Student " + i, "androidstudent" + i + "#gmail.com"));
//
// }
mAdapter.setLoaded();
//or you can add all at once but do not forget to call mAdapter.notifyDataSetChanged();
}
}, 2000);
}
});
}
// load initial data
private void loadData(final boolean removePreData) {
Call<Results> listCall = mManager.getFeedApi().getAllFeeds(1);
listCall.enqueue(new Callback<Results>() {
#Override
public void onResponse(Call<Results> call, Response<Results> response) {
if (response.isSuccess()) {
if (response.body() != null) {
// if(removePreData) mAdapter.removeAll();
Results feedList = response.body();
Log.d("Retrofut", String.valueOf(feedList));
for (int i = 0; i < feedList.results.size(); i++) {
Feed feed = feedList.results.get(i);
// mFeed.add(feed);
//mAdapter.notifyDataSetChanged();
mAdapter.addFeed(feed);
}
mSwipeRefreshLayout.setRefreshing(false);
}
}
}
#Override
public void onFailure(Call<Results> call, Throwable t) {
Log.d("Retrofut", String.valueOf(t));
mFeed.remove(mFeed.size() - 1);
mAdapter.notifyItemRemoved(mFeed.size());
mAdapter.setLoaded();
mSwipeRefreshLayout.setRefreshing(false);
}
}
);
// for (int i = 1; i <= 20; i++) {
// studentList.add(new Student("Student " + i, "androidstudent" + i + "#gmail.com"));
//
// }
mSwipeRefreshLayout.setRefreshing(true);
}
#Override
public void onRefresh() {
mFeed.clear();
mAdapter.notifyDataSetChanged();
loadData(true);
currentPage=1;
}
}
put this line along with setting recyclerView. issue was fixed by
setting ItemAnimator to null for RecyclerView.
in kotlin
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.itemAnimator = null
in java
recyclerView.setItemAnimator(null);
It looks similar with known android bug
There are quite ugly, but working approach
public class WrapContentLinearLayoutManager extends LinearLayoutManager {
//... constructor
#Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
try {
super.onLayoutChildren(recycler, state);
} catch (IndexOutOfBoundsException e) {
Log.e("Error", "IndexOutOfBoundsException in RecyclerView happens");
}
}
}
mRecyclerView.setLayoutManager(new WrapContentGridLayoutManager(getContext(), spanCount));
For me it works without any by-effect.
This issue is a known bug of RecyclerView. The best solution is, clear the list every time before refresh RecyclerView.
For fix this issue just call notifyDataSetChanged() with empty list before updating recycle view.
For example
//Method for refresh recycle view
if (!yourList.isEmpty())
yourList.clear(); //The list for update recycle view
adapter.notifyDataSetChanged();
Use this to refresh a RecyclerView
items.clear(); //here items is an ArrayList populating the RecyclerView
adapter.notifyDataSetChanged();
items.addAll(list);// add new data
adapter.notifyItemRangeInserted(0, items.size);// notify adapter of new data
`
I had similiar issue, and also this solution has helped me, after I've added new item to my RV:
recyclerView.getRecycledViewPool().clear();
adapter.notifyDataSetChanged();
Maybe you can try this before refresh the adapter:
dataList.clear();
patrolListAdapter.notifyDataSetChanged();
In my case I was doing it as notifyItemInserted(position);
That caused me this issue then i used as and it worked perfectly.notifyItemRangeInserted(startIndex,endIndex);
I had this problem when scrolling fast through my endless/paging RecyclerView. The root of my problem came from the fact that I had a “header” item at the beginning of the list, this header item was not a part of the data source, it was just inserted at the beginning of the adapter list. So when scrolling fast and adding new pages of items to the RecyclerView Adapter and notify the adapter that new data had been inserted, I was not taking into account the additional header item, thus making the size of the adapter’s list wrong... and causing this exception...
So in short, if you’re using a header/footer in our RecyclerView adapter make sure you take it into account when updating the adapters data.
Example:
public void addNewPageToList(List<MyData> list)
{ //
// Make sure you account for any header/footer in your list!
//
// Add one to the currentSize to account for the header item.
//
int currentSize = this.adapterList.size() + 1;
this.adapterList.addAll(list);
notifyItemRangeInserted(currentSize, this.adapterList.size());
}
Edit:
I guess you could always just use the adapter method getItemCount() to get the size, instead of getting the size from the “data list” and adding to it. Your getItemCount() method should already be taking into account any additional headers/footers/etc that you have in your list.
The problem is in this line of code:
mFeed = feeds;
you are assigning mFeed to the caller's instance feeds so whenever the caller changes it's variable (may be adding, clearing or removing items), your local mFeed will change
try to change to
mFeed.addAll(feeds);
don't forget to initialize mFeed to any list tat fits your needs like mFeed = new ArrayList<>();
put this line along with setting recyclerView. issue was fixed by setting ItemAnimator to null for RecyclerView.
in kotlin
recyclerView.layoutManager = LinearLayoutManager(this)
recyclerView.itemAnimator = null
I'm using the recyclerview from mikepenz. And any update to the items using .set(item) was causing this issue.
For some reason, setting recylerView.itemAnimator = null, resolved the issue. This is a known android bug.
In my case, I was using RecyclerView from Firebase UI. Initially, the logic to initialize the RecyclerView was in onCreate(). To fix, I put the logic in onResume() and seems to be working for me. I had this error when going back to the Activity which had the RecyclerView. So, everytime the Activity screen is refreshed, the new data is loaded.
I had similar problem. Removing all views from RecyclerView helped me:
RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
layoutManager.removeAllViews();
For me the issue was I wasn't posting notifyDatasetChanged when the data set changed as I implemented incremental search.
I had a list that was filtered based on what the user searched in the search widget. For each item in the list, I was making a remote request, and when I got the result back, I was updating that particular cell.
I had to do both notifies for the recycler view to work
Filter the original data set then post the dataset change
this.searchResultTable?.post {
this.searchResultTable?.adapter?.notifyDataSetChanged()
}
After receiving response, post notifications again
this.searchResultTable?.post {
this.searchResultTable?.adapter?.notifyItemChanged(index, updateDataHashMap)
}
You have to post updates rather than sending notifiy messages directly in order to prevent the recycler view from crashing when the update comes in before the view is laid out.
Another important gotcha is that when you post the individual updates after the remote response, you have to make sure that the list the user currently sees is the list that existed when the requests were sent.
For my case in adapter there was notifyItemRangeInserted and I replaced it with notifyItemRangeChanged

AutoCompleteTextView OnItemClickListener null param (landscape mode on HTC Desire S)

My Problem : I have an AutoCompleteTextView with an OnItemClickListener. This has been working fine for 18 months, but I have now noticed it throws a NullPointerException when I select an item in landscape mode on my HTC Desire S. (There is no error in portrait mode or on any other phone or emulator I've tested it on).
The AdapterView<?> av parameter is coming through as null. Why would this be, and how can I get around it?
Code :
myAutoCompleteTextView = (AutoCompleteTextView) findViewById(R.id.myAutoCompleteTextView);
myAutoCompleteTextView.setSingleLine();
myAutoCompleteTextView.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> av, View v, int index, long arg) {
String selectedItem = (String)av.getItemAtPosition(index);
//Do stuff with selected item ...
}
}
Error :
java.lang.NullPointerException
at uk.co.myCompany.mobile.android.myCompanymobile.pages.groups.AbstractGroupSelectionPage$3.onItemClick(AbstractGroupSelectionPage.java:228)
at android.widget.AutoCompleteTextView.onCommitCompletion(AutoCompleteTextView.java:993)
at com.android.internal.widget.EditableInputConnection.commitCompletion(EditableInputConnection.java:76)
at com.android.internal.view.IInputConnectionWrapper.executeMessage(IInputConnectionWrapper.java:368)
at com.android.internal.view.IInputConnectionWrapper$MyHandler.handleMessage(IInputConnectionWrapper.java:86)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:150)
at android.app.ActivityThread.main(ActivityThread.java:4385)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:507)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:849)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:607)
at dalvik.system.NativeStart.main(Native Method)
Extra Code - my custom adapter inner class :
/**
* An inner class to simply make a custom adapter in which we can alter the on-screen look of selected groups.
*/
private class SelectedGroupAdapter extends ArrayAdapter<Group> {
private ArrayList<Group> items;
private int layout;
public SelectedGroupAdapter(Context context, int layout, ArrayList<Group> items) {
super(context, layout, items);
this.items = items;
this.layout = layout;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(layout, null);
}
Group o = items.get(position);
//Display the group name and number of contacts
if (o != null) {
TextView groupName = (TextView) v.findViewById(R.id.groupName);
TextView noOfContacts = (TextView) v.findViewById(R.id.noOfContacts);
if (groupName != null) {
groupName.setText(o.getGroupName());
}
if(noOfContacts != null) {
if (o.isDynamic())
noOfContacts.setText(getString(R.string.dynamic));
else {
int contactsCount = o.getGroupSize();
if(contactsCount == 1) noOfContacts.setText(contactsCount + " " + getString(R.string.contact));
else noOfContacts.setText(contactsCount + " " + getString(R.string.contacts));
}
}
}
return v;
}
}
My hunch is that since you are declaring android:configChanges="orientation" in your manifest, then when you rotate the old OnItemClickListener is still sticking around, and since you technically have a new layout, the AdapterView that was being used prior to orientation change doesn't exist anymore, thus is null when you click on an item.
There's 2 things I think that would solve this if this is the case:
Remove the orientation option in your manifest. Any events you place in configChanges tells Android "I'm taking care of this configuration change, so let me handle it" as opposed to letting Android handle it. The normal operation for Android in the event of an orientation change is to destroy and recreate your Activity (it will take care of repopulating some Views with data automatically).
If you decide you need to handle orientation changes, then override onConfigurationChanged() and set the OnItemClickListener to the new AdapterView object (ListView, GridView, whichever you are using) that should have been recreated in the onCreate method.