How to refresh an imageView inside an adapter for a GridView - android

I've been fooling around with Android but I'm stuck. I have a adapter class that
sets an image in an imageview for each item in my gridView. I use the observer pattern to notify the activity that calls the update method and it refreshes the adapter with notifydatasetchanged and that invalidates the gridview itself and the imageView.
The problem is that when i change an object (which has a reference to a drawable), and try to update, nothing happens in the imageview. I've tried to debug and the objects are changed so i don't understand why it doesn't update...
Also, with this it works perfect to make the images disappear, so that's pretty akward..: shape.setImage(android.R.color.transparent)
#Override
public void update(Observable arg0, Object arg1)
{
// Toast.makeText(this, "I am notified",0).show();
adapter.notifyDataSetChanged();
gridView.findViewById(R.id.picture).invalidate();
gridView.invalidate();
}
This is an example of an object that needs to be showed:
public class Square extends Shape
{
public Square()
{
setImage(R.drawable.square);
}
public String print()
{
return "s ";
}
public String getName()
{
return "Square";
}
}
my adapter:
public class GridAdapter extends ArrayAdapter<Shape>
{
Context context;
int layoutResourceId;
List<Shape> data = null;
public GridAdapter(Context context, int layoutResourceId, List<Shape> data)
{
super(context, layoutResourceId, data);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.data = data;
}
public View getView(int position, View convertView,ViewGroup parent)
{
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.list_item, parent, false);
rowView.setBackgroundResource(R.drawable.customshape);
ImageView imgView =(ImageView)rowView.findViewById(R.id.picture);
imgView.setImageResource(data.get(position).getImage());
return rowView;
}

I think the actual problem here is updating the drawable from outside the UI thread. notifyDataSetChanged() isn't the answer. The dataset didn't change, in my case at least. The dataset changed and I set the drawable to null (which worked). Later, when the image finished loading, I wasn't updating from the UI thread, so the GridView didn't know it was supposed to learn about the invalidation of the ImageView.
The answer in my case was to use AsyncTask which automatically uses the UI thread and thread syncs.
Note that notifyDatasetChanged() is for when the getViewId() changes in the adapter — which isn't the same as and actually has nothing to do with when the drawable changes. Also note that setImageDrawable() automatically invalidates the ImageView. There's actually no need to call ImageView.invalidate() or any other invalidate(). You just have to update the drawable in the UI thread, and AsyncTask comes with all the right baggage to not only do it at the right time, but in such a way that the GridView can learn about it before it updates the various canvases and drawable caches.
I learned most of the above from the Google Android dev pages and from source diving for hours on end. Curiously, source diving in Android Studio (ctrl-B) is actually easier than slogging through all the docs hoping to stumble on the right paragraph that explains the problem. Though, the problem is explained nicely here:
http://developer.android.com/training/displaying-bitmaps/process-bitmap.html

I cannot comment, so I am going to share my thoughts here, let me know in the comments if I'm missing something.
Seems like you need to add the following method in your GridAdapter:
public void setData(List<Shape> stuff) {
data.clear();
data.addAll(stuff);
notifyDataSetChanged();
}
Now, wherever in your activity you created the adapter, you should have a List that was passed to create the GridAdapter. Let's call that initial list 'data1'.
Assuming by 'refresh' you mean that you want to modify an existing item. You need to find that item in data1 and make the changes. Then you need to find the GridAdapter instance that you have already created and then use the setData method and pass data1.
Hope this is not totally useless info for you.
Note: it would be helpful if you posted more of your code.

The problem is that when i change an object (which has a reference to
a drawable), and try to update, nothing happens in the imageview.
Lets have a look at the doc:
public void notifyDataSetChanged()
Notifies the attached observers that the underlying data has been
changed and any View reflecting the data set should refresh itself.
the only reason that when you call notifyDataSetChanged() and it dose not update your gridview is that your adapter data set and your object are two different things. that means your gridview data has not changed. you must change those objects that you passed to your gridView constructor (List data). if you change any of them and then you call notifyDataSetChanged() it will work. The references of your object and the adapter data must be the same.
another things that I have seen on the net is you must change the data set of adapter by manipulating it with functions like (add(), insert(), remove(), clear(), etc.).

Problem solved lol, i just made a new adapter instead and it worked

Related

How to restore to the previous state in RecyclerView after loading more data

I have been working with RecyclerView for a while. I am following lazy loading, so I am showing 10 data on the view each time. If user scroll to the bottom, the page re-load from the TOP! however, I want to stay where it was previously! So, I have tried to use
recyclerView.scrollToPosition(position);
However, this breaks the UI flow!
My second try is using onSaveInstanceState() and onRestoreInstanceState(Bundle state); However, that does not work! My page is re-loads to the top!
Parcelable state = layoutManager.onSaveInstanceState();
layoutManager.onRestoreInstanceState(state);
I have tried every other methods, apparently none is working for me!
Any help would be highly appreciated! Thanks in advance!
Note : Make sure that you are not initialising or calling setAdapter() method each time after updating your dataset. If not, then
You have to update your data list and call notifyDataSetChanged() which will update your adapter from existing position.
Let's say you have stored your data into ArrayList mData;
Your getItemCount() would be
#Override
public int getItemCount() {
if (mData != null && mData.length() > 0)
return mData.size();
return 0;
}
Now create one more method in your adapter which you will called each time whenever you will get new data from server. This method will simply override your dataset and will update your adapter
private void updateDataSet(ArrayList<String> mData){
this.mData = mData;
notifyDataSetChanged();
}
I have done this functionality before 2 days ago
I share my idea with you
Make
Listview lv; //Assume Find view by Id
List<Model> models = new ArrayList();
Now Make an Adapter and assign blank models
CustomAdapter adapter = new CustomeAdapter(context,models);
lv.setAdapter(adapter);
Now when you have new data to load with lazylodaing
do this
models.addAll(newModels); //new ModelList
adapter.notifyDataSetChanged();
Thats it.
This is the default behaviour Recycler View to recycle/resuse views. As in official docs:
https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html
If you are having any view related issues then save your view state in your List<Object> like setting visible item or not per position. And in your onBindViewHolder method as per position show/hide your view.

After setData into an adapter the view move up

In my adapter I have a method to set information:
public void setData(List<ListItem> dataList) {
this.listItems = dataList;
notifyDataSetChanged();
}
But after I call this method, the data is refreshed and my View move up. I don't want this behavior, because if someone is changing something in the end of the page, the focus is lost.
There is any way to set data from an adapter and don't lose the focus?
You must call adapter.notifyDataSetChanged() from the UI thread. May be this & this SO posts can help you.

How to reload activity from onClickListener in ArrayAdapter

I'm working on a fragment that contains a listview (this fragment is PlaceHolderFragment generated when create activity). I extends ArrayAdapter to make my custom adapter and fill my listview with this adapter.
One important thing is in one row of listview, there are 2 buttons: first is the enable/disable button to change status of an user (when user's status is active then it's disable, otherwise enable), second is the delete button (to delete user). So I have to implement OnClickListener for this 2 buttons in method getView() of adapter
When click either buttons, it will send request to server and manipulate database (change user's status or delete user from database). The thing is when I click enable button (for example), it is success and user's status in database is changed, or when I click delete button, user will be delete from database successfully
BUT after I click that button, it's state does not changed (I mean if user is enabled, now the button must change to disable, or if user is deleted, that row must be remove from screen). I have to reload this fragment by hand (switch to other fragment and then come back)
The question is how can I reload activity (I already implement onResume to load all data to adapter, so if I can make this method onResume of fragment run, it will work as my expectation), or at least how can I reload the listview to update new data?
Note: notifyDataSetChanged() DOES NOT work because the data in adapter actually doesn't change yet, only data on server are changed
Note 2: if you need me to post my code, please comment and I will edit my post, because I think my code is long
Thank you and best regards!
EDIT 1
I've posted my solution in the answer below, it fix the problem but I have to say that this is a very very BAD practice in android. For example, when you want to delete an item with this approach, you may want to put a AlertDialog for user to confirm, but AlertDialog can only show in Activity (or Fragment), it can't be show from Adapter. So instead, you should use some different methods such as ContextMenu or CustomDialog.
After couple of weeks searching google and try different methods, I finally found a way to archive what I want, and it's very simple. I post my answer here for anyone facing this problem like me in the future
public class CustomAdapter extends ArrayAdapter<YourClass> {
private List<YourClass> items;
private CustomAdapter adapter;
private Context context;
private Button button;
private YourClass item;
public CustomAdapter(Context context, List<YourClass> items) {
super(context, R.layout.custom_list_item, items);
this.items = items;
this.context = context;
this.adapter = this; //This is an important line, you need this line to keep track the adapter variable
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
LayoutInflater li = LayoutInflater.from(getContext());
v = li.inflate(R.layout.custom_list_item, null);
}
item = items.get(position);
button = (Button) v.findViewById(R.id.button); //Find the button
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
//Do something inside here like update, remove your item...
//But the important is
items.remove(item); //Actually change your list of items here
adapter.notifyDataSetChanged(); //notify for change
}
});
}
return v;
}
That's it, all you need to implement when you need the listview to reload in case you need to implement OnClick inside the adapter. Hope you guy find it easier than me when you face this problem
What I understand from your question that you want to refresh the list data when click on one of the button as you need.
Now You should call notifydataset change rather reloading the activity again.
For this you need to change the array that yu are using for the adapter(Don't change the array object just the Item of the array)
like arraylst.get(index).activ=true etc etc and then call notifiydataset change.

Android ArrayAdapter not clearing?

I'm having a strange issue with a custom implementation of Android's ArrayAdapter. To give some background, I'm trying update a ListView's contents while preserving the current scroll position.
I have a service which executes a thread to update data that's displayed in the ListView. That data is stored in an ArrayList and that ArrayList is used to generate some custom ArrayAdapters for the ListView. The adapters are also updated when an item in the ListView is pressed (either adding or removing an item). I used to just create new adapters each time there was any type of change and then set this new adapter to the ListView. This worked, but caused the ListView to scroll to the top each time. Given the nature of my application this was undesirable. The current scrolled position in the ListView must be maintained between updates.
Instead of creating new adapters I began clearing the adapter that I needed to update using the adapter's clear() method, then rebuild the adapter's items by using the adapter's add() method. Both of these methods are being called on the adapter. The adapters are all set to notifyDataOnChange in their constructors so I don't have to manually call notiftyDatasetChanged() each time (although given my issue I've tried calling it manually as well to no avail).
Here's what my custom adapter looks like:
public class RealmAdapter extends ArrayAdapter<Realm>
{
Context c;
public RealmAdapter(Context context, int resource, int textViewResourceId)
{
super(context, resource, textViewResourceId);
setNotifyOnChange(true);
c = context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
...
}
...
}
Long story short, here's my issue. When I call clear() on the adapter, the adapter is not being cleared.
Here's a snippet from my onPostExecute in my thread that does updating. I'm being sure to put it here so it's updating on the UI thread. I also have this exact code copied in a private method in my UI activity. This code does not work in either place:
appState.favoriteAdapter.clear();
Log.d(LOG_TAG, "COUNT: " + appState.favoriteAdapter.getCount());
for(Realm r : appState.favorites) {
appState.favoriteAdapter.add(r);
}
Log.d(LOG_TAG, "COUNT: " + appState.favoriteAdapter.getCount());
As an example, if the above adapter had 3 items in it, calling a getCount() right after the clear() is returning 3 instead of 0. Likewise, if the appState.favorites ArrayList only has 2 items in it, the getCount() after the loop is still returning 3, not 2. Because the adapter is not responding to any of these calls it makes it impossible to update in any fashion. I can post a Logcat later if that will be helpful, but there are no exceptions or anything useful being displayed.
After busting my head for hours, the issue I appear to be having is that the adapter is not responding to calls to any methods that alter it. I've tried passing an empty ArrayList into the adapter's super() call, this does not help. Am I missing something or using the ArrayAdapter incorrectly? I've searched all over and I've already checked a lot of the common problems such as modifying the underlying array and expecting it to update, not calling (or in my casing setting to the adapter) notifyDatasetChanged(), and using an unsupported operation on the underlying collection.
The declaration of the favoriteAdapter is very simple and is contained in my Application class:
public RealmAdapter favoriteAdapter;
Here is the initialization of the favoriteAdapter from above:
if(appState.favoriteAdapter == null) {
appState.favoriteAdapter = new RealmAdapter(c, R.layout.list_item, R.layout.realm_entry, appState.favorites);
}
else {
appState.favoriteAdapter.clear();
Log.d(LOG_TAG, "COUNT: " + appState.favoriteAdapter.getCount());
for(Realm r : appState.favorites) {
appState.favoriteAdapter.add(r);
}
Log.d(LOG_TAG, "COUNT: " + appState.favoriteAdapter.getCount());
}
The above code is in both my UI thread and the thread that downloads the refreshed data.
Underneath the code above a filter is put in place:
if(appState.favoriteAdapter != null && RealmSelector.realmFilter != null) appState.favoriteAdapter.getFilter().filter(RealmSelector.realmFilter.getText().toString());
Would the filter affect clearing the list? Logic would dictate not...
I had filters being applied to the custom ArrayAdapter. Apparently this interferes with adding and removing items from the adapter itself? I added this code to my method and it is now working:
if(appState.favoriteAdapter != null && realmFilter != null) {
appState.favoriteAdapter.getFilter().filter(realmFilter.getText().toString());
}
I'd love if anyone could explain why this matters. I thought filters were meant to select subsets of items in the adapter. In my testing I was leaving the text box that is used for the filter empty, thus no actual filter text should have been applied. Again, if someone knows what's going on and could explain to me why this fixes the problem I'd love to know.

Android: How to add data to an empty listView

I have a problem with listView, I used ArrayList to store data, and a customized Adapter. But, when I remove all the data, and add one item again, it does not display anything in this list. What happens to my List, can anyone help me?
static ArrayList<String> chattingListData=new ArrayList<String>();
static IconicAdapter chattingListDataAdapter;
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
chattingListDataAdapter=new IconicAdapter(this);
setListAdapter(chattingListDataAdapter);
registerForContextMenu(this.getListView());
}
class IconicAdapter extends ArrayAdapter {
Activity context;
IconicAdapter(Activity context) {
super(context, R.layout.chatting_list, chattingListData);
this.context=context;
}
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater=context.getLayoutInflater();
View row=inflater.inflate(R.layout.chatting_list, null);
TextView label=(TextView)row.findViewById(R.id.chattinglist_userName);
label.setText(chattingListData.get(position));
return(row);
}
}
I use static ArrayList to modify data from outside, when I start the ListView activity and add data, it's Ok, but when I remove all data, I can not add anymore to the data list. Please help me.
I use static ArrayList to modify data from outside
Don't do that.
Step #1: Use a non-static ArrayList data member as the basis for your ArrayAdapter.
Step #2: Expose methods on your activity that adds and removes items via the add(), insert(), and remove() methods on ArrayAdapter. By using these methods, your ListView will automatically update when you make the changes.
Step #3: Use the methods you wrote in Step #2 to modify the contents of your ListView, by whoever is doing this (not shown in your code).
You can also use the Adapter's notifyDataSetChanged() method, in order to force it to refresh.

Categories

Resources