How to move Recyclerview items to the bottom after countdown timer ends? - android-recyclerview

In my recyclerview their are countdown timer in each item..and when the first item countdown timer ends i want the item at the tom to go straight to bottom. The cycler should go on, till their are items in recylerview. I know how to add countdowntimer. Please let me know how to do the positioning logic Right image is what is required.
This is my adapter class...please let me know if here i have to insert the code..
public class MyAdapter extends RecyclerView.Adapter<myadapter.holder> {
// creating variables for our ArrayList and context
private ArrayList<userdet> UserArrayList;
private HomeFragment context;
String timey ;
// creating constructor for our adapter class
public MyAdapter(ArrayList<userdet> UserArrayList, HomeFragment context) {
this.UserArrayList = UserArrayList;
this.context = context;
}
#NonNull
#Override
public MyAdapter.Holder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new Holder(LayoutInflater.from(context.getActivity()).inflate(R.layout.onerowforrecview, parent, false));
}
#Override
public void onBindViewHolder(#NonNull Holder holder, int position) {
// setting data to our text views from our modal class.
UserDet userDet = UserArrayList.get(position);
holder.ques_name_h.setText(userDet.getCategoryName());
}
long timer = userDet.getCategoryTime().toDate().getTime();
Date today = new Date();
final long currentTime = System.currentTimeMillis();
long expiryTime = timer - currentTime;
new CountDownTimer(expiryTime, 1000) {
public void onTick(long millisUntilFinished) {
long seconds = millisUntilFinished / 1000;
long minutes = seconds / 60;
long hours = minutes / 60;
long days = hours / 24;
timey = days+"d, " +hours % 24 + "h left";
holder.ques_time_h.setText(timey);
}
public void onFinish() {
holder.ques_amount_h.setButtonColor(R.color.teal_200);
holder.itemView.setEnabled(false); //makig disclick
holder.ques_time_h.setText("Time up!");
}
}.start();
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent= new Intent(context.getActivity(),TournamentDetails.class);
context.startActivity(intent);
}
});
}

Related

How to compare which drawable is in an imageview with a user spinner selection

Currently I am displaying a random image using math.random, this part works but I need to check if the users selection from the dropdown list matches this. The only part I can get to work is the incorrect selection displaying the correct name of the car displayed.
My comparison function never becomes true as Im not sure how to compare the tag of the imageview with a random int
See below my activity
public class IdentifyTheBrandActivity extends AppCompatActivity implements AdapterView.OnItemClickListener,
AdapterView.OnItemSelectedListener { String[] Brands = { "Audi", "Bentley", "BMW" };
Button SubmitButton;
ImageView imageView;
int[] images;
int chosenCar;
int locationOfCorrectAnswer;
String[] answers = new String[3];
ArrayList<String> carBrands = new ArrayList<>();
public void brandChosen(View view) {
if (imageView.getTag().toString().equals(carBrands.get(locationOfCorrectAnswer))){
Toast.makeText(getApplicationContext(), "Correct", Toast.LENGTH_LONG).show();
}else{
Toast.makeText(getApplicationContext(), "Wrong it was " + carBrands.get(locationOfCorrectAnswer), Toast.LENGTH_LONG).show();
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_identify_the_brand);
imageView = findViewById(R.id.RandomImage);
imageView.setTag("bg");
carBrands.add("Audi");
carBrands.add("Bentley");
carBrands.add("BMW");
images = new int[]{R.drawable.audi_a3_2010_27_17_200_20_4_77_56_169_21_fwd_5_4_4dr_igm, R.drawable.bentley_bentayga_2017_229_21_600_60_12_78_68_202_12_awd_5_4_suv_cce,
R.drawable.bmw_6_series_2014_82_18_310_30_6_74_53_192_20_rwd_4_2_convertible_mua};
imageView.setTag(R.drawable.audi_a3_2010_27_17_200_20_4_77_56_169_21_fwd_5_4_4dr_igm);
imageView.setTag(R.drawable.bentley_bentayga_2017_229_21_600_60_12_78_68_202_12_awd_5_4_suv_cce);
imageView.setTag(R.drawable.bmw_6_series_2014_82_18_310_30_6_74_53_192_20_rwd_4_2_convertible_mua);
if (imageView.getTag() != null) {
int resourceID = (int) imageView.getTag();
}
Random random = new Random();
chosenCar = random.nextInt(3);
locationOfCorrectAnswer = chosenCar;
imageView.setBackgroundResource(images[chosenCar]);
int incorrectAnswerLocation;
//Get a random between 0 and images.length-1
//int imageId = (int) (Math.random() * images.length);
Spinner spin = (Spinner) findViewById(R.id.spinner);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_spinner_item, Brands);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spin.setAdapter(adapter);
spin.setOnItemSelectedListener(this);
}
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
}
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long l) {
Toast.makeText(getApplicationContext(), "Selected Brand: " + Brands[position]
,Toast.LENGTH_SHORT).show();
}
#Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
}
I tried comparing the imageview and the randomly assigned index to the array but it never becomes true
Essentially my issue is how to compare which drawable is actively in the imageview with the users selection

Trouble getting recyclerview to load elements

I am making a networking app's chat section from this tutorial: https://blog.sendbird.com/android-chat-tutorial-building-a-messaging-ui.
I have everything hooked up so that I know that the messages are coming in from the database. It seems to be an issue with how the adapter or recyclerview are set up. I can't see any messages on my activity even though I can see them if I poke in my async/background worker.
This is the adapter class with inner view holders from the tutorial, adapted to my needs:
public class MessageListAdapter extends RecyclerView.Adapter {
private static final int VIEW_TYPE_MESSAGE_SENT = 1;
private static final int VIEW_TYPE_MESSAGE_RECEIVED = 2;
private Activity mContext;
private ArrayList<Message> mMessageList;
public MessageListAdapter(Activity context, ArrayList<Message> messageList) {
mContext = context;
mMessageList = messageList;
}
#Override
public int getItemCount() {
return mMessageList.size();
}
// Determines the appropriate ViewType according to the sender of the message.
#Override
public int getItemViewType(int position) {
Message message = (Message) mMessageList.get(position);
if (message.isOther() == "2") {
// If the current user is the sender of the message
return VIEW_TYPE_MESSAGE_SENT;
} else {
// If some other user sent the message
return VIEW_TYPE_MESSAGE_RECEIVED;
}
}
// Inflates the appropriate layout according to the ViewType.
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
if (viewType == VIEW_TYPE_MESSAGE_SENT) {
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_message_sent, parent, false);
return new SentMessageHolder(view);
} else if (viewType == VIEW_TYPE_MESSAGE_RECEIVED) {
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.item_message_received, parent, false);
return new ReceivedMessageHolder(view);
}
return null;
}
// Passes the message object to a ViewHolder so that the contents can be bound to UI.
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
Message message = mMessageList.get(position);
switch (holder.getItemViewType()) {
case VIEW_TYPE_MESSAGE_SENT:
((SentMessageHolder) holder).bind(message);
break;
case VIEW_TYPE_MESSAGE_RECEIVED:
((ReceivedMessageHolder) holder).bind(message);
}
}
private class SentMessageHolder extends RecyclerView.ViewHolder {
TextView messageText, timeText;
SentMessageHolder(View itemView) {
super(itemView);
messageText = (TextView) itemView.findViewById(R.id.text_message_body);
timeText = (TextView) itemView.findViewById(R.id.text_message_time);
}
void bind(Message message) {
messageText.setText(message.getMessage());
// Format the stored timestamp into a readable String using method.
timeText.setText(message.getCreatedAt());
}
}
private class ReceivedMessageHolder extends RecyclerView.ViewHolder {
TextView messageText, timeText, nameText;
ImageView profileImage;
ReceivedMessageHolder(View itemView) {
super(itemView);
messageText = (TextView) itemView.findViewById(R.id.text_message_body);
timeText = (TextView) itemView.findViewById(R.id.text_message_time);
nameText = (TextView) itemView.findViewById(R.id.text_message_name);
profileImage = (ImageView) itemView.findViewById(R.id.image_message_profile);
}
void bind(Message message) {
messageText.setText(message.getMessage());
// Format the stored timestamp into a readable String using method.
timeText.setText(message.getCreatedAt());
nameText.setText(message.getSender());
Picasso.get().load(message.getProfile())
.transform(new RoundedCornersTransformation(150, 0)).into(profileImage);
// Insert the profile image from the URL into the ImageView.
// Utils.displayRoundImageFromUrl(mContext, message.getSender().getProfileUrl(), profileImage);
}
}
}
I create the adapter and pair it to the recyclerview in a background worker during postExecute()
try {
JSONArray messages = new JSONArray(s);
ArrayList<Message> messageList = new ArrayList<Message>();
for (int i = 0; i < messages.length(); i++) {
String currentMessage = (String) messages.get(i);
String[] fields = currentMessage.split(",", 0);
if (fields.length == 5) {
String isOther = fields[0];
String messageText = fields[1];
String dateTime = fields[2];
String author = fields[3];
String pictureLocation = fields[4];
Message newMessage = new Message(isOther, messageText, dateTime, author, pictureLocation);
messageList.add(newMessage);
}
}
mMessageRecycler = (RecyclerView) ha.findViewById(R.id.reyclerview_message_list);
mMessageRecycler.setLayoutManager(new LinearLayoutManager(ha.getApplicationContext()));
mMessageAdapter = new MessageListAdapter(ha, messageList);
mMessageRecycler.setAdapter(mMessageAdapter);
}
I've tried adding alert dialogs to debug within the OnCreateViewHolder, but can't get them to appear. I've tried using ha, ha.getApplicationContext() on every field. There must be something that's just staring me in the face. I've used this pattern before and it works for another section of my app.
It turns out that the tutorial's recyclerview information is just barely off, so I used the one I had in a previous part of the app:
<android.support.v7.widget.RecyclerView
android:id="#+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF" />

Recyclerview: can't get the item count value

im newbie here... I have a problem in getting the item count on my recyclerview? I've try this code but it's not working.
int count = 0;
if (recyclerViewInstance.getAdapter() != null) {
count = recyclerViewInstance.getAdapter().getItemCount();
}
also this one but it's not working too..
int count = 0;
if (mAdapter != null) {
count = mAdapter.getItemCount();
}
this is my code:
mainActivity:
private List<NavDrawerFleetGetterSetter> navList= new ArrayList<>();
private RecyclerView recyclerView;
private NavDrawerFleetAdapter mAdapter;
Button add;
TextView successCount;
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nav_drawer_fleet);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
add = findViewById(R.id.fab);
successCount=findViewById(R.id.count);
recyclerView =findViewById(R.id.nav_sent);
mAdapter = new NavDrawerFleetAdapter(navList);
RecyclerView.LayoutManager mLayoutManager = new
LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setAdapter(mAdapter);
if (recyclerView.getAdapter() != null) {
successCount.getText(recyclerViewInstance.getAdapter().getItemCount());
}
add.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
NavDrawerFleetGetterSetter navD= new
NavDrawerFleetGetterSetter(lati,longi,dateTime);
navList.add(navD);
mAdapter.notifyDataSetChanged();
}
myGetterSetter:
public class NotSentModuleGetterSetter {
String lat,lon,dateTime;
public NotSentModuleGetterSetter(String lat, String lon, String dateTime) {
this.lat = lat;
this.lon = lon;
this.dateTime = dateTime;
}
public String getLat() {
return lat;
}
public void setLat(String lat) {
this.lat = lat;
}
public String getLon() {
return lon;
}
public void setLon(String lon) {
this.lon = lon;
}
public String getDateTime() {
return dateTime;
}
public void setDateTime(String dateTime) {
this.dateTime = dateTime;
}
}
myOutput:
as you see, the success count was turned into zero.
btw, my data was getting on my database and i was pickup the code need since i have a bunch of codes as for now for my project.
an also, using debug, the data of my mAdapter=null.
In this case, i was getting the count data on my adapter but it should be get the "Size" of adapter than getting the "count". It is now working on me. :)

My code is not working for Setting Date in Multiple TextViews

I am trying to display different date in different TextViews in a fragment following the answer of MH to this question at TimePicker onTimeSet not being called but I am not getting the same results for a fragment.
I have added a separate DatePickerFragment class in which I am using an Interface class which is used as a callback method and is defined in a fragment class Medication.
How do I set multiple date pickers in a fragment?
The DatePickerFragment is written as:
public class DatePickerFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener {
private int mId;
DatePickerDialogListener mListener;
protected static DatePickerFragment newInstance(int id) {
Bundle args = new Bundle();
args.putInt("picker_id", id);
DatePickerFragment fragment = new DatePickerFragment();
fragment.setArguments(args);
return fragment;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// ... omitted
final Calendar c = Calendar.getInstance();
int mDay = c.get(Calendar.DAY_OF_MONTH);
int mMonth = c.get(Calendar.MONTH);
int mYear = c.get(Calendar.YEAR);
mId = getArguments().getInt("picker_id");
mListener = getActivity() instanceof DatePickerFragment.DatePickerDialogListener ? (DatePickerFragment.DatePickerDialogListener) getActivity() : null;
// Create a new instance of TimePickerDialog and return it
return new DatePickerDialog(getActivity(), this, mYear, mMonth, mDay);
}
#Override
public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
if (mListener != null) mListener.onDateSet(mId, view, year, month, dayOfMonth);
}
public static interface DatePickerDialogListener {
public void onDateSet(int id, DatePicker view, int year, int month, int dayOfMonth);
}
}
The Fragment class Medication which would use this datepickerdialog has the following code:
public class Medication extends Fragment implements DatePickerFragment.DatePickerDialogListener {
private static final int FIRST_VISIT_ID = 1;
private static final int SECOND_VISIT_ID = 2;
private static final int THIRD_VISIT_ID = 3;
private int mYear;
private int mMonth;
private int mDay;
protected static TextView date1;
protected static TextView date2;
protected static TextView date3;
public Medication() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_medication, container, false);
final Calendar c = Calendar.getInstance();
mYear = c.get(Calendar.YEAR);
mMonth = c.get(Calendar.MONTH);
mDay = c.get(Calendar.DAY_OF_MONTH);
date1 =(TextView) view.findViewById(R.id.visit_date1);
date2 =(TextView) view.findViewById(R.id.visit_date2);
date3 =(TextView) view.findViewById(R.id.visit_date3);
date1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
DialogFragment newFragment = DatePickerFragment.newInstance(FIRST_VISIT_ID);
newFragment.show(getFragmentManager(), "timePicker");
}
});
date2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
DialogFragment newFragment = DatePickerFragment.newInstance(SECOND_VISIT_ID);
newFragment.show(getFragmentManager(), "timePicker");
}
});
date3.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
DialogFragment newFragment = DatePickerFragment.newInstance(THIRD_VISIT_ID);
newFragment.show(getFragmentManager(), "timePicker");
}
});
date1.setText(
new StringBuilder()
// Month is 0 based so add 1
.append(mDay).append("/")
.append(mMonth + 1).append("/")
.append(mYear).append(" "));
date2.setText(
new StringBuilder()
// Month is 0 based so add 1
.append(mDay).append("/")
.append(mMonth + 1).append("/")
.append(mYear).append(" "));
date3.setText(
new StringBuilder()
// Month is 0 based so add 1
.append(mDay).append("/")
.append(mMonth + 1).append("/")
.append(mYear).append(" "));
return view;
}
#Override
public void onDateSet(int id, DatePicker view, int year, int month, int dayOfMonth) {
Log.i("DatePicker", "Date picker set from id " + id + "!");
if(id == FIRST_VISIT_ID)
{
this.date1.setText(
new StringBuilder()
// Month is 0 based so add 1
.append(dayOfMonth).append("/")
.append(month + 1).append("/")
.append(year).append(" "));
}
if(id == SECOND_VISIT_ID)
{
this.date2.setText(
new StringBuilder()
// Month is 0 based so add 1
.append(dayOfMonth).append("/")
.append(month + 1).append("/")
.append(year).append(" "));
}
if(id == THIRD_VISIT_ID)
{
this.date3.setText(
new StringBuilder()
// Month is 0 based so add 1
.append(dayOfMonth).append("/")
.append(month + 1).append("/")
.append(year).append(" "));
}
}
}
I have tried this method of adding multiple pickers for Time Picker Dialog as well but the same problem occurs that onTimeSet method in Medication Class is not being called as in here onDateSet.
The problem is that the dialog opens when I click on a textView but after I change the date, the date of that textView doesn't change. Also the log info message is not shown as the onDateSet method is not being called.
Maybe the mListener is null in this case, I can only guess.
Please help me solve this problem. Thanks in advance.
Try to add some logs in onDateSet of a fragment to see what values are set there and if it calls a listener.
#Override
public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
Log.i("Fragment/onDateSet", "Date picker set from id " + mId+ "!");
if (mListener != null)
mListener.onDateSet(mId, view, year, month, dayOfMonth);
else
Log.w("Fragment/onDateSet", "But no listener available!");
}
And try to put different tags for different fragments, because right now you are marking them with the same tag
newFragment.show(getFragmentManager(), "timePicker2");
I didn't get the answer to why above code doesn't work but after some search on net I found another very useful method for adding date or time in multiple TextView. Here I am adding two dates in fragment, same technique works for activity as well.
public class Medication extends Fragment implements View.OnClickListener{
private DatePickerDialogFragment mDatePickerDialogFragment;
protected static TextView date1;
protected static TextView date2;
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_medication, container, false);
date1 = (TextView) view.findViewById(R.id.date1);
date2 = (TextView) view.findViewById(R.id.date2);
displayCurrentDate(); // display current date as default in date1 and date2
mDatePickerDialogFragment = new DatePickerDialogFragment();
date1.setOnClickListener(this);
date2.setOnClickListener(this);
}
public void displayCurrentDate(){
static Calendar calendar = Calendar.getInstance();
private int year = calendar.get(Calendar.YEAR); // get current year
private int month = calendar.get(Calendar.MONTH);// get current month
private int day = calendar.get(Calendar.DAY_OF_MONTH); // get current day
calendar.set(year, month, day);
SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");
// display Current Date in date1 and date2
date1.setText(format.format(calendar.getTime()));
date2.setText(format.format(calendar.getTime()));
}
#Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.date1) {
mDatePickerDialogFragment.setDateFlag(FLAG_DATE_ONE);
mDatePickerDialogFragment.show(getFragmentManager(), "datePicker1");
}else if (id == R.id.date2) {
mDatePickerDialogFragment.setDateFlag(FLAG_DATE_TWO);
mDatePickerDialogFragment.show(getFragmentManager(), "datePicker2");
}
}
public static class DatePickerDialogFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener {
protected static final int FLAG_DATE_ONE = 1;
protected static final int FLAG_DATE_TWO = 2;
private int flag = 0;
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
Calendar calendar = Calendar.getInstance();
int year = calendar.get(Calendar.YEAR);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);
return new DatePickerDialog(getActivity(), this, year, month, day);
}
public void setDateFlag(int i) {
flag = i;
}
#Override
public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
Calendar calendar = Calendar.getInstance();
calendar.set(year, monthOfYear, dayOfMonth);
SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");
if (flag == FLAG_DATE_ONE) {
date1.setText(format.format(calendar.getTime()));
} else if (flag == FLAG_DATE_TWO) {
date2.setText(format.format(calendar.getTime()));
}
}
}
}
Note: Please add android:clickable = "true" for each TextView in xml layout for enabling the click action, otherwise the TextView won't be clickable.
I hope this will help others :)

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