【问题标题】:RemoveView not working删除视图不起作用
【发布时间】:2014-04-06 02:35:17
【问题描述】:

我遇到了一个不寻常的错误。我在自定义视图组中有这个。该方法接收一个视图并将其添加到布局中,但我不断收到相同的错误:

if((ViewGroup)view.getParent() != null){
    ((ViewGroup)view.getParent()).removeView(view);
}

addView(view); <--- Breakpoints puts the error on this line

错误是:

java.lang.IllegalStateException:指定的孩子已经有一个父母。您必须先在孩子的父母上调用 removeView()。

在此周围使用断点表明“视图”即使在父级上调用 removeView 后仍保留对其父级的引用..

有些人建议使用 runnable 等待几秒钟,然后再将其添加到视图中。我没有尝试过,因为它似乎更像是一种黑客攻击而不是解决方案。无论哪种方式,我都希望有人能够提供帮助

谢谢!

PS:没关系,但我添加的这个视图的父级是我制作的自定义网格布局,它的父级是 viewpager。

编辑:

我做了更多的断点和调试,从外观上看,网格有效地从其子列表中删除了视图(调试),但子视图在其 mParent 变量中保留了对同一网格的引用(调试)。这怎么可能

编辑:

活动中:

Button button = new Button(mContext);
button.setOnClickListener(mClickListener);
(...)
Random random = new Random();
button.setText(random.nextInt(9999) + " ");

mCurrentGridLayout.addCustomView(button);

在 CustomGridLayout 视图组类中:

public void addCustomView(View view){
    if((ViewGroup)view.getParent() != null){
        ((ViewGroup)view.getParent()).removeView(view);
    }

    addView(view);
}

【问题讨论】:

  • 您确定正在调用 removeView 吗?这是在哪个班?为什么第一种情况你必须使用getParent()方法,而第二种情况你只是调用addView()
  • 因为网格不同。我在两个网格之间移动子视图,这是我在网格内调用以将视图从一个网格移动到另一个网格的方法是的,正在调用删除视图
  • view是如何声明的,代码是什么?
  • 不确定这是否会有所帮助,但您是否尝试在添加之前致电view.invalidate()?但是您正在发送一个新创建的View(来自您发布的代码),因此它没有父级。
  • 我在几个地方调用 addCustomView .. 在子视图点击事件、长点击事件等

标签: android viewgroup


【解决方案1】:

我在尝试创建自定义横幅时遇到了同样的问题。我相信这是因为布局期间的动画,这就是延迟可能起作用的原因。就我而言,我制作了一个自定义视图组类来消除动画延迟:

private class BannerLayout extends LinearLayout {
  public BannerLayout(Context context) {
     super(context);
  }

  @Override
  protected void removeDetachedView(View child, boolean animate) {
     super.removeDetachedView(child, false);
  }
}

一旦我这样做了,一切都按预期进行。

希望对你有帮助!

【讨论】:

  • 谢谢!没想到^^
  • 另一种在不创建子类的情况下消除动画的方法是将父级的 layout transition 设置为 null 类似的东西:parentView.setLayoutTransition(null); parentView.removeView(view);
  • @Chupik 请注意setLayoutTransition(...) was added in API 11
【解决方案2】:

我遇到了同样的问题,Iree 的回答对我很有帮助。原因是布局过渡的动画,但如果我将其设置为 null 我将失去我的过渡动画。所以我所做的是添加一个布局转换侦听器,这样您就可以在转换完成时进行侦听,然后将视图添加到它的新父级。

使用 Java

LayoutTransition layoutTransition = ((ViewGroup)view.getParent()).getLayoutTransition();
layoutTransition.addTransitionListener(new TransitionListener(){

    @Override
    public void startTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType) {

    }

    @Override
    public void endTransition(LayoutTransition transition, ViewGroup container, View view, int transitionType) {
         // now you can add the same view to another parent
         addView(view);
    } 
});

使用 Kotlin

val layoutTransition = (view.parent as ViewGroup).layoutTransition
layoutTransition.addTransitionListener(object : LayoutTransition.TransitionListener {

      override fun startTransition(transition: LayoutTransition?,container: ViewGroup?,view: View?,transitionType: Int) {

      }

      override fun endTransition(transition: LayoutTransition?,container: ViewGroup?,view: View?,transitionType: Int) {
         // now you can add the same view to another parent
         addView(view)
      }
})

【讨论】:

    【解决方案3】:

    如果您必须处理具有layoutTransitionviewGroups,您可以执行以下操作:

    /**
     * When we don't have [LayoutTransition] onEnd is called directly
     * Or
     * function [onEnd] will be called on endTransition and when 
     * the parent is the same as container and view parent is null,
     * and will remove also the transition listener
     */
    private fun doOnParentRemoved(parent: ViewGroup, onEnd: () -> Unit) {
        val layoutTransition = parent.layoutTransition
    
        if (layoutTransition == null) {
            onEnd.invoke()
            return
        }
    
        val weakListener = WeakReference(onEnd)
        layoutTransition.addTransitionListener(object : LayoutTransition.TransitionListener {
            override fun startTransition(
                transition: LayoutTransition?,
                container: ViewGroup?,
                view: View?,
                transitionType: Int
            ) {
            }
    
            override fun endTransition(
                transition: LayoutTransition?,
                container: ViewGroup?,
                view: View?,
                transitionType: Int
            ) {
                transition?.removeTransitionListener(this)
                weakListener.get()?.invoke()
            }
        })
    }
    

    你可以这样使用它:

    sourceLayout.removeView(textView)
    doOnParentRemoved(sourceLayout) {
       // do your stuff when view has no parent
       // add more logic to check is your view who called it in case of multiple views
    }
    

    我建议仔细检查您的实现,因为有时不能保证调用 endTransition 或动画可能会在中间停止。就我而言,我在拖放操作中使用了它

    【讨论】:

      猜你喜欢
      • 2017-11-12
      • 1970-01-01
      • 2020-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多