Populate ListView on Fragment after Async done from Activity
Asked Answered
U

7

7

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

Unbidden answered 21/7, 2014 at 20:4 Comment(2)
Yep, your last sentence. Create a public method in your Fragment to populate the ListView, and call it in the AsyncTask's onPostExecute() method.Eudiometer
@MikeM. so how is this public method will be? I'm sort of beginner in Android, been doing this by combining multiple tutorial, so if you could write example code, that would be helpful. Thank youUnbidden
H
4

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.

Hogback answered 21/7, 2014 at 21:22 Comment(11)
I tried this and it gives me nullpointerexception on adapter.clear() any idea what's wrong? check my updated description for the errorUnbidden
This because you need to get your fragment instance calling this.mSermonFragment = (SermonsFragment) getFragmentManager().findFragmentById(R.id.your_fragment_id); in the activity onCreate and leave the call to updateListView() in onPostExecute().Hogback
how do I know what is my fragment_id? where can I get it?Unbidden
I did try something like this, this.mSermonFragment = (SermonsFragment) getFragmentManager().findFragmentById(android.R.id.list); but it still does not work. throwing the same errorUnbidden
@Harts: how do you load your fragment in your view? I don't see any fragment transaction there. Do you delcared your fragment into the activity xml? If yes, you need to assign an id to your fragment then get the fragment instance with the method above.Hogback
I'm sorry I'm not sure how to answer your question. But the way I create my fragment is exactly like this site (I just follow their tutorial) androidhive.info/2013/10/… from here, that's when I start to develop my appUnbidden
Hi, I update the description to show pretty much everything I have on this app. please take a look. Thank you for the help.Unbidden
Ok... this is another scenario xD. Here you need to get a fragment managed by a view pager. In this case, Android avoid the possibility to get the fragment instance (so you need to get it in a "dirty way"). I will update my post to reflect the new scenario. In meantime you can read about getting view pager fragments from the fragment manager in this wayHogback
where should I put the callback method? is it in MainActivity.java? I did try it and it still cause the same error. I might need some help in writing the code exactly where on each file. And I am wondering is mViewPager the same as viewPager on my code? is it possible because myfragment always create new fragment (because the way I wrote getItem on TabsPagerAdapter), which cause new id everytime?Unbidden
I'm sorry, I follow everything in your code still does not work. Do I still need to use getFragmentByTag and how?Unbidden
you don't need it any more. The code i posted i'm sure it's working. You need to refactor your code to make this solution work... please use the Android Developers documentation for better understandig. I can't write yor app for you :)....Hogback
C
2

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

Contempt answered 21/7, 2014 at 20:18 Comment(0)
G
2

There lots of options:

  1. Pass reference of your fragment into AsyncTask and update adapter in the onPostExecute.
  2. Create method in your activity and call it after AsyncTask finished. Inside this method update your fragment.
  3. Execute AsyncTask inside your fragment.

Sample of updating data in your fragment:

public class SermonsFragment extends ListFragment {
  public void update(String[] sermonsList){
    listAdapter.addAll(sermonsList);
    listAdapter.notifyDataSetChanged();
  }
}
Girth answered 21/7, 2014 at 20:19 Comment(3)
Hi, could you give me example code how to update my fragment? Also what's best practice for case 3, should I put asynctask in my fragment or activity? I will need to use this data multiple times, at multiple fragment, that's why I put it in activity.Unbidden
@Unbidden I've added sample. And yes you're right, the better place in your case for executing AsyncTask is activity.Girth
so, I tried this code, but it gives me java.lang.NullPointerException any idea what goes wrong? I updated my description to reflect this changes. please adviseUnbidden
E
1

This might be of the the topic, but I just ran into a similar handshake error using volley and https urls. Check here for further informations.

Simple check if your connection is refused is to trust all SSL-Certificates. (This should only be used for testing!!).

Run this before your first call (f.e. extend Application and call the following in onCreate)

//trust all SSL
SSLCertificateHandler.nuke();

The Trust-everything-class:

public class SSLCertificateHandler {

protected static final String TAG = "NukeSSLCerts";

public static void nuke() {
    try {
        TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() {
                X509Certificate[] myTrustedAnchors = new X509Certificate[0];
                return myTrustedAnchors;
            }

            @Override
            public void checkClientTrusted(X509Certificate[] certs, String authType) {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] certs, String authType) {
            }
        } };

        SSLContext sc = SSLContext.getInstance("SSL");
        sc.init(null, trustAllCerts, new SecureRandom());
        HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
        HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String arg0, SSLSession arg1) {
                return true;
            }
        });
    } catch (Exception e) {
    }
}

}

Exsiccate answered 30/7, 2014 at 20:5 Comment(0)
C
0

onResume() the method that you are populating the listview

Cartercarteret answered 29/7, 2014 at 23:51 Comment(0)
S
0

It would be nice if i can help you because i have also faced the problem like that...

you want to set adapter which includes the string array provided by your asynctask.

create constructor in TabsPagerAdapter.class .........

  ArrayList<String> arrayList_Tab = new ArrayList<String>();
   ** Constructor of the class */
   public TabsPagerAdapter(FragmentManager fm,
        ArrayList<String> arrayList1) {
    super(fm);
    this.arrayList = arrayList1;
  ................
   @Override
   public Fragment getItem(int index) {
    Bundle data = new Bundle();
    switch (index) {

        case 0:
              SermonsFragment ment = new SermonsFragment ();
        data.putStringArrayList("data", arrayList);
        ment.setArguments(data);
        return ment;

        case 1:
            //More fragment activity
            return new MoreFragment();

    }

    return null;
}

I have done with this arraylist but you can do this with passing array as parameters. in your MainActivity.. add your asynctask data to an arraylist and then pass to TabsPagerAdapter class

  mAdapter = new TabsPagerAdapter(getFragmentManager());

change above to

  mAdapter = new TabsPagerAdapter(getFragmentManager(),ArrayLListgotfromAsyctask);

now get your arraylist at SermonsFragment class as :-

ArrayList<String> arrayList = new ArrayList<String>();

@Override
public void onCreate(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onCreate(savedInstanceState);
    arrayList = getArguments().getStringArrayList("data");
}

now you can set arraylist to adapter.

Good luck !!!

Slaveholder answered 31/7, 2014 at 10:35 Comment(0)
V
0
public void updateData(String[] newData) {

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

// call this method in  onPostExecute() method . By calling listAdapter.notifyDataSetChanged(); this adapter is updated with the data which comes from JSON object . 

hope it ll help u :)

Varicolored answered 1/8, 2014 at 12:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.