Android - 从 pageView 中删除对象会导致空白

Posted

技术标签:

【中文标题】Android - 从 pageView 中删除对象会导致空白【英文标题】:Android - removing Object from pageView results in blank space 【发布时间】:2020-10-26 16:14:28 【问题描述】:

我即将编写一个应用程序来控制房子周围的一些基于 LED 的小工具。为了在一个活动上拥有多个设备,我使用了带有视图适配器的页面视图,以便能够在链接的设备之间进行选择。目前,我坚持从容器中删除视图。一旦我删除视图,它就会消失,但会在我的 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 又名数据集。这导致我的应用程序出现异常行为。 卡不会再消失了。显示,但数据被删除。 我将不胜感激。

【问题讨论】:

【参考方案1】:

要完全销毁视图本身,您应该尝试像这样编写destroyItem

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

所以 View 对象被完全销毁。 并尝试在deleteCardItem中将View设置为null

@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;
        

在那之后,你就再也看不到它们了

【讨论】:

我尝试实施您的解决方案。非常感谢,这对我来说效果很好。这似乎和我想的一样。【参考方案2】:

我主要看到三个问题。首先,您会看到一个空白区域,因为该项目没有被删除;相反,它被设置为空。当在destroyItem() 中调用mData.set(position, null); 时,结果数组将为[(CardItem), null, (CardView)]。这个数组的大小还是3。解决方法:调用mData.remove(position)

其次,调用@Override注解的所有函数是PagerAdapter的职责。当您调用notifyDatasetChanged() 时,它将调用destroyItem()。你应该改变mData,打电话给notifyDatasetChanged(),让PagerAdapter做它的工作。它将删除视图本身并为您调用destroyItem(),因此您可以进行额外的清理,例如从mView 中删除视图。

第三,如果这只是一个一次性的项目,您可以保留它。但是,如果这是一个长期项目,您可以研究 fi。 SOLID 或其他设计原则。一个例子:你的 PagerAdapter 应该做一件事,而且只做一件事。它应该只将您的数据集 mData 与您的视图连接,知道它还处理更新 mData、显示对话框等。另一个例子:你对 MainActivity 的引用可以用 Context 代替;该代码仍然可以工作,但可以在其他活动或片段中使用而无需任何额外工作。

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

【讨论】:

感谢您的大力解释。它只是让我明白,destroyItem() 方法是由 notifyDatasetChanged() 调用的。我只是按照你建议的方式实现了这些东西,但这导致了 View 的一个非常奇怪的行为。我试图解释。该卡不再被删除。它仍然显示。我只是在 destroyItem() 方法上使用断点调试了整个事情,并注意到当我调用 notifyDatasetChanged() 时没有调用该方法。 我注意到另一个奇怪的行为。数据实际上被删除,但视图没有。卡片都在那里,但我无法滚动到最后一张。我认为实际上没有调用 destroyItem() 方法是否正确? 我发现,只有当 getItemPosition() 方法返回 POSITION_NONE 时,才会调用 destroyItem() 方法。如果它返回一个对象,destroyItem() 将不会被调用。问题是,这完全没有发生。我只从 getItemPosition 方法中取回对象。我现在的问题是我如何才能完全改变它。文档说,“super.getItemPosition(object)”通常会返回一个对象,或者实际上是 null。但就我而言,它似乎不起作用。你对我有什么进一步的建议可以解决这个问题吗?或者任何想法也可能是问题? 你是对的。解决方案是自己实现getItemPosition(),例如:`int position = mData.indexOf(object);如果(位置 == -1)返回 POSITION_NONE;否则返回位置; 目前我试图弄清楚如何实施您的解决方案。如果我猜对了,我应该将数据与视图进行比较以识别缺失的内容,然后返回 -1 以删除流程中的相应视图。由于“对象”会给您一个 CardView 对象,而您无法将 mData 与之比较,因此该操作将始终返回错误结果。因为我无法将视图对象与“自定义”对象进行比较,所以此操作将失败并且 `int position = mData.indexOf(object);每张卡总是返回-1。这会导致异常。我必须先解决这个问题。

以上是关于Android - 从 pageView 中删除对象会导致空白的主要内容,如果未能解决你的问题,请参考以下文章

将值从子视图页面快速传递到 Pageview 控制器

BigQuery - 从数组中删除重复项

Android - 新的 BottomNavigationBar 中的 PageView - 防止重新加载片段

Android:FragmentAdapter 和 pageview 不更新视图

如何使用流畅的动画从页面视图中删除页面?

Flutter PageView 禁止滑动