NullPointerExeption with AppCompat BottomSheets

2019-01-26 05:06发布

LinearLayout bottomSheetViewgroup = (LinearLayout) findViewById(R.id.bottomSheet);

bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetViewgroup);

bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); //this line

I have this code within my activity's onCreate() method and I'm getting the below NPE exception when the last line is executed:

Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object java.lang.ref.WeakReference.get()' on a null object reference at android.support.design.widget.BottomSheetBehavior.setState(BottomSheetBehavior.java:440)

5条回答
霸刀☆藐视天下
2楼-- · 2019-01-26 05:17

The issue with your code is you are trying to call the setState method directly inside onCreate. This is will throw a nullPointer because the WeakReference is not initialized yet. It will get initialized when the Coordinator layout is about to lay its child view.

onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection)

Called when the parent CoordinatorLayout is about the lay out the given child view.

So the best approach is set the peek height to 0 and show/hide inside the onItemClick listener.

I have answered this question here: https://stackoverflow.com/a/36236743/1314796

查看更多
倾城 Initia
3楼-- · 2019-01-26 05:19

You can also consider listening to the global layout event, this way you'll be sure that the bottomsheet has been laid out when setting the collapsed state.

final View bottomSheet = findViewById(R.id.bottom_sheet);
bottomSheet.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            bottomSheet.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            bottomSheetBehavior = BottomSheetBehavior.from(bottomSheet);
            bottomSheetBehavior.setPeekHeight(300);
            bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
        }
    });
查看更多
一夜七次
4楼-- · 2019-01-26 05:27
public class ExpandedBottomSheetBehavior<V extends View> extends
    android.support.design.widget.BottomSheetBehavior<V> {

  public ExpandedBottomSheetBehavior(Context context, AttributeSet attrs) {
    super(context, attrs);
}

  @Override
  public boolean onLayoutChild(final CoordinatorLayout parent, final V child, final int layoutDirection) {
    return super.onLayoutChild(parent, child, layoutDirection);
  }

  @Override
  public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
    try {
      return super.onInterceptTouchEvent(parent, child, event);
    } catch (NullPointerException ignored) {
      return false;
    }
  }
}
查看更多
女痞
5楼-- · 2019-01-26 05:40

I've found a solution but I still don't known why this happen. The solution is put this last line to user call directly after the activity is running. Ex: in a contextMenu callback or in any OnClickListener.

查看更多
男人必须洒脱
6楼-- · 2019-01-26 05:41

While Sanf0rds answer is correct, it doesn't allow the ability to define the BottomSheet as expanded by default. The issue is caused by the WeakReference not being set until the last line of onLayoutChild.

The solution is to provide our own class which extends BottomSheetBehavior, but setting the state inside an overridden onLayoutChild. The code is provided below.

uk/ac/qub/quibe/misc/ExpandedBottomSheetBehavior.java

package uk.ac.qub.quibe.misc;

import android.content.Context;
import android.support.design.widget.CoordinatorLayout;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by mcp on 15/03/16.
 */
public class ExpandedBottomSheetBehavior<V extends View> extends android.support.design.widget.BottomSheetBehavior<V> {

    public ExpandedBottomSheetBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean onLayoutChild(final CoordinatorLayout parent, final V child, final int layoutDirection) {
        SavedState dummySavedState = new SavedState(super.onSaveInstanceState(parent, child), STATE_EXPANDED);
        super.onRestoreInstanceState(parent, child, dummySavedState);
        return super.onLayoutChild(parent, child, layoutDirection);
        /*
            Unfortunately its not good enough to just call setState(STATE_EXPANDED); after super.onLayoutChild
            The reason is that an animation plays after calling setState. This can cause some graphical issues with other layouts
            Instead we need to use setInternalState, however this is a private method.
            The trick is to utilise onRestoreInstance to call setInternalState immediately and indirectly
         */
    }

}

In the layout file reference reference your new custom behavior.

Change

app:layout_behavior="android.support.design.widget.BottomSheetBehavior"

To

app:layout_behavior="uk.ac.qub.quibe.misc.ExpandedBottomSheetBehavior"
查看更多
登录 后发表回答