【问题标题】:Can't update/redraw listView inside of onPostExecute无法在 onPostExecute 内更新/重绘 listView
【发布时间】:2014-02-10 18:32:24
【问题描述】:

我有这个代码:

package com.gs.britishjokes;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;

import org.json.JSONArray;
import org.json.JSONException;

import android.app.Activity;
import android.app.Application;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.Menu;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;


public class TopJokes extends Activity {

    public static class Globals extends Application{
           String[] myStringArray = {"a","b","c","a","b","c","a","b","c","a","b","c","a","b","c","a","b","c"};

        public String[] getMyStringArray() {
            return myStringArray;
        }

        public void setMyStringArray(String[] myStringArray) {
            this.myStringArray = myStringArray;
        }

    }

    public static ArrayAdapter<String> adapter;
    public static ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_top_jokes);        

        Globals globals = (Globals)getApplication();

        new loadJson().execute();

        adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, globals.getMyStringArray());

        listView = (ListView) findViewById(R.id.topJokesList);
        listView.setAdapter(adapter);

        listView.setOnItemClickListener(new OnItemClickListener() {
            public void onItemClick(AdapterView<?> parent, View view,
                    int position, long id) {
                // When clicked, show a toast with the TextView text
                Toast.makeText(getApplicationContext(),
                ((TextView) view).getText(), Toast.LENGTH_SHORT).show();
            }
        });

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.top_jokes, menu);
        return true;
    }

    public class loadJson extends AsyncTask<Void, Integer, String>{
        @Override
        protected String doInBackground(Void... params) {
            URL u;
            StringBuffer buffer = new StringBuffer();
            try {
                u = new URL("https://site.com/android/britishJokes/showJokes.php");
                URLConnection conn = u.openConnection();
                BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));

                String inputLine;
                while ((inputLine = in.readLine()) != null) 
                    buffer.append(inputLine);
                in.close();         
            }catch(Exception e){
                e.printStackTrace();
            }
            return buffer.toString();
        }

        protected void onPostExecute(String buffer) {
            Globals globals = (Globals)getApplication();
            JSONArray jsonArray = new JSONArray();
            String[] newJokes = {};
            String[] myStringArray = {"co","ao","bo","co","ao","bo","co","ao","bo","co"};
            try {
                jsonArray = new JSONArray(buffer);
            } catch (JSONException e) {
                e.printStackTrace();
            }

            for (int i = 0; i < jsonArray.length(); i++) {
                try {                   
                    newJokes[i] = jsonArray.getJSONObject(i).toString();
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
            globals.setMyStringArray(myStringArray);
            System.out.println("GLOBAL ARRAY: " + globals.getMyStringArray());

            adapter.notifyDataSetChanged();
//          listView.invalidate();
//          listView.destroyDrawingCache();
//          listView.setVisibility(ListView.INVISIBLE);
//          listView.setVisibility(ListView.VISIBLE);

            //listView.invalidateViews();

         }

    }
}

我正在尝试使用下面的代码更新onPostExecute 内部的listView(也可以在上面的代码块中找到)

adapter.notifyDataSetChanged();
//          listView.invalidate();
//          listView.destroyDrawingCache();
//          listView.setVisibility(ListView.INVISIBLE);
//          listView.setVisibility(ListView.VISIBLE);

            //listView.invalidateViews();

注释行是我尝试过但没有成功的东西。

我注意到代码运行正常,但 listView 没有得到更新。如果我按下后退按钮然后再次打开相同的活动,我可以看到正确的结果myStringArray,但是一旦asyncTask 完成,如何更新 listView?应该有办法做到这一点。我知道我错过了一些非常小的东西,但作为一个完全的初学者,我无法发现它。请给我一个线索

编辑:

添加后的catlog异常

adapter.clear();
adapter.addAll(myStringArray);

02-10 13:50:09.594: E/AndroidRuntime(1460): FATAL EXCEPTION: main
02-10 13:50:09.594: E/AndroidRuntime(1460): Process: com.gs.britishjokes, PID: 1460
02-10 13:50:09.594: E/AndroidRuntime(1460): java.lang.UnsupportedOperationException
02-10 13:50:09.594: E/AndroidRuntime(1460):     at java.util.AbstractList.remove(AbstractList.java:638)
02-10 13:50:09.594: E/AndroidRuntime(1460):     at java.util.AbstractList$SimpleListIterator.remove(AbstractList.java:75)
02-10 13:50:09.594: E/AndroidRuntime(1460):     at java.util.AbstractList.removeRange(AbstractList.java:658)
02-10 13:50:09.594: E/AndroidRuntime(1460):     at java.util.AbstractList.clear(AbstractList.java:466)
02-10 13:50:09.594: E/AndroidRuntime(1460):     at android.widget.ArrayAdapter.clear(ArrayAdapter.java:258)
02-10 13:50:09.594: E/AndroidRuntime(1460):     at com.gs.britishjokes.TopJokes$loadJson.onPostExecute(TopJokes.java:114)
02-10 13:50:09.594: E/AndroidRuntime(1460):     at com.gs.britishjokes.TopJokes$loadJson.onPostExecute(TopJokes.java:1)
02-10 13:50:09.594: E/AndroidRuntime(1460):     at android.os.AsyncTask.finish(AsyncTask.java:632)
02-10 13:50:09.594: E/AndroidRuntime(1460):     at android.os.AsyncTask.access$600(AsyncTask.java:177)
02-10 13:50:09.594: E/AndroidRuntime(1460):     at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:645)
02-10 13:50:09.594: E/AndroidRuntime(1460):     at android.os.Handler.dispatchMessage(Handler.java:102)
02-10 13:50:09.594: E/AndroidRuntime(1460):     at android.os.Looper.loop(Looper.java:137)
02-10 13:50:09.594: E/AndroidRuntime(1460):     at android.app.ActivityThread.main(ActivityThread.java:4998)
02-10 13:50:09.594: E/AndroidRuntime(1460):     at java.lang.reflect.Method.invokeNative(Native Method)
02-10 13:50:09.594: E/AndroidRuntime(1460):     at java.lang.reflect.Method.invoke(Method.java:515)
02-10 13:50:09.594: E/AndroidRuntime(1460):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777)
02-10 13:50:09.594: E/AndroidRuntime(1460):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:593)
02-10 13:50:09.594: E/AndroidRuntime(1460):     at dalvik.system.NativeStart.main(Native Method)

【问题讨论】:

    标签: java android android-asynctask android-activity


    【解决方案1】:

    执行以下操作:

     adapter.clear();
     adapter.addAll(myStringArray);
    

    如果低于 API 11:

    执行以下操作:

     adapter.clear();
     for(String s:myStringArray){
         adapter.add(s);
     }
     adapter.notifyDataSetChanged();
    

    问题是当你打电话时

      globals.setMyStringArray(myStringArray);
    

    您正在更改数组全局字符串数组指向,但不是一个适配器指向。

    假设 A 和 B 分别是之前和之后的数组。 最初:

    全局数组------->A

    当你创建适配器时:

    适配器------------->A

    但是当你再次调用 setMyStringArray 时,你会这样做:

    全局数组------>B

    编辑

    改变:

    adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, globals.getMyStringArray());
    

    到:

     adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, new ArrayList<String>());
     adapter.clear();
     adapter.addAll(globals.getMyStringArray());
    

    【讨论】:

    • adapter.addAll(myStringArray); 正在使我的活动崩溃:(
    • 显示日志猫。这个函数也只有在 API 11 之后才可用,否则循环遍历数组并逐个添加
    • 刚刚添加到帖子中 - 我使用的是 API 19
    • 当您使用数组初始化数组适配器时会发生这种情况。只需用数组列表初始化它,然后使用 clear 和 addAll 函数来更新它
    • 完美运行!!!非常感谢您的帮助!!!我希望有一天我能成为像你一样伟大的开发者!再次感谢!!!
    【解决方案2】:

    您已经调用的adapter.notifyDataSetChanged(); 将为您执行无效(等),因此没有必要,您似乎忽略了预先将新数据设置到适配器中。 adapter.addAll(globals); 之类的东西或在您正在更改的数据上注册一个观察者(当您创建适配器时)

    【讨论】:

      【解决方案3】:

      对于您完成的工作,我可以建议一个解决方案,将其从该行中删除并粘贴/代替该行。

       listView.setAdapter(adapter);
       listView.notifyDataSetChanged;
      

      对于 ArrayAdapter,notifyDataSetChanged 仅在您使用 Adapter 上的 add、insert、remove 和 clear 函数时才有效。按照这个链接notifyDataSetChanged example

      编辑

        package com.example.testandroid;
      
        import java.io.BufferedReader;
        import java.io.InputStreamReader;
        import java.net.URL;
        import java.net.URLConnection;
      
        import org.json.JSONArray;
        import org.json.JSONException;
      
        import android.os.AsyncTask;
        import android.os.Bundle;
        import android.provider.ContactsContract;
        import android.app.Activity;
        import android.app.Application;
        import android.content.res.Resources;
        import android.database.Cursor;
        import android.view.Menu;
        import android.view.View;
        import android.widget.AdapterView;
        import android.widget.AdapterView.OnItemClickListener;
        import android.widget.ArrayAdapter;
        import android.widget.ImageButton;
        import android.widget.ImageView.ScaleType;
        import android.widget.LinearLayout;
        import android.widget.ListView;
        import android.widget.Toast;
      
        public class MainActivity extends Activity {
      
      public static class Globals {
          String[] myStringArray = { "a", "b", "c", "a", "b", "c", "a", "b", "c",
                  "a", "b", "c", "a", "b", "c", "a", "b", "c" };
      
          public String[] getMyStringArray() {
              return myStringArray;
          }
      
          public void setMyStringArray(String[] myStringArray) {
              this.myStringArray = myStringArray;
          }
      
      }
      
      public static ArrayAdapter<String> adapter;
      public static ListView listView;
      Globals globals;
      
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
          globals = new Globals();
          new loadJson().execute();
      
          listView = (ListView) findViewById(R.id.mylist);
          adapter = new ArrayAdapter<String>(MainActivity.this,
                  android.R.layout.simple_list_item_1, globals.getMyStringArray());
      
          listView.setAdapter(adapter);
          listView.setOnItemClickListener(new OnItemClickListener() {
              public void onItemClick(AdapterView<?> parent, View view,
                      int position, long id) {
                  // When clicked, show a toast with the TextView text
                  Toast.makeText(getApplicationContext(), view + "",
                          Toast.LENGTH_SHORT).show();
              }
          });
      
      }
      
      public class loadJson extends AsyncTask<Void, Integer, String> {
          @Override
          protected String doInBackground(Void... params) {
              URL u;
              StringBuffer buffer = new StringBuffer();
              try {
                  u = new URL(
                          "https://gelasoft.com/android/britishJokes/showJokes.php");
                  URLConnection conn = u.openConnection();
                  BufferedReader in = new BufferedReader(new InputStreamReader(
                          conn.getInputStream()));
      
                  String inputLine;
                  while ((inputLine = in.readLine()) != null)
                      buffer.append(inputLine);
                  in.close();
              } catch (Exception e) {
                  e.printStackTrace();
              }
              return buffer.toString();
          }
      
          protected void onPostExecute(String buffer) {
      
              String[] myStringArray = { "co", "ao", "bo", "co", "ao", "bo",
                      "co", "ao", "bo", "co" };
      
              globals.setMyStringArray(myStringArray);
              System.out.println("GLOBAL ARRAY: " + globals.getMyStringArray());
      
              adapter = new ArrayAdapter<String>(MainActivity.this,
                      android.R.layout.simple_list_item_1,
                      globals.getMyStringArray());
              adapter.notifyDataSetChanged();
              listView.setAdapter(adapter);
              // listView.invalidate();
              // listView.destroyDrawingCache();
              // listView.setVisibility(ListView.INVISIBLE);
              // listView.setVisibility(ListView.VISIBLE);
      
              // listView.invalidateViews();
      
          }
      
      }
       }
      

      【讨论】:

      • @vipulmittal 你能告诉我为什么这不起作用。我知道这肯定会显示 o/p。
      • 正如 vipul 所说。如果您查看了他已经完成的代码,并且您的答案是因为您认为每次数据更改时设置一个新适配器是一个可接受的解决方案,您应该通读 android 教程。 “我知道这肯定会显示 o/p。”也是一个直接的谎言 - 你不知道,事实上考虑到数据从未设置到适配器中,我们可以放心地假设我们知道它不会
      • 问题是@monmohan,问题是他有一个数组而不是数组列表,他正在更改对数组的引用而不是它的内容。即使重新设置适配器也无法解决问题
      • @vipulmittal 我从没想过可以使用这种方式(设置参考然后更新),我尝试并粘贴了我告诉 setAdapter 的代码,我知道多次使用 setAdapter 不是好办法。
      【解决方案4】:

      确保使用支持 ArrayAdapter 上的 clear() 和 add() 方法的数组列表类型来初始化 ArrayAdapter,如果使用 String[] 初始化它就不会!

      要解决此问题,请执行以下操作:

      String[] stringArray = {"item_1","Item_2"}
      //IMPORTANT!
      List<String> listArray = new ArrayList<String>(Arrays.asList(stringArray));
      //Then initialize the ArrayAdapter with the List array
      adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,listArray);
      listView.setAdapter(adapter);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-01-27
        • 2019-06-05
        • 2014-01-10
        • 1970-01-01
        • 1970-01-01
        • 2012-01-28
        相关资源
        最近更新 更多