Android 解決 adapter.notifyDataSetChanged() 不起作用
使用 Listview 的時候,給 adapter 的資料來源 List 添加了新的資料,然後呼叫 adapter.notifyDataSetChanged(),發現 listview 並沒有顯示出新增的資料,但是遍歷輸出 List 中的元素,發現新增資料已經被新增到 List 中了,資料既然已經被新增到資料來源中了,為什麼 Listview 沒有更新呢?
上網,查書,查了半天,終於在《Android群英傳》中找到答案:
使用 adapter.notifyDataSetChanged() 時,必須保證傳進 Adapter 的資料 List 是同一個 List
而不能是其他物件,否則無法更新 listview。
即,你可以呼叫 List 的 add(), remove(), clear(),addAll() 等方法,這種情況下,List 指向的始終是你最開始 new 出來的 ArrayList ,然後呼叫 adapter.notifyDataSetChanged() 方法,可以更新 ListView;但是如果你重新 new 了一個 ArrayList(重新申請了堆記憶體),那麼這時候,List 就指向了另外一個 ArrayLIst,這時呼叫 adapter.notifyDataSetChanged() 方法,就無法重新整理 listview 了。
說了這麼多,不如來寫個小 Demo 來感受下:
開啟 Android Studio,新建一個 ListViewTest 專案。
activity_main.xml 程式碼如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height ="match_parent"
android:orientation="vertical"
tools:context="com.example.listviewtest.MainActivity">
<Button
android:id="@+id/btn_add_success"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="新增資料成功"/>
<Button
android:id="@+id/btn_add_fail"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="新增資料失敗"/>
<Button
android:id="@+id/btn_delete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="刪除資料"/>
<Button
android:id="@+id/btn_clear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="清空資料"/>
<ListView
android:id="@+id/listview"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</LinearLayout>
佈局檔案很簡單,從上至下,依次是 3 個按鈕和一個 ListView。
MainActivity.java 程式碼如下:
package com.example.listviewtest;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
public static final String TAG = "MainActivity";
/**
* 新增成功 按鈕
*/
private Button btnAddSuccess;
/**
* 新增失敗 按鈕
*/
private Button btnAddFail;
/**
* 刪除資料 按鈕
*/
private Button btnDelete;
/**
* 清空資料 按鈕
*/
private Button btnClear;
private ListView listView;
/**
* 介面卡的資料來源
*/
private List<Integer> dataList;
/**
* 介面卡
*/
private ArrayAdapter adapter;
/**
* 新增的資料
*/
private int addNumber = 3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 例項化控制元件
btnAddSuccess = (Button) findViewById(R.id.btn_add_success);
btnAddFail = (Button) findViewById(R.id.btn_add_fail);
btnDelete = (Button) findViewById(R.id.btn_delete);
btnClear = (Button) findViewById(R.id.btn_clear);
listView = (ListView) findViewById(R.id.listview);
// 設定點選監聽
btnAddSuccess.setOnClickListener(this);
btnAddFail.setOnClickListener(this);
btnDelete.setOnClickListener(this);
btnClear.setOnClickListener(this);
// 初始化資料
initData();
// 建立介面卡
adapter = new ArrayAdapter<>(MainActivity.this, android.R.layout
.simple_list_item_1, dataList);
// 設定介面卡
listView.setAdapter(adapter);
}
/**
* 初始化資料來源
*/
private void initData() {
dataList = new ArrayList<>();
for (int i = 0; i < 3; i++) {
dataList.add(i);
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
// 如果點選的是 新增資料成功 按鈕
case R.id.btn_add_success:
// 新增資料 成功
dataList.add(addNumber);
adapter.notifyDataSetChanged();
Toast.makeText(MainActivity.this, "新增成功", Toast.LENGTH_SHORT).show();
Log.d(TAG, "資料來源:" + dataList.toString());
break;
// 如果點選的是 新增資料失敗 按鈕
case R.id.btn_add_fail:
dataList = new ArrayList<>();
initData();
// 新增資料失敗
dataList.add(addNumber);
adapter.notifyDataSetChanged();
Toast.makeText(MainActivity.this, "新增失敗", Toast.LENGTH_SHORT).show();
Log.d(TAG, "資料來源:" + dataList.toString());
break;
// 如果點選的是 刪除資料 按鈕
case R.id.btn_delete:
if (!dataList.isEmpty()) {
// 刪除 list 最後一個數據
int deleteNumber = dataList.get(dataList.size() - 1);
dataList.remove(deleteNumber);
adapter.notifyDataSetChanged();
Toast.makeText(MainActivity.this, "刪除資料", Toast.LENGTH_SHORT).show();
Log.d(TAG, "資料來源:" + dataList.toString());
} else {
Toast.makeText(MainActivity.this, "已經沒有資料了", Toast.LENGTH_SHORT).show();
Log.d(TAG, "資料來源:" + dataList.toString());
}
break;
// 如果點選的是 清空資料 按鈕
case R.id.btn_clear:
// 清空資料
dataList.clear();
adapter.notifyDataSetChanged();
Toast.makeText(MainActivity.this, "清空資料", Toast.LENGTH_SHORT).show();
Log.d(TAG, "資料來源:" + dataList.toString());
break;
}
}
}
MainActivity.java 程式碼也很簡單,給四個按鈕加上了按鍵監聽,然後讓 ListView 顯示了 3 個數字,接下來,我們先點選 新增資料成功,刪除資料,清除資料 ,這 3 個按鈕,看 adapter.notifyDataSetChanged() 能否正常工作。
檢視動態圖,我們可以看到,點選這 3 個按鈕,adapter.notifyDataSetChanged() 都可以正常工作,因為資料來源始終是同一個 List,修改的只是 List 中的資料。
我們重新啟動下 ListViewTest,再來看看點選 新增資料失敗 按鈕,adapter.notifyDataSetChanged() 能否正常工作。
檢視 Logcat ,我們發現,資料來源中的資料已經更新了:
但是檢視動態圖,我們可以看到,點選按鈕,ListView 並沒有更新資料,這時因為重新 new 了一個 ArrayList(重新申請了堆記憶體),那麼這時候,List 就指向了另外一個 ArrayLIst,而要更新 ListView,必須保證傳進 Adapter 的資料 List 是同一個 List 而不能是其他物件,所以 ListView 就無法更新了。