Android — удаление объекта из просмотра страницы приводит к пробелу

#android #view #android-viewpager

#Android #Вид #android-viewpager

Вопрос:

Я собираюсь запрограммировать приложение для управления некоторыми светодиодными гаджетами по всему дому. Чтобы использовать несколько устройств в одном действии, я использовал просмотр страницы с адаптером просмотра, чтобы иметь возможность выбирать между связанными устройствами. В настоящее время я застрял с удалением представлений из контейнера. Как только я удаляю представление, оно исчезает, но оставляет пустое пространство внутри моего ViewPager, как вы можете видеть на рисунке ниже. Я попробовал все, что предлагали другие пользователи, например, возврат

POSITION_NONE

при перезаписи

getItemPosition

метод. Я предоставляю вам код моего pageViewAdapter. Я надеюсь, что кто-нибудь может предложить решение этой проблемы. Я здесь очень застрял.

 package com.example.fabsinnenraumgestaltung;
import android.content.DialogInterface;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.cardview.widget.CardView;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;

import java.util.ArrayList;
import java.util.List;

public class CardPagerAdapter extends PagerAdapter implements CardAdapter {

ViewPager viewPager;
MainActivity mainActivity;

private List<CardView> mViews;
private List<CardItem> mData;
private float mBaseElevation;

public CardPagerAdapter(MainActivity mainActivity, ViewPager viewPager) {
    mData = new ArrayList<>();
    mViews = new ArrayList<>();
    this.mainActivity = mainActivity;
    this.viewPager = viewPager;
}

public void addCardItem(CardItem item) {
    mViews.add(null);
    mData.add(item);
}

public float getBaseElevation() {
    return mBaseElevation;
}

@Override
public CardView getCardViewAt(int position) {
    return mViews.get(position);
}

@Override
public int getCount() {
    return mData.size();
}

@Override
public boolean isViewFromObject(View view, Object object) {
    return view == object;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    container.removeView((View) object);
    mData.set(position, null);
    mViews.set(position, null);
    notifyDataSetChanged();

}

@Override
public void notifyDataSetChanged() {
    super.notifyDataSetChanged();
}

@Override
public int getItemPosition(@NonNull Object object) {
    return super.getItemPosition(object);
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Button deleteButton;
    Button editButton;

    View view = LayoutInflater.from(container.getContext())
            .inflate(R.layout.device_card_layout, container, false);

    deleteButton = view.findViewById(R.id.deleteButton);
    editButton = view.findViewById(R.id.editButton);

    deleteButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            System.out.println("delete");
            deleteCardOperation(viewPager.getCurrentItem(), container);

        }
    });

    editButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            System.out.println("edit");
            int currentCard = viewPager.getCurrentItem();
        }
    });

    container.addView(view);
    bind(mData.get(position), view);
    CardView cardView = (CardView) view.findViewById(R.id.itemCard);

    if (mBaseElevation == 0) {
        mBaseElevation = cardView.getCardElevation();
    }



    cardView.setMaxCardElevation(mBaseElevation * MAX_ELEVATION_FACTOR);
    mViews.set(position, cardView);

    return view;
}





private void bind(CardItem item, View view) {
    TextView titleTextView = (TextView) view.findViewById(R.id.itemText);
    TextView ipTextView = (TextView) view.findViewById(R.id.itemIPAdress);
    TextView statusView = (TextView) view.findViewById(R.id.itemActiveStatus);
    titleTextView.setText(item.getName());
    ipTextView.setText(item.getIp());
    statusView.setText(item.getStatus());


}

private void deleteCardOperation(int cardIndicator, ViewGroup desV){


    CardItem x = mData.get(cardIndicator);

    AlertDialog.Builder builder = new AlertDialog.Builder(mainActivity, android.R.style.Theme_Material_Dialog_Alert);
    builder.setTitle("Confirm");
    builder.setMessage("Are you sure you want to delete this device: "  x.getName()   " ?");
    builder.setPositiveButton("YES", new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
            destroyItem(desV, cardIndicator, getCardViewAt(cardIndicator));
            dialog.dismiss();
        }
    });

    builder.setNegativeButton("NO", new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            // Do nothing
            dialog.dismiss();
        }
    });

    AlertDialog alert = builder.create();
    alert.show();

}

private void editCard(){

}




}
  

введите описание изображения здесь

Я действительно надеюсь, что вы сможете помочь. Спасибо.

Первое редактирование:

Я отредактировал свой код, как предложил Michiel. Теперь я вызываю метод deleteCardItem() для удаления данных из моего набора данных, как показано ниже.

 public void deleteCardItem(int position){
    mData.remove(position);
    notifyDataSetChanged();
}
  

При вызове notifyDataSetChanged система берет верх и вызывает getItemPosition() . Я также отредактировал этот метод, как и предложил Michiel.

 @Override
 public int getItemPosition(@NonNull Object object) {
    //return super.getItemPosition(object);

    int position = mData.indexOf(object);

    if(position == -1){
        return POSITION_NONE;
    }else{
        return position;
    }


}
  

Проблема сейчас в том, что я не могу сравнить данный «объект» с каким-либо набором данных, потому что «объект» — это объект просмотра, а mData — это мой Arraylist, он же dataset. Это приводит к поведению starnge в моем приложении.
Карта больше не исчезнет. Он отображается, но данные удаляются.
Я был бы признателен за дальнейшую помощь.

Ответ №1:

Чтобы полностью уничтожить само представление, вы должны попытаться написать destroyItem так:

 @Override
public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View) object);
        mViews.set(position, null);
}
  

Таким образом, объект просмотра полностью уничтожен.
А также попробуйте установить для представления значение null в deleteCardItem

 @Override
public void deleteCardItem(int position){
        mData.remove(position);
        mViews.set(position, null);
        notifyDataSetChanged();
}
  

А также попробуйте выполнить поиск представления вместо данных внутри getItemPosition .

 @Override
public int getItemPosition(@NonNull Object object) {
        int position = mViews.indexOf(object);
        if(position == -1){
                return POSITION_NONE;
        }else{
                return position;
        }
}
  

После этого вы их больше не видите

Комментарии:

1. Я попытался реализовать ваше решение. Большое вам спасибо, для меня это сработало очень хорошо. Похоже, это аналогичная проблема, как я и думал.

Ответ №2:

Я вижу в основном три проблемы. Сначала вы видите пустое пространство, потому что элемент не удаляется; вместо этого ему присваивается значение null. При destroyItem() вызове in mData.set(position, null); , результирующий массив будет иметь вид [(CardItem), null, (CardView)] . Размер этого массива по-прежнему равен 3. Решение: вызов mData.remove(position) .

Во-вторых, ответственность PagerAdapter за вызов всех функций @Override , аннотированных с. Когда вы вызываете notifyDatasetChanged() , он будет вызывать destroyItem() . Вы должны изменить mData , вызвать notifyDatasetChanged() и позволить PagerAdapter выполнять свою работу. Он удалит само представление и вызовет destroyItem() вас, чтобы вы могли выполнить дополнительную очистку, например, удалить представление из mView .

В-третьих, если это всего лишь одноразовый проект, вы можете оставить это как есть. Однако, если это долгосрочный проект, вы можете заглянуть в fi. SOLID или другие принципы проектирования. Пример: ваш PagerAdapter должен делать только 1 вещь и только одну вещь. Он должен только связывать ваш набор данных mData с вашими представлениями, знать, что он также обрабатывает обновление mData, отображение диалогового окна и многое другое. Другой пример: ваша ссылка на MainActivity может быть заменена контекстом; код все равно будет работать, но может использоваться в других действиях или фрагментах без какой-либо дополнительной работы.

TL; DR:

 // This is called by the PagerAdapter itself
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    mViews.remove(position);
}

// Call this when the user taps yes
private void deletePage(int position) {
    mData.remove(position);
    notifyDataSetChanged();
}
  

Комментарии:

1. Спасибо за ваше огромное объяснение. Это просто дало мне понять, что метод destroyItem() вызывается notifyDataSetChanged() . Я просто реализовал эти вещи так, как вы предложили, но это привело к очень странному поведению представления. Я пытаюсь объяснить. Карта больше не удаляется. Он все еще отображается. Я только что отладил все это с точками останова в методе destroyItem() и заметил, что метод не вызывается при вызове notifyDataSetChanged() .

2. Я заметил еще одно странное поведение. Данные фактически удаляются, а представление — нет. Все карточки есть, но я не могу прокрутить до последней из них. Прав ли я со своей мыслью, что метод destroyItem() на самом деле не вызывается?

3. Как я выяснил, метод destroyItem() будет вызываться только тогда, когда метод getItemPosition() возвращает POSITION_NONE . Если он возвращает объект, destroyItem() вызываться не будет. Проблема в том, что этого точно не происходит. Я получаю объекты обратно только из метода getItemPosition. Теперь мой вопрос в том, как я могу это точно изменить. В документации говорится, что «super.getItemPosition (object)» обычно возвращает как объект, так И фактически null. Но в моем случае это, похоже, не работает. Есть ли у вас какие-либо дополнительные предложения для меня, как я могу это решить? Или есть идеи, в чем также может быть проблема?

4. Вы правы. Решение состоит в том, чтобы реализовать getItemPosition() себя, что-то вроде: `int position = mData.indexOf(object); if (position == -1) возвращает POSITION_NONE; else возвращает позицию;

5. На данный момент я пытаюсь выяснить, как я могу реализовать ваше решение. Если я вас правильно понял, я должен сравнить данные с представлениями, чтобы определить, чего не хватает, а затем вернуть -1, чтобы удалить соответствующее представление в процессе. Из-за того, что «объект» даст вам объект CardView и ничего, с чем вы можете сравнить mData, операция всегда будет возвращать ложные результаты. Поскольку я не могу сравнить объект просмотра с «пользовательским» объектом, эта операция завершится неудачно и `int position = mData.indexOf(object); всегда возвращайте -1 для каждой карты. Это приводит к исключению. Сначала я должен это исправить.