onConfigurationChanged 后在自定义视图中单击侦听器

clicklisteners in a customview after onConfigurationChanged

我有这个自定义视图作为系统警报添加到 WindowManager 中,当应用程序 运行 时,它应该作为全屏覆盖来阻止 phone 的某些部分。

这在显示叠加层时效果很好,它会阻止 phone window 全屏。现在我想支持旋转变化。

这部分也工作正常,我在 layout layout-landlayout-h600dp-land 中有 3 个布局文件,当我旋转 phone 时它会更改为正确的布局。我遇到的问题是在 onConfigurationChanged(Configuration newConfig) 被调用之后,我再次膨胀布局,所有点击监听器都消失了,所以视图中的 none 按钮对点击做出反应。当我点击按钮时,按钮的按下状态确实发生了变化,但是 none 的 onClickListeners 被触发了。在方向改变之前,所有按钮都可以工作。我一直在使用 Butterknife 来减少 onClicklisteners 和 findViewById 的样板代码,但我已经将其更改为 findViewById 并在视图上手动添加了一个 clicklistener,这没有什么区别。

现在是一些代码。该服务将视图添加到 windowmanager

public class OverlayService extends Service {

    public void showOverlay() {
        startForeground();
        mUiHandler.post(new Runnable() {
            @Override
            public void run() {
                if (!DrivingOverlay.isShowing()) {
                    if (mOverlay == null) {
                        mOverlay = new Overlay(OverlayService.this);
                        mOverlay.setTag(OVERLAY_TAG);
                        mOverlay.setId(R.id.overlay);
                    }
                addView(mOverlay);
            }
        });
    }

    private void addView(@NonNull final View view) {
        try {
            final WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
            windowManager.addView(view, Overlay.LAYOUT_PARAMS);
        } catch (IllegalStateException error) {
            Log.e(TAG, Log.getStackTraceString(error));
        }
    }
}

作为叠加层的自定义视图。

public class Overlay extends FrameLayout {
    private static boolean sIsOverlayShowing;
    public static final WindowManager.LayoutParams LAYOUT_PARAMS = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.MATCH_PARENT,
            WindowManager.LayoutParams.MATCH_PARENT,
            WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
            WindowManager.LayoutParams.FLAG_FULLSCREEN
                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
                    | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
            PixelFormat.TRANSLUCENT);

    public Overlay(Context context) {
        super(context);
        init(context);
    }

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

    public Overlay(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public Overlay(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
    }

    protected void init(Context context) {
        inflate(context, R.layout.driving_overlay, this);
        if (!isInEditMode()) {
            ButterKnife.bind(this);
        }
    }

    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        Log.d(TAG, "onConfigurationChanged");
        init(getContext());
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        setIsOverlayShowing(true);
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        setIsOverlayShowing(false);
    }

    @Override
    public void onViewAttachedToWindow(View v) {
        setIsOverlayShowing(true);
    }

    @Override
    public void onViewDetachedFromWindow(View v) {
        setIsOverlayShowing(false);
    }

    public static boolean isShowing() {
        return sIsOverlayShowing;
    }

    public static void setIsOverlayShowing(boolean isOverlayShowing) {
        sIsOverlayShowing = isOverlayShowing;
    }
}
Assuming you have not restricted the Restarting of activity due to change in
the orientation. When a device s configuration gets changed in your case    
Orientation, its user interface has to be updated according to the new   
configuration. 

Being the primary component for interaction, it can be updated with some      
attributes to handle changes. Default behavior of Activity when device gets 
rotated is it gets destroyed and restarted again. This may be your issue.

To handle the rotation of activity use android:configChanges = "orientation"  
attribute in manifest for your activity.

终于想通了。

在 init 中调用 inflate(context, R.layout.driving_overlay, this) 将正在膨胀的视图添加到本例中的根视图到 Overlay 这是一个 FrameLayout 所以每次旋转都会膨胀一个新布局并将其添加到根视图中,因此在旋转之后 Overlay class 有超过 1 个子视图。 OnClickListeners 会将位置 0 的子视图按钮绑定到 class 中的 OnClickListeners,它会显示在位置 1 或更高位置新膨胀的子视图。所以在添加这个检查并删除过时的视图后,OnClickListeners 再次工作。

protected void init(Context context) {
        if (getChildCount() >= 1) {
            removeViewAt(0);
        }
        final View view = inflate(context, R.layout.driving_overlay, this);
        if (!isInEditMode()) {
            ButterKnife.bind(this, view);
        }
}