Update Listview in from other fragment

2020-02-15 03:17发布

问题:

I am using the tutorial on Manishkpr to create a app where you swipe between 1) layoutOne: here you create a file and 2) layoutTwo: shows a listview of all created files in a certain folder.

Problem: if you create a file, it is not immediately shown in the listview. I found that I should use this code in my LayoutOne.java:

   LayoutTwo fragment = (LayoutTwo) getFragmentManager().findFragmentByTag("TESTTWO");
            fragment.getAdapter().notifyDataSetChanged();

In LayoutTwo.java I added:

private static final String TAG = "TESTTWO";

//and the function getAdapter:

public CustomArrayAdapter getAdapter() {

        return adapter;
    }

However, I am getting a nullpointer exception on fragment.getAdapter().notifyDataSetChanged();. How can I solve this, and is this the best way actually?

EDIT

myList = new ArrayList<RecordedFile>();

        File directory = Environment.getExternalStorageDirectory();
        file = new File(directory + "/test/");

        File list[] = file.listFiles();

        for (int i = 0; i < list.length; i++) {
            if (checkExtension(list[i].getName()) == true) {

                RecordedFile q = new RecordedFile();
                q.setTitle(list[i].getName());
                q.setFileSize(readableFileSize(list[i].length()));


                myList.add(q);
            }
        }

        adapter = new CustomArrayAdapter(myContext,
                R.layout.listview_item_row, myList);
        listView.setAdapter(adapter);

回答1:

I am using the tutorial on Manishkpr to create a app where you swipe between 1) layoutOne: here you create a file and 2) layoutTwo: shows a listview of all created files in a certain folder.

Problem: if you create a file, it is not immediately shown in the listview.

If you just have two layouts to swipe that means both will have their views in memory and available to access. You could then assign an id to the ListView and when it's time to refresh the data simply look for that ListView in the Activity, get its adapter and update it, something like this:

ListView list = (ListView) getActivity().findViewById(R.id.theIdOfTheList);
((BaseAdapter)list.getAdapter()).notifyDataSetChanged();

No matter if you call notifyDataSetChanged() on the adapter the ListView will not update because it doesn't see the new file, from its point of view the data set is intact. Depending on how your adapter looks like you have two options:

Rebuild the data of the ListView, basically redo what you did when the ListView is first constructed: check that directory and re-list all files.

// create the new file
File directory = Environment.getExternalStorageDirectory();
file = new File(directory + "/test/");
File list[] = file.listFiles();
ListView list = (ListView) getActivity().findViewById(R.id.theIdOfTheList);
// I'm assuming your adapter extends ArrayAdapter(?!?)
CustomArrayAdapter caa = (CustomArrayAdapter) list.getAdapter();
caa.clear();
for (int i = 0; i < list.length; i++) {
     if (checkExtension(list[i].getName())) {
         RecordedFile q = new RecordedFile();
         q.setTitle(list[i].getName());
         q.setFileSize(readableFileSize(list[i].length()));
         caa.add(q);
     }
}

Or you could manually create a RecordFile object for the newly created file and add that to the existing adapter:

ListView list = (ListView) getActivity().findViewById(R.id.theIdOfTheList);
(CustomArrayAdapter) caa = (CustomArrayAdapter) list.getAdapter();
File newFile = new File("directory" + "/test/theNewFileName.extension");
RecordFile rf = new RecordFile();
rf.setTitle(newFile.getName());
rf.setFileSize(readableFileSize(newFile.length()));
// have a method to return a reference of the data list(or a method
// to add the data directly in the adapter)
List<RecordFile> data = caa.getListData();
data.add(rf);
caa.notifyDataSetChanged();

I don't know how your adapter looks like so try what I said and if it doesn't work please post the code for the adapter.



回答2:

I think this is because the viewpager loads the layoutTwo, BUT you do not refresh it with the notifyDataSetChanged. You only refresh the data of the LayoutTwo, but it does not get refreshed, since the viewpager has a method: pager.setOffscreenPageLimit(1); AFAIK this is the default, so the next thing will happen in row: load LayoutOne, load LayoutTwo, do things in layoutOne, do things in layoutTwo. But as you see there will be no load layoutTwo afterwards.

I had a very similar problem and it is a kind of a workaround, but restarted the actvity (and pager.removeAllViews(); ) and it worked well without disturbing the user.

But to be certain that this causes the problem, put this in your code for a button clicklistener:

viewPager.removeAllViews();
finish();
startActivity(new Intent(MyActivity.this, MyActivity.class));

This will not solve your problem exactly, but you will able to see if the layoutTwo chaged or not, getting the cause of the problem.



回答3:

Had almost the same problem in a project a while ago.

The solution was to add the Observer pattern to the application.

Have a look at the official documentation about the problem:

http://developer.android.com/training/basics/fragments/communicating.html

What happens is that you register the different Fragments with the Activity that holds the Fragments. Whenever one Fragment is changing, you make a callback to your Activity who in turn will act and deliver some message to one of the other Fragments.

Then you can get for instance the ListView of the specific Fragment and update it or whatever you want to do.

It's really simple and powerful ;-)



回答4:

Hi maybe its a little late but I was struggling to do this and this is the way I managed to do it.

It definitely isn't the best way to do it but it's working.

Initialize your fragments at the start so we can hold each instance of them and then select if we want to instantiate them.

ExampleFragment searchFragment = null;
ExampleFragment fileListFragment = null;

Now change the FragmentStatePagerAdapter to the following so we use our previously initialiazed Fragments

ExampleFragment searchFragment = null;
ExampleFragment fileListFragment = null;

Now on the PagerAdapter class:

class pagerAdapter extends FragmentStatePagerAdapter
{

    public pagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        // getItem is called to instantiate the fragment for the given page.
        // Return a PlaceholderFragment (defined as a static inner class
        // below).
        switch (position) {
        case 0:
            if (searchFragment != null) {
                return searchFragment;
            }
            return searchFragment = new ExampleFragment();
        case 1:
            if (downFragment != null) {
                return downFragment;
            }
            return downFragment = new ExampleFragment();
        }
        return null;
    }

    @Override
    public int getCount() {
        return 2;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        Locale l = Locale.getDefault();
        switch (position) {
        case 0:
            return getString(R.string.title_section1).toUpperCase(l);
        case 1:
            return getString(R.string.title_section2).toUpperCase(l);
        }
        return null;
    }

}

In this way we can call the methods inside of every fragment and communicate with them from the Main Activity without any view or context issue.

Then on the onTabSelected method of the action bar or any other "onChangeTab" like method you just reset call the method you want from your fragment.

@Override
public void onTabSelected(ActionBar.Tab tab,
        FragmentTransaction fragmentTransaction) {
    // When the given tab is selected, switch to the corresponding page in
    // the ViewPager.
    if ((tab.getPosition()) == 1) {
        downFragment.yourMethod();
    }
    mViewPager.setCurrentItem(tab.getPosition());
}

It's not elegant but it is working like a charm.