在子布局(视图)上,当视图从其父级中删除时是否有回调?视图完成后,我需要回收一些图像。我一直在网上四处寻找该怎么做,但还没有发现任何有用的东西。
4 回答
我也一直在寻找这样的东西。我能找到的最好的是 View.OnAttachStateChangeListener。我怀疑它是否理想,因为它是在窗口中添加和删除视图时的回调 - 不是父级,但它足以满足我的需要。
onDetachedFromWindow
您可以在自定义View
代码中覆盖,而不是注册新的侦听器。
我掉进了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) {
}
因此,您可以覆盖您的RelativeLayout
for 此方法。我的动画是无限动画,不会很快调用任何方法!
如果您有无限动画,正确的方法是编写此代码,当您调用删除所有视图时:
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()
!
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
当此视图与窗口分离时执行给定的操作。如果视图未附加到窗口,则该操作将立即执行,否则该操作将在视图与其当前窗口分离后执行。该操作只会被调用一次,然后将删除所有侦听器。