android on view removed from parent

I've been looking for something like this too. The best I can find is View.OnAttachStateChangeListener. I doubt it's ideal, as it's the callback for when the View is added & removed from the Window - not the parent, but it's sufficient for my needs.


Instead of registering a new listener, you can override onDetachedFromWindow in your custom View code.


I fall in that trap what marmor said:)

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

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

This code is in a CustomView.

The calling code is:

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

To understand why is not working you have to go inside 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);
        }
    }
}

The key is here:

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

So, if you have animation ( and in 1 case I have and in 9 cases not) it will not called the onDetachedFromWindow() and it will mess the whole 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();
        }
    }
}

Again in some cases will be called even with animation. addDisappearingView(view);

The accepted answer suggest something like this:

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

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

Sadly on animation will not print the desired text.

Some important code from android.view.ViewGroup API:

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

public void onViewRemoved(View child) {
}

So, you can override your RelativeLayout for this method. My animation is an infinite animation, and it will not be called very soon any of the methods!

If you have an infinite animation the correct way is to write this code, when you call remove all views:

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

Now it will be called the protected void onDetachedFromWindow()!