Android:notifyDataSetChanged()无效

29

我在服务器上有一个数据库,从平板电脑上我可以获取数据库中一个表的一些值。我将这些信息正确地加载到了列表中,但我想知道为什么即使我使用notifyDataSetChanged();,当有变化时什么也不会发生。我必须说,为了加载数据,我使用了AsyncTaskClass。

所以,我的问题是我不知道是否正确使用了notifyDataSetChanged();方法,因为如果有变化,我想刷新图像。这里是该类的一部分代码:

@Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.all_candidatos);


        candidatosList = new ArrayList<HashMap<String, String>>();

        new CargarCandidatos().execute();
    }


//  public void timer(){
//       new CountDownTimer(tiempo, 100) {
//
//              public void onTick(long millisUntilFinished) {
//                  
//              }
//
//              public void onFinish() {
//              //  new CargarCandidatos().execute();
//
//              }
//           }.start();}



    /**
     * Background Async Task to Load all product by making HTTP Request
     * */
    class CargarCandidatos extends AsyncTask<String, String, String> {

        /**
         * Before starting background thread Show Progress Dialog
         * */
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            pDialog = new ProgressDialog(Monitorizacion.this);
            pDialog.setMessage("Loading ...");
            pDialog.setIndeterminate(false);
            pDialog.setCancelable(false);
            pDialog.show();
        }

        /**
         * getting All products from url
         * */
        protected String doInBackground(String... args) {
            List<NameValuePair> params = new ArrayList<NameValuePair>();
            JSONObject json = jParser.makeHttpRequest(url_candidatos, "GET", params);

            Log.d("Candidatos: ", json.toString());

            try {
                int success = json.getInt(TAG_SUCCESS);

                if (success == 1) {

                    candidatos = json.getJSONArray(TAG_CANDIDATOS);

                    for (int i = 0; i < candidatos.length(); i++) {
                        JSONObject c = candidatos.getJSONObject(i);

                        // Storing each json item in variable
                        String nserie = c.getString(TAG_NSERIE);
                        String dni = c.getString(TAG_DNI);
                        String nombre = c.getString(TAG_NOMBRE);
                        String test = c.getString(TAG_TEST);
                        String pregunta = c.getString(TAG_PREGUNTA);
                        String bateria = c.getString(TAG_BATERIA);

                        // creating new HashMap
                        HashMap<String, String> map = new HashMap<String, String>();

                        // adding each child node to HashMap key => value
                        map.put(TAG_NSERIE, nserie);
                        map.put(TAG_DNI, dni);
                        map.put(TAG_NOMBRE, nombre);
                        map.put(TAG_TEST, test);
                        map.put(TAG_PREGUNTA, pregunta);
                        map.put(TAG_BATERIA, bateria);

                        // adding HashList to ArrayList
                        candidatosList.add(map);
                    }
                } 
            } catch (JSONException e) {
                e.printStackTrace();
            }

            return null;
        }

        /**
         * After completing background task Dismiss the progress dialog
         * **/
        protected void onPostExecute(String file_url) {
            pDialog.dismiss();
            runOnUiThread(new Runnable() {
                public void run() {
                    /**
                     * Updating parsed JSON data into ListView
                     * */
                    adapter = new SimpleAdapter(
                            Monitorizacion.this, candidatosList,
                            R.layout.list_item, new String[] { TAG_NSERIE,
                                    TAG_DNI, TAG_NOMBRE, TAG_TEST, TAG_PREGUNTA, TAG_BATERIA},
                            new int[] { R.id.id, R.id.dni, R.id.nombre, R.id.test, R.id.pregunta, R.id.bateria});
                    setListAdapter(adapter);
                    adapter.notifyDataSetChanged();
                //  timer();
                }
            });

        }

    }
}

有任何崩溃吗?你不想要那个runOnUIThread,只需设置适配器。确保candidatosList具有一些值。 - Triode
嗨Triode,我在屏幕上加载了所有数据库值,活动没有崩溃。问题是,即使数据库得到更新,UI也不会使用新值进行刷新。 - Katherine99
@Katherine99 如果我的回答解决了您的问题,您可以将其标记为已选答案。 :) - Sazzad Hissain Khan
8个回答

119

主要原因之一,为什么notifyDataSetChanged()无法工作 - 是因为:

您的适配器失去了对列表的引用

当您首次初始化Adapter时,它会获取arrayList的引用并将其传递给其超类。但是,如果重新初始化现有的arrayList,它将失去引用,从而与Adapter的通信渠道也丢失了。

创建并向Adapter添加新列表时,请始终遵循以下准则:

  1. 在全局声明期间初始化arrayList
  2. 直接将列表添加到适配器中,而不检查null和empty值。直接将适配器设置到列表中(不检查任何条件)。适配器保证您无论何时对arrayList的数据进行更改,都会加以处理,但绝不会丢失引用。
  3. 始终在arrayList本身中修改数据(如果您的数据完全是新的,则可以在实际添加数据到列表之前调用adapter.clear()arrayList.clear()),但不要设置适配器,即:如果新数据填充在arrayList中,那么只需调用adapter.notifyDataSetChanged()

遵循文档的指导。


4
非常感谢,我的问题在于不相信适配器的功率。 - J.Vassallo
2
非常感谢您的解释,我已经理解了关于Adapter和ListView的许多内容,并找到了许多疑问的解决方案。 - Sai
如果数据集是游标,我该如何实现相同的操作呢? 我不能清除游标并重新添加所有项目。 - Rohan Taneja
您可以使用CursorAdapter。请参阅 https://github.com/codepath/android_guides/wiki/Populating-a-ListView-with-a-CursorAdapter。 - Sazzad Hissain Khan
大家都在谈论ArrayList!但如果你的数据是Cursor类型呢?我向数据库发出了一个新的查询(不同的查询),现在我用新的Cursor设置适配器,但通知更改却不起作用!第二次调用中的适配器具有空游标! - Araz

5

你可能需要检查是否存在一个空的registerDataSetObserver()覆盖。Android Studio为我添加了一个,但没有实现调用super的方法。按照以下方式添加即可使listView正常工作:

@Override
    public void registerDataSetObserver(DataSetObserver observer) {
        super.registerDataSetObserver(observer);    
    }

5
你需要编辑的内容是放置你的
runOnUiThread(new Runnable() {
                public void run() {
                    /**
                     * Updating parsed JSON data into ListView
                     * */
                    adapter = new SimpleAdapter(
                            Monitorizacion.this, candidatosList,
                            R.layout.list_item, new String[] { TAG_NSERIE,
                                    TAG_DNI, TAG_NOMBRE, TAG_TEST, TAG_PREGUNTA, TAG_BATERIA},
                            new int[] { R.id.id, R.id.dni, R.id.nombre, R.id.test, R.id.pregunta, R.id.bateria});
                    setListAdapter(adapter);
                    adapter.notifyDataSetChanged();
                //  timer();
                }
            });

将其放入OnCreate()中,并从Asynctask返回candidatosList列表。然后设置定时器以更新candidatosList列表。


抱歉,它在不刷新的情况下继续运行。 - Katherine99
嗨,如果我使用candidatosList.clear();,当我运行活动时,列表视图将不包含任何数据。 - Katherine99
请确保您的candidatosList数据每次都会被更新。如果与之前相同,您可以删除单个项目并设置adapter.notifyDataSetChanged();然后调用setListAdapter(adapter); - user1621629
感谢在添加runOnUiThread(new Runnable())后,代码可以正常工作,但我想知道这是如何运作的? - h_patel

3
一个适配器定义了布局的行为! -> setListAdapter():为ListView/GridView/Gallery定义适配器...但您需要指定数据!
我建议您在' onCreate '或构造函数中初始化'setListAdapter'。 在将数据设置到适配器中后(例如:adapter.setItem(yourData)),请调用notifyDataSetChanged!
因为您已更改数据,但视图未刷新,notifydatasetchanged()重新加载视图的内容(ListView/GridView/Gallery...)
为了良好的实践和正确理解,我建议您使用“自定义适配器”使用“baseAdapter”
阅读并完成此教程(我曾经通过这个学习):http://www.androidhive.info/2012/02/android-custom-listview-with-image-and-text/ 阅读文档:http://developer.android.com/reference/android/widget/BaseAdapter.html

谢谢Anthony,我会阅读那些网站并告诉你结果。 - Katherine99

2
更新函数应该从UI线程调用。 我的答案实际上与@user1621629的答案类似,不同之处在于我使用了rxJava,所以这里是解决这个问题的工作代码:
this.subscriber = myAdapter.getSubscriber(); // keep for unsubscribe in destroy
dataSource.subscribeOn(Schedulers.computation()).observeOn(AndroidSchedulers.mainThread()).subscribe(this.subscriber);

我将所有获取列表数据的执行操作都放到了计算线程中,但是将结果显示在UI线程中。

下面是我创建此订阅者的方法:

public class MyListAdapter extends RecyclerView.Adapter<LocationListAdapter.ViewHolder> {


private List<ListItem> mDataset = new ArrayList<>();

public Subscriber<ListItem[]> getSubscriber() {
    return Subscribers.create(new Action1<ListItem[]>() {
        @Override
        public void call(ListItem[] listItems) {
            mDataset.clear();
            mDataset.addAll(Arrays.asList(listItems));
            notifyDataSetChanged();
        }
    });
}
......

1
我没有足够的声望来评论Hissain先生的回答。它是正确的,但我想提一件事情,就是列表的引用不应该改变。如果数据源发生了改变,请勿将引用更改为新列表。只需对同一列表对象执行操作即可。为此,请使用clear()清除列表,然后使用add()或addAll()向同一列表添加数据,然后调用notifyDataSetChanged()。例如,在列表的第一次初始化时。
list = dataSource.getList();

如果在代码中尝试更改到其他对象的引用,那么可以添加和删除列表中的内容并调用notifyDataSetChanged(),它可以正常工作。
list = dataSource.getList();

getList()方法每次都返回新的列表,因此引用会更改为其他列表对象,并且调用notifyDataSetChanged对列表没有影响。但是如果getList()方法返回相同的列表对象,则可以正常工作。


1

正如Hissain上面所描述的那样

您需要维护对列表的引用。

以下是我让它正常工作的方法:

  1. 让传递给适配器的列表在活动中设置为实例成员

  2. 在执行更改数据的逻辑中,请确保更新活动传递给适配器的同一列表实例

  3. 然后调用.notifyDataSetChanged(); 即可

请记住,listView位置从1开始,因此您必须对java.util.List进行(listViewPosition - 1)处理。


0
如果您的所有设置都正确,但仍未生效,则需要检查您的列表是否是可变类型!

private val demoList: MutableList<AnyClass> = mutableListOf()

如果您像上面那样以可变方式定义列表,那么您就可以获取该方法

.add
.addAll
.remove

如果您创建了普通列表,则无法使用notifyDataSetChanged

等等...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接