1

我有一个列表视图,我想用来自文本文件 (rollcall.txt) 的信息进行更新。每次更新 rollcall.txt 时,我都会调用 rollcall()(代码如下)。在调用 rollcall() 之前,文本文件中的数据已正确更新,我已经检查过了。我遇到的问题是,直到我下次调用 rollcall() 时,列表视图才会显示更新的条目(IE 它似乎总是落后一个更新步骤)。

我哪里错了?

   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

编辑:从此方法调用点名:

  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 
}

感谢您的帮助,我是使用阵列适配器的新手。

安迪

4

3 回答 3

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

这是您按下“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() 函数中的对话框中输入数据。您应该使用更优雅的方法在其他地方更新您的列表视图,以防您的应用程序抛出异常,例如围绕该 try/catch 块。至少也许你应该finally{}在它的末尾添加一个块并在那里更新你的列表视图,即使尝试部分失败。希望这回答了你的问题:)

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

于 2013-07-26T01:34:33.570 回答
1

每次更新适配器时调用adapter.notifyDataSetChanged(),然后列表视图将自动更新

于 2013-07-19T07:34:59.863 回答
1

我建议您将点名作为异步任务运行,原因有两个。首先,它不会在rollcall()运行时停止您的 UI。

其次,您将能够调用onPostExecute(Object o)您可以调用 `adapter.notifyDataSetChanged(); '

于 2013-07-25T19:14:20.543 回答