【问题标题】:DynamicListView items disappear on drag & dropDynamicListView 项目在拖放时消失
【发布时间】:2015-07-09 03:48:04
【问题描述】:

我遵循这个谷歌教程并实现了一个动态的 ListView 拖放 https://www.youtube.com/watch?v=_BZIvjMgH-Q

listView = (DynamicListView) findViewById(R.id.listview);
adapter = new StableArrayAdapter(this, R.layout.text_view, arraylist);
listView.setAdapter(adapter);
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);

// added the items later and called notifyDataSetChanged();
    arraylist.add("item 1");
    arraylist.add("item 2");
    arraylist.add("item 3");
    arraylist.add("item 4");

adapter.notifyDataSetChanged();

它显示了列表视图中的所有项目,但是当我拖放元素时它们消失了。

如果我在设置适配器 listView.setAdapter(adapter); 之前添加元素,它完全可以正常工作。

库源码http://developer.android.com/shareables/devbytes/ListViewDraggingAnimation.zip

我该如何解决这个问题?

更新

StableArrayAdapter.java

package com.example.android.navigationdrawerexample;

import android.content.Context;
import android.widget.ArrayAdapter;

import java.util.HashMap;
import java.util.List;

public class StableArrayAdapter extends ArrayAdapter<String> {

    final int INVALID_ID = -1;

    int mRowLayout;
    List<String[]> mStrings;
    HashMap<String, Integer> mIdMap = new HashMap<String, Integer>();

    // constructor
    public StableArrayAdapter(Context context, int rowLayout, List<String> objects) {
        super(context, rowLayout, objects);
        for (int i = 0; i < objects.size(); ++i) {
            mIdMap.put(objects.get(i), i);
        }
    }

    @Override
    public long getItemId(int position) {
        if (position < 0 || position >= mIdMap.size()) {
            return INVALID_ID;
        }
        String item = getItem(position);
        return mIdMap.get(item);
    }

    @Override
    public boolean hasStableIds() {
        return true;
    }
}

更新 2 我按照建议在此处将项目添加到 objects 中,现在它工作正常,但它与在设置适配器之前在 MainActivity 中添加项目不同吗?

public StableArrayAdapter(Context context, int rowLayout, List<String> objects) {
    super(context, rowLayout, objects);

    objects.add("item 1");
    objects.add("item 2");
    objects.add("item 3");
    objects.add("item 4");
    objects.add("item 5");

    for (int i = 0; i < objects.size(); ++i) {
        mIdMap.put(objects.get(i), i);
    }
}

更新 3 对象的大小仅在构造函数中知道 和 mIdMap.put("项目 6", objects.size());在代码中没有任何效果。

 // constructor
    public StableArrayAdapter(Context context, int rowLayout, List<String> objects) {
        super(context, rowLayout, objects);

        this.objects = objects;

        objects.add("item 1");
        objects.add("item 2");
        objects.add("item 3");
        objects.add("item 4");
        objects.add("item 5");

        for (int i = 0; i < objects.size(); ++i) {
            mIdMap.put(objects.get(i), i);
        }

        mIdMap.put("item 6", objects.size());    // has no effect

    }

    public void addItem(String item) {
        int index = objects.size();
        mIdMap.put(item, index);
    }

【问题讨论】:

  • 请发布 StableArrayAdapter 的代码。并添加另一个“拖放”标签。在我看来,这篇文章不适用于“android-animation”。
  • 这个问题你解决了吗?如果没有,您可以回复我发布的答案以了解您对此的状态。
  • Vinay,下次,在我的回答中发表您的评论,以便我注意到它。我在更新 2 中几乎忽略了您的问题。或者在您的帖子中添加您的问题,并作为对我发布的答案的评论。无论如何....
  • Vinay,答案是它与设置适配器后在 MainActivity 中添加项目不同。您的原始代码在设置适配器后添加了更多字符串。原因是对象 mIdMap 的内存地址与对象 arraylist 不同。这是Java中的重要知识,祝你好运。
  • Vinay,我在发布的答案的 UPDATE 部分下添加了 cmets/code。

标签: android android-listview drag-and-drop


【解决方案1】:

我通常将项目添加到 ArrayList 或修改适配器代码(在您的情况下为 StableArrayAdapter)中的对象,而不是像您所做的那样在适配器之外。但只要 ArrayAdapter 之外的对象 StableArrayAdapter 中的列表 共享相同的地址,这可能没问题。我怀疑不是。

在您的情况下,请确保 arraylist.add("item 1") 之类的代码将字符串添加到 StableArrayAdapter 中的 ArrayList 对象中。同样,我怀疑不是。

为了加快速度,请张贴StableArrayAdapter 的代码。如果一定要下载代码,不方便读者帮忙。

新建议代码:

StableArrayAdapter 中,添加一个公共方法,如:

// This method is opposite of getItemId().
public void addItem(String item) {
    // Add the string into the objects
    objects.add(item);

    // size of objects should be added by one after add() above
    int index = objects.size();
    mIdMap.put(item, index);
}

StableArrayAdapter 外部:

listView.setAdapter(adapter);
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);

// added the item and called notifyDataSetChanged();
adapter.addItem("item 1");
adapter.addItem("item 2");

adapter.notifyDataSetChanged();

注意:不是向arraylist添加项目,而是调用StableArrayAdapter的方法,对象适配器。

更新 1:问题是 DynamicListView 类中的方法 setCheeseList() 和 swapElements() 不适用于动态添加项目。 StableArrayAdapter 中的addItem() 也发生了变化。 我已经在我的项目中解决了这个问题,但解释起来并不容易。所以我会试试的。

代码建议:

第 1 部分,来自:

private void handleCellSwitch() {
...
   swapElements(mCheeseList, originalItem, getPositionForView(switchView));
   ((BaseAdapter) getAdapter()).notifyDataSetChanged();
...

到:

private void handleCellSwitch() {
...
   // Swap the 2 items with animation for the dragNdrop feature
   final SwappableListAdapter adapter = (SwappableListAdapter) getAdapter();
   adapter.swap(originalItem, getPositionForView(switchView));
...

注意事项:

  • 我没有调用本地方法swapElements() 在mCheeseList 之间交换值,而是调用接口SwappableListAdapter 的方法swap(),该方法在StableArrayAdapter 类中实现。
  • 基本上,这个想法是依赖于在StableArrayAdapter 中获取正确的数据。

第 2 部分 接口代码SwappableListAdapter

public interface SwappableListAdapter {
        /**
         * Swaps between 2 items in the adapter, one is the mobile item, another is the selected item.
         * Method notifyDataSetChanged is called in the swap() when finished.
         * @param index1 The selected row view, dragged and to be moved.
         * @param index2 The row which moved due to the dragged row item.
         */
    public void swap(int index1, int index2);
}

注意事项:

  • 这是在 DynamicListView 类中创建的接口,因为这是将进行实际调用的类。
  • 这将修复编译器错误问题。

第 3 部分StableArrayAdapter 类中实现swap() 方法的代码。

收件人:

public class StableArrayAdapter extends ArrayAdapter<String> implements DynamicListView.SwappableListAdapter {
   ...
   @Override
   public void swap(int indexOne, int indexTwo) {
      // Swap mIdMap object related to indexOne and indexTwo
      String item = getItem(indexOne);

      notifyDataSetChanged();
}

注意事项:

  • swap() 需要工作。我的项目不使用 HashMap 进行交换,而且我不喜欢 HashMap 的当前实现(来自 Google 工程师)。我可能有时间修复它。也许您可以修复此方法。希望您现在已经有了这个想法。
  • StableArrayAdapter 类现在实现了接口 SwappableListAdapter,并且可以访问覆盖 swap()。
  • 接口的想法是适配器现在是存储数据的中心点!当前的实现将数据同时存储在 Adapter 和 DynamicListView 中,这不应该。

【讨论】:

  • @Vinay,查看发布的代码,现在我知道我是正确的。一个简单的解决方案是在 StableArrayAdapter 中创建一个公共方法,该方法接受 arraylist 中的新字符串值。该方法将更新对象 mIdMap。你怎么看?你能做到吗?
  • 有没有其他方法可以在MainActivity中添加对象?因为在我的一个应用程序中,我使用 Retrofit API 来检索 5 个不同位置的天气信息。因此,我在 for 循环中调用 API -> 将其添加到 arraylist -> adapter.notifyDataSetChanged();添加了元素,但是当我拖动Ndrop时它们消失了。
  • @Vinay,我在“新建议代码部分”中添加了文本。我希望这很清楚。最重要的是,我希望你明白为什么你的原始代码不起作用。
  • 我现在可以更好地理解,但我仍然无法使其工作。当我添加行 mIdMap.put("item 6", objects.size()); 时看到很奇怪在 UPDATE 2 中的 for 循环之后,它不会显示在列表视图中。我错过了什么吗?
  • @Vinay,我在 UPDATE 部分下添加了 cmets/code。
【解决方案2】:

感谢@The Original Android

在动态添加项目时使其工作的更改摘要。

StableArrayAdapter.java

添加了addItem方法

public void addItem(String item) {
    // Add the string into the objects
    objects.add(item);

    // size of objects should be added by one after add() above
    int index = objects.size();
    mIdMap.put(item, index);
}

MainActivity.java

listView.setCheeseList(al);
listView.setAdapter(adapter);
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);

// called addItem method inside MainActivity
adapter.addItem("item 1");
adapter.addItem("item 2");
adapter.addItem("item 3");
adapter.addItem("item 4");

adapter.notifyDataSetChanged();

DynamicListView.java

改变自

    private void handleCellSwitch() {
     ...   
       swapElements(mCheeseList, originalItem, getPositionForView(switchView));
       ((BaseAdapter) getAdapter()).notifyDataSetChanged();
     ...
    }

   private void handleCellSwitch() { 
    ...      
      final SwappableListAdapter adapter = (SwappableListAdapter) getAdapter();
      adapter.swap(mCheeseList,originalItem, getPositionForView(switchView)); // I still had to pass the arraylist   
    ...
    }

在 DynamicListView.java 中创建了一个接口

public interface SwappableListAdapter {    
    public void swap(int index1, int index2);
}

StableArrayAdapter.java 实现了 SwappableListAdapter 接口并覆盖了 swap 方法

public class StableArrayAdapter extends ArrayAdapter<String> implements DynamicListView.SwappableListAdapter {

    @Override
    public void swap(ArrayList a,int index1, int index2) {

        Object temp = a.get(index1);
        a.set(index1, a.get(index2));
        a.set(index2, temp);

        notifyDataSetChanged();
    }
}

【讨论】:

  • 很高兴您解决了这个问题!我仍然不想依赖 mCheeseList 列表对象。 mCheeseList 太强大了,因为即使 DynamicListView 也可以更改该对象中的内容。我也很高兴您对代码进行了全面测试,因为由于我的代码结构,我无法测试。但是我仍然想对此做进一步的调查并做出更好的解决方案(在我看来)。你想让我通知你吗?
  • 请。如果您有任何发现,请通知我
【解决方案3】:

这可能不是完全相同的问题,但它是相似的,谷歌引导我来到这里。我的列表视图大部分都在工作,但是有一定的动作序列会使某些项目消失。之后单击它们会导致 NullPointerException。

以下是重现该错误的步骤:

  1. 将项目拖到顶部。
  2. 向上或向下拖动另一个项目。
  3. 顶部的项目将消失。
  4. 向上或向下拖动另一个项目。
  5. 顶部的项目将重新出现。
  6. 如果您转到第 2 步,行为将继续

调试后发现我的 StableArrayAdapter.getView() 方法被调用了两次,只针对列表顶部的空白项。这让我想到了这个问题Adapter getView is called multiple times with position 0 其中提到“给出一个动态的全高”。

为了解决这个问题,我将 DynamicListView 的 layout_height 设置为“wrap_content”。

【讨论】:

    猜你喜欢
    • 2012-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多