我动态添加的 table 行没有出现在我的应用程序中
My dynamically added table rows are not appearing in my app
我正在尝试创建一个具有开始和停止按钮的应用程序,该按钮收集加速计数据并在表格行中显示 x、y 和 z 坐标。每次传感器检测到变化时,都应添加具有新坐标集的行。最初,我没有包含按钮或 onPause() 或 onResume() 方法,行将被动态添加。但是,由于添加了太多行,应用程序会冻结并耗尽我的电池。因此,除了用于记录数据的开始和停止按钮外,我还添加了 onPause 和 onResume 方法。不幸的是,按钮可以工作,但在屏幕充满行后数据行停止更改。我使用 LogCat 来检查是否正在添加行,它们确实是。我只是不知道为什么他们没有全部出现。我的代码粘贴在下面。布局文件在同一代码块中的 java 文件之后。任何帮助将不胜感激!
package com.explorer.extractor;
//import packages
import android.app.ActionBar;
import android.content.Context;
import android.graphics.Color;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Calendar;
public class MainActivity extends ActionBarActivity implements SensorEventListener,OnClickListener {
Button button1;
Button button2;
private SensorManager mSensorManager;
Sensor accelerometer;
private static final String TAG = MainActivity.class.getName();
private static final String FILENAME = "myFile.txt"; //file where data is written
//layout variables
TableLayout t1;
TextView dataReading; //declare data text object
Integer count = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//initialize sensor manager
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
//initialize accelerometer
accelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
//mSensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_FASTEST);
t1 = (TableLayout) findViewById(R.id.main_table);
button1 = (Button) findViewById(R.id.button1);
button2 = (Button) findViewById(R.id.button2);
button1.setOnClickListener(this);
button2.setOnClickListener(this);
}
public void onAccuracyChanged(Sensor sensor, int accuracy){}
/**onResume() registers the accelerometer for listening
* to the events
*/
protected void onResume(){
super.onResume();
mSensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_FASTEST);
}
protected void onPause(){
super.onPause();
mSensorManager.unregisterListener(this);
}
public void onSensorChanged(SensorEvent event){
//if sensor status result is unreliable return
if (event.accuracy == SensorManager.SENSOR_STATUS_UNRELIABLE){
return;
}
Sensor sensor = event.sensor;
//check sensor type
if (sensor.getType() == Sensor.TYPE_ACCELEROMETER){
//assign directions
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
try {
//write to text file the x, y, and z values each type a sensor detects change
writeToFile(Float.toString(x), Float.toString(y), Float.toString(z));
//return string of text file
String textFromFileString = readFromFile();
TableRow tr = new TableRow(this);
if(count%2!=0) {
tr.setBackgroundColor(Color.GRAY);
}
tr.setId(100 + count);
tr.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT));
//show data read from file
dataReading = new TextView(this);
dataReading.setId(200 + count);
dataReading.setText(textFromFileString);
dataReading.setPadding(2, 0, 5, 0);
dataReading.setTextColor(Color.WHITE);
tr.addView(dataReading);
//finally add data to table row
t1.addView(tr, new TableLayout.LayoutParams(ActionBar.LayoutParams.MATCH_PARENT, ActionBar.LayoutParams.WRAP_CONTENT));
count++;
Log.i("LIMA","Add row. There are now " + t1.getChildCount()+"rows");
}catch (IOException e) {
e.printStackTrace();
}
}
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1:
mSensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_FASTEST);
break;
case R.id.button2:
mSensorManager.unregisterListener(this);
}
}
/**
* writeToFile: writes data recordings of accelerometer to text file
* @param x
* @param y
* @param z
* @throws IOException
*/
void writeToFile(String x, String y, String z) throws IOException {
//get exact instance of time in which call to write is being made
Calendar c = Calendar.getInstance();
//create string to print to text using values in parameter
String s = c.get(Calendar.HOUR) + ":" + c.get(Calendar.MINUTE) + ":" + c.get(Calendar.SECOND) + ":" + c.get(Calendar.MILLISECOND) + " - " + "x: " + x + " y: " + y + " z: " + z + "\n";
try {
//append new string to file
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(openFileOutput(FILENAME, Context.MODE_PRIVATE));
BufferedWriter bw = new BufferedWriter(outputStreamWriter);
bw.write(s);
bw.flush();
bw.close();
} catch(IOException e) {
Log.e(TAG, "File write failed: " + e.toString());
}
}
private String readFromFile(){
String ret = "";
try {
//open text file to read from
InputStream inputStream = openFileInput(FILENAME);
if (inputStream != null) {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String receiveString = "";
StringBuilder stringBuilder = new StringBuilder();
while ((receiveString = bufferedReader.readLine()) != null) {
//continue appending to stringBuilder until you've reached the end of file
stringBuilder.append(receiveString);
}
inputStream.close();
ret = stringBuilder.toString();
}
} catch (FileNotFoundException e) {
Log.e(TAG, "File not found: " + e.toString());
} catch (IOException e) {
Log.e(TAG, "Can not read file: " + e.toString());
}
return ret;
}
}
Here is my layout file:
<?xml version="1.0" encoding="utf-8"?>
<TableLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_table"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:shrinkColumns="*"
android:stretchColumns="*">
<TableRow
android:id="@+id/tablerow"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:id="@+id/button1"
android:layout_height="wrap_content"
android:text="Start"
android:layout_weight=".5"
android:layout_width="match_parent"
android:gravity="center_horizontal"/>
<Button
android:id="@+id/button2"
android:layout_height="wrap_content"
android:text="Stop"
android:layout_weight=".5"
android:layout_width="match_parent"
android:gravity="center_horizontal"/>
</TableRow>
</TableLayout>
要回答您的问题:您需要将 TableLayout
包装在 ScrollView
:
中
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TableLayout
android:id="@+id/main_table"
...
但是,您的代码存在几个问题:-)
可能是最严重的:不要在主线程上执行任何 I/O。 IE。不要 read/write 到 main/UI 线程上的文件。在Android中有很多创建线程的方法,但两个例子是AsyncTask
和RxJava/RxAndroid。您可以在 logcat 日志中看到这是一个问题,例如:
I/Choreographer﹕ Skipped 34 frames! The application may be doing too much work on its main thread.
记录接收到的每一个事件不是一个好主意,因为当您使用 SensorManager.SENSOR_DELAY_FASTEST
时,加速度计事件接收得非常频繁。即使降低频率,将数万个条目添加到 UI 列表仍然不是一个好主意。您可能只想显示最后 x 个条目,出于 UI 目的,您不需要超过每秒 1 或 2 个条目或类似的东西,即使您想登录到文件更频繁。
TextView dataReading
不应是成员字段 - 只需在需要的地方将其声明为局部变量即可。始终尽可能靠近使用它们的地方声明变量。
- 您没有将
TableLayout
用作 table,常规的 ListView
就足够了。
- 稍微分解一下代码。看看单一职责原则。就像一个随机的例子,文件处理的东西可以移到一个单独的 class.
- 为什么要将数据写入文本文件,然后再次从文件中读取相同的数据?这是不必要的 I/O :-) 您根本不需要阅读该文件。 (假设您首先有充分的理由写入文件)。再次记住:使用
AsyncTask
或其他线程机制在后台线程中写入文件。
- 次要:与您的字段命名保持一致,使用或不使用 "m" 前缀,然后坚持使用:-)
- 您在多个地方缺少
@Override
注释,例如对于 onResume
和 onPause
.
祝你好运!
我正在尝试创建一个具有开始和停止按钮的应用程序,该按钮收集加速计数据并在表格行中显示 x、y 和 z 坐标。每次传感器检测到变化时,都应添加具有新坐标集的行。最初,我没有包含按钮或 onPause() 或 onResume() 方法,行将被动态添加。但是,由于添加了太多行,应用程序会冻结并耗尽我的电池。因此,除了用于记录数据的开始和停止按钮外,我还添加了 onPause 和 onResume 方法。不幸的是,按钮可以工作,但在屏幕充满行后数据行停止更改。我使用 LogCat 来检查是否正在添加行,它们确实是。我只是不知道为什么他们没有全部出现。我的代码粘贴在下面。布局文件在同一代码块中的 java 文件之后。任何帮助将不胜感激!
package com.explorer.extractor;
//import packages
import android.app.ActionBar;
import android.content.Context;
import android.graphics.Color;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.util.Calendar;
public class MainActivity extends ActionBarActivity implements SensorEventListener,OnClickListener {
Button button1;
Button button2;
private SensorManager mSensorManager;
Sensor accelerometer;
private static final String TAG = MainActivity.class.getName();
private static final String FILENAME = "myFile.txt"; //file where data is written
//layout variables
TableLayout t1;
TextView dataReading; //declare data text object
Integer count = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//initialize sensor manager
mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
//initialize accelerometer
accelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
//mSensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_FASTEST);
t1 = (TableLayout) findViewById(R.id.main_table);
button1 = (Button) findViewById(R.id.button1);
button2 = (Button) findViewById(R.id.button2);
button1.setOnClickListener(this);
button2.setOnClickListener(this);
}
public void onAccuracyChanged(Sensor sensor, int accuracy){}
/**onResume() registers the accelerometer for listening
* to the events
*/
protected void onResume(){
super.onResume();
mSensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_FASTEST);
}
protected void onPause(){
super.onPause();
mSensorManager.unregisterListener(this);
}
public void onSensorChanged(SensorEvent event){
//if sensor status result is unreliable return
if (event.accuracy == SensorManager.SENSOR_STATUS_UNRELIABLE){
return;
}
Sensor sensor = event.sensor;
//check sensor type
if (sensor.getType() == Sensor.TYPE_ACCELEROMETER){
//assign directions
float x = event.values[0];
float y = event.values[1];
float z = event.values[2];
try {
//write to text file the x, y, and z values each type a sensor detects change
writeToFile(Float.toString(x), Float.toString(y), Float.toString(z));
//return string of text file
String textFromFileString = readFromFile();
TableRow tr = new TableRow(this);
if(count%2!=0) {
tr.setBackgroundColor(Color.GRAY);
}
tr.setId(100 + count);
tr.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT));
//show data read from file
dataReading = new TextView(this);
dataReading.setId(200 + count);
dataReading.setText(textFromFileString);
dataReading.setPadding(2, 0, 5, 0);
dataReading.setTextColor(Color.WHITE);
tr.addView(dataReading);
//finally add data to table row
t1.addView(tr, new TableLayout.LayoutParams(ActionBar.LayoutParams.MATCH_PARENT, ActionBar.LayoutParams.WRAP_CONTENT));
count++;
Log.i("LIMA","Add row. There are now " + t1.getChildCount()+"rows");
}catch (IOException e) {
e.printStackTrace();
}
}
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1:
mSensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_FASTEST);
break;
case R.id.button2:
mSensorManager.unregisterListener(this);
}
}
/**
* writeToFile: writes data recordings of accelerometer to text file
* @param x
* @param y
* @param z
* @throws IOException
*/
void writeToFile(String x, String y, String z) throws IOException {
//get exact instance of time in which call to write is being made
Calendar c = Calendar.getInstance();
//create string to print to text using values in parameter
String s = c.get(Calendar.HOUR) + ":" + c.get(Calendar.MINUTE) + ":" + c.get(Calendar.SECOND) + ":" + c.get(Calendar.MILLISECOND) + " - " + "x: " + x + " y: " + y + " z: " + z + "\n";
try {
//append new string to file
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(openFileOutput(FILENAME, Context.MODE_PRIVATE));
BufferedWriter bw = new BufferedWriter(outputStreamWriter);
bw.write(s);
bw.flush();
bw.close();
} catch(IOException e) {
Log.e(TAG, "File write failed: " + e.toString());
}
}
private String readFromFile(){
String ret = "";
try {
//open text file to read from
InputStream inputStream = openFileInput(FILENAME);
if (inputStream != null) {
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String receiveString = "";
StringBuilder stringBuilder = new StringBuilder();
while ((receiveString = bufferedReader.readLine()) != null) {
//continue appending to stringBuilder until you've reached the end of file
stringBuilder.append(receiveString);
}
inputStream.close();
ret = stringBuilder.toString();
}
} catch (FileNotFoundException e) {
Log.e(TAG, "File not found: " + e.toString());
} catch (IOException e) {
Log.e(TAG, "Can not read file: " + e.toString());
}
return ret;
}
}
Here is my layout file:
<?xml version="1.0" encoding="utf-8"?>
<TableLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_table"
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:shrinkColumns="*"
android:stretchColumns="*">
<TableRow
android:id="@+id/tablerow"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:id="@+id/button1"
android:layout_height="wrap_content"
android:text="Start"
android:layout_weight=".5"
android:layout_width="match_parent"
android:gravity="center_horizontal"/>
<Button
android:id="@+id/button2"
android:layout_height="wrap_content"
android:text="Stop"
android:layout_weight=".5"
android:layout_width="match_parent"
android:gravity="center_horizontal"/>
</TableRow>
</TableLayout>
要回答您的问题:您需要将 TableLayout
包装在 ScrollView
:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TableLayout
android:id="@+id/main_table"
...
但是,您的代码存在几个问题:-)
可能是最严重的:不要在主线程上执行任何 I/O。 IE。不要 read/write 到 main/UI 线程上的文件。在Android中有很多创建线程的方法,但两个例子是
AsyncTask
和RxJava/RxAndroid。您可以在 logcat 日志中看到这是一个问题,例如:I/Choreographer﹕ Skipped 34 frames! The application may be doing too much work on its main thread.
记录接收到的每一个事件不是一个好主意,因为当您使用
SensorManager.SENSOR_DELAY_FASTEST
时,加速度计事件接收得非常频繁。即使降低频率,将数万个条目添加到 UI 列表仍然不是一个好主意。您可能只想显示最后 x 个条目,出于 UI 目的,您不需要超过每秒 1 或 2 个条目或类似的东西,即使您想登录到文件更频繁。TextView dataReading
不应是成员字段 - 只需在需要的地方将其声明为局部变量即可。始终尽可能靠近使用它们的地方声明变量。- 您没有将
TableLayout
用作 table,常规的ListView
就足够了。 - 稍微分解一下代码。看看单一职责原则。就像一个随机的例子,文件处理的东西可以移到一个单独的 class.
- 为什么要将数据写入文本文件,然后再次从文件中读取相同的数据?这是不必要的 I/O :-) 您根本不需要阅读该文件。 (假设您首先有充分的理由写入文件)。再次记住:使用
AsyncTask
或其他线程机制在后台线程中写入文件。 - 次要:与您的字段命名保持一致,使用或不使用 "m" 前缀,然后坚持使用:-)
- 您在多个地方缺少
@Override
注释,例如对于onResume
和onPause
.
祝你好运!