Prompt, faced with scrollToPosition speed. I RecyclerView 900 positions and need to quickly establish a position as scrollToPosition very slow, c ListView such problems were not setSelection working out perfectly. It is possible to accelerate the scrollToPosition?
public class EventAdapter extends RecyclerView.Adapter<EventAdapter.Holder> {
final static public String TAG = "EventAdapter";
Context context;
List<Event> items;
public EventAdapter(List<Event> items) {
this.items = items;
}
#Override
public EventAdapter.Holder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list_event, parent, false);
context = parent.getContext();
return new EventAdapter.Holder(v);
}
#Override
public void onBindViewHolder(EventAdapter.Holder holder, int position) {
holder.setModel(context, items.get(position), position);
}
#Override
public int getItemCount() {
return items.size();
}
public class Holder extends RecyclerView.ViewHolder {
public View block;
Holder(View view) {
super(view);
block = (View) view.findViewById(R.id.cv);
}
public void setModel(Context context, Event model, int position) {
}
}
}
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
rv.setLayoutManager(linearLayoutManager);
rv.setHasFixedSize(true);
linearLayoutManager.scrollToPosition(index);
There are couple of ways to do it:
1) Try smoothScrollToPosition as : [will certainly work]
Toast.makeText(this, "Scroll...", Toast.LENGTH_SHORT).show();
myList.smoothScrollToPosition(0);
2) Also read about scrollToPositionWithOffset as this may help too
Related
I'm desperate.
I have a nested recycler view. Each outer element has an array of inner elements. A different adapter has been created for the inner elements. I am creating an array of external elements of class "KairosWithEvents", each of which contains internal elements of class "Event". Everything is displayed well. When elements are added, everything is also updated. For testing, I created two objects of the "KairosWithEvent" class. In the first object I have placed two objects of the "Event" class, and in the second - three objects. But when I want to change the EditText value, the keyboard appears. And the last element of the second object appears in the first object. How can I fix it? Objects are not moved or duplicated, but showed incorrectly.
This is what a nestled recycler view looks like initially.
And this is what a nested recycler view looks like after the keyboard appears. The "Эвент5" element is duplicated to the first element for some reason.
Here's my code: Outer Adapter:
public class EventAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static List<Item> items;
private static RecyclerViewClickInterface listener;
private RecyclerView.RecycledViewPool sharedPool = new RecyclerView.RecycledViewPool();
private Context context;
public EventAdapter() {
items = new ArrayList<>();
this.context = context;
}
public EventAdapter(List<Item> newItems){
items = newItems;
}
class KairosViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
private TextView title;
private CheckBox iv;
private RecyclerView rv;
public KairosViewHolder(#NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.tv);
iv = itemView.findViewById(R.id.iv);
rv = itemView.findViewById(R.id.rvSteaks);
itemView.setBackgroundColor(Color.parseColor("#91b3f2"));
itemView.setOnClickListener(this);
itemView.setOnLongClickListener(this);
}
#RequiresApi(api = Build.VERSION_CODES.N)
void bind(KairosWithEvents kairosWithEvents){
title.setText(kairosWithEvents.kairos.kairosId + " = " + kairosWithEvents.kairos.title);
LinearLayoutManager layoutManager = new LinearLayoutManager(
rv.getContext(),
LinearLayoutManager.VERTICAL,
false);
List<Item> itemsEvents = new ArrayList<>();
kairosWithEvents.events.forEach(i -> itemsEvents.add(new Item(Constants.EVENT_KAIROS, i)));
SubAdapter childAdapter = new SubAdapter(itemsEvents);
rv.setLayoutManager(layoutManager);
rv.setAdapter(childAdapter);
rv.setRecycledViewPool(sharedPool);
}
#Override
public void onClick(View view) {
int position = getAdapterPosition();
if (listener != null && position != RecyclerView.NO_POSITION){
listener.onItemKairosWithEvents( ((KairosWithEvents) items.get(position).object) );
}
}
#Override
public boolean onLongClick(View view) {
return false;
}
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
switch (viewType){
case Constants.KAIROS:
return new KairosViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item_ex, parent, false));
}
return null;
}
#RequiresApi(api = Build.VERSION_CODES.N)
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)){
case Constants.KAIROS:
KairosWithEvents kairos = ((KairosWithEvents) items.get(position).object);
((KairosViewHolder) holder).bind(kairos);
break;
}
}
#Override
public int getItemCount() {
return items.size();
}
#Override
public int getItemViewType(int position) {
return items.get(position).type;
}
}
Inner Adapter:
public class SubAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private static List<Item> items;
public SubAdapter(List<Item> newItems) {
items = newItems;
}
static class EventViewHolder extends RecyclerView.ViewHolder {
private TextView title;
public EventViewHolder(#NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.tv);
itemView.setBackgroundColor(Color.parseColor("#ffc8a8"));
}
void bind(Event event){
title.setText(event.eventId + " = " + event.title);
}
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
switch (viewType) {
case Constants.EVENT_KAIROS:
return new EventViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item_sub, parent, false));
}
return null;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)){
case Constants.EVENT_KAIROS:
Event event = (Event) items.get(position).object;
((EventViewHolder) holder).bind(event);
break;
}
}
#Override
public int getItemCount() {
return items.size();
}
#Override
public int getItemViewType(int position) {
return items.get(position).type;
}
}
In SubAdapter, remove the static keyword from your items field:
private static List<Item> items;
Should be this instead:
private List<Item> items;
You should make the same change to your outer adapter, but since there's only one of them at a time it doesn't wind up causing issues.
In my SectionsListAdapter adapter i have nested Adapter named MonthLessonsList, after get date from web service and set new data and call notifyDataSetChanged method for adapter, main adapter as SectionsListAdapter can be refresh, but nested adapter dont refresh and after calling notifyDataSetChanged for that, doesn't work correctly and don't show new data
call and set new data from Fragment:
monthSectionsItems = SQLite.select().from(MonthSections.class).queryList();
adapter.setData(monthSectionsItems);
and my Adapter with nested adapter:
public class SectionsListAdapter extends RecyclerView.Adapter<SectionsListAdapter.MyViewHolder> {
private final OnItemSelected listener;
private List<MonthSections> list;
private Context context;
private MonthLessonsList lessonsListAdapter;
public SectionsListAdapter(List<MonthSections> followingsList, Context mContext, OnItemSelected listener) {
this.list = followingsList;
this.context = mContext;
this.listener = listener;
}
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.sections_list_row, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull final MyViewHolder holder, final int position) {
holder.indicatorIcon.setText(list.get(position).getSection_month_name());
...
List<SectionLesson> lessonsList = list.get(position).getLessons();
lessonsListAdapter = new MonthLessonsList(lessonsList);
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(context);
holder.userChildCategories.setLayoutManager(mLayoutManager);
holder.userChildCategories.setAdapter(lessonsListAdapter);
...
}
#Override
public int getItemCount() {
return list.size();
}
public void setData(List<MonthSections> data) {
list.clear();
list.addAll(data);
notifyDataSetChanged();
lessonsListAdapter.notifyDataSetChanged();
}
public List<MonthSections> getData() {
return list;
}
class MyViewHolder extends RecyclerView.ViewHolder {
...
public MyViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
section_title.setGravity(Gravity.RIGHT);
}
}
public class MonthLessonsList extends RecyclerView.Adapter<MonthLessonsList.LessonsViewHolder> {
private List<SectionLesson> lessonItem;
public class LessonsViewHolder extends RecyclerView.ViewHolder {
public TextView title;
public LessonsViewHolder(View view) {
super(view);
title = view.findViewById(R.id.title);
}
}
public MonthLessonsList(List<SectionLesson> lists) {
this.lessonItem = lists;
}
#Override
public LessonsViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.child_list_row, parent, false);
return new LessonsViewHolder(itemView);
}
#SuppressLint("SetTextI18n")
#Override
public void onBindViewHolder(#NonNull LessonsViewHolder holder, int position) {
SectionLesson lesson = lessonItem.get(position);
holder.title.setText("درس: " + (position + 1) + ") " + lesson.getTitle());
}
#Override
public int getItemCount() {
return lessonItem.size();
}
}
}
Declare child adapter inside onBindViewHolder, and remove from global level.
And no need to notify child adapter. Because that will be automatically filled when parent onBindViewHolder is called.
Like
MonthLessonsList lessonsListAdapter = new MonthLessonsList(lessonsList);
holder.userChildCategories.setAdapter(lessonsListAdapter);
I have to set just four data from API in Recyclerview. The data from API are dynamic.If there are more than 4 data then the last one should hide itself and the recent data should appear at the top. I have researched but could not find the applicable solution.
My Adpater
public class EventRecyclerAdapter extends RecyclerView.Adapter<EventRecyclerAdapter.MyViewHolder> {
Context context;
private ArrayList<String> start;
private AdapterView.OnItemClickListener listener;
private ArrayList<String> desc;
private ArrayList<EventData>data;
public EventRecyclerAdapter(Context context, ArrayList<EventData> data) {
this.context = context;
this.data = data;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.content_events, parent, false);
return new EventRecyclerAdapter.MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(EventRecyclerAdapter.MyViewHolder holder, int position) {
holder.start.setText(data.get(position).getStartDate());
holder.des.setText(Html.fromHtml(data.get(position).getName()));
}
#Override
public int getItemCount() {
return data.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
private TextView start, des, viewall,name;
private CardView cardView;
public MyViewHolder(View itemView) {
super(itemView);
start = (TextView) itemView.findViewById(R.id.startdate);
des = (TextView) itemView.findViewById(R.id.description);
viewall = (TextView) itemView.findViewById(R.id.viewall);
}
}
}
I have set the data as follows
private void initEventRecycler() {
RecyclerView.LayoutManager mLayoutManager = new LinearLayoutManager(getApplicationContext());
eventR.setLayoutManager(mLayoutManager);
eventR.setItemAnimator(new DefaultItemAnimator());
eventR.setNestedScrollingEnabled(false);
eventRecyclerAdapter = new EventRecyclerAdapter(MainActivity.this, events);
eventR.setAdapter(eventRecyclerAdapter);
}
If you have shorted data in the array list you are getting in adapter then use this code to show only 5 items.
#Override
public int getItemCount() {
if (data!= null && data.size() < 5) {
return data.size();
} else {
return 5;
}
}
if you have a data list in random order then first short it from latest to old and send to adapter.
Hope it will work for you.
EventData[] mData = new EventData[4];
int writeIndex = 0;
public void add(Object o) {
ring[writeIndex % ring.length] = o;
writeIndex++;
}
// Return the size of your dataset (invoked by the layout manager)
#Override
public int getItemCount() {
return mData.lenght;
}
You can also Check ArrayCircularQueue.java
The RecyclerView shows only items that was in ArrayList (data of the adapter) on creation. All my attempts to add more lines seems to work, but the list stays with initial data.
It isn't duplicate, as I tried all the suggestions found on google.
What I already did:
adapter.notifyDataSetChanged();
adapter.notifyItemChanged(5);
recycleList.invalidate();
recycleList.setAdapter(adapter);
running the update on UIThread with Handler
The adapter work as it should - onCreateView do it's work (creat new lines) as well as onBindView (bind new data). getItemCount() returns correct value (larger after update). Everything looks like it should be - instead of the final view - does not change.
The only thing that will work is recreate the adapter from scratch with new dataset - completely unacceptable by design (ugly on long lists).
Here is the code:
Create in fragment:
adapter = new MyListAdapter(getContext());
adapter.addItems(initialItems); // These and only these I see on the list
LinearLayoutManager mLayoutManager=new LinearLayoutManager(getContext());
recycleList.setLayoutManager(mLayoutManager);
recycleList.setAdapter(adapter);
Trying to update from fragment:
public void onUpdateReady(ArrayList<Model> freshData) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() { // UI thread
adapter.addItems(freshData);
adapter.notifyDataSetChanged();
adapter.notifyItemChanged(5);
recycleList.invalidate();
//notifyItemRangeInserted(3,freshData.size());
}
});
}
Adapter:
private Context mContext;
private ArrayList<Model> data;
public MyListAdapter(Context mContext) {
this.mContext = mContext;
data=new ArrayList<>();
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(R.layout.list_item, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
viewHolder.textName.setText(data.get(position).getName());
}
#Override
public int getItemCount() {
return data.size();
}
public void addItems(Collection<Model> list) {
data.addAll(list);
}
public class ViewHolder extends RecyclerView.ViewHolder {
#BindView(R.id.textName) TextView textName;
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this,itemView);
}
}
UPDATE: Changed according to comments:
public void addItems(Collection<Model> list) {
ArrayList<Model> newData=new ArrayList<>(); // Make new array
newData.addAll(data); // Take old
newData.addAll(list); // Add new
data=newData; // change to data to newly created array
notifyDataSetChanged();
}
try this
In Fragment
ArrayList<Data> items=new ArrayList<>();
items.addAll(initialItems);
adapter = new MyListAdapter(getContext(),items);
LinearLayoutManager mLayoutManager=new LinearLayoutManager(getContext());
recycleList.setLayoutManager(mLayoutManager);
recycleList.setAdapter(adapter);
public void onUpdateReady(ArrayList<Model> freshData) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() { // UI thread
items.addAll(freshData);
adapter.notifyDataSetChanged();
followList.invalidate();
}
});
}
private Context mContext;
private ArrayList<Model> data;
public MyListAdapter(Context mContext,ArrayList<Model> data) {
this.mContext = mContext;
this.data=data;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(mContext);
View view = inflater.inflate(R.layout.list_item, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
viewHolder.textName.setText(data.get(position).getName());
}
#Override
public int getItemCount() {
return data.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
#BindView(R.id.textName) TextView textName;
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this,itemView);
}
}
Change your Adapter constructor:
private ArrayList<Model> data;
public MyListAdapter(Context mContext,ArrayList<Model> data) {
this.mContext = mContext;
this.data = data;}
public void addItems(Collection<Model> list) {
data.addAll(list);
}
fragment:
adapter = new MyListAdapter(getContext(),initialItems);
I had a similar problem once and calling invalidateViews on the ListView worked, even if it was a hack. Instead of recycleList.invalidate(); try recycleList.invalidateViews();
I have created generic adapter for RecyclerView by using DataBinding. Here is small code snippet
public class RecyclerAdapter<T, VM extends ViewDataBinding> extends RecyclerView.Adapter<RecyclerAdapter.RecyclerViewHolder> {
private final Context context;
private ArrayList<T> items;
private int layoutId;
private RecyclerCallback<VM, T> bindingInterface;
public RecyclerAdapter(Context context, ArrayList<T> items, int layoutId, RecyclerCallback<VM, T> bindingInterface) {
this.items = items;
this.context = context;
this.layoutId = layoutId;
this.bindingInterface = bindingInterface;
}
public class RecyclerViewHolder extends RecyclerView.ViewHolder {
VM binding;
public RecyclerViewHolder(View view) {
super(view);
binding = DataBindingUtil.bind(view);
}
public void bindData(T model) {
bindingInterface.bindData(binding, model);
}
}
#Override
public RecyclerViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(layoutId, parent, false);
return new RecyclerViewHolder(v);
}
#Override
public void onBindViewHolder(RecyclerAdapter.RecyclerViewHolder holder, int position) {
T item = items.get(position);
holder.bindData(item);
}
#Override
public int getItemCount() {
if (items == null) {
return 0;
}
return items.size();
}
}
You can find full code in Github repo : Recyclerview-Generic-Adapter
The problem i am facing is after using generic adapter RecyclerView loading time increase and for a second it shows design time layout and than loads original data.
The piece you're missing is the binding.executePendingBindings() in bindData:
public void bindData(T model) {
bindingInterface.bindData(binding, model);
binding.executePendingBindings();
}