In my application I'm using number of items checked on list.
I'm also updating items on list, deleting them and inserting new ones.
Case scenario:
I'm removing item from list used by adapter
I'm calling notifyDataSetChange
I'm refreshing my component
My component is asking mMyListView for number of checked items
View is returning old number
I was browsing android sources and find out that even if I call notifyDataSetChange, handleDataChanged method (some package-protected AbsListView method) is not call immediately, but later. And this is the function responsible for changing number of checked items.
I resolved it by extending ListView with my own class with only one change:
#Override
public long[] getCheckedItemIds() {
handleDataChanged();
return super.getCheckedItemIds();
}
To get checked items number, I'm calling mMyListView.getCheckedItemIds().length (because of API level I cannot use getCheckedItemCount).
Now it works, but I don't like this solution. Am I missing something here?
Related
I'm new to all of this and was making good progress but I have hit a brick wall with working out how to do the next thing. I am using Kotlin and have a Fragment with an associated Recyclerview Adapter. I would like to set OnClick (On or Off) against items in a row depending upon a value in the Fragment which could change at any time.
My adapter works fine to show and update the array of data and also to implement OnClick.
I have tried sending a data element via a constructor which changed in the fragment but always showed as the initial setting in the adapter. The same with trying to call a method.
Many other questions touch on the issue but only show snippets of code, and it seems that I'm not advanced enough to get them working successfully in my code.
Could anyone please provide a pointer to a working set of Kotlin code that includes parsing a variable from fragment to adapter - perhaps in Git or a tutorial. I'm sure that if I can study a working program I can move forward. Thank you.
It would have been better if you had included your Adapter and Fragment code in the question, that would have helped us in understanding how you have setup everything and what data model are you passing to adapter.
But looking at your question, one solution that comes to my mind is to add an enabled boolean in your data model that is displayed in the ViewHolder. Using this you can set view.clickable = model.enabled. Now whenever your "value in the Fragment" changes you can update this list and let the adapter rebind items.
Note that the above solution is when you want to selectively enable/disable clicks on individual items. If you want to do this for all items at once, it's better to create a variable in adapter that you can change from the Fragment, and inside the clickListener you can check the value of that adapter variable. If it's false, just return out of the click listener. Something like,
view.setOnClickListener {
if(adapterValue) {
// handle Click
}
}
If this approach doesn't help, I would ask you to add more context in your question and show what you have done so far.
I have an API which returns JSON and I would like to load an Image from a URL which is provided by this API. The Image should be passed into Adapter for a Recycling View.
Right now all Items which contain an Imgae_URL are getting skipped by my Adapter and I dont really understand why.
if (json_img_url.isNotEmpty()) {
Executors.newSingleThreadExecutor().execute({
val conn = URL(json_img_url).openConnection()
conn.connect()
val iStream:InputStream = conn.getInputStream()
val img_bitmap:Bitmap? = BitmapFactory.decodeStream(iStream)
newItems.add(Item(....img_bitmap))
})
....
itemArrayAdapter.addItems(newItems)
URL :"https://s3.us-east-2.amazonaws.com/c...."
The URls used are valid and Images on the S3 Bucket are all public.
The If statment returns true (I checked with Log.d) but the Item does not appear on the Phone, I dont recive an error and the app does not crash its just like the Item was never there...
I know there are librarys like Picasso or Glide but even with them I could not make it work and to be honest I would like to accomplish this task without a having to install an extra package, it just feels wrong.
Unlike ListView, there is no way to add or remove items directly through the RecyclerView adapter. You need to make changes to the data source directly and notify the adapter of any changes.There are many method available to use when notifying the adapter of different changes:
notifyItemChanged(int pos) : Notify that item at position has changed.
notifyItemInserted(int pos): Notify that item reflected at position has been newly inserted.
notifyItemRemoved(int pos): Notify that items previously located at position has been removed from the data set.
notifyDataSetChanged(): Notify that the dataset has changed. Use only as last resort.
Every time we want to add or remove items from the RecyclerView, we will need to explicitly inform to the adapter of the event. Unlike the ListView adapter, a RecyclerView adapter should not rely on notifyDataSetChanged() since the more granular actions should be used. See the API documentation for more details.
Also, if you are intending to update an existing list, make sure to get the current count of items before making any changes. For instance, a getItemCount() on the adapter should be called to record the first index that will be changed.
// record this value before making any changes to the existing list
int curSize = itemArrayAdapter.getItemCount();
// update the existing list
newItems.add(Item(....img_bitmap));
// curSize should represent the first element that got added
// newItems.size() represents the itemCount
itemArrayAdapter.notifyItemRangeInserted(curSize, newItems.size());
I have a set of data in a Realm that is displayed in a RecyclerView using a RealmRecyclerViewAdapter. Basically, I'll have 25 elements displayed in the RecyclerView -- if items are being inserted into the Realm for the first time there are no issues. It seems to work properly if the Realm isn't being added to shortly after all its data has been deleted because the issue only arises when I need to delete all the items in the Realm and then add a new Collection of items shortly afterwards.
What will happen is I will send an http request for the new items, and on receiving them, I'll delete all the items currently in the Realm, and then add the new ones, both in different transactions; I've also tried doing it in the same transaction.
The unexpected behavior is that the contents of the RecyclerView will flicker for a very brief moment, as if all the items are about to be rebound, and I can even see the data of the new items being shown for a split second, but then the contents of the RecyclerView just goes back to showing the previous data -- the data I know to be deleted from the Realm. I know it's deleted because if I interact with the RecyclerView after this flicker, even just a click on a static view, all the items will be instantly rebound and reflect the new items that have been added; as if notifyDataSetChanged() is finally being called.
This behavior isn't always exhibited. Most of the time the items are all deleted, the new ones are added, and it just works perfectly; no flicker. But as far as the user behavior goes when it works properly and when it doesn't, there is nothing different. The same thing can be done over and over again and sometimes it will work and other times it won't.
I've tried deleting the items using mRealm.deleteAll() and mOrderedRealmList.deleteAllFromRealm(). And I've tried adding the items to the collection using both mRealm.insert(items) and mRealm.copyToRealm(items). I've also tried manually calling notifyDataSetChanged() only after the items are added, only after the items are all deleted, and after both events, but the issue still occurs.
Any idea on what could be causing this behavior?
This is the only code that is run that manipulates Realm data:
mRealm.beginTransaction();
mOrderedRealmCollection().deleteAllFromRealm();
mRealm.commitTransaction();
...
...
processing of new items...
...
...
mRealm.beginTransaction();
mRealm.insert(items);
mRealm.commitTransaction();
Update:
When I delete all the items from the Realm and add the new items within the same transaction the unexpected behavior occurs more frequently. Also, I've noticed that when the changes are made in the same transaction and the issue occurs, sometimes there is no flicker, the RecyclerView just continues to display the old data, uninterrupted, but if I touch the RecyclerView anywhere, just like when the flicker does occur, all the items will be instantly rebound and reflect the new items that have been added.
Update: How Adapter is Set Up
//in onCreate() method
mItems = mRealm.where(Item.class).findAllSorted("mIndex", Sort.ASCENDING);
//in onCreateView() method
mRecyclerView.setAdapter(mAdapter = new MyRealmRecyclerViewAdapter(this, mItems));
Update: Cancel Button onClick() Code
private void setListeners(){
mCancelButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mSearchEditText.clearFocus();
mCancelButton.setVisibility(View.GONE);
mCreateItemButton.setVisible(true);
Toast.makeText(A_Main.this,"Cancel clicked",Toast.LENGTH_SHORT).show();
}
});
i have used SimpleAdapter to display a list of items in a listview.While displaying the getView() is called every time to display every list item with different layout.Here i got succeeded in doing so.But here the problem is whenever i scroll the list items again the getView() method is executing and iam getting the unreliable results.Here my requirement is i dont want to execute the getView() method while scrolling the listview.Any body please guide me out of this situation.
This is the intended behaviour of the ListView, it is automatically buffered. So as you scroll, new items are inflated and added and the old ones are knocked off. This mechanism works by calling your overridden getView() method.
If you don't want this then just use a LinearLayout inside a scrollview and manually populate it with your list items. You can even still use your adapter by using:
linear.addView(adapter.getView(index, null, linear));
Hope this helps!
I want to add one thing that if
you have more than one records in your adapter just use a for loop like this.
ArrayAdapter<Question> adapter = MyCustomArrayAdapter
.GetArrayAdapterInstance(this, flag1, flag1, lst_question);
for(int i=0;i<adapter.getCount();i++)
{
linear.addView(adapter.getView(i, null, linear));
}
I also get the same problem, and here is our response !
Android, how to stop reading getView() function again, if the content is already downloaded in the ListView
Let me know if it work for you ;)
sorry for stupid question. But really interesting and incomprehensible. In this session discussed about notifyDataSetChanged() method.
From documentation for this method - "called when the data set being observed has changed, and which when read contains the new state of the data". My English bad and I do not understand all. But I right if guess that method called when I need refresh ListView with new data set?
If I'm right then I'm confused. In the past and my first program I played with contacts api of android. And run some processing in an asynctask. At this time appeared dialog with progress bar and in the background, you could see how the state of ListView changed in real time. Data for ListView row changed via BindView.
Why? So I'm in something wrong. Explain please.
As i read it, BindView is only used with cursors, which are a specific type of a data set basically. You can have alternative data sets, there is for example an ArrayListAdapter in the API which uses an ArrayList as its dataset. In case that data set changes, notifyDataSetChanged() will have to be called to notify the list view that its bounds will have to be recalculated and its views have to be redrawn (and probably some more).
If you decide to write your own and create the possibility to modify the data shown in the list view through an adapter (one could imagine adding method like addObject(SomeObject o) in your home made adapter for example), then you'd call notifyDataSetChanged() in that method.
Similarly if you have a deleteObject(SomeObject x), if the remaining data set is larger than zero you'd call notifyDataSetChanged() or when the remaining data set is empty you'd call notifyDataSetInvalidated() which in turn will to some extra stuff like setting the so called empty view in the list if you have one specified.