#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 для каждой карты. Это приводит к исключению. Сначала я должен это исправить.