【问题标题】:Converting FragmentActivity to a ListFragment with listview -- null pointer exception使用 listview 将 FragmentActivity 转换为 ListFragment -- 空指针异常
【发布时间】:2014-10-24 13:56:41
【问题描述】:

我在将以前工作的 FragmentActivity 和 Customlistadapter 转换为 ListFragment 以与我的导航抽屉一起使用时遇到问题。目前,导航抽屉工作正常。

下面是GetContacts的修改代码

public class GetContacts extends android.support.v4.app.ListFragment {

// Log tag
private static final String TAG = MainActivity.class.getSimpleName();

// json URL
private static final String url = "http://url.json.php";
private ProgressDialog pDialog;
private List<Contacts> contactList = new ArrayList<Contacts>();
private ListView listView;
private ContactListAdapter adapter;


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    View rootview = inflater.inflate(R.layout.fragment_contacts, container, false);
    return rootview;

}


@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    //See the second issue about wotking with ListFragment's list.
    adapter = new ContactListAdapter(this, contactList);
    setListAdapter(adapter);

    // pDialog = new ProgressDialog(this);
    // Showing progress dialog before making http request
    // pDialog.setMessage("Loading...");
    //pDialog.show();


    // Creating volley request obj
    JsonArrayRequest contactReq = new JsonArrayRequest(url,
            new Response.Listener<JSONArray>() {
                @Override
                public void onResponse(JSONArray response) {
                    Log.d(TAG, response.toString());
                    hidePDialog();

                    // Parsing json
                    for (int i = 0; i < response.length(); i++) {
                        try {

                            JSONObject obj = response.getJSONObject(i);
                            Contacts contacts = new Contacts();
                            contacts.setDivision(obj.getString("Division"));
                            contacts.setName(obj.getString("name"));
                            contacts.setNumber(obj.getString("number"));


                            // adding contacts to contacts array
                            contactList.add(contacts);

                        } catch (JSONException e) {
                            e.printStackTrace();
                        }

                    }

                    // notifying list adapter about data changes
                    // so that it renders the list view with updated data
                    adapter.notifyDataSetChanged();
                }
            }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            VolleyLog.d(TAG, "Error: " + error.getMessage());
            hidePDialog();

        }
    });

    // Adding request to request queue
    AppController.getInstance().addToRequestQueue(contactReq);
    //end volley code paste
}


public void onDestroy() {
    super.onDestroy();
    hidePDialog();
}

private void hidePDialog() {
    if (pDialog != null) {
        pDialog.dismiss();
        pDialog = null;
    }
}

}

另外,我修改了 ContactListAdapter 的代码。目前我收到以下编译错误。 “错误:(49, 56) 错误:找不到符号方法 getContext()”。我检查了这个SO question 并试图了解是什么导致了这个问题,但没有运气。

public class ContactListAdapter extends BaseAdapter {
private Activity activity;
private Fragment fragment;
private LayoutInflater inflater;
private List<Contacts> contactItems;


public ContactListAdapter(Fragment fragment, List<Contacts> contactItems) {
    this.fragment = fragment;
    this.contactItems = contactItems;
}

@Override
public int getCount() {
    return contactItems.size();
}

@Override
public Object getItem(int location) {
    return contactItems.get(location);
}

@Override
public long getItemId(int position) {
    return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {


    if (convertView == null){
        LayoutInflater inflater = (LayoutInflater)    getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.list_rowcontacts, null);
    }

    TextView division = (TextView) convertView.findViewById(R.id.division);
    TextView name = (TextView) convertView.findViewById(R.id.name);
    TextView number = (TextView) convertView.findViewById(R.id.number);


    // getting contact data for the row
    Contacts m = contactItems.get(position);


    // Division
    division.setText(m.getDivision());

    // Name
    name.setText("Name: " + m.getName());

    // number
    number.setText("Number: " + m.getNumber());


    return convertView;
}

}

【问题讨论】:

    标签: android android-fragments navigation android-listfragment


    【解决方案1】:

    主要问题是 FragmentActivity 不是 Fragment,是 Activity(请参阅参考资料 here)。您不能像在 selectItem() 中那样将其实例化为 Fragment。

    如果您希望将联系人加载到您的主要活动(带有导航抽屉的活动)中,那么 GetContacts 应该继承自 Fragment 而不是 FragmentActivity。

    更新

    您的代码存在三个问题。

    空指针异常(将适配器设置在正确的位置)

    首先,您应该将与设置适配器相关的所有内容移至在 Fragment 附加到其 Activity 后调用的方法(有关 Fragment 生命周期的更多信息here)。在 onCreateView 中,Activity 尚未附加 Fragment,因此适配器的 Context 将不可用(参见第三期)。覆盖onAttach() 并将其放在那里:

    public class GetContacts extends ListFragment {
    
    // Log tag
    private static final String TAG = MainActivity.class.getSimpleName();
    
    // json URL
    private static final String url = "http://url.json.php";
    private ProgressDialog pDialog;
    private List<Contacts> contactList = new ArrayList<Contacts>();
    private ListView listView;
    public ContactListAdapter adapter;
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_contacts, container, false);
        return rootView;
    }
    
    @Override
    public void onAttach(Activity activity) {
        //See the third issue about the Context passed to the adapter.
        adapter = new ContactListAdapter(activity, contactList);
        //See the second issue about working with ListFragment's list.
        setListAdapter(adapter);
    
       // pDialog = new ProgressDialog(this);
    
        // Showing progress dialog before making http request
      // pDialog.setMessage("Loading...");
       //pDialog.show();
    
    
        // Creating volley request obj
        JsonArrayRequest contactReq = new JsonArrayRequest(url,
                new Response.Listener<JSONArray>() {
                    @Override
                    public void onResponse(JSONArray response) {
                        Log.d(TAG, response.toString());
                        hidePDialog();
    
                        // Parsing json
                        for (int i = 0; i < response.length(); i++) {
                            try {
    
                                JSONObject obj = response.getJSONObject(i);
                                Contacts contacts = new Contacts();
                                contacts.setDivision(obj.getString("Division"));
                                contacts.setName(obj.getString("name"));
                                contacts.setNumber(obj.getString("number"));
    
    
                                // adding contacts to contacts array
                                contactList.add(contacts);
    
                            } catch (JSONException e) {
                                e.printStackTrace();
                            }
    
                        }
    
                        // notifying list adapter about data changes
                        // so that it renders the list view with updated data
                        adapter.notifyDataSetChanged();
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                VolleyLog.d(TAG, "Error: " + error.getMessage());
                hidePDialog();
    
            }
        });
    
        // Adding request to request queue
        AppController.getInstance().addToRequestQueue(mcontactReq);
        //end volley code paste
    }
    
    
    public void onDestroy() {
        super.onDestroy();
        hidePDialog();
    }
    
    private void hidePDialog() {
        if (pDialog != null) {
            pDialog.dismiss();
            pDialog = null;
        }
    }
    }
    

    空指针异常(使用 ListFragment 的列表

    ListFragments 提供了一些方法,如 setListAdapter() 来使用内部 ListView,而不必先使用 findViewById() 获取它,但它们使用它们的“默认”列表显示。为了识别这个 ListView,他们在布局中查找 ID 为 @id/android:list 的 ListView。您正在为您的 ListView 使用自定义 ID,因此 setListAdapter() 无法找到默认 ListView(有关 ListFragment here 的更多信息)。您有三种可能的解决方案:

    首先: 如果您的布局不包含多个 ListView,则您不必使用自定义布局。不要覆盖 onCreateView,删除所有关于膨胀自定义视图的内容。

    第二: 在 xml 布局文件中将 ListView 的 id 更改为 @id/android:list。

    第三: 维护您的自定义布局和 id,但始终使用 ListView 首先使用 findViewById() 获取它,而不是使用 ListFragment 处理列表的方法(例如当 GetContacts 是 FragmentActivity 时)。所以这个:

    listView  = (ListView) rootView.findViewById(android.R.id.list);
    adapter = new ContactListAdapter(this, contactList);
    setListAdapter(adapter);
    

    会变成这样:

    listView  = (ListView) getView().findViewById(android.R.id.list);
    adapter = new ContactListAdapter(this, contactList);
    listView.setListAdapter(adapter);
    

    前两个允许您使用默认列表。我不建议您选择第三个,因为 ListFragment 旨在帮助您更快地处理列表,并且您不会使用它们的辅助方法,因此像任何其他 Fragment 一样使用 ListFragment。

    附:我在使用第二种解决方案之前发布的 GetContacts 的代码。

    适配器中的LayoutInflater

    使用 BaseAdapter,您必须保留对适配器工作的上下文的引用。由于 Fragment 不是 Context,因此 Fragment 必须使用它们附加到的 Activity 作为 Context。查看我刚刚更新的 GetContact 代码中的这一行(在 GetContact 的 onAttached() 中):

    adapter = new ContactListAdapter(activity, contactList);
    

    然后在适配器中使用它作为 Context 来获取 LayoutInflater:

    public class ContactListAdapter extends BaseAdapter {
    //There is no need for this two
    //private Activity activity;
    //private Fragment fragment;
    
    //We use this instead
    private Context context
    
    private LayoutInflater inflater;
    private List<Contacts> contactItems;
    
    //Now the constructor receives a Context
    public ContactListAdapter(Context context, List<Contacts> contactItems) {
        this.context = context;
        this.contactItems = contactItems;
    }
    
    //Every other method remains the same
    ...
    
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        //Now we use context to get the LayoutInflater
        if (convertView == null){
            LayoutInflater inflater = (LayoutInflater)    context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.list_rowcontacts, null);
        }
    
        //The rest of the code is the same
        ...
    }
    }
    

    编辑

    为“在正确的位置设置适配器”添加了更改的代码。

    更改了使用 BaseAdapter 的第三个问题(我很困惑)。

    【讨论】:

    • 感谢您的帮助。当我更改该类并扩展 Fragment 而不是 FragmentActivity 时,列表视图会出现一些编译错误。是否有关于将活动转换为片段的一般指南?
    • @bears22 我想我应该扩展我的答案来解决这些问题。您可以看到关于同一主题的 SO question here。但实际上我建议您从头开始了解片段及其生命周期,这样您就会自动了解您必须在代码中更改哪些内容(教程herehere)。
    • @bears22 同时,如果您想尽快解决这些问题,请更新您的问题,我会尽我所能提供帮助。
    • 感谢@Alberto Casares 的帮助。我已更新问题以反映我当前的问题。如果您能提供帮助,我将不胜感激!我很欣赏这些链接,但仍然无法修复代码。我正在阅读一些关于 android 编程书籍的介绍,并尝试通过一些现实生活中的例子来帮助扩展我的知识。任何帮助表示赞赏!
    • 感谢您的帮助,感谢您对细节的关注以及您解释不同选项的方式!我修改了我的代码并更新了我的问题,因为我在 ContactListAdapter 中的 getContext() 方法出现错误。我在解决上一期问题时将您的问题标记为正确!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多