【问题标题】:ListView data deleted and data updated, notifyDataSetChanged called, but no updated of my listListView 数据删除和数据更新, notifyDataSetChanged 调用,但我的列表没有更新
【发布时间】:2012-03-01 16:35:00
【问题描述】:

我有一个带有自定义适配器的 ListView,它是从本地 sqlite 数据库填充的。当用户长按项目并选择“删除”时,将弹出对话框再次确认操作,并在确认单击后删除项目。 我的问题是该项目已从数据库中删除,并且 arraylist(adapter 的数据集)已更新,但 ListView 未更新,即使我已经调用了 notifyDataSetChanged()。
//删除方法设置在AlertDialog/PositiveButton click listener

我已经检查或尝试了以下事情,但仍然...
1。重建它(继续创建),然后列表视图将正确显示,所以应该不是其他问题。
2。我调用了相同的适配器,已通过相同的 hashCode 确认
3。 mListView.setAdapter((ListViewAdapter)(ListViewer.mListView.getAdapter()));
4。 allResArray.clear();加载数据();

谁能帮帮我?谢谢。我已经卡了2天了!!

这是我的代码:

private static ListView mListView;                  
private ListViewAdapter mListViewAdapter;
public String[] strListRID;
public List<List<String>> allResArray;

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);    
    setView(R.layout.listviewer);
    init(); 
}

public void init(){
    setTitle(getString(R.string.app_name));
    setTitleBar(R.drawable.title_home, 0, R.drawable.title_add, 0, 
            0, 0, R.drawable.titlebar_title, 0);
    initListView();     
}

//MainListView initialization    
private void initListView(){
    boolean SD_STATE = checkSDState();
    allResArray = new ArrayList<List<String>>();
    loadData();

    mListView = (ListView) findViewById(R.id.ListView);
    mListViewAdapter = new ListViewAdapter(this, SD_STATE, allResArray);
    mListView.setAdapter(mListViewAdapter);
    Log.i("mListViewAdapter", String.valueOf(mListViewAdapter.hashCode()));

    mListView.setCacheColorHint(0x00000000);    

    // Click listener to ListView
    mListView.setOnItemClickListener(new OnItemClickListener() {
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            goIntent(5, strListRID[position]);
        }   
    });   

    // ContextMenu listener to ListView
    mListView.setOnCreateContextMenuListener(new OnCreateContextMenuListener() {       
        @Override
        public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {
            menu.setHeaderTitle(R.string.conext_menu_title);       
            menu.add(0, 0, 0, R.string.conext_menu_edit);
            menu.add(0, 1, 0, R.string.conext_menu_delete);
        }       
    });  
}

@Override  
public boolean onContextItemSelected(MenuItem item){
    AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
    switch (item.getItemId()) {
    case 0:         //Edit
        goIntent(5, strListRID[menuInfo.position]);
        return true;    
    case 1:         //Delete
        showDeleteDialog(strListRID[menuInfo.position], menuInfo.position);
        break;                  
    default:
        break;        
    }
    return super.onContextItemSelected(item); 
}

private void loadData() {
    allResArray = getAllDatabaseRes();
    int size = allResArray.get(0).size();
    strListRID = allResArray.get(0).toArray(new String[size]);
}

/**
 * AlertDialog: confirm before delete data
 * Remove it from ArrayList and notifyDataSetChanged() to adapter
 * @param strListRID
 */
private void showDeleteDialog(final String strListRID, final int position) {
    AlertDialog.Builder dialog = new AlertDialog.Builder(this);
    dialog.setIcon(R.drawable.dialog_notice);
    dialog.setMessage(R.string.text_delete);        
    dialog.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
            if (delRes(Integer.parseInt(strListRID))){
                                    int size =((ListViewAdapter)(mListView.getAdapter())).mResArray.size();
                for (int i = 0; i < size; i++){
                    ((ListViewAdapter)(mListView.getAdapter())).mResArray.get(i).remove(position);
                }
                ((ListViewAdapter)(ListViewer.mListView.getAdapter())).notifyDataSetChanged();
                Log.i("mListViewAdapter_check", String.valueOf(((ListViewAdapter)(ListViewer.mListView.getAdapter())).hashCode()));
                showShortToast((String) getResources().getText(R.string.text_record_delete_ok));
            }
            else{
                showShortToast((String) getResources().getText(R.string.text_record_delete_ng));
            }   
        }
    });
    dialog.setNegativeButton(R.string.text_no, null);   
    dialog.show();      
}

BaseActivity中的delRes()代码:

 /**
 * Delete the specific rid record from database
 * @return boolean DEL_OK
 * @throws SQLException
 */
protected boolean delRes(int strRID) throws SQLException {    
    boolean DEL_OK = false;
    String deleteWhere = "rid=?";
    String[] deleteWhereValue = { String.valueOf(strRID)};
    Log.i("rid to delete", deleteWhereValue[0]);

    int delrow = dbHelper.delete(tables[0], deleteWhere, deleteWhereValue);
    Log.i("delrow", String.valueOf(delrow));

    if (delrow == 1){
        DEL_OK = true;
    }
    return DEL_OK;
}

还有 logCat:

02-09 15:19:59.744: I/mListViewAdapter(28235): 1163110296
02-09 15:19:59.784: I/tmp(28235): /sdcard/Photo/20120208164204.jpg
02-09 15:19:59.814: I/tmp(28235): /sdcard/Photo/20120208163627.jpg
02-09 15:19:59.854: I/tmp(28235): /sdcard/Photo/20120209151917.jpg
02-09 15:20:05.514: I/menuInfo.position(28235): 1
02-09 15:20:08.634: I/rid to delete(28235): 10
02-09 15:20:08.674: I/delrow(28235): 1
02-09 15:20:08.674: I/mListViewAdapter_check(28235): 1163110296

当前解决方案:设置新的 listviewadapter

    /**
 * AlertDialog: confirm before delete data
 * Remove it from ArrayList and notifyDataSetChanged() to adapter
 * @param strListRID
 */
private void showDeleteDialog(final String mRID, final int position) {
    AlertDialog.Builder dialog = new AlertDialog.Builder(this);
    dialog.setIcon(R.drawable.dialog_notice);
    dialog.setMessage(R.string.text_delete);        
    dialog.setPositiveButton(R.string.btn_delete, new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {

            if (delRes(Integer.parseInt(mRID))){    
                int size = allResArray.size();
                for (int i = 0; i < size; i++){
                    allResArray.get(i).remove(position);
                }
                strListRID = allResArray.get(0).toArray(new String[size-1]);

                //((ListViewAdapter)(mListView.getAdapter())).notifyDataSetChanged();
                mListView.setAdapter(new ListViewAdapter(getApplicationContext(), true, allResArray));
                showShortToast((String) getResources().getText(R.string.text_record_delete_ok));
            }
            else{
                showShortToast((String) getResources().getText(R.string.text_record_delete_ng));
            }   
        }
    });
    dialog.setNegativeButton(R.string.text_no, null);   
    dialog.show();      
}

【问题讨论】:

  • 你能解释一下你在这个声明中做了什么吗? for (int i = 0; i
  • List> allResArray;其中包括 8 个 List。这是从每个列表中删除选定的项目。
  • 抱歉 4 问了太多问题,但请告诉我一些事情..1) 您的列表视图实际上向用户显示了什么?列出对象? 2)您的 Arraylist 绑定到您的适配器(我同意),但不是从该 ArrayList 中删除项目,而是从该 ArrayList 内的列表中删除单个项目。您认为这将如何影响您的列表视图对最终用户的外观?
  • 感谢您的提问,但我有点困惑。 listview 将 allResArray 作为参数传递给 listviewadapter 以构建一个包含多个 textview 和 imageview 的视图。这是我定制的listviewadapter。调用adapter.notifyDataSetChanged()不是目的吗?它可以像 setAdapter() 一样调用 getView() 来刷新列表视图,我有什么误解吗?谢谢。顺便说一句,我也试过 setAdapter(),没有运气......
  • 目前的解决方案是新建一个适配器。我不认为重新初始化一个是正确的,UI总是重新加载并显示在顶部位置,我猜用户体验很糟糕......

标签: android android-listview


【解决方案1】:

尝试直接从适配器中删除指定的行,然后调用 notifyDataSetChanged()。或者您可以创建新适配器,迭代您的 ArrayList 并将数据添加到您的新适配器,然后将此适配器设置为您的 ListView。

【讨论】:

  • 确实,创建一个新的适配器可以很好地工作,但我就是不明白为什么 notifyDataSetChanged() 不能工作并更新列表视图?跟踪arraylist,在调用delete(removeAll或clear方法)后它变成不同的arraylist(hashCode=1),但我确信arraylist只初始化了一次。那么,问题出在哪里?
  • 我才意识到你说的删除应该是在(ListViewAdapter)(mListView.getAdapter())).mResArray上操作的吧?我试了一下,数据集是正确的,但是listview还是没有更新! (问题中包含的代码包括 listviewadapter 更新)
  • 初始化arraylist一次不是问题,当你从arraylist中删除数据时,你应该再次这样做:mListViewAdapter = new ListViewAdapter(this, SD_STATE, allResArray); mListView.setAdapter(mListViewAdapter);
  • 谢谢,是的,init 一个 listviewadapter 可以更新 listview,但我究竟是如何使用 notifyDataSetChanged() 做错了?顺便说一句,当前解决方案会将列表视图卷起在顶部位置,用户需要向下滚动然后...
  • 我不确定出了什么问题,因为我也遇到了这种方法的问题。关于滚动列表,您应该在 arrayList 中保留所选项目的一些索引,然后调用listview.setSelection(index)
【解决方案2】:

我也遇到过同样的问题。在删除后备存储中的项目并通过调用 notifyDataSetChanged() 通知适配器之后,似乎 ListView.confirmCheckedPositionsById() 在内部被调用,因为适配器开始工作以响应 notifyDataSetChanged() 。 ONE 解决方案 如您所述,是用新的适配器替换适配器 - 但正如您所指出的,这会弄乱用户在列表中的位置,并且用户必须再次向下滚动 - 这很糟糕.

所以我所做的是检查我的派生适配器是否超出了保存数据的数组的范围,当超出范围时,只返回一个 0 的项目 ID。(请注意,我在此适配器中有稳定的 ID也许这有帮助)在这种情况下,我的后备存储是 mTC,我在调用检查项目 ID 的适配器时执行此操作:

类 TaskAdapter 扩展 BaseAdapter 实现 OnClickListener {

... @Override public long getItemId(int position){ // 将来从 Task.java 中拉出 tode 后更新它 if (位置>=mTC.size()) 返回0; 返回 mTC.getTask(position).hashCode(); } ... }

这解决了问题,BaseAdapter 的内部代码似乎意识到最后一项不再有效......并且一切正常。我希望这对那些遇到这个问题的人有所帮助。

【讨论】:

    【解决方案3】:

    看来你所有的数据都来自 ArrayList。在这种情况下,从数据库中删除一个条目时,您可以执行以下操作,

    1. 清除所有ResArray
    2. 从同一数组中的数据库再次加载数据
    3. notifyDataSetChanged

    【讨论】:

    • 嗨,我确实尝试过 allResArray.clear();加载数据();但它仍然不起作用。我也检查过数据的正确性,无论是remove()还是reload方式,列表都没有更新。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-24
    • 1970-01-01
    • 2016-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多