我有一个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的新手。
安迪
答案 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放到它上面。第一次按下按钮时,所有内容都会被调用,列表视图会更新,并且您的对话框会挂在屏幕上,以便按下任一按钮:
你会看到这样的日志:
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()。因此,您的列表视图将使用您刚刚在对话框中输入的新数据进行更新:
按“确定”后,这是日志的最后一部分你可以看到正在调用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(); “