从Activity完成异步后,在Fragment上填充ListView

时间:2014-07-21 20:04:49

标签: android android-fragments android-listview android-asynctask

我有一个主要活动,它将从url async

获取JSON数据

这是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) {

            }
        });
    }

这是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>

这是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();
    }

这是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>

这是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;
    }
}

最后这是错误

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)

我不确定为什么会出现OpenGLRenderer错误,但是自从我创建这个应用程序以来它一直存在并运行得很好,所以这不是问题(尽管如果你能指出如何解决这个问题,它将是大)。 MainActivity.java:174就是mSermonFragment.updateListView(sermonsList);

上的这一行

如何确保填充正确?最好是异步,因此它不会锁定UI,可能是在异步完成后更新listview。

如果您还可以指出,如果在异步期间如何制作它会很好,它将检查是否有互联网连接,列表视图将显示加载圈,如果它没有互联网,或者空列表它将不显示任何数据,否则将显示列表

7 个答案:

答案 0 :(得分:4)

由于您只有两个片段,因此可以跟踪它们保存实例。使用回调建议它不是最好的选择。

所以,在你的寻呼机适配器中:

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)
    }
}

创建一个方法来更新片段中的适配器:

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

然后你可以从你的适配器方法的AsyncTask的onPostExecute中调用这个方法:

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);
 }

另一种最佳做法是在片段中定义静态newInstace(String[] data)方法,这样您就可以在第一次网络调用时初始化片段数据,以确保您的片段有一个数据集可以使用,然后按照描述进行更新上方。

答案 1 :(得分:2)

你为什么不打电话/执行

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

在AsyncJob的onPostExecute()中?

onPostExecute在UI-Thread上运行 - 所以应该没问题

答案 2 :(得分:1)

有很多选择:

  1. 将您的片段的引用传递到AsyncTask并更新onPostExecute
  2. 中的适配器
  3. 在您的活动中创建方法,并在AsyncTask完成后调用它。在此方法中更新您的片段。
  4. 在您的片段中执行AsyncTask
  5. 更新片段中数据的示例:

    public class SermonsFragment extends ListFragment {
      public void update(String[] sermonsList){
        listAdapter.addAll(sermonsList);
        listAdapter.notifyDataSetChanged();
      }
    }
    

答案 3 :(得分:1)

这可能是主题,但我刚使用handshakevolley网址遇到了类似的https错误。请查看here以获取更多信息。

简单检查您的连接是否被拒绝是信任所有SSL证书。 (这应该仅用于测试!! )。

在第一次通话之前运行此功能(f.e.extend Application并在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) {
    }
}

}

答案 4 :(得分:0)

onResume()填充listview的方法

答案 5 :(得分:0)

如果我可以帮助你,那将是很好的,因为我也遇到过这样的问题......

你想设置包含你的asynctask提供的字符串数组的适配器。

在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;
}

我已经完成了这个arraylist但你可以通过传递数组作为参数来做到这一点。 在你的MainActivity .. 将您的asynctask数据添加到arraylist然后传递给TabsPagerAdapter类

  mAdapter = new TabsPagerAdapter(getFragmentManager());

更改为

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

现在让你在SermonsFragment课上的arraylist: -

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

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

现在您可以将arraylist设置为适配器。

祝你好运!!!

答案 6 :(得分: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 . 

希望它能帮助你:)