Сначала вызовите removeView () для родительского элемента ребенка


139

Сначала немного предыстории:

У меня есть макет внутри прокрутки. Сначала, когда пользователь прокручивает экран, прокручивается прокрутка. Однако после определенного количества прокрутки я должен был отключить прокрутку в представлении прокрутки, чтобы переместить «фокус прокрутки» на веб-представление внутри дочернего макета. Таким образом, прокрутка фиксируется, и все события прокрутки переходят в веб-просмотр внутри нее.

Итак, для решения, когда достигается порог прокрутки, я удаляю дочерний макет из scrollview и помещаю его в родительский scrollview (и делаю прокрутку невидимой).

// 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);

Общая идея: (-> означает содержит)

Раньше: parentlayout -> scrollview -> scrollChildLayout

После: parentLayout -> scrollChildLayout

Приведенный выше код дает мне это исключение:

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)

Вы знаете, что происходит? Я явно вызываю removeView для родителя.

Ответы:


290

Решение:

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

Используйте дочерний элемент, чтобы получить ссылку на родительский. Приведите родительский элемент к ViewGroup, чтобы получить доступ к методу removeView и использовать его.

Спасибо @Dongshengcn за решение


1
Решение правильное, но почему scollView.removeView (scrollChildLayout) не работает? Разве две строки не должны быть одинаковыми?
andrea.rinaldi

@ andrea.spot, похоже, scrollView.removeView(scrollChildLayout)пытается удалить дочерний элемент scrollView, а не сам scrollView из его родительской ViewGroup
Дмитрий Грязин

1
@ user25 я знаю, что это может быть слишком поздно для вас, но исключение нулевого указателя связано с тем, что родительский элемент пуст, что означает, что представление не привязано ни к одному из родителей. так как в начале нет родителя, удалять нечего. if (mAdView.getParent()!=null) ((ViewGroup) mAdView.getParent()).removeView(mAdView);
Тарек

@kasgoku, не могли бы вы мне помочь? Я пытаюсь показать фрагмент в относительном макете при нажатии кнопки. но отображается ошибка: «У указанного дочернего элемента уже есть родитель. Сначала необходимо вызвать removeView () для родительского элемента дочернего элемента». как это решить?
Imranrana07

Я также получил эту ошибку в моем приложении, которое является одним из приложений чата. Я пытаюсь решить эту проблему. этот значок удален, но мне снова нужен значок для загрузки изображений. ( stackoverflow.com/q/58117428/10971384 ) это моя ссылка на вопрос
Thangapandi

21

Попробуйте сначала удалить scrollChildLayout из родительского представления?

scrollview.removeView(scrollChildLayout)

Или удалите всех дочерних элементов из родительского представления и добавьте их снова.

scrollview.removeAllViews()

Я уже вызываю removeView в коде в моем вопросе выше. removeAllViews () тоже не работает.
kasgoku

Вы уверены, что это родитель, которого вы вызываете removeView () / removeAllViews ()? Я делаю очень похожую вещь, и у меня это работает.
dongshengcn

4
Вы можете посмотреть на его родителя, посмотрев на: view.getParent () developer.android.com/reference/android/view/…
dongshengcn

@dongshengcn, не могли бы вы мне помочь? Я пытаюсь показать фрагмент в относительном макете при нажатии кнопки. но отображается ошибка: «У указанного дочернего элемента уже есть родитель. Сначала необходимо вызвать removeView () для родительского элемента дочернего элемента». как это решить?
Imranrana07

19

В onCreate с действием или в onCreateView с фрагментом.

 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) {

}

15

Хорошо, называйте меня параноиком, но я предлагаю:

  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

кастинг без instanceofпросто кажется неправильным. И (спасибо IntelliJ IDEA за то, что сообщили мне) removeViewявляется частью ViewManagerинтерфейса. И не следует приводить к конкретному классу, когда доступен идеально подходящий интерфейс.


3

Все, что вам нужно сделать, это post () Runnable, который выполняет addView ().


Не сработало. Вот что я пробовал: scrollView.removeView (scrollChildLayout); scrollView.setVisibility (View.GONE); parentLayout.post (новый Runnable () {@Override public void run () {parentLayout.addView (scrollChildLayout);}});
kasgoku

2

Котлин Решение

Kotlin упрощает родительское приведение с помощью as?, возвращая null, если левая сторона имеет значение null или приведение не выполняется.

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

Kotlin Extension Solution

Если вы хотите еще больше упростить это, вы можете добавить это расширение.

childView.removeSelf()

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

Он не будет безопасно ничего делать, если это представление имеет значение null, родительское представление имеет значение null или родительское представление не является ViewGroup.

ПРИМЕЧАНИЕ: Если вы также хотите безопасное удаление дочерних представлений родителем, добавьте это:

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

1

Я звонил parentView.removeView (childView), а childView все еще показывал. В конце концов я понял, что метод каким-то образом срабатывает дважды, и дважды добавил childView к parentView.

Итак, используйте parentView.getChildCount (), чтобы определить, сколько дочерних элементов имеет родительский элемент до добавления представления и после него. Если дочерний элемент добавляется слишком много раз, то самый верхний дочерний элемент удаляется, а копия childView остается, что похоже, что removeView работает, даже если это так.

Также не следует использовать View.GONE для удаления представления. Если он действительно удален, вам не нужно его скрывать, иначе он все еще там, и вы просто скрываете его от себя :(


1

В моем случае у меня есть BaseFragment, а все остальные фрагменты наследуются от него.

Итак, мое решение было добавить эти строки в OnDestroyView()метод

@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();
}

Вы пробовали выбрать следующий фрагмент и нажать назад слишком быстро?
iamthevoid

0

Вы также можете сделать это, проверив, возвращает ли метод indexOfView View, если метод indexOfView возвращает -1, тогда мы можем использовать.

DetachViewFromParent ViewGroup (v); за которым следует removeDetachedView ViewGroup (v, true / false);


0

Что я делал неправильно, поэтому я получил эту ошибку, я не создавал динамический макет и не добавлял к нему дочерние элементы, поэтому получил эту ошибку


0

Вот мое решение.

Допустим, у вас есть два, TextViewsи поместите их на LinearLayout(с именем ll). Вы наденете это LinerLayoutна другое 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> 

Если вы хотите создать эту структуру, вам нужно передать родительский элемент в качестве наследования.

Если вы хотите использовать его в onCreateметоде, thisбудет достаточно.

В противном случае вот сольон:

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());
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.