8

在子布局(视图)上,当视图从其父级中删除时是否有回调?视图完成后,我需要回收一些图像。我一直在网上四处寻找该怎么做,但还没有发现任何有用的东西。

4

4 回答 4

11

我也一直在寻找这样的东西。我能找到的最好的是 View.OnAttachStateChangeListener。我怀疑它是否理想,因为它是在窗口中添加和删除视图时的回调 - 不是父级,但它足以满足我的需要。

于 2012-09-11T11:27:10.387 回答
5

onDetachedFromWindow您可以在自定义View代码中覆盖,而不是注册新的侦听器。

于 2014-10-05T08:44:23.883 回答
4

我掉进了marmor所说的那个陷阱:)

@Override
protected void onDetachedFromWindow() { I want to do something here, sometimes called sometimes not!!}

protected void onAttachedToWindow() {It is working fine, always}

此代码位于CustomView.

调用代码是:

    contentHolder.removeAllViews();
    // ... init my  CustomView   ...
    contentHolder.addView(myCustomView);
    contentHolder.requestLayout();// useless, not need
    contentHolder.invalidate();// useless, not need

要了解为什么不起作用,您必须进入 Android API:

public void removeAllViews() {
    removeAllViewsInLayout();
    requestLayout();
    invalidate(true);
}

public void removeAllViewsInLayout() {
    final int count = mChildrenCount;
    if (count <= 0) {
        return;
    }

    final View[] children = mChildren;
    mChildrenCount = 0;

    final View focused = mFocused;
    final boolean detach = mAttachInfo != null;
    boolean clearChildFocus = false;

    needGlobalAttributesUpdate(false);

    for (int i = count - 1; i >= 0; i--) {
        final View view = children[i];

        if (mTransition != null) {
            mTransition.removeChild(this, view);
        }

        if (view == focused) {
            view.unFocus(null);
            clearChildFocus = true;
        }

        view.clearAccessibilityFocus();

        cancelTouchTarget(view);
        cancelHoverTarget(view);

        if (view.getAnimation() != null ||
                (mTransitioningViews != null && mTransitioningViews.contains(view))) {
            addDisappearingView(view);
        } else if (detach) {
           view.dispatchDetachedFromWindow();
        }

        if (view.hasTransientState()) {
            childHasTransientStateChanged(view, false);
        }

        dispatchViewRemoved(view);

        view.mParent = null;
        children[i] = null;
    }

    if (clearChildFocus) {
        clearChildFocus(focused);
        if (!rootViewRequestFocus()) {
            notifyGlobalFocusCleared(focused);
        }
    }
}

关键在这里:

        if (view.getAnimation() != null ||
                (mTransitioningViews != null && mTransitioningViews.contains(view))) {

所以,如果你有动画(在 1 种情况下我有,在 9 种情况下没有)它不会调用它onDetachedFromWindow(),它会弄乱整个 UI :)

public void endViewTransition(View view) {
    if (mTransitioningViews != null) {
        mTransitioningViews.remove(view);
        final ArrayList<View> disappearingChildren = mDisappearingChildren;
        if (disappearingChildren != null && disappearingChildren.contains(view)) {
            disappearingChildren.remove(view);
            if (mVisibilityChangingChildren != null &&
                    mVisibilityChangingChildren.contains(view)) {
                mVisibilityChangingChildren.remove(view);
            } else {
                if (view.mAttachInfo != null) {
                    view.dispatchDetachedFromWindow();
                }
                if (view.mParent != null) {
                    view.mParent = null;
                }
            }
            invalidate();
        }
    }
}

在某些情况下,即使使用动画也会再次调用。addDisappearingView(视图);

接受的答案建议如下:

    addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
        @Override
        public void onViewAttachedToWindow(View v) {
        }

        @Override
        public void onViewDetachedFromWindow(View v) {
            System.out.println("MyCustomView.onViewDetachedFromWindow");
        }
    });

可悲的是动画不会打印所需的文本。

来自 android.view.ViewGroup API 的一些重要代码:

void dispatchViewRemoved(View child) {
    onViewRemoved(child);
    if (mOnHierarchyChangeListener != null) {
        mOnHierarchyChangeListener.onChildViewRemoved(this, child);
    }
}

public void onViewRemoved(View child) {
}

因此,您可以覆盖您的RelativeLayoutfor 此方法。我的动画是无限动画,不会很快调用任何方法!

如果您有无限动画,正确的方法是编写此代码,当您调用删除所有视图时:

    if(contentHolder.getChildCount() > 0 ){
        View child0 = contentHolder.getChildAt(0);
        Animation animation = child0.getAnimation();
        if(animation != null) {
            animation.cancel();
            child0.clearAnimation();
        }
    }
    contentHolder.removeAllViews();

现在它将被称为protected void onDetachedFromWindow()

于 2017-05-06T10:28:12.200 回答
0

Android KTX (Core KTX) 库为此提供了一个很好的解决方案。

你需要这个依赖:androidx.core:core-ktx:1.3.0

然后,您可以调用函数“doOnDetach”来表示您希望在从窗口中删除视图时(一次)运行一些代码:

fun myInitCode() {
    ...
    myView.doOnDetach(this::doOnMyViewDetachFromWindow)
    ...
}

fun doOnMyViewDetachFromWindow(view: View) {
    ... put your image cleanup code here ...
}

您可以将 lambda 传递给“doOnDetach”,但如上所示的方法引用可能更简洁,具体取决于您需要做多少工作。

doOnDetach 的描述如下:

androidx.core.view ViewKt.class public inline fun View.doOnDetach(crossinline action: (View) → Unit): Unit

当此视图与窗口分离时执行给定的操作。如果视图未附加到窗口,则该操作将立即执行,否则该操作将在视图与其当前窗口分离后执行。该操作只会被调用一次,然后将删除所有侦听器。

于 2020-07-21T09:30:23.987 回答