0

maybe I'm tired but I miss something in what's happening in my code. I have an activity with fragments.

My activity HomeActivity calls a webservice to fill a List (my business object), it does this onResume().

My fragment WalletFragment has a mehtod refreshCardListView() that uses an adapter to display the list. It does this onResume().

What I was doing and that was working until now was:

  • HomeActivity.onCreate() displays WalletFragment, WalletFragment.onResume() calls this.refreshCardList() but at this instant cardList is null so the adapter displays nothing
  • HomeActivity.onResume() calls webservice, which on success calls walletFragment.refreshCardList(), this time cardList has been filled by the webservice so tid displays the list correctly.

Now I thout it was stupid to call refreshList twice so I tried to move the displayWalletFragment from HomeActivity.onCreate() to the success callback of the webservice and remove the call to refreshCardList from HomeActivity and leaving it only in WalletFragment.onResume(), so it'd go like this:

  • HomeActivity.onResume() calls webservice, which on success displays WalletFragment, WalletFragment.onResume() calls this.refreshCardList(), cardList having been filled by the webservice.

However at this point my adapter crashes, because parent.getWidth() == 0 (and I needed parent width to display card images).

I don't understad why, by moving this bit of code, the parent view would now not be initialized at this point, do you have an idea?

So this is the original code I used that is working, the only things I changed are removing displayWalletFragment(false) from onCreate and moving it in the success return of refreshCardList and removing walletFragment.refreshCardListView() from there.

HomeActivity.java

public class HomeActivity extends Activity {
    CardService cardService = new CardService(this);
    UserService userService = new UserService(this);

    User user = null;
    Set<WegaCard> userCards = null;

    ProfileFragment profileFragment;
    WalletFragment walletFragment;

    /*
     * Saving card images for performance concerns.
     */
    Map<String, Bitmap> cardsImageBitmaps = new HashMap<>();

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

        initializeComponents();
        styleCompnents();

        displayWalletFragment(false);
    }

    @Override
    protected void onResume() {
        Log.d(this, "fired HomeActivity.onResume()");
        super.onResume();
        user = null;
        userCards = null;
        refreshUser(
                new SingleResultCallback<User>() {
                    @Override
                    public void onResponse(User cards) {
                        Log.d(this, "user: " + user.toString());
                        HomeActivity.this.user = user;
                        profileFragment.refreshUser();
                    }
                },
                new ErrorCallback() {
                    @Override
                    public void onResponse(Throwable error) {
                        Log.e(this, "error: " + error.toString());
                    }
                });

        refreshCardList(
                new SingleResultCallback<Set<WegaCard>>() {
                    @Override
                    public void onResponse(Set<WegaCard> cards) {
                        Log.d(this, "cards: " + cards.toString());
                        userCards = cards;
                        walletFragment.refreshCardListView();
                    }
                },
                new ErrorCallback() {
                    @Override
                    public void onResponse(Throwable error) {
                        Log.e(this, "error: " + error.toString());
                    }
                });
    }

    private void refreshCardList(SingleResultCallback<Set<WegaCard>> successCallback, ErrorCallback errorCallback) {
        Log.d(this, "fired HomeActivity.refreshCardList()");
        // First empty list...
        userCards = new LinkedHashSet<>();

        // ...then fill it back
        cardService.getCards(false, true, successCallback, errorCallback);
    }

    private void refreshUser(SingleResultCallback<User> successCallback, ErrorCallback errorCallback) {
        Log.d(this, "fired HomeActivity.refreshUser()");
        // First empty user...
        userCards = new LinkedHashSet<>();

        // ...then recreate it
        userService.getUser(successCallback, errorCallback);
    }

    public void displayWalletFragment(boolean addToBackStack) {
        displayFragment(WalletFragment.newInstance(), addToBackStack);
    }

    public void displayCardFragment(String cardNumber, boolean addToBackStack) {
        displayFragment(CardFragment.newInstance(cardNumber), addToBackStack);
    }

    public void displayProfileFragment(boolean addToBackStack) {
        displayFragment(ProfileFragment.newInstance(), addToBackStack);
    }

    private void displayFragment(HomeFragment fragment, boolean addToBackStack) {
        FragmentManager fragmentManager = getFragmentManager();
        FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
        fragmentTransaction.replace(R.id.home_fragment, fragment);
        if (addToBackStack) {
            fragmentTransaction.addToBackStack(null);
        }
        fragmentTransaction.commit();
    }

    public void profileEdit(View view) {
        ActivityLauncher.getInstance().startProfileActivityForResult(this, ProfileActivity.ProfileEditMode.EDIT_PROFILE, user);
    }

    public Map<String, Bitmap> getCardsImageBitmaps() {
        return cardsImageBitmaps;
    }
}

WalletFragment.java

public class WalletFragment extends HomeFragment {
    List<WegaCard> cardList;
    ListView cardListView;
    WegaCardAdapter adapter;

    public static WalletFragment newInstance() {
        WalletFragment fragment = new WalletFragment();
        // Bundle args = new Bundle();
        // fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        activity.walletFragment = this;
        cardListView = (ListView) getView().findViewById(R.id.fragment_home_wallet_list);
        cardListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                activity.displayCardFragment(cardList.get(position).getCardNumber(), true);
            }
        });
    }

    @Override
    public void onResume() {
        Log.d(this, "fired WalletFragment.onResume()");
        super.onResume();
        refreshCardListView();
    }

    void refreshCardListView() {
        Log.d(this, "fired WalletFragment.refreshCardListView()");
        // First empty list...
        cardList = new ArrayList<>();
        cardList.addAll(activity.userCards);
        adapter = new WegaCardAdapter(activity, R.layout.adapter_card_item, cardList);
        cardListView.setAdapter(adapter);

        getView().findViewById(R.id.fragment_home_wallet_empty).setVisibility(cardList.isEmpty() ? View.VISIBLE : View.GONE);
    }
}

WegaCardAdapter.java

public class WegaCardAdapter extends ArrayAdapter<WegaCard> {
    public WegaCardAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull List<WegaCard> objects) {
        super(context, resource, objects);
    }

    /**
     * Data that should appear in the view should be added here
     */
    private class ViewHolder {
        ImageView imageView;
    }

    /*
     * Note: using layout_height="match_parent" on the ListView helps Android calculate faster
     * the elements that are displayed on screen, and prevent the array adapter for calling
     * getView() too many times, thus improving the display speed
     */
    public View getView(int position, View convertView, ViewGroup parent) {
        Log.d(this, "fired WegaCardAdapter.getView(" + position + ")");
        ViewHolder holder = null;
        WegaCard card = getItem(position);

        LayoutInflater mInflater = (LayoutInflater) getContext().getSystemService(android.app.Activity.LAYOUT_INFLATER_SERVICE);
        if (convertView == null) {
            convertView = mInflater.inflate(R.layout.adapter_card_item, null);
            holder = new ViewHolder();
            holder.imageView = (ImageView) convertView.findViewById(R.id.adapter_card_image);
            convertView.setTag(holder);
        }
        else {
            holder = (ViewHolder) convertView.getTag();
        }

        Bitmap cardImage = null;
        if (getContext() instanceof HomeActivity) {
            Log.d(this, "In home activity \\o/");
            Map<String, Bitmap> savedBitmaps = ((HomeActivity) getContext()).getCardsImageBitmaps();
            if (savedBitmaps.containsKey(card.getCardNumber())) {
                Log.d(this, "Found saved image, using it ^^");
                cardImage = savedBitmaps.get(card.getCardNumber());
            }
            else {
                Log.d(this, "Didn't found saved image éè building and saving it for later!");
                cardImage = card.getRoundedScaledBitmap(getContext(), parent.getWidth());
                savedBitmaps.put(card.getCardNumber(), cardImage);
            }
        }
        else {
            Log.d(this, "Not in home activity?");
            cardImage = card.getRoundedScaledBitmap(getContext(), parent.getWidth());
        }

        holder.imageView.setImageBitmap(cardImage);
        return convertView;
    }
}

activity_home.xml

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

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true">

        <!-- Fragment will be displayed here -->
        <LinearLayout
            android:id="@+id/home_fragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
        </LinearLayout>
    </ScrollView>
</RelativeLayout>

fragment_home_wallet.xml

<?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">

    <LinearLayout
        android:id="@+id/fragment_home_wallet_root"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:gravity="center">

        <LinearLayout
            android:id="@+id/fragment_home_wallet_empty"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:gravity="center"
            android:visibility="gone">

            <ImageView
                android:src="@drawable/icon_card_white"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />

            <com.gaetanl.aspa.ui.component.StaticTextView
                android:id="@+id/fragment_home_wallet_empty_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/defaultSpacing"
                android:text="@string/dashboard_text_nocard" />

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/defaultSpacing"
                android:gravity="center_vertical"
                android:orientation="horizontal">

                <com.gaetanl.aspa.ui.component.StaticTextView
                    android:id="@+id/fragment_home_wallet_empty_text1"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@string/dashboard_text_tap1" />

                <ImageView
                    android:src="@drawable/icon_plus_white"
                    android:layout_width="@dimen/inlineIcon"
                    android:layout_height="@dimen/inlineIcon"
                    android:layout_marginLeft="@dimen/wordSpacing"
                    android:layout_marginRight="@dimen/wordSpacing" />

                <com.gaetanl.aspa.ui.component.StaticTextView
                    android:id="@+id/fragment_home_wallet_empty_text2"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@string/dashboard_text_tap2" />
            </LinearLayout>
        </LinearLayout>

        <!-- Note: using layout_height="match_parent" on the ListView helps Android calculate faster
         the elements that are displayed on screen, and prevent the array adapter for calling
         getView() too many times, thus improving the display speed -->
        <ListView
            android:id="@+id/fragment_home_wallet_list"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        </ListView>
    </LinearLayout>
</LinearLayout>
Gaetan L.
  • 649
  • 6
  • 20
  • Probably because the method is called before your view is fully initialized, have a look here: http://stackoverflow.com/questions/3591784/getwidth-and-getheight-of-view-returns-0 – Denny May 05 '17 at 08:34
  • Yes I understand that but the thing is I didn't move the method call in my fragment, refreshCardList() (which calls my adapter) is still in WalletFragment.onResume(), which is supposed to be called after the view is created if I understand fragment lifecycle correctly. That's why I don't understand why it worked before and not anymore. – Gaetan L. May 05 '17 at 08:49

2 Answers2

1

I found the solution here: When should I get Width View in Fragment.

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    view.post(new Runnable() {
        @Override
        public void run() {
            // do operations or methods involved
            // View.getWidth(); or View.getHeight();
            // here
        }
    });
}
Community
  • 1
  • 1
Gaetan L.
  • 649
  • 6
  • 20
0

You can try calling just displayWalletFragment(false); on your success callback and move walletFragment.refreshCardListView(); to the onViewCreated() of your WallentFragment.

This will ensure the right sequence of your Fragment being created and the list being populated.

Kamran Ahmed
  • 7,661
  • 4
  • 30
  • 55