Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Populate ListView on Fragment after Async done from Activity

I have main activity which will get JSON data from a url async

This is MainActivity.java

public class MainActivity extends FragmentActivity implements ActionBar.TabListener{
    private ViewPager viewPager;
    private TabsPagerAdapter mAdapter;
    private ActionBar actionBar;

    //Tab titles
    private String[] tabs = {"Sermons", "More"};
    private String[] sermonsList = new String[0];

    //JSON URL for sermonList data
    private static String sermonListJSONUrl = "https://dl.dropboxusercontent.com/u/12345/index.php";

    //Variable to save JSON Object
    private static final String JSON_KEY_SERMONS = "sermon";

    private JSONObject sermonListJSONObject = null;
    private JSONArray sermonListJSONArray = null;
    SermonsFragment mSermonFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //Call JSONParser Asynchronously to get sermonList in JSON Format

        new callJSONParserAsync().execute(sermonListJSONUrl);
        //this.mSermonFragment = (SermonsFragment) getFragmentManager().findFragmentById(android.R.id.list);
        this.mSermonFragment = (SermonsFragment) getFragmentManager().findFragmentById(R.id.pager);

        //Initialization
        viewPager = (ViewPager) findViewById(R.id.pager);
        actionBar = getActionBar();
        mAdapter = new TabsPagerAdapter(getFragmentManager());

        viewPager.setAdapter(mAdapter);
        actionBar.setHomeButtonEnabled(false);
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

        //Adding Tabs
        for(String tab_name : tabs) {
            actionBar.addTab(actionBar.newTab().setText(tab_name).setTabListener(this));
        }

        /**
         * on swiping the viewpager make respective tab selected
         */
        viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                //on changing the page
                //make respected tab selected
                actionBar.setSelectedNavigationItem(position);
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });
    }

This is activity_main.xml

<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/pager"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
</android.support.v4.view.ViewPager>

This is SermonsFragment.java

public class SermonsFragment extends ListFragment {
    private String[] sermonsList;
    ArrayAdapter listAdapter;

    String imageUrl = "https://fbcdn-sphotos-b-a.akamaihd.net/" +
            "hphotos-ak-prn2/t1.0-9/10415605_10132423542_6220704573655530117_n.jpg";

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        sermonsList = ((MainActivity)getActivity()).getSermonsList();

        //ListView for the sermon list, only show if there is something in listview
        listAdapter = new ArrayAdapter<String>(getActivity(),
                android.R.layout.simple_list_item_1, sermonsList);

        if(listAdapter.getCount() != 0) {
            setListAdapter(listAdapter);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_sermons, container, false);

        // show The Image
        new getSermonBannerImage((ImageView) rootView.findViewById(R.id.series_banner))
                .execute(imageUrl);

        return rootView;
    }

    public void updateListView(String[] newData) {
        if(newData.length == 0) {
            Log.d("myTesting3", "sermonsList is Empty");
        } else {
            Log.d("myTestingSermonFragment", newData[0]);
            Log.d("myTestingSermonFragment", newData[1]);
        }

        sermonsList = newData;
        Log.d("myTestingSermonFragment", sermonsList[1]);
        this.listAdapter.clear();
        this.listAdapter.addAll(newData);
        this.listAdapter.notifyDataSetChanged();
    }

This is fragment_sermon.xml

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#222222" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <!-- Shows an image from your drawable resources -->
        <ImageView
            android:id="@+id/series_banner"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:layout_gravity="center"
            android:src="@drawable/series_banner" />
        <!-- Closing tag for the horizontal nested layout -->

        <View
            android:layout_width="fill_parent"
            android:layout_height="10dp"
            android:layout_marginBottom="10dp"
            android:background="@android:color/darker_gray"/>

        <ListView
            android:id="@android:id/list"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:background="#ffffffff" />

        <TextView
            android:id="@android:id/empty"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:gravity="center"
            android:text="No Data" />
    </LinearLayout>
</RelativeLayout>

This is TabsPagerAdapter.java

public class TabsPagerAdapter extends FragmentPagerAdapter {

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

    @Override
    public Fragment getItem(int index) {
        switch (index) {
            case 0:
                //Sermons fragment activity
                return new SermonsFragment();
            case 1:
                //More fragment activity
                return new MoreFragment();

        }

        return null;
    }

    @Override
    public int getCount() {
        //get item count - equal to number of tabs
        return 2;
    }
}

and lastly this is the error

07-27 16:42:27.921    1549-1555/org.myapp.myapp E/jdwp﹕ Failed writing handshake bytes: Broken pipe (-1 of 14)
07-27 16:42:29.941    1549-1549/org.myapp.myapp E/OpenGLRenderer﹕ Getting MAX_TEXTURE_SIZE from GradienCache
07-27 16:42:29.941    1549-1549/org.myapp.myapp E/OpenGLRenderer﹕ MAX_TEXTURE_SIZE: 16384
07-27 16:42:29.953    1549-1549/org.myapp.myapp E/OpenGLRenderer﹕ Getting MAX_TEXTURE_SIZE from Caches::initConstraints()
07-27 16:42:29.953    1549-1549/org.myapp.myapp E/OpenGLRenderer﹕ MAX_TEXTURE_SIZE: 16384
07-27 16:42:30.353    1549-1549/org.myapp.myapp E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: org.myapp.myapp, PID: 1549
    java.lang.NullPointerException
            at org.myapp.myapp.MainActivity$callJSONParserAsync.onPostExecute(MainActivity.java:174)
            at org.myapp.myapp.MainActivity$callJSONParserAsync.onPostExecute(MainActivity.java:157)
            at android.os.AsyncTask.finish(AsyncTask.java:632)
            at android.os.AsyncTask.access$600(AsyncTask.java:177)
            at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:645)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5017)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
            at dalvik.system.NativeStart.main(Native Method)

I'm not sure why there is OpenGLRenderer Error, but it's always been there ever since I create this app and it run just fine, so that's not the problem (Although if you can point out how to fix this it will be great). MainActivity.java:174 is this line on mSermonFragment.updateListView(sermonsList);

How can I make sure it is populate correctly? preferably async so it's not locking the UI, maybe by updating the listview later after async complete.

It would be nice, if you could also point out, how to make it so during async, it will check if there's internet connection, the list view will show loading circle, and if it has no internet, or empty list it will show no data, otherwise it will show the list

like image 827
Harts Avatar asked Jul 21 '14 20:07

Harts


2 Answers

Since you only have two fragments, you can keep track of them saving the instances. Using callbacks as suggested it's not the best option.

So, in your pager adapter:

public class TabsPagerAdapter extends FragmentPagerAdapter {

    SermonsFragment mSermonsFragment;
    MoreFragment mMoreFragment;

    public TabsPagerAdapter(FragmentManager fm) {
        super(fm);
        this.mSermonsFragment = new SermonsFragment();
        this.mMoreFragment = new MoreFragment();
    }

    @Override
    public Fragment getItem(int index) {
        switch (index) {
            case 0:
                //Sermons fragment activity
                return mSermonsFragment;
            case 1:
                //More fragment activity
                return mMoreFragment;

        }

        return null;
    }

    @Override
    public int getCount() {
        //get item count - equal to number of tabs
        return 2;
    }

    public updateSermonsFragment(String[] data) {
         mSermonsFragment.updateData(data);
    }

    //i don't know what's the data type managed by MoreFragment
    public updateMoreFragment(Object data) {
         mMoreFragment.updateData(data)
    }
}

Create a method to update your adapter in your fragment:

public void updateData(String[] newData) {
    this.listAdapter.clear();
    for(int i = 0; i < newData.length; i++) {
        this.listAdapter.add(newData[i]);
    }
    this.listAdapter.notifyDataSetChanged();
}

Then you can call this method from the onPostExecute of your AsyncTask trought your adapter method:

protected void onPostExecute(JSONObject jsonObject) {
    sermonListJSONObject = jsonObject;
    sermonListJSONArray = 
            parseJSONObjToJSONArray(sermonListJSONObject, JSON_KEY_SERMONS);

    String[] sermonsList;

    .... // here you need set an array with the strings you parsed

    //here you call the new method on the adapter to update your data.
    mAdapter.updateSermonsFragment(sermonsList);
 }

Another best practice it's to define a static newInstace(String[] data) method in your fragment, so you can initialize the fragment data at the very first network call, to be sure your fragment has a dataset to work with then update as described above.

like image 162
ReDirEct__ Avatar answered Sep 25 '22 13:09

ReDirEct__


Why do you not call/execute

sermonsList = ((MainActivity)getActivity()).getSermonsList();

in onPostExecute() of the AsyncJob?

onPostExecute is running on UI-Thread - so it should be no problem

like image 32
user3862149 Avatar answered Sep 21 '22 13:09

user3862149