calling BaseAdapter's notifyDataSetChanged() inside CustomAdapter not refreshing ListView - java

I have ArrayList<MyObject> data which is populated on the ListView using a CustomAdapter.
I've implemented View.OnClickListener in the CustomAdapter class and clicking a button deletes the particular row. After deleting the row, both from database and from the data object using data.remove(i) inside the Custom Adapter, I call notifyDataSetChanged().
Problem is that it does not refresh teh ListView.
I'm using a tabbed activity with fragment. So, when go to some distant tab and come back to this tab, it refreshes the ListView and the deleted item does not show anymore.
Why is the ListView not refreshing immediately when i call notifyDataSetChanged() but changing when i move to another fragment and back?
My CustomAdapter class is as follows:
public class CustomAdapter extends BaseAdapter implements View.OnClickListener {
private LayoutInflater mInflater;
Context context;
private ArrayList<MyObject> data;
public CustomAdapter(Context context, ArrayList<MyObject> data) {
mInflater = LayoutInflater.from(context);
this.data = data;
this.context = context;
}
.....
.....
.....
.....
.....
#Override
public void onClick(View v) {
DataBaseHelper db = new DataBaseHelper(context);
int id = data.get((Integer) v.getTag()).id;
//use getTag() to get the position, set position in tag before adding listener.
switch (v.getId()){
case R.id.txtName:
Toast.makeText(context, "Show Details" + v.getTag(), Toast.LENGTH_SHORT).show();
break;
case R.id.btnRemove:
db.removePaper(id); // works fine
data.remove(v.getTag()); // works fine
notifyDataSetChanged(); // PROBLEM HERE: Does not refresh listview
break;
case R.id.btnFavorite:
Toast.makeText(context, "Favorite" + v.getTag(), Toast.LENGTH_SHORT).show();
break;
}
}
}

I had a similar issue some time ago, I fixed it extending ArrayAdapter<MyObject> instead of BaseAdapter and overriding some methods:
public class CustomAdapter extends ArrayAdapter<MyObject> {
private List<MyObject> list = new ArrayList<MyObject>();
public CustomAdapter(Context context, int resource) {
super(context, resource);
}
#Override
public void add(MyObject obj) {
this.list.add(obj);
super.add(obj);
}
#Override
public void remove(MyObject obj) {
int i = getPosition(obj);
if(i >= 0)
list.remove(i);
super.remove(obj);
}
#Override
public void clear() {
this.list.clear();
super.clear();
}
#Override
public MyObject getItem(int position) {
return this.list.get(position);
}
#Override
public int getPosition(MyObject obj) {
for(int i = 0; i < list.size(); i++) {
if(list.get(i).equals(obj))
return i;
}
return -1;
}}
Then call these methods instead of calling directly methods of your List.

Related

How to display RecyclerView items after clicking on "View All" button

I am a beginner in android. I am creating an application where data will be shown through recyclerview, but I want that only some items should display in recyclerview.
However when the user clicks the "View All" button then only whole items should display. Can anyone tell me how can I do this?
viewtext.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getContext(), AppviewActivity.class);
intent.putExtra("mylist","newmodel");
getContext().startActivity(intent);
}
});
DetailsActivity.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_appview);
ArrayList<String> mylist = (ArrayList<String>) getIntent().getSerializableExtra("mylist");
}
NewModel class
public class NewModel {
Drawable sociallogo;
String socailtext;
String href;
public NewModel(Drawable sociallogo, String socailtext, String href){
this.sociallogo = sociallogo;
this.socailtext= socailtext;
this.href = href;
}
public Drawable getSociallogo(){
return sociallogo;
}
public String getSocailtext(){
return socailtext;
}
public String getHref(){
return href;
}
}
getItemCount method work for limitation.
If you bind with the below code then it shows limited data.
private final int limit = 10;
#Override
public int getItemCount() {
if(arrayList.size() > limit){
return limit;
}
}
When clicking on the "View All" button then redirect to the new activity and bind data in recyclerview adapter:
If you bind with the below code then it shows All data.
#Override
public int getItemCount() {
if(arrayList.size() > limit){
return ad_list.size();
}
}
Implement your model class like below code:
public class NewModel implements Serializable {
Now pass your data with below code:
viewtext.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getContext(), AppviewActivity.class);
intent.putExtra("mylist", newmodel);
getContext().startActivity(intent);
}
});
Get your data in the new activity like:
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
ArrayList<NewModel> mylist = (ArrayList<NewModel>) bundle.getSerializable("mylist");
}
You can add a parameter in class Recycler View Adapter to set a limit data. That's the function to display data recycler view with limit data. You make 2 constructors in that class.
public class ArrayDataAdapter extends RecyclerView.Adapter<ArrayDataAdapter.ArrayDataViewHolder> {
private int limit = 0;
private ArrayData<ModelData> listData;
// Constructor 1: To display all data
public class ArrayDataAdapter(ArrayList<ModelData> listData) {
this.listData = listData;
this.limit = listData.size;
}
// Constructor 2: To display data with limit data
public class ArrayDataAdapter(ArrayList<ModelData> listData, int limit) {
this.listData = listData;
this.limit = limit;
}
}
So, when you click "View All", you call class Recycler View Adapter with constructor type 1. For viewing limited items , Call constructor type 2 ,then you set method getItemCount with "limit" variable.

recyclerview notifyDataSetChanged() is not working

I am trying to update myadapter using notifyDataSetchanged() in updateList().But not working for recyclerview. I can able to see list size in updateList() and its displaying actual result but only reclerview is not updating.This class was designed in MVVM Design Pattern.
public class TasksAdapter extends RecyclerView.Adapter<TasksAdapter.TasksAdapterViewHolder> {
private static final String TAG = TasksAdapter.class.getSimpleName();
static ArrayList<ItemModel> list;
static Context mContext;
private Fragment fragment;
private int row_index = -1;
private SessionManager session;
// SelectColorVM colorVM;
public TasksAdapter(Fragment fragment, Context context, ArrayList<ItemModel> itemList) {
this.mContext = context;
this.list = itemList;
this.fragment = fragment;
session = new SessionManager(fragment.getContext());
}
public void updateList(ArrayList<ItemModel> itemList){
list.clear();
this.list = itemList;
notifyDataSetChanged();
}
#Override
public TasksAdapterViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
TasksItemBinding binding =
DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.tasks_item,
parent, false);
return new TasksAdapter.TasksAdapterViewHolder(binding);
}
#Override
public void onBindViewHolder(TasksAdapterViewHolder holder, final int position) {
holder.binding.setViewModel(list.get(position));
if(position == 0 && list.get(position).btnText.get().toString().length() > 0)
holder.binding.btnUpdate.setVisibility(View.VISIBLE);
else
holder.binding.btnUpdate.setVisibility(View.INVISIBLE);
holder.binding.layoutItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// row_index = position;
// notifyDataSetChanged();
Fragment fragment = new DetailFragment();
FragmentManager manager = ((FragmentActivity) mContext).getSupportFragmentManager();
FragmentTransaction fragmentTransaction = manager.beginTransaction();
Bundle bundle = new Bundle();
ItemModel item = list.get(position);
bundle.putSerializable("item", item);
fragment.setArguments(bundle);
fragmentTransaction.replace(R.id.id_frame, fragment, "details");
fragmentTransaction.addToBackStack("details");
fragmentTransaction.commit();
}
});
holder.binding.btnUpdate.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
HashMap<String, String> user = session.getUserDetails();
if(list.get(position).btnText.get().equals("Task Start")){
LandingEngine.updateStatusAndSetStartTimeForRequest(fragment, user.get(SessionManager.KEY_CLEANER_ID), list.get(position).sid.get().toString());
} else if(list.get(position).btnText.get().equals("Task Complete")){
LandingEngine.updateStatusAndSetCompleteTimeForRequest(fragment, user.get(SessionManager.KEY_CLEANER_ID), list.get(position).sid.get().toString());
}
}
});
}
#Override
public int getItemCount() {
return list.size();
}
public static class TasksAdapterViewHolder extends RecyclerView.ViewHolder {
TasksItemBinding binding;
public TasksAdapterViewHolder(TasksItemBinding rowbinding) {
super(rowbinding.getRoot());
this.binding = rowbinding;
}
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
}
I am calling adapter from my fragment like below
if(list.size() > 0) {
if(adapter == null) {
view.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(fragment.getContext());
view.setLayoutManager(mLayoutManager);
adapter = new TasksAdapter(fragment, fragment.getContext(), list);
view.setAdapter(adapter);
view.addItemDecoration(new DividerItemDecoration(fragment.getContext()));
} else {
adapter.updateList(list);
// view.getAdapter().notifyDataSetChanged();
// view.invalidate();
}
}
Please check this and let me know where i am doing wrong here.Thanks in advance..
Finally its working by calling this method from fragment class once getting API response from the volley library.
I have written following method in ViewModel class.
public void updateList(ArrayList<Class> list){
if(adapter != null) {
adapter.updateList(list);
}
}
And calling this method from fragment class like following.
ArrayList<StatisticsModel> list = (ArrayList) data;
viewModel.updateList(list);
So in the adapter class update method is following.
public void updateList(ArrayList<StatisticsModel> itemList){
list.clear();
// this.list = itemList;
// list.addAll(itemList);
// notifyItemInserted(list.size());
// notifyDataSetChanged();
this.list.addAll(itemList);
// notifyItemInserted(list.size());
notifyDataSetChanged();
}
Don't
public void updateList(ArrayList<ItemModel> itemList)
{
list.clear();
this.list = itemList;
notifyDataSetChanged();
}
Whenever calling this method list.clear(); clear all value.
The clear() method is used to remove all of the elements from a list.
DO
public void updateList(ArrayList<ItemModel> itemList)
{
this.list.addAll(itemList);
notifyDataSetChanged();
}
I have faced the same problem, you are doing mistake in this, change list into itemList:
#Override
public int getItemCount() {
return itemList.size();
}
Try this one
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
view.adapter.notifyDataSetChanged();
}
});
Try to add notifyDataSetChanged() in your Fragment class like...
adapter.notifyDataSetChanged();
try this remove list.clear(); from your updateList method
public void updateList(ArrayList<ItemModel> itemList){
this.list.addAll(itemList);
notifyItemInserted(list.size());
notifyDataSetChanged();
}
notifyItemInserted(int position)
Notify any registered observers that the item reflected at position has been newly inserted.
also try this
list.addAll(your new list);
if (adapter == null) {
view.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(fragment.getContext());
view.setLayoutManager(mLayoutManager);
adapter = new TasksAdapter(fragment, fragment.getContext(), list);
view.setAdapter(adapter);
view.addItemDecoration(new DividerItemDecoration(fragment.getContext()));
} else {
view.notifyItemInserted(list.size());
view.notifyDataSetChanged();
}
have a same issue. In my case there was error message in the logcat RecyclerView: No layout manager attached; skipping layout
which i have fixed with following code
LinearLayoutManager llm = new LinearLayoutManager(this.getActivity());
llm.setOrientation(LinearLayoutManager.VERTICAL);
mRecyclerView.setLayoutManager(llm);
then the notifyDataSetChanged start work
I don't know why sometimes we are not creating two instances of the adapter and also we are on the main thread adapter.notifyDataSetChanged() is not working In this case just call the notifyDataSetChanged() method from the Adapter class.
adapter.setNewValue(newlist)
and on AdapterClass
fun setNewValue(newlist:ArrayList<Any>) {
list = newlist
notifyDataSetChanged()
}

Interface value is always null

I am using custom recycler view and in adapter class i have implemented interface which is always null on button click. Here is my adapter class.
public class FeedListAdapter extends
RecyclerView.Adapter<AddtoCartHolder> {
private OnFeedItemClickListener onFeedItemClickListener;
public FeedListAdapter(Activity activity, ArrayList<CartItem> feedItems) {
this.activity = activity;
this.feedItems = feedItems;
this.filteredfeedItems = feedItems;
inflater = LayoutInflater.from(activity);
}
public void setOnFeedItemClickListener(OnFeedItemClickListener onFeedItemClickListener) {
this.onFeedItemClickListener = onFeedItemClickListener;
}
#Override
public AddtoCartHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.feed_item, parent, false);
AddtoCartHolder viewHolder = new AddtoCartHolder(v);
setupClickableViews(v, viewHolder);
return viewHolder;
}
#Override
public void onBindViewHolder(final AddtoCartHolder holder, int position) {
CartItem item = (CartItem) filteredfeedItems.get(position);
holder.price.setText((String.valueOf(item.getProductName()) + ""));
holder.location.setText((String.valueOf(item.getQuantity())) + "");
}
private void setupClickableViews(final View view, final AddtoCartHolder cellFeedViewHolder) {
cellFeedViewHolder.plus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(onFeedItemClickListener !=null){
onFeedItemClickListener.onAddClick(v, cellFeedViewHolder.getAdapterPosition());
}
else{
Toast.makeText(activity, "Data is null", Toast.LENGTH_LONG).show();
}
}
});
}
public interface OnFeedItemClickListener {
void onAddClick(View v, int position);
}
#Override
public int getItemCount() {
return filteredfeedItems.size();
}
I am always getting null whenever clicking on button really down know why it is coming null...
Here is my fragment class which have implemented interface.
public class MyFragment extends Fragment implements FeedListAdapter.OnFeedItemClickListener{
// the method
#Override
public void onAddClick(View v, int position) {
// TODO Auto-generated method stub
Snackbar.make(clContent, "Product removed from cart!",
Snackbar.LENGTH_SHORT).show();
}
You must be instantiating a FeedListAdapter in your fragment correct buddy ???
Like using statement :
FeedListAdapter adapter = new FeedListAdapter(this.getActivity(),your_array_list)
After instantiating your adapter just call your adapter's setOnFeedItemClickListener with 'this' as argument :) That's all :)
adapter.setOnFeedItemClickListener(this)
Hope my answer helped you :) Happy coding buddy :)
By the looks of things your aren't setting your listener. Thus, onFeedItemClickListener is always null.
Also MyFragment isn't actually doing anything, you haven't inflated a view, overridden onCreateView(...), etc.
There's a few things that you could definitely change to improve your implementation. But to get your listener working:
Just get rid of MyFragment you don't appear to be using it properly.
Move your implements FeedListAdapter.OnFeedItemClickListener to your Activity. i.e. Make your Activity implement your OnFeedItemClickListener interface rather than MyFragment (which doesn't appear to be doing anything).
Make FeedListAdapter set the listener in its constructor:
public FeedListAdapter(Activity activity, ArrayList<CartItem> feedItems)
{
this.activity = activity;
// Assume we the activity implements OnFeedItemClickListener
setOnFeedItemClickListener((OnFeedItemClickListener)activity);
this.feedItems = feedItems;
this.filteredfeedItems = feedItems;
inflater = LayoutInflater.from(activity);
}
Please keep in mind that this is a pretty bad implementation and you can definitely improve on it but for the purpose of the question, it's sufficient.

Find how many RecyclerView items fit screen before creation

Basically, I'm trying to programatically find out how many items are going to fit in a RecyclerView (and be visible to user, of course) in order to determine how many of them to fetch from cache.
I'm using a LinearLayoutManager.
Also, I'm aware of the LinearLayoutManager method findLastVisibleItemPosition, but obviously it's useless in this case since we're talking on before-initialization time, not after (so it returns -1).
Tried reading the docs or thinking on a creative but efficient idea, but I got nothing on my mind.
Any ideas?
This sounds actually pretty interesting, but only works if your height (vertical scroll) or width (horizontal scroll) is fixed, meaning no wrap_content.
No sample code and nothing tested here:
Create an Adapter with a setter for getCount that gets returned in case your data-source is null/empty
in getCount return at least 1 if your data is empty/null
make sure onBindViewHolder() can handle empty/non-existent data
add a OnChildAttachStateChangeListener to your RecyclerView, everytime the listener gets called, use the view to view.post(new Runnable() {...increase adapters getCount...adapter.notifyItemInserted()} (that runnable is necessary to avoid crash+burn)
OnChildAttachStateChangeListener gets called again >>> compare getCount and findLastVisibleItemPosition. If getCount > findLastVisibleItemPosition + 1 remove that listener. The number of fixed-size views fitting into ListView is findLastVisibleItemPosition + 1
Get your data and set it into you adapter, call notifyDataSetChanged
make sure getCount returns the data-source length from now on.
you could hide the listview behind a loadingscreen, or you can set the child views to invisible in onBindViewHolder
EDIT:
Create an Adapter which returns a ridiculous high count when no data is set and make sure it handles missing data correctly in onBindViewHolder
Extend LinearLayoutManager and Override onLayoutChildren() after the super call if getItemCount() > getChildCount() getChildCount() is the number of Views that would be visible in your RecyclerView
MainActivity.class
public class MainActivity extends AppCompatActivity {
private PreCountingAdapter mAdapter;
private RecyclerView mRecyclerView;
private PreCountLinearLayoutManager mPreCountLayoutManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
mPreCountLayoutManager = new PreCountLinearLayoutManager(this,
LinearLayoutManager.VERTICAL, false);
mPreCountLayoutManager.setListener(new PreCountLinearLayoutManager.OnPreCountedListener() {
#Override
public void onPreCounted(int count) {
mPreCountLayoutManager.setListener(null);
loadData(count);
}
});
mRecyclerView.setLayoutManager(mPreCountLayoutManager);
mAdapter = new PreCountingAdapter();
mRecyclerView.setAdapter(mAdapter);
}
private void loadData(final int visibleItemCount) {
// load data here, probably asynchronously,
// for simplicity just an String Array with size visibleItemCount
final List<String> data = new ArrayList<>();
for (int i = 0; i < visibleItemCount; i++) {
data.add(String.format("child number #%d", i));
}
mRecyclerView.post(new Runnable() {
#Override
public void run() {
mAdapter.swapData(data);
}
});
}
#Override
protected void onDestroy() {
mPreCountLayoutManager.setListener(null);
super.onDestroy();
}
}
PreCountLinearLayoutManager.class
public class PreCountLinearLayoutManager extends LinearLayoutManager {
private OnPreCountedListener mListener;
public interface OnPreCountedListener {
void onPreCounted(int count);
}
public PreCountLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
#Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
super.onLayoutChildren(recycler, state);
if (getItemCount() > getChildCount()) {
if (mListener != null) {
mListener.onPreCounted(getChildCount());
}
}
}
public void setListener(OnPreCountedListener listener) {
mListener = listener;
}
}
PreCountingAdapter.class
public class PreCountingAdapter extends RecyclerView.Adapter<PreCountingAdapter.ViewHolder> {
private List<String> mData;
public void swapData(List<String> data) {
mData = data;
notifyDataSetChanged();
}
public class ViewHolder extends RecyclerView.ViewHolder {
View mItemView;
TextView mTextView;
public ViewHolder(View itemView) {
super(itemView);
mTextView = (TextView) itemView.findViewById(R.id.text_view);
mItemView = itemView;
}
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new ViewHolder(inflater.inflate(R.layout.recycler_child, parent, false));
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
if (mData == null) {
// we are in precounting stage
holder.mItemView.setVisibility(View.INVISIBLE);
} else {
String item = mData.get(position);
holder.mItemView.setVisibility(View.VISIBLE);
holder.mTextView.setText(item);
}
}
#Override
public int getItemCount() {
return mData == null ? Integer.MAX_VALUE : mData.size();
}
}

Modify Navigation Fragment after Async Call

I have an Aync task
public class XmlNetwork extends AsyncTask< String, Void, List<PEMenuItem>
which essentially loads up an xml file into some objects that I want to use to change my navigation drawer menu items. it has an override function
protected void onPostExecute(List<PEMenuItem> result)
and so result is now a list of 3 objects i loaded from the xml.
at this point, i would like to make a call within my class
public class NavigationDrawerFragment extends Fragment
to build out the menu, add the onclicklisteners, etc.
i've previously learned how to add some callbacks, to call from the fragment out to my main activity, but how would i go about doing the inverse, calling from my Async class out to the fragment? i want to send the list along with it, something along the lines of
#Override
protected void onPostExecute(List<PEMenuItem> result) {
NavigationDrawerFragment fragment = (NavigationDrawerFragment) getFragment().modifyMenu(result);
}
The menu items in your NavigationDrawerFragment are usually just items in a ListView. As such, they are backed by an Adapter. Set your new items as that adapter's content and call notifyOnDatasetChanged on the adapter. This will cause the list to re-obtain the items from that adapter. For example you could use something like this as an adapter to your menu list (simplified):
public class MenuItemsAdapter extends BaseAdapter {
private List<String> menuItems;
public MenuItemsAdapter(Context context) {
this.context = context;
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//Empty for now
menuItems = new ArrayList<>();
}
public void setNewMenuItems(List<String> newMenuItems) {
this.menuItems = newMenuItems;
notifyDataSetChanged();
}
#Override
public int getCount() {
return menuItems.size();
}
#Override
public String getItem(int i) {
return menuItems.get(i);
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
if(view == null) {
view = inflater.inflate(R.layout.your_menu_item_layout, viewGroup, false);
}
TextView menuItemText = view.findViewByIt(R.id.your_menu_item_text);
menuItemText = menuItems.get(position);
return view;
}
}

Categories