Chame removeView () no pai da criança primeiro

138

Primeiro um pouco de fundo:

Eu tenho um layout dentro de uma scrollview. Inicialmente, quando o usuário rola na tela, a exibição de rolagem rola. No entanto, após uma certa quantidade de rolagem, desativei a rolagem na exibição de rolagem e movi o "foco da rolagem" para uma visualização da web dentro do layout filho. Dessa forma, o scrollview adere e todos os eventos de rolagem vão para o webview dentro dele.

Portanto, para uma solução, quando o limite de rolagem é atingido, removo o layout filho da scrollview e o coloco no pai da scrollview (e torno a scrollview invisível).

// Remove the child view from the scroll view
scrollView.removeView(scrollChildLayout);

// Get scroll view out of the way
scrollView.setVisibility(View.GONE);

// Put the child view into scrollview's parent view
parentLayout.addView(scrollChildLayout);

Ideia geral: (-> meios contém)

Antes: parentlayout -> scrollview -> scrollChildLayout

Depois: parentLayout -> scrollChildLayout

O código acima está me dando essa exceção:

java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
           at android.view.ViewGroup.addViewInner(ViewGroup.java:1976)
           at android.view.ViewGroup.addView(ViewGroup.java:1871)
           at android.view.ViewGroup.addView(ViewGroup.java:1828)
           at android.view.ViewGroup.addView(ViewGroup.java:1808)

Você sabe o que está acontecendo? Estou claramente chamando removeView no pai.

kasgoku
fonte

Respostas:

289

Solução:

((ViewGroup)scrollChildLayout.getParent()).removeView(scrollChildLayout);
//scrollView.removeView(scrollChildLayout);

Use o elemento filho para obter uma referência ao pai. Converta o pai em um ViewGroup para obter acesso ao método removeView e usá-lo.

Obrigado a @ Dongshengcn pela solução

kasgoku
fonte
1
A solução está certa, mas por que "scollView.removeView (scrollChildLayout)" não funcionaria? As duas linhas não deveriam ser as mesmas?
andrea.rinaldi
@ andrea.spot, parece que scrollView.removeView(scrollChildLayout)tenta remover a criança de scrollView, para não remover o próprio scrollView de sua ViewGroup pai
Dmitry Gryazin
1
@ user25 Eu sei que isso talvez seja tarde demais para você, mas a exceção do ponteiro nulo se deve ao fato de o pai estar vazio, o que significa que a exibição não está anexada a nenhum pai. Como não há pai no início, não há nada a remover. if (mAdView.getParent()!=null) ((ViewGroup) mAdView.getParent()).removeView(mAdView);
Tarek
@kasgoku você pode me ajudar por favor. Eu estou tentando mostrar um fragmento no layout relativo em um clique de botão. mas está exibindo o erro "O filho especificado já tem um pai. Você deve chamar removeView () no pai da criança primeiro." como resolver isso?
precisa saber é o seguinte
Também recebi esse erro no meu aplicativo, que é um dos aplicativos de bate-papo. Estou tentando resolver esse problema. esse ícone foi removido, mas preciso do ícone novamente para fazer upload de imagens. ( Stackoverflow.com/q/58117428/10971384 ), este é o meu link questão
Thangapandi
21

Tente remover o scrollChildLayout da visualização principal primeiro?

scrollview.removeView(scrollChildLayout)

Ou remova todo o filho da exibição pai e adicione-o novamente.

scrollview.removeAllViews()
dongshengcn
fonte
Já estou chamando removeView no código da minha pergunta acima. removeAllViews () também não funciona.
kasgoku
Tem certeza de que é o pai que você está chamando removeView () / removeAllViews ()? Estou fazendo algo muito parecido, e tem funcionado para mim.
precisa saber é o seguinte
4
Você pode ver seu pai acessando: view.getParent () developer.android.com/reference/android/view/…
dongshengcn
@dongshengcn você pode me ajudar por favor. Eu estou tentando mostrar um fragmento no layout relativo em um clique de botão. mas está exibindo o erro "O filho especificado já tem um pai. Você deve chamar removeView () no pai da criança primeiro." como resolver isso?
precisa saber é o seguinte
19

No onCreate com atividade ou no onCreateView com fragmento.

 if (view != null) {
    ViewGroup parent = (ViewGroup) view.getParent();
    if (parent != null) {
        parent.removeView(view);
    }
}
try {
    view = inflater.inflate(R.layout.fragment_main, container, false);
} catch (InflateException e) {

}
Cabezas
fonte
15

Ok, me chame de paranóico, mas sugiro:

  final android.view.ViewParent parent = view.getParent ();

  if (parent instanceof android.view.ViewManager)
  {
     final android.view.ViewManager viewManager = (android.view.ViewManager) parent;

     viewManager.removeView (view);
  } // if

fundição sem instanceofapenas parece errado. E (obrigado IntelliJ IDEA por me dizer) removeViewfaz parte da ViewManagerinterface. E não se deve converter para uma classe concreta quando uma interface perfeitamente adequada estiver disponível.

Martin
fonte
3

Tudo o que você precisa fazer é postar () um Runnable que faça o addView ().

Romain Guy
fonte
Não funcionou. Isto é o que eu tentei: scrollView.removeView (scrollChildLayout); scrollView.setVisibility (View.GONE); parentLayout.post (new Runnable () {@Override public void run () {parentLayout.addView (scrollChildLayout);}});
kasgoku
1

Eu estava chamando parentView.removeView (childView) e childView ainda estava sendo exibido. Acabei percebendo que um método estava sendo disparado duas vezes e adicionei o childView ao parentView duas vezes.

Portanto, use parentView.getChildCount () para determinar quantos filhos o pai tem antes de adicionar uma exibição e depois. Se o filho for adicionado muitas vezes, o filho mais importante será removido e a cópia childView permanecerá - que parece que removeView está funcionando mesmo quando está.

Além disso, você não deve usar o View.GONE para remover uma exibição. Se ele for realmente removido, você não precisará escondê-lo; caso contrário, ele ainda estará lá e você estará escondendo de si mesmo :(

Chris Sprague
fonte
1

No meu caso, tenho BaseFragment e todos os outros fragmentos herdam disso.

Então, minha solução foi adicionar essas linhas no OnDestroyView()método

@Override
public final View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
    if (mRootView == null)
    {
        mRootView = (inflater == null ? getActivity().getLayoutInflater() : inflater).inflate(mContentViewResourceId, container, false);
    }
....////
}

@Override
public void onDestroyView()
{
    if (mRootView != null)
    {
        ViewGroup parentViewGroup = (ViewGroup) mRootView.getParent();

        if (parentViewGroup != null)
        {
            parentViewGroup.removeAllViews();
        }
    }

    super.onDestroyView();
}
Aleksey Timoshchenko
fonte
você já tentou selecionar o próximo fragmento e pressionar muito rápido?
iamthevoid
1

Solução Kotlin

Kotlin simplifica a conversão pai com as?, retornando nulo se o lado esquerdo for nulo ou a conversão falhar.

(childView.parent as? ViewGroup)?.removeView(childView)

Solução de extensão Kotlin

Se você deseja simplificar ainda mais isso, você pode adicionar esta extensão.

childView.removeSelf()

fun View?.removeSelf() {
    this ?: return
    val parentView = parent as? ViewGroup ?: return
    parentView.removeView(this)
}

Ele não fará nada com segurança se essa Visualização for nula, a visualização pai for nula ou a visualização pai não for um ViewGroup

NOTA: Se você também deseja remover com segurança as visualizações filho por pai, adicione este:

fun ViewGroup.removeViewSafe(toRemove: View) {
    if (contains(toRemove)) removeView(toRemove)
}
Gibolt
fonte
0

Você também pode fazer isso verificando se o método indexOfView do View se o método indexOfView retorna -1, então podemos usar.

DesanexarViewFromParent (v) do ViewGroup; seguido pelo removeDetachedView do ViewGroup (v, true / false);

Arun Gupta
fonte
0

O que eu estava fazendo de errado, por isso, recebi esse erro é que não estava instanciando o layout dinâmico e adicionando childs a ele.

Falcão
fonte
0

Aqui está a minha solução.

Digamos que você tenha dois TextViewse coloque-os em um LinearLayout(nomeado ll). Você vai colocar isso LinerLayoutem outro LinerLayout.

< lm Linear Layout> 
       < ll Linear Layout> 
             <name Text view>
             </name Text view>
             <surname Text view>
             </surname Text view>
       </ll Linear Layout> 
</lm Linear Layout> 

Quando você deseja criar essa estrutura, precisa fornecer o pai como herança.

Se você quiser usá-lo em um onCreatemétodo thisserá suficiente.

Caso contrário, aqui está a solição:

LinerLayout lm = new LinearLayout(this); // You can use getApplicationContext() also
LinerLayout ll = new LinearLayout(lm.getContext());
TextView name = new TextView(ll.getContext());
TextView surname = new TextView(ll.getContext());
ishak O.
fonte