Android ListView 未 displayed/refreshing 且 getView 无法正常工作

Android ListView not being displayed/refreshing and getView not working properly

Objective: 我有一个 activity 并且在两个片段中。 ideia是将两个fragments进行通信,选择一个category并更新其item list.On activity load,一个category是默认settle的。

问题: 在 activity 加载时,ItemsFragment 没有 display/update itemsLisView。 ItemAdapter 的 getView 方法也只被调用了两次,即使列表有五个项目。

事实:

  1. 项目列表在传递给适配器之前已正确完成
  2. 断点代码,不确定为什么或是否重要,ItemsFragment rendered/called 在 CategoriesFragments 之前。我发现这很奇怪,因为 CategoriesFragments 位于 ItemsFragment 之上。这就是我写if (getArguments() != null)的原因。在我得到空指针异常之前。

研究:关于这两个主题有很多问题。

  1. Android ListView not refreshing after notifyDataSetChanged with data as Map
  2. ListFragment Not Rendering and getView() in Adapter Not Being Called
  3. Android getView() not being called properly?
  4. BaseAdapter notifyDatasetChanged() called but getView() is never called

这些是我经历过的一些 link 个例子(数以千计),试图为我的案例找到解决方案。不幸的是 none 他们成功了。

我已经被这个问题困扰将近 5 天了,我想我在这里遗漏了一些 Android 概念。这与 Java 编程无关,至少我希望如此。

问题:

  1. 为什么 ItemsFragment 在 activity 加载时不显示项目列表? (这可能解决了更新问题)
  2. 为什么 ItemAdapter 只调用 getView 两次,即使 Items List 正确地给他更多的项目?

如需任何进一步的信息,请不要介意询问。 在此先感谢您的帮助。


编辑 - MCVE:

activity_triple_list:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    tools:context=".controller.activities.Lists">

    <include layout="@layout/content_triple_list" />
</android.support.design.widget.CoordinatorLayout>

content_triple_list:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v4.view.ViewPager
        android:id="@+id/listsPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v4.view.PagerTabStrip
            android:id="@+id/pager_header"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="top"
            android:paddingBottom="4dp"
            android:paddingTop="4dp" />

    </android.support.v4.view.ViewPager>
</LinearLayout>

fragment_categories:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/categoriesFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TableLayout
        android:id="@+id/categoriesTable"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:stretchColumns="4">

        <TableRow
            android:layout_width="fill_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="5dp"
            android:gravity="center_horizontal">

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_column="0"
                android:layout_marginLeft="13dp"
                android:layout_marginRight="13dp"
                android:orientation="vertical">

                <TextView
                    android:id="@+id/category1"
                    android:layout_width="fill_parent"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="5dp"
                    android:text="CATEGORY 1"
                    android:textAlignment="center"
                    android:textAppearance="?android:attr/textAppearanceSmall" />
            </LinearLayout>

        </TableRow>

    </TableLayout>
</LinearLayout>

fragment_items:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@id/itemsFragment"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">

    <ListView
        android:id="@+id/itemsListView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:divider="@null" />

</LinearLayout>

content_row_choose_item:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/custom_row"
    android:orientation="vertical"
    android:paddingBottom="5dp"
    android:paddingLeft="15dp"
    android:paddingTop="5dp">

    <TextView
        android:id="@+id/itemName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="TEST"
        android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>

custom_row:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:background="#2fff00">    
    <item android:drawable="@color/colorPrimary" android:state_pressed="true" /> <!-- pressed -->       
</selector>

ApplicationUtils:

public final class ApplicationUtils extends Application {

    private static Context context;

    public void onCreate() {
        super.onCreate();
        ApplicationUtils.context = getApplicationContext();
    }

    public static Context getContext() {
        return ApplicationUtils.context;
    }

    public static String getJavaPackageName() {
        return ApplicationUtils.context.getPackageName();
    }

    public static Resources getAppResources() {
        return getContext().getResources();
    }

    public static String getResouceName(Integer id) {
        return getAppResources().getResourceName(id);
    }

    public static String getResourceString(Integer id) {
        return getAppResources().getString(id);
    }

    public static int getResourceId(String variableName, String resourceName, String packageName) {
        try {
            return getContext().getResources().getIdentifier(variableName, resourceName, packageName);
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }
}

列表:

public class Lists extends AppCompatActivity implements CategoriesFragment.OnHeadlineSelectedListener {

    private String listName;

    public void onItemSelected(String currentCategory) {

        ItemsFragment itemsCallBackFragment = (ItemsFragment) getSupportFragmentManager().findFragmentById(R.id.itemsFragment);

        if (itemsCallBackFragment != null) {
            itemsCallBackFragment.updateArticleView(currentCategory);
        } else {
            ItemsFragment itemFragment = new ItemsFragment();
            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

            transaction.replace(R.id.itemsFragment, itemFragment);
            transaction.addToBackStack(null);

            transaction.commit();
        }
    }

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

        setListsAdapter();
    }

    private void setListsAdapter() {
        ViewPager tripleListViewPager = (ViewPager) findViewById(R.id.listsPager);
        FragmentPagerAdapter tripleListFragmentAdapt = new ListsPagerAdapter(getSupportFragmentManager(), "LIST NAME", ApplicationUtils.getContext());
        tripleListViewPager.setAdapter(tripleListFragmentAdapt);
    }
}

ListsPagerAdapter:

public class ListsPagerAdapter extends FragmentPagerAdapter {

    private static int NUM_TABS = 1;
    private String listName;
    private Context listsContext;

    public ListsPagerAdapter(FragmentManager fm, String newListName, Context listsContext) {
        super(fm);
        setListName(newListName);
        setListsContext(listsContext);
    }

    // Returns total number of pages
    @Override
    public int getCount() {
        return NUM_TABS;
    }

    // Returns the fragment to display for that page
    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0:
                return NewItemPagerFragment.newInstance();
            default:
                return null;
        }
    }

    // Returns the page title for the top indicator
    @Override
    public CharSequence getPageTitle(int position) {
        switch (position) {
            case 0:
                return listsContext.getResources().getString(R.string.lists_tab_newItem, getListName());
            default:
                return "$value";
        }
    }

    public String getListName() {
        return listName;
    }

    public void setListName(String listName) {
        this.listName = listName;
    }

    public Context getListsContext() {
        return listsContext;
    }

    public void setListsContext(Context listsContext) {
        this.listsContext = listsContext;
    }
}

类别片段:

public class CategoriesFragment extends Fragment {


    private List<Integer> categoryIds = new ArrayList<>();
    private String currentCategory;
    private View categoriesView;

    OnHeadlineSelectedListener itemCallback;

    public interface OnHeadlineSelectedListener {
        void onItemSelected(String categoryName);
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            itemCallback = (OnHeadlineSelectedListener) context;
        } catch (ClassCastException e) {
            throw new ClassCastException(context.toString()
                    + " must implement OnHeadlineSelectedListener");
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        categoriesView = inflater.inflate(R.layout.fragment_categories, container, false);
        categoriesView.setId(R.id.categoriesFragment);
        return categoriesView;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        setCategoriesListener();
        setDefaultSelectedCategory();
    }

    private void setCategoriesListener() {
        categoryIds.add(R.id.category1);

        for (Integer id : categoryIds) {
            setListener(id);
        }
    }

    private void setListener(final int categoryId) {
        final ImageView categoryImg = (ImageView) categoriesView.findViewById(categoryId);

        categoryImg.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                removePreviousSelectedCategory(categoryImg);
                selectCategory(categoryId, categoryImg);
                loadItemList();
            }
        });
    }

    private void removePreviousSelectedCategory(ImageView categoryImg) {
        for (Integer id : categoryIds) {
            final ImageView imgView = (ImageView) categoriesView.findViewById(id);

            if (imgView.getTag() != null) {
                String previousSelectedCategory = (String) imgView.getTag();
                if (StringUtils.contains(previousSelectedCategory, Application.SELECTED)) {
                    previousSelectedCategory = previousSelectedCategory.replace(Application.SELECTED, "");
                    categoryImg.setTag(previousSelectedCategory);

                    Integer previousSelectedCategoryId = ApplicationUtils.getResourceId(previousSelectedCategory, Application.RES_DRAWABLE, ApplicationUtils.getJavaPackageName());
                    imgView.setImageResource(previousSelectedCategoryId);
                }
            }
        }
    }

    private void selectCategory(int categoryId, ImageView categoryImg) {
        String newSelectedCategory = ApplicationUtils.getResouceName(categoryId) + Application.SELECTED;
        setCurrentCategory(newSelectedCategory);

        newSelectedCategory = newSelectedCategory.replace(Application.META_INFO_ID, "");
        categoryImg.setTag(newSelectedCategory);

        Integer currentCategoryId = ApplicationUtils.getResourceId(newSelectedCategory, Application.RES_DRAWABLE, ApplicationUtils.getJavaPackageName());
        categoryImg.setImageResource(currentCategoryId);
    }

    public String getCurrentCategory() {
        return currentCategory;
    }

    public void setCurrentCategory(String currentCategory) {
        this.currentCategory = currentCategory;
    }

    private void loadItemList() {
        itemCallback.onItemSelected(getCurrentCategory());
    }

    private void setDefaultSelectedCategory() {
        Integer categoryId = R.id.category1;
        final ImageView categoryImg = (ImageView) categoriesView.findViewById(categoryId);
        selectCategory(categoryId, categoryImg);
        loadItemList();
    }
}

项目片段:

public class ItemsFragment extends Fragment {

    private View itemsFragmentView;
    private ListView itemsListView;
    private ItemAdapter itemsListAdapter;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        itemsFragmentView = inflater.inflate(R.layout.fragment_items, container, false);
        itemsListView = (ListView) itemsFragmentView.findViewById(R.id.itemsListView);

        setItemsListAdapter();
        return itemsFragmentView;
    }

    public void updateArticleView(String categoryName) {
        getSelectedCategoryList();
    }

    private void getSelectedCategoryList() {
        List<String> testList = new ArrayList<>();

        testList.add("TEST ITEM");
        itemsListAdapter.update(testList);
    }

    private void setItemsListAdapter() {
        itemsListAdapter = new ItemAdapter(getActivity(), R.layout.fragment_items, new ArrayList<String>());
        itemsListView.setAdapter(itemsListAdapter);
    }
}

物品适配器:

public class ItemAdapter extends ArrayAdapter<String> {

    private List<String> items = new ArrayList<>();
    private LayoutInflater rowInflater;

    public ItemAdapter(Context context, int resource, List<String> itemsList) {
        super(context, resource, itemsList);
        this.items = itemsList;
        this.rowInflater = LayoutInflater.from(context);
    }

    private class ItemHolder {
        TextView itemNameView;
    }

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

        ItemHolder itemHolder;

        if (rowView == null) {
            rowView = rowInflater.inflate(R.layout.content_row_choose_item, parent, false);

            itemHolder = new ItemHolder();
            itemHolder.itemNameView = (TextView) rowView.findViewById(R.id.itemName);

            rowView.setTag(itemHolder);
        } else {
            itemHolder = (ItemHolder) rowView.getTag();
        }

        String itemName = getItem(position);
        if (StringUtils.isNotEmpty(itemName) && itemHolder.itemNameView != null) {
            itemHolder.itemNameView.setText(itemName);
            System.out.println("ITEM NAME ### " + itemHolder.itemNameView.getText());
        }

        return rowView;
    }

    public void update(List<String> items) {
        this.items.clear();
        this.items.addAll(items);
        this.notifyDataSetChanged();
    }


    public List<String> getItems() {
        return items;
    }
}

随机刺...

如果使用 ListFragment,则 fragment_items.xml 需要 ListViewandroid:id="@android:id/list"

您甚至试图加载具有该 ID 的 ListView。

itemsFragmentView = inflater.inflate(R.layout.fragment_items, container, false);
itemsListView = (ListView) itemsFragmentView.findViewById(android.R.id.list);

但是,你有android:id="@+id/itemsListView"

fragment_items:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@id/itemsFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ListView
        android:id="@+id/itemsListView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:divider="@null" />
</LinearLayout>

如果使用 ListFragment,则无需实现自己的 XML 布局文件。也就是说,onCreateView 不需要实施。请改用 onActivityCreated

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    itemsListView = getListView();
    itemsListAdapter = new ItemAdapter(getActivity());
    setListAdapter(itemsListAdapter);
    super.onActivityCreated(savedInstanceState);
}

现在,如果您想向适配器添加项目,我通常建议您只使用 ArrayAdapter<String>。这将为您提供一个 add(String object) 方法,该方法将添加到基础列表并通知更新。

关于getView的问题,真的不好说。我真的不知道你为什么需要 ItemAdapter,因为默认的 ArrayAdapter<String> 会起作用,假设你给它传递一个带有 android:id="@android:id/text1 的布局,例如使用 android.R.layout.simple_list_item_1 代替 R.layout.content_row_choose_item

所以,这看起来像。

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    itemsListView = getListView();
    ArrayAdapter<String> itemsListAdapter = new ArrayAdapter<String>(
          getActivity(), android.R.layout.simple_list_item_1);
    setListAdapter(itemsListAdapter);

    adapter.add("Hello");
    adapter.add("World");

    super.onActivityCreated(savedInstanceState);
}

给定的 MCVE 工作正常。经过 7 天的调试和取消注释代码,我找到了问题的答案。

我的列表已正确更新,但未呈现,因为第二个 ItemsFragemntLists Activity 加载时被初始化。第二个片段覆盖了第一个片段,这就是为什么我没有看到我的列表是 displayed/refreshed.

列表:

第一个创建的片段:

private void setListsAdapter() {
                ViewPager tripleListViewPager = (ViewPager) findViewById(R.id.listsPager);
                FragmentPagerAdapter tripleListFragmentAdapt = new ListsPagerAdapter(getSupportFragmentManager(), "LIST NAME", ApplicationUtils.getContext());
                tripleListViewPager.setAdapter(tripleListFragmentAdapt);       }

ListsPagerAdapter 中有一个调用,用 CategoriesFragmentItemsFragment.

填充我的 ViewPager (NewItemPagerFragment)

第二个创建的片段 - 叠加层:

        ItemsFragment itemFragment = new ItemsFragment();
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

        transaction.replace(R.id.itemsFragment, itemFragment);
        transaction.addToBackStack(null);

        transaction.commit();

我试图断点我的代码,之前在事实中提到过问题范围,但有时这真的很痛苦。我想到了使用 Log.d(TAG_KEY, TAG_NAME); 的想法,那时我找到了答案:我意识到 ItemsFragment 在 Activity 负载上被调用了两次。

我在我的项目中搜索 new ItemsFragment 并看到在我的 NewItemPagerFragment onCreate 中,正在调用 ItemsFragment。同样在 CategoriesFragment 之前-就像这样:

NewItemPagerFragment:

    Fragment itemsFragment = new ItemsFragment();
    FragmentManager fm1 = getChildFragmentManager();
    FragmentTransaction ft1 = fm1.beginTransaction();
    ft1.replace(R.id.itemsFragment, itemsFragment);
    ft1.commit();


    Fragment categoriesFragment = new CategoriesFragment();
    FragmentManager fm = getChildFragmentManager();
    FragmentTransaction ft = fm.beginTransaction();
    ft.replace(R.id.categoriesFragment, categoriesFragment);
    ft.commit();

这解释了 事实 2.。在 commenting/remove 对 NewItemPagerFragment 的调用之后,问题就消失了。

底线是: 确保你的列表是正确的 fulfilled/updated 像 notifyDataSetChanged 这样的cricket_007 在他的回答和我在互联网上阅读的其他数千篇文章中提到。同样,请确保您没有覆盖调用其初始化两次的片段