Victor Oliveira Victor Oliveira - 1 month ago 10
Android Question

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

Objective: I have one activity and within two fragments. The ideia is to communicate both fragments, selecting one category and updating its item list.On activity load, a category is settle by default.

Problem: On activity load, ItemsFragment doesn't display/update itemsLisView. Also ItemAdapter's getView Method is being called only twice, even though the list has five items.

Facts:


  1. Items List is being correctly fulfilled before being passed to adapter

  2. Breakpointing the code, not sure why or if it matters, ItemsFragment is being rendered/called before CategoriesFragments. I found this weird since CategoriesFragments is positioned above ItemsFragment. This is the reason why I wrote
    if (getArguments() != null)
    . Before I was getting Null Pointer Exception.



Research: There is a lot of questions regarding both subjects.


  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



Those are some link examples (of thousands) I went through, trying to find a solution for my case. Unfortunately none of them worked.

I've been with this problem for almost 5 days now and I guess I missing some Android concept here. It's not something related with Java Programming, or at least I hope so.

Questions:


  1. Why ItemsFragment is not displaying Items List on activity load? (This probably answers updating problem)

  2. Why ItemAdapter just calls getView only twice, even that Items List is correctly given to him with more items?



For any further needed information, please don't mind to ask.
Thanks in advance for any help.




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


Lists:

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


CategoriesFragment:

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


ItemsFragment:

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


ItemAdapter:

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

Answer

The given MCVE was working fine. After 7 days debugging and uncommenting code I found the answer to my problem.

My list was correctly being updated, but wasn't being rendered because of a second ItemsFragemnt being initialized on Lists Activity load. This second fragment was overlaying the first one and that's why I wasn't seing my list being displayed/refreshed.

Lists:

First created fragment:

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

Inside ListsPagerAdapter there was a call to populate my ViewPager (NewItemPagerFragment) with CategoriesFragment and ItemsFragment.

Second created fragment - Overlay:

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

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

        transaction.commit();

I tried to breakpoint my code, mentioned before in Facts at question scope, but sometimes this is really painful. I came with the idea to use Log.d(TAG_KEY, TAG_NAME); and that was when I found out the answer: I realized that ItemsFragment was being called twice on Activity load.

I searched for new ItemsFragment in my project and saw that inside my NewItemPagerFragment onCreate, ItemsFragment was being called. Also before CategoriesFragment- just like this:

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

which explains Facts 2.. After commenting/remove this call on NewItemPagerFragment the problem was gone.

Bottom line is: Make sure your list is correctly being fulfilled/updated with things like notifyDataSetChanged as cricket_007 mentioned in his answer and other thousand posts I read on internet. Not less, make sure you are not overlaying the fragment calling its initialization twice.