来自arrayadapter填充文本文件的Listview总是一个更新

时间:2013-06-22 16:38:47

标签: android-arrayadapter

我有一个listview,我想用文本文件(rollcall.txt)中的信息更新。每次更新rollcall.txt时,我都会调用rollcall()(下面的代码)。在调用rollcall()之前,数据在文本文件中正确更新,我已经检查过了。我遇到的问题是listview没有显示更新的条目,直到我下次调用rollcall()时(I.E它似乎总是落后一个更新步骤)。

我哪里错了?

   public void rollcall(){
             String[] splitdata = null;
            try{
               File myFile = new File(Environment.getExternalStorageDirectory() + "/rollcall.txt");
                    FileInputStream fIn = new FileInputStream(myFile);
                    BufferedReader myReader = new BufferedReader(
                            new InputStreamReader(fIn));
                    String aDataRow = "";
                    String aBuffer = "";
                    while ((aDataRow = myReader.readLine()) != null) {
                        aBuffer += aDataRow + "\n";
                    }   
                    splitdata = aBuffer.split("`"); //recover the file and split it based on `
                    myReader.close();
            }
               catch (Exception e) {
                      Toast.makeText(getBaseContext(), e.getMessage(),
                              Toast.LENGTH_SHORT).show();
              }
            ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.logbooklayout, splitdata);
            lv1.setAdapter(adapter);
            adapter.notifyDataSetChanged(); //called to ensure updated data is refreshed into listview without reload

编辑:从此方法调用rollcall:

  public void onClick(View v) {
if (v==badd){
                    AlertDialog.Builder alert = new AlertDialog.Builder(this);
                    alert.setTitle("ROLLCALL"); //Set Alert dialog title here
                    alert.setMessage("Enter data: "); //Message here

                    // Set an EditText view to get user input 
                    final EditText input = new EditText(this);
                    alert.setView(input);

                    alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton) {
                     //You will get as string input data in this variable.
                     // here we convert the input to a string and show in a toast.
                     add = input.getEditableText().toString();
                     try {
                        File myFile = new File(Environment.getExternalStorageDirectory() + "/rollcall.txt");
                        myFile.createNewFile();
                        FileOutputStream fOut = new FileOutputStream(myFile, true);
                        OutputStreamWriter myOutWriter = new OutputStreamWriter(fOut);
                        myOutWriter.append(add);
                        myOutWriter.append("`");  // ` used to split the file down later in lv section
                        myOutWriter.close();
                        fOut.close();
                }
                catch (Exception e) {
                    Toast.makeText(getBaseContext(), e.getMessage(),
                            Toast.LENGTH_SHORT).show();
                    }               
                    } // End of onClick(DialogInterface dialog, int whichButton)
                }); //End of alert.setPositiveButton
                    alert.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() {
                      public void onClick(DialogInterface dialog, int whichButton) {
                        // Canceled.
                          dialog.cancel();
                      }
                }); //End of alert.setNegativeButton
                    AlertDialog alertDialog = alert.create();
                    alertDialog.show();
                    rollcall();
            }//end badd 
}

感谢您的帮助,我是使用arrayadapters的新手。

安迪

3 个答案:

答案 0 :(得分:2)

对您的问题的简短回答是UI线程中的所有内容都是异步的,除非您以某种方式设法冻结/锁定整个应用程序,否则您无法让其余的UI等待警报获取输入。在您的警报中按“确定”按钮之前很久,您的rollcall()方法正在从您的onClick()函数调用,并且您的.txt文件中的任何内容正在您的UI上读取/显示,紧随其后您的警报对话框挂起,您可以异步按下其中一个按钮。

在确认适配器的馈送数据实际已被更改之后,可能最快的解决方案就是在其他地方调用rollcall()功能。如果您必须在onClick()函数内调用它,而不会质疑您这样做的理由,则应在关闭输出流后立即在try{}块内调用它。

像这样:

try {
    File myFile = new File(Environment
            .getExternalStorageDirectory() + "/rollcall.txt");
    myFile.createNewFile();
    FileOutputStream fOut = new FileOutputStream(myFile, true);
    OutputStreamWriter myOutWriter = new OutputStreamWriter(
            fOut);
    myOutWriter.append(add);
    myOutWriter.append("`"); // ` used to split the
                                // file down later
                                // in lv section
    myOutWriter.close();
    fOut.close();

    rollcall();

}

这个“有效”的原因是你已经为你的“确定”按钮宣布了听众,每当你按下它时,你EditText input内的任何内容都会写在档案中。为了让它像以前一样工作,我认为你需要超人技能在警告对话框上写一些文本,然后在同一范围内调用rollcall()函数之前单击按钮。

显然更新列表视图的更好方法是能够使用adapter.notifyDataSetChanged(),但我相信你应该把它调到别的地方而不是你在文件上写的地方,在这种情况下你的适配器必须在外面声明rollcall()函数的范围。

无论如何为了展示这一切是怎么回事,我创建了一个简单(相当丑陋)的Android应用程序,并在一些日志中发现了神秘的东西:

public class MainActivity extends Activity {

    private ListView lv1;
    private Button refreshButton;

    ArrayAdapter<String> adapter;
    String[] splitdata;

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

        refreshButton = (Button) findViewById(R.id.refreshButton);

        lv1 = (ListView) findViewById(R.id.someTextViewId);

        refreshButton.setOnClickListener(myButtonhandler);
        splitdata = null;
    }

    View.OnClickListener myButtonhandler = new View.OnClickListener() {
        public void onClick(View v) {
            Log.d("main", "la noliy");

            someFunction();
        }
    };

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

    public void someFunction() {

        AlertDialog.Builder alert = new AlertDialog.Builder(this);
        alert.setTitle("ROLLCALL"); // Set Alert dialog title here
        alert.setMessage("Enter data: "); // Message here

        // Set an EditText view to get user input
        final EditText input = new EditText(this);
        alert.setView(input);

        alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int whichButton) {
                // You will get as string input data in this
                // variable.
                // here we convert the input to a string and show in
                // a toast.
                String add = input.getEditableText().toString();
try {
    File myFile = new File(Environment
            .getExternalStorageDirectory() + "/rollcall.txt");
    myFile.createNewFile();
    FileOutputStream fOut = new FileOutputStream(myFile, true);
    OutputStreamWriter myOutWriter = new OutputStreamWriter(
            fOut);
    myOutWriter.append(add);
    myOutWriter.append("`"); // ` used to split the
                                // file down later
                                // in lv section
    myOutWriter.close();
    fOut.close();

    if (splitdata.length > 0) {
        rollcall(new String("call from inside"));
}
                } catch (Exception e) {
                    Toast.makeText(getBaseContext(), e.getMessage(),
                            Toast.LENGTH_SHORT).show();
                }
            } // End of onClick(DialogInterface dialog, int
                // whichButton)
        }); // End of alert.setPositiveButton
        alert.setNegativeButton("CANCEL",
                new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int whichButton) {
                        // Canceled.
                        dialog.cancel();
                    }
                }); // End of alert.setNegativeButton
        AlertDialog alertDialog = alert.create();
        alertDialog.show();
        Log.d("someFunction", "before rollcall");
        Log.d("someFunction", "location: "
                + Environment.getExternalStorageDirectory().getAbsolutePath());
        rollcall(new String("call from outside"));
        Log.d("someFunction", "after rollcall");
    }// end badd

    public void rollcall(String message) {

        Log.d("rollcall", message);

        try {
            File myFile = new File(Environment.getExternalStorageDirectory()
                    + "/rollcall.txt");
            FileInputStream fIn = new FileInputStream(myFile);
            BufferedReader myReader = new BufferedReader(new InputStreamReader(
                    fIn));
            String aDataRow = "";
            String aBuffer = "";
            while ((aDataRow = myReader.readLine()) != null) {
                aBuffer += aDataRow + "\n";
            }
            splitdata = aBuffer.split("`"); // recover the file and split it
                                            // based on `
            myReader.close();

        } catch (Exception e) {
            Toast.makeText(getBaseContext(), e.getMessage(), Toast.LENGTH_SHORT)
                    .show();
        }

        int length = splitdata.length;
        for (int i = 0; i < length; i++) {
            Log.d("rollcall", splitdata[i]);
        }
        adapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_1, splitdata);
        lv1.setAdapter(adapter);
    }
}

我把一个按钮和一个onClickListener放到它上面。第一次按下按钮时,所有内容都会被调用,列表视图会更新,并且您的对话框会挂在屏幕上,以便按下任一按钮: first_press_on_refresh

你会看到这样的日志:

07-26 04:09:20.802: D/someFunction(11273): before rollcall
07-26 04:09:20.802: D/someFunction(11273): location: /mnt/sdcard
07-26 04:09:20.802: D/rollcall(11273): call from outside
07-26 04:09:20.802: D/rollcall(11273): some data
07-26 04:09:20.802: D/rollcall(11273): some other data
07-26 04:09:20.812: D/someFunction(11273): after rollcall

您可以看到rollcall()已从外部调用,而不是在try / catch块内调用,因为还有另一个调用来自rollcall()。但是当你按下按钮时,你的try / catch块将在你的onClick()函数中完成它的工作,之后将调用rollcall()。因此,您的列表视图将使用您刚刚在对话框中输入的新数据进行更新:

after_press_ok

按“确定”后,这是日志的最后一部分你可以看到正在调用rollcall()并且它可以读取新数据:

07-26 04:09:46.347: D/rollcall(11273): call from inside
07-26 04:09:46.357: D/rollcall(11273): some data
07-26 04:09:46.357: D/rollcall(11273): some other data
07-26 04:09:46.357: D/rollcall(11273): new data

最后,我确信在你的问题的整个方法中有很多丑陋。最重要的是你需要知道UI线程中发生的一切都是异步的,没有人在等待你在onClick()函数的对话框中输入数据。您应该使用更优雅的方法在其他地方更新listview,以防您的应用程序抛出异常,例如围绕该try / catch块。至少可能你应该在它的末尾添加一个finally{}块并在那里更新你的listview,即使try部分失败了。希望这能回答你的问题:)

PS。对于那些想在家中试用的人,请记住在layout.xml文件中向findViewById()函数提供TextView id,以获取代码中的ListView引用,而不是实际的ListView id。是的,我知道......

答案 1 :(得分:1)

每次更新适配器时都会调用adapter.notifyDataSetChanged(),然后listview会自动更新

答案 2 :(得分:1)

我建议您将rollcall作为异步任务运行有两个原因。首先,当rollcall()正在运行时,它不会停止您的用户界面。

其次,你可以调用onPostExecute(Object o)你可以调用`adapter.notifyDataSetChanged(); “