Implementing QuickActions UI Pattern

598 views Asked by At

I've been attempting to implement the QuickActions UI Pattern in my application. I've found several libraries that add QuickAction, but all of them have one thing in common - the QuickAction popup is always implemented inside an activity.

As part of my application, I have a swipable view with 2 ListFragments inside of it, which allows me to go between the two on the fly. However, whenever I attempt to implement this UI pattern, it results in NullPointerExceptions when QuickAction.show is called. I have so far tried NewQuickActions, as well as Qberticus' LikeQuickActions. Both have the exact same issue. My question is, is there a fundamental limitation that prevents the PopupWindow from being anchored to a ListItem inside a fragment? Or am I just doing something incorrectly. For reference, here is the code I used.

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

    View v = inflater.inflate(R.layout.fridge_food_tab, container,false);
    listView = (ListView) v.findViewById(R.id.fridge_food_list);

    getActivity().getSupportLoaderManager().initLoader(FRIDGE_FOOD_LOADER, null, this);

    adapter = new FridgeCursorAdapter(getActivity().getApplicationContext(), 
                  R.layout.fridge_list_layout, null, uiBindFrom, uiBindTo, 
                  CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);

    final QuickAction qa = new QuickAction(getActivity());

    ActionItem item = new ActionItem(getResources()
                          .getDrawable(android.R.drawable.ic_input_add));

    qa.addActionItem(item); //<-- Line 51

    listView.setOnItemClickListener(new OnItemClickListener() {

        public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
            qa.show(arg1);
        }
    });

listView.setAdapter(adapter);
return v;
}

And here is the exception that occurs

10-06 18:40:53.617: E/AndroidRuntime(17832): FATAL EXCEPTION: main
10-06 18:40:53.617: E/AndroidRuntime(17832): java.lang.NullPointerException
10-06 18:40:53.617: E/AndroidRuntime(17832): at net.londatiga.android.QuickAction.addActionItem(QuickAction.java:151)
10-06 18:40:53.617: E/AndroidRuntime(17832): at app.le.sauce.fridge.FoodTabFragment.onCreateView(FoodTabFragment.java:51)
10-06 18:40:53.617: E/AndroidRuntime(17832): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:871)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1083)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:635)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1431)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.support.v4.app.FragmentManagerImpl.executePendingTransactions(FragmentManager.java:431)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.support.v4.app.FragmentStatePagerAdapter.finishUpdate(FragmentStatePagerAdapter.java:160)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.support.v4.view.ViewPager.populate(ViewPager.java:895)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.support.v4.view.ViewPager.populate(ViewPager.java:772)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1234)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.view.View.measure(View.java:15172)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4814)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1390)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.widget.LinearLayout.measureVertical(LinearLayout.java:681)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.widget.LinearLayout.onMeasure(LinearLayout.java:574)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.view.View.measure(View.java:15172)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4814)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.view.View.measure(View.java:15172)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.widget.LinearLayout.measureVertical(LinearLayout.java:833)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.widget.LinearLayout.onMeasure(LinearLayout.java:574)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.view.View.measure(View.java:15172)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4814)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2150)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.view.View.measure(View.java:15172)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:1848)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1100)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1273)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:998)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4212)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.view.Choreographer.doCallbacks(Choreographer.java:555)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.view.Choreographer.doFrame(Choreographer.java:525)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.os.Handler.handleCallback(Handler.java:615)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.os.Handler.dispatchMessage(Handler.java:92)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.os.Looper.loop(Looper.java:137)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at android.app.ActivityThread.main(ActivityThread.java:4931)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at java.lang.reflect.Method.invokeNative(Native Method)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at java.lang.reflect.Method.invoke(Method.java:511)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:558)
10-06 18:40:53.617: E/AndroidRuntime(17832):    at dalvik.system.NativeStart.main(Native Method)
1

There are 1 answers

1
Cat On BEST ANSWER

Unfortunately, this may not be the answer you're looking for, as it's more of an explanation than a solution.

On line 51, you call qa.addActionItem(item);. This leads to addActionItem(ActionItem action) of QuickAction.java. So, I did some looking; your crash occurs on line 151 of that file, namely:

public void addActionItem(ActionItem action) {
    mActionItemList.add(action);

    String title    = action.getTitle();
    Drawable icon   = action.getIcon();

    View container  = (View) inflater.inflate(R.layout.action_item, null);

    ImageView img   = (ImageView) container.findViewById(R.id.iv_icon);
    TextView text   = (TextView) container.findViewById(R.id.tv_title);

    if (icon != null) { 
        img.setImageDrawable(icon); // This line here.
    } else {
        img.setVisibility(View.GONE);
    }

    // ...

}

Now, obviously, on this line, icon cannot be null. So, it would seem img is. From there, we can conclude that container contains no element with ID iv_icon. However, when I look in the action_item XML file, it does indeed exist.

So, I have a few suggestions:

  1. Verify that there is not a second action_item.xml file that it may be reading (in your library, its, or any other).
  2. Clean the QuickAction project, let it build, then clean yours, let it build, then run it.
  3. Play around with the library code. (Test nulls, try to figure out what's being inflated and what isn't.)
  4. If all else fails, contact the library creator. His email can be found on the library homepage.