ListView in swipe tab not updated unless restart
Asked Answered
C

5

5

In my app, it has two tabs, one is Reminder and the other is Completed Task.

enter image description here

When the toggle button is clicked, I want it move the list to Completed Task.

The idea are :

  • Get the checked row id from sqlite
  • Retrieve the data based on id from Reminder Table and insert into Completed Table.
  • Call Retrieve method in Completed Tab.

But when I clicked the toggle button and swipe to Completed, it still empty. After I exit the app, and swipe to the Tab,only the data shown.

How can I made the data straight away show in Completed Tab when swipe instead of exit the app and re-open again ? Thanks

AllAdapter (Reminder)

 holder.toggle.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (((ToggleButton)v).isChecked()) {
                        int getPosition = (Integer) v.getTag();  // Here we get the position that we have set for the checkbox using setTag.
                        search.get(getPosition).setSelected(((ToggleButton) v).isChecked());
                        int id= search.get(getPosition).getID();
                        mdb = new MyDatabaseHelper(v.getContext());
                        database = mdb.getReadableDatabase();
                        Cursor cursor = database.rawQuery("SELECT * FROM " + MyDatabaseHelper.TABLE__TASK + " WHERE ID = ? ", new String[]{id+""}, null);
                        if (cursor != null && cursor.getCount() > 0) {
                            while (cursor.moveToNext()) {
                                String allTask = cursor.getString(cursor.getColumnIndex("Title"));
                                String name = cursor.getString(cursor.getColumnIndex("Name"));
                                String allTime = cursor.getString(cursor.getColumnIndex("Time"));
                                String allDate = cursor.getString(cursor.getColumnIndex("Date"));
                                insertDataToCompleteTab(id,name,allTask,allTime,allDate);    // insert to another table
                            } 

                        }
                    } else {
                        int getPosition = (Integer) v.getTag();  // Here we get the position that we have set for the checkbox using setTag.
                        search.get(getPosition).setSelected(((ToggleButton) v).isChecked());
                    }
                }
            });

CompletedTask

retrieveList(name);

 public void retrieveList(String name) {
        Toast.makeText(getActivity(),name,Toast.LENGTH_SHORT).show();
        search.clear();
        database = mdb.getReadableDatabase();
        Cursor cursor = database.rawQuery("SELECT * FROM " + MyDatabaseHelper.TABLE_TASKCOMPLETED + " WHERE Name = ? ", new String[]{name}, null);
        if (cursor != null && cursor.getCount() > 0) {
            while (cursor.moveToNext()) {
                int iD = cursor.getInt(cursor.getColumnIndex("ID"));
                String allTask = cursor.getString(cursor.getColumnIndex("Title"));
                String allTime = cursor.getString(cursor.getColumnIndex("Time"));
                String allDate = cursor.getString(cursor.getColumnIndex("Date"));

                if (adapter != null) {
                    adapter.add(iD, allTask, allTime, allDate);
                    listview.setAdapter(adapter);
                    adapter.getCount();
//                    check();
                }
            }
        } else {
        }
    }

AllAdapter

http://pastebin.com/qbLDtf4v

Completed Tab

http://pastebin.com/WCCbZ0h4

CompleteAdapter

http://pastebin.com/QdbuTQKm

Contemplate answered 24/11, 2016 at 10:3 Comment(9)
If you're using lists to display your data make sure you call notifyDataSetChanged() on your list's adapter so it updates the views with the new data.Fleshings
Instead of moving data from one table to another, create only one table of tasks which will have a boolean column saying completed or not. while toggling just update that field from the table. where is your code?Uphold
@AmrutBidri please checkContemplate
@VeselinTodorov please check.Contemplate
Can you show us your adapter code. Also where do you keep your data array/list. The changes you get from your Cursor you should reflect in your array/list that the adapter uses and after that just call listview.getAdapter().notifyDataSetChanged(). No need to set a new adapter every time.Fleshings
@VeselinTodorov can you check my post again ? ThxContemplate
In your CompletedTask, try override onResume() and put "retrieveList(name);" inside it. Hope that help!Perlaperle
@I_A_Mok tried but get android.support.v4.app.SuperNotCalledException: Fragment CompletedTask{41e4bca8 #1 id=0x7f0c005d android:switcher:2131492957:1} did not call through to super.onResume()Contemplate
@Contemplate this issue is due Fragment lifecycle which is tightly attached to its Activity.you can see my answer if it is helpful for youHypogeal
H
3

From the Android documentation for fragment lifecycle

System Displayed fragments onResume() or onPause() will be called only when the Activities onResume() or onPause() is called. They are tightly coupled to the Activity.

Due to this you are not able to refresh your view when tab is changing.

To resolve this problem i found one solution in setUserVisibleHint in my application as we were having same requirement. Viewpager calls following function of your fragment and implement it in your fragment code

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);

    // Make sure that we are currently visible
    if (this.isVisible()) {
        // If we are becoming invisible, then...
        if (!isVisibleToUser) {
            Log.d("TAG", "Stop animation and audio you are not visible");

        }
    }
}

This is what documentation says

Set a hint to the system about whether this fragment's UI is currently visible to the user. This hint defaults to true and is persistent across fragment instance state save and restore.An app may set this to false to indicate that the fragment's UI is scrolled out of visibility or is otherwise not directly visible to the user. This may be used by the system to prioritize operations such as fragment lifecycle updates or loader ordering behavior.This method may be called outside of the fragment lifecycle. and thus has no ordering guarantees with regard to fragment lifecycle method calls.

ViewPager calls this on its childfragment. For more information, ViewPager also calls setMenuVisibility you can use it if you are facing issue in displaying or removing Menu. please see the link for setMenuVisiblity documnet which states that

Set a hint for whether this fragment's menu should be visible. This is useful if you know that a fragment has been placed in your view hierarchy so that the user can not currently seen it, so any menu items it has should also not be shown.

Hypogeal answered 3/12, 2016 at 1:37 Comment(0)
V
4

By default closest tabs inside ViewPager are loaded at the same time. So that happened because the data had been loaded to the second tab before you made any switches with your toggle button. To fix an issue you have to update data in the completed tab in case any data changes.

This could be achieved with several ways:

  • First fragment should send an event on each change and second should subscribe on that event and update data manually

  • Use loader & content provider ( or just custom uri's). Loader subscribes to any uri changes and your repository/dao database helpers notify such uri's about any change (when insert/update/delete methods have been called)

context.getContentResolver().notifyChange(uri);

Loader for your objects.

abstract class CachedLoader<T> extends AsyncTaskLoader<T> {

@Nullable
private T cachedData;

CachedLoader(Context context) {
    super(context);
}

void registerUri(Uri... observerUris) {
    for (Uri uri:observerUris) {
        getContext().getContentResolver().registerContentObserver(uri, true, createContentObserver());
    }
}

ContentObserver createContentObserver() {
    return new ForceLoadContentObserver();
}

@Override
public void deliverResult(T data) {
    super.deliverResult(data);
    this.cachedData = data;
}

@Override
public void onContentChanged() {
    super.onContentChanged();
    cachedData = null;
}

@Override
protected void onStartLoading() {
    if (!takeContentChanged() && cachedData != null) {
        deliverResult(cachedData);
        return;
    }

    forceLoad();
}

@Override
protected void onReset() {
    super.onReset();
    cachedData = null;
}
}

call register uri to subscribe on that uri changes.

Or use a CursorLoader, because you operate with cursors directly. As i see in updated question

I prefer second approach, but first one is simpler

Vexed answered 24/11, 2016 at 10:20 Comment(1)
I don't get you :(Contemplate
H
3

From the Android documentation for fragment lifecycle

System Displayed fragments onResume() or onPause() will be called only when the Activities onResume() or onPause() is called. They are tightly coupled to the Activity.

Due to this you are not able to refresh your view when tab is changing.

To resolve this problem i found one solution in setUserVisibleHint in my application as we were having same requirement. Viewpager calls following function of your fragment and implement it in your fragment code

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);

    // Make sure that we are currently visible
    if (this.isVisible()) {
        // If we are becoming invisible, then...
        if (!isVisibleToUser) {
            Log.d("TAG", "Stop animation and audio you are not visible");

        }
    }
}

This is what documentation says

Set a hint to the system about whether this fragment's UI is currently visible to the user. This hint defaults to true and is persistent across fragment instance state save and restore.An app may set this to false to indicate that the fragment's UI is scrolled out of visibility or is otherwise not directly visible to the user. This may be used by the system to prioritize operations such as fragment lifecycle updates or loader ordering behavior.This method may be called outside of the fragment lifecycle. and thus has no ordering guarantees with regard to fragment lifecycle method calls.

ViewPager calls this on its childfragment. For more information, ViewPager also calls setMenuVisibility you can use it if you are facing issue in displaying or removing Menu. please see the link for setMenuVisiblity documnet which states that

Set a hint for whether this fragment's menu should be visible. This is useful if you know that a fragment has been placed in your view hierarchy so that the user can not currently seen it, so any menu items it has should also not be shown.

Hypogeal answered 3/12, 2016 at 1:37 Comment(0)
C
3

So I just put below code in my Completed Task fragment

 @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if(isVisibleToUser){
            retrieveList(name);//call the method to be refreshed
        }
        else{
            //no
        }

    }

Getting the hint from @Swapnil and answer from https://mcmap.net/q/1041034/-my-fragments-in-viewpager-tab-dont-refresh

Contemplate answered 3/12, 2016 at 17:35 Comment(0)
D
3

put below code on fragment onResumse like this

 @Override
  public void onResume() {

     super.onResume();
     retrieveList(name);
  }
Durware answered 4/12, 2016 at 4:11 Comment(0)
U
3

What you could do is create an interface along the lines of

public interface ReminderCompletedListener
{
    void reminderCompleted();
}

In CompletedTask create an instance of that listener and register it to your Activity.

((MyActivity) getActivity()).setReminderCompletedListener(new ReminderCompletedListener()
{
    @Override
    public void reminderCompleted()
    {
        retrieveList(name);
    }
});

In your Activity create a method that you can call from your toggle click listener

((MyActivity) getActivity()).reminderCompleted();

That will trigger the Completed list reload when you complete a task from your Reminders tab. One main consideration to take from this solution is that it directly couples your Fragments with your Activity.

Theres other solutions to this as well, you could use things like a bus (eventbus/otto), probably rxjava as well, you can explore those if you like but the vanilla solution I've given should do the job fine.

Uninspired answered 4/12, 2016 at 5:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.