![](/img/trans.png)
[英]Error using notifyDataSetChanged in android array adapter
[英]Android ListView Adapter error calling notifyDataSetChanged, Android bug?
在我一直在努力的應用程序中,我有一個自定義類DeviceListAdapter
擴展BaseAdapter
,它被傳遞給我的ListView
。 在我的DeviceListAdapter
類中,我保留了自己的ArrayList<Device>
,我使用它來使用View getView(... )
生成列表的視圖。 每當應用程序導致數據更改時,我都會使用DeviceListAdapter
自定義方法更新ArrayList<Device>
以反映更改。 我已經使用調試器和許多print語句檢查數據是否按照我預期的方式更改,添加和刪除指定的Device
對象。 但是,每次更改數據后我也會調用notifyDataSetChanged()
,但在UI上沒有任何元素得到更新。 在調試器中,我發現在調用notifyDataSetChanged()
之后,沒有調用getView(... )
方法,這解釋了為什么ListView
沒有被重繪。 為了找出原因,我使用調試器的“步入”功能來跟蹤程序執行進入android框架的位置,因為我已經下載了SDK源代碼。 我發現的非常有趣。 執行的路徑是這樣的:
DeviceListAdapter.notifyDataSetChanged()
BaseAdapter.notifyDataSetChanged()
DataSetObservable.notifyChanged()
AbsListView.onInvalidated()
而是調用onChanged()
方法,它跳轉軌道並在到達AbsListView
執行onInvalidated()
方法。 最初我認為這是一個錯誤,調試器可能讀錯了行號,但我重新啟動了我的Android Studio以及完全卸載並重新安裝了應用程序,但結果是一樣的。 任何人都可以告訴我,如果這是合法的Android框架問題,或者調試器是否不可靠跟蹤我自己的項目文件之外的執行?
更多關於我的notifyDataSetChanged()
...我創建了本地方法來覆蓋BaseAdapter
的notifyDataSetChanged()
這樣我就可以在我的DeviceListAdapter
設置一個布爾標志mForceRedraw
,以確定是否應該強制重繪我的列表條目。 在getView(... )
方法中,我通常檢查第二個參數, View convertView
是否為null,如果是,那么我重繪視圖,如果沒有,那么我通過convertView並返回它。 但是,當'mForceRedraw'為真時,我永遠不會返回convertView
,我明確地重繪了視圖。 出現的問題是由我之前的關注引起的,即我執行notifyDataSetChanged()
后沒有調用getView()
notifyDataSetChanged()
。
編輯:這是我的DeviceListAdapter
的代碼片段:
/**
* Serves data about current Device data to the mDeviceListView. Manages the dynamic and
* persistent storage of the configured Devices and constructs views of each individual
* list item for placement in the list.
*/
private class DeviceListAdapter extends BaseAdapter {
private boolean mForceRedraw = false;
/**
* Dynamic array that keeps track of all devices currently being managed.
* This is held in memory and is readily accessible so that system calls
* requesting View updates can be satisfied quickly.
*/
private List<Device> mDeviceEntries;
private Context mContext;
public DeviceListAdapter(Context context) {
this.mContext = context;
this.mDeviceEntries = new ArrayList<>();
populateFromStorage();
}
/**
* Inserts the given device into storage and notifies the mDeviceListView of a data update.
* @param newDevice The device to add to memory.
*/
public void put(Device newDevice) {
Preconditions.checkNotNull(newDevice);
boolean flagUpdatedExisting = false;
for (Device device : mDeviceEntries) {
if (newDevice.isVersionOf(device)) {
int index = mDeviceEntries.indexOf(device);
if(index != -1) {
mDeviceEntries.set(index, newDevice);
flagUpdatedExisting = true;
break;
} else {
throw new IllegalStateException();
}
}
//If an existing device was not updated, then this is a new device, add it to the list
if (!flagUpdatedExisting) {
mDeviceEntries.add(newDevice);
}
TECDataAdapter.setDevices(mDeviceEntries);
notifyDataSetChanged();
}
/**
* If the given device exists in storage, delete it and remove it from the mDeviceListView.
* @param device
*/
public void delete(Device device) {
Preconditions.checkNotNull(device);
//Remove device from mDeviceEntries
Iterator iterator = mDeviceEntries.iterator();
while(iterator.hasNext()) {
Device d = (Device) iterator.next();
if(device.isVersionOf(d)) {
iterator.remove();
}
}
TECDataAdapter.setDevices(mDeviceEntries);
notifyDataSetChanged();
}
/**
* Retrieves Device entries from persistent storage and loads them into the dynamic
* array responsible for displaying the entries in the listView.
*/
public void populateFromStorage() {
List<Device> temp = Preconditions.checkNotNull(TECDataAdapter.getDevices());
mDeviceEntries = temp;
notifyDataSetChanged();
}
public int getCount() {
if (mDeviceEntries != null) {
return mDeviceEntries.size();
}
return 0;
}
public Object getItem(int position) {
return mDeviceEntries.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(final int position, View convertView, ViewGroup parent) {
LinearLayout view;
if (convertView == null || mForceRedraw) //Regenerate the view
{
/* Draws my views */
} else //Reuse the view
{
view = (LinearLayout) convertView;
}
return view;
}
@Override
public void notifyDataSetChanged() {
mForceRedraw = true;
super.notifyDataSetChanged();
mForceRedraw = false;
}
}
notifyDataSetChanged()
存在許多錯誤,如果您嘗試對列表數據進行一些復雜的工作,通常會出現這些錯誤。
大多數情況下,這是因為該方法是惰性的,無法區分更改,因此要避免此問題,請使用以下方案測試代碼:
notifyDataSetChanged()
notifyDataSetChanged()
並告訴我它是否解決了你的問題。
編輯:放入適配器代碼后,我看到了代碼中的缺陷。
很抱歉遲到了回復:
convertView
是在初始化之前填充的視圖。
當在方法getView()
獲得convertView
的實例時,必須在返回之前填充它。 所以要清楚,做這樣的事情:
public View getView(final int position, View convertView, ViewGroup parent) {
View view;
if (convertView == null) //Regenerate the view
{
/* Instantiate your view */
} else {
view = convertView;
}
// populate the elements in view like EditText, TextView, ImageView and etc
return view;
}
您在適配器和調用通知數據集已更改 。理想情況下甚至不需要。因為您正在修改適配器內部使用的數據集。只要需要呈現視圖,就會始終調用適配器的getView方法。
convertView方法是單獨回收視圖(而不是數據)。它只是為您提供了昂貴的視圖通脹過程的替代方法。
那么你的代碼應該是什么:
public View getView(final int position, View convertView, ViewGroup parent) {
LinearLayout view;
if (convertView == null) //Regenerate the view
{
/* inflate Draws my views */
} else
{
view = (LinearLayout) convertView;
}
//repopulate this view with the data that needs to appear at this position using getItem(position)
return view;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.