带有 AppCompat BottomSheets 的 NullPointerException
NullPointerException with AppCompat BottomSheets
LinearLayout bottomSheetViewgroup = (LinearLayout) findViewById(R.id.bottomSheet);
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetViewgroup);
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); //this line
我的 activity 的 onCreate()
方法中有这段代码,在执行最后一行时出现以下 NPE 异常:
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)
我找到了解决办法,但我仍然不知道为什么会这样。解决方案是将这最后一行放在 activity 是 运行 之后直接供用户调用。例如:在 contextMenu 回调或任何 OnClickListener 中。
虽然 Sanf0rds 的回答是正确的,但它不允许将 BottomSheet 定义为默认展开的能力。该问题是由于 WeakReference 直到 onLayoutChild 的最后一行才被设置。
解决方案是提供我们自己的 class,它扩展 BottomSheetBehavior,但在重写的 onLayoutChild 中设置状态。代码如下。
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
*/
}
}
在布局文件引用中引用您的新自定义行为。
改变
app:layout_behavior="android.support.design.widget.BottomSheetBehavior"
到
app:layout_behavior="uk.ac.qub.quibe.misc.ExpandedBottomSheetBehavior"
您的代码存在问题,您正试图在 onCreate 中直接调用 setState 方法。这将抛出一个 nullPointer,因为 WeakReference 尚未初始化。当 Coordinator 布局即将放置其子视图时,它将被初始化。
onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection)
Called when the parent CoordinatorLayout is about the lay out the
given child view.
所以最好的方法是在 onItemClick 侦听器中将窥视高度设置为 0 和 show/hide。
我在这里回答了这个问题:
也可以考虑监听全局布局事件,这样设置收起状态时就可以确定bottomsheet已经布局好了。
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);
}
});
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;
}
}
}
LinearLayout bottomSheetViewgroup = (LinearLayout) findViewById(R.id.bottomSheet);
bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetViewgroup);
bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); //this line
我的 activity 的 onCreate()
方法中有这段代码,在执行最后一行时出现以下 NPE 异常:
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)
我找到了解决办法,但我仍然不知道为什么会这样。解决方案是将这最后一行放在 activity 是 运行 之后直接供用户调用。例如:在 contextMenu 回调或任何 OnClickListener 中。
虽然 Sanf0rds 的回答是正确的,但它不允许将 BottomSheet 定义为默认展开的能力。该问题是由于 WeakReference 直到 onLayoutChild 的最后一行才被设置。
解决方案是提供我们自己的 class,它扩展 BottomSheetBehavior,但在重写的 onLayoutChild 中设置状态。代码如下。
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
*/
}
}
在布局文件引用中引用您的新自定义行为。
改变
app:layout_behavior="android.support.design.widget.BottomSheetBehavior"
到
app:layout_behavior="uk.ac.qub.quibe.misc.ExpandedBottomSheetBehavior"
您的代码存在问题,您正试图在 onCreate 中直接调用 setState 方法。这将抛出一个 nullPointer,因为 WeakReference 尚未初始化。当 Coordinator 布局即将放置其子视图时,它将被初始化。
onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection)
Called when the parent CoordinatorLayout is about the lay out the given child view.
所以最好的方法是在 onItemClick 侦听器中将窥视高度设置为 0 和 show/hide。
我在这里回答了这个问题:
也可以考虑监听全局布局事件,这样设置收起状态时就可以确定bottomsheet已经布局好了。
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);
}
});
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;
}
}
}