10

I'm having an unusual error. I have this inside a custom viewgroup. The method receives a view and add it to the layout but i keep getting the same error:

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

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

The error is:

java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.

Using breakpoints around this shows that "view" even after calling removeView onthe parent keep a reference to its parent..

Some people proposed using a runnable to wait a few seconds before adding it to the view. I havent tried this because it seems more a hack than a solution.. Either way i hope someone may be able to help

Thanks!

PS: Not that it should matter but the parent of this view i'm adding is a custom grid layout i made and its parent is a viewpager.

Edit:

I did a little more breakpoints and debugging and from the looks of it the grid effectively remove the view from its child list (debug) but the child view keeps a reference to that same grid in its mParent variable (debug). How is this possible

EDIT:

In activity:

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

mCurrentGridLayout.addCustomView(button);

In CustomGridLayout viewgroup class:

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

    addView(view);
}
nunoh123
  • 1,087
  • 1
  • 10
  • 17
  • Are you sure removeView is being called? What class is this in? Why is it that in the first case you have to use the `getParent()` method, and in the second case you are simply calling `addView()`? – Tamby Kojak Apr 06 '14 at 02:40
  • because the grids are diferent. I move childviews between two grids and the is the method i call inside the grid to move a view from one grid to the other And yes, remove view is being called – nunoh123 Apr 06 '14 at 02:42
  • How is `view` declared, what is the code? – codeMagic Apr 06 '14 at 02:57
  • Not sure if this will help but have you tried calling `view.invalidate()` before adding? But you are sending a newly created `View` (from the code you've posted) so it wouldn't have a parent. – codeMagic Apr 06 '14 at 03:06
  • i call addCustomView on several spots.. on the childs view click event, on long click event etc – nunoh123 Apr 06 '14 at 03:10
  • tried invalidate() on the childview, doesnt solve it, the error keeps apperaring – nunoh123 Apr 06 '14 at 03:10
  • What is the kind of previous parent of your button. Is it an instance of AdpaterView? – Abolhassan Abdolalizade Apr 06 '14 at 05:49
  • nop, the parent are a custom grid layout i made extending Viewgroup but right now it behaves like a linear layout, so it cant be the grid layout implementation – nunoh123 Apr 06 '14 at 12:44

3 Answers3

13

I had that same issue when trying to create a custom banner. I believe it's because of animation during layout, that's why a delay could work. In my case, I made a custom viewgroup class to eliminate the animation delay:

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

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

Once I did this, everything worked as expected.

Hope it helps!

Iree
  • 358
  • 3
  • 13
  • Thanks! I hadn't think that ^^ – nunoh123 Jun 11 '14 at 20:49
  • 6
    Another way to eliminate the animation without creating subclass is to set **layout transition** for parent to **null** Something like that: `parentView.setLayoutTransition(null); parentView.removeView(view);` – Chupik May 07 '15 at 09:35
  • 1
    @Chupik It's important to note that `setLayoutTransition(...)` [was added in API 11](https://developer.android.com/reference/android/view/ViewGroup.html#setLayoutTransition%28android.animation.LayoutTransition%29) – Iree May 07 '15 at 19:48
0

I had the same problem, and the answer from Iree really helped me. The cause was the animation for the layout transition, but if i set it its value null i will lose my transition animation. So what i did is add a layout transition listener, that way you can listener when the transition is done, and then add the view to its new parent.

Using 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);
    } 
});

Using 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)
      }
})
slaviboy
  • 1,407
  • 17
  • 27
0

In case you have to do with viewGroups that have layoutTransition or not you can do something like this:

/**
 * 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()
        }
    })
}

This is how you can use it:

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
}

I would suggest to double check your implementation as it can happen sometimes endTransition is not guaranteed to be called or animations can stop in middle. In my case I have used it in drag drop actions

Ultimo_m
  • 4,724
  • 4
  • 38
  • 60