我正在检测设备的方向,检查它是否倒置在表面上,然后调用方法。我遇到的问题是,用户可能无意中将设备的方向转换为该位置一瞬间并返回面朝上位置以纠正其错误,但是该方法仍然是在他们第一次执行时调用。 / p>
我正在尝试在延迟800毫秒的方向上添加“双重检查”,以便用户有时间在触发该方法之前纠正方向。不幸的是,我无法控制调用onSensorChanged
的频率,以便添加一个延迟800毫秒的线程,这会仔细检查方向以防止无意的方向改变。
我的代码如下,
public SensorEventListener accelerometerListener = new SensorEventListener(){
@Override
public void onSensorChanged(SensorEvent event) {
float z_value = event.values[2];
if (z_value < 0) {
//the device is now upside-down
try {
Thread.sleep(800);
//this if statement is never called correctly because
// onSensorChanged is not called again.
if (z_value < 0) {
toggleMethod();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
我的问题:我是否有办法在onSensorChanged方法本身的延迟时间内调用onSensorChanged
以便进行双重检查?
答案 0 :(得分:2)
使用onSensorCheck时最好的办法就是使用过滤器:你总是要对传感器给出的最后n个值的平均值做出决定,这样你的应用程序就会表现得很平稳,突然的传感器变化就会变得越来越困难。影响它。
这是你如何做到的:
z_value = 0.2*event.values[2]+0.8*z_value; // 0.2+0.8 = 1
通过这种方式,您只需要获得新值的20%,其余的是最后四个值的平均值。尝试使用coefs,直到您对结果感到满意为止。
对我来说这很有效:
z_value = 0.02*event.values[2]+0.98*z_value;
---------------------------------------------------------------
修改强>
以下是初始化z_value的方法:
z_value应该是一个字段,你必须添加另一个仅用于初始化的布尔字段,即第一个onSensorChanged调用。所以你有两个领域:
double z_value =0;
boolean firstTime = true;
这是onSensorChanged方法:
@Override
public void onSensorChanged(SensorEvent event) {
if(firstTime){
z_value = event.values[2]; // you will enter here only the first time
firstTime = false
}
else{
z_value = 0.02*event.values[2] + 0.98*z_value;
}
if (z_value < 0) {
//the device is now upside-down
// do what you gotta do
}
}
答案 1 :(得分:2)
一种非常简单的方法是让传感器有机会在即将到来的n毫秒内给出一个新值(正如所要求的那样):
1 - 决定您希望为用户撤消其操作提供多少毫秒(您使用了800):
private static final long TIME_MARGIN_FOR_USER_TO_UNDO = 800;
2 - 创建一个处理程序和一个runnable来执行您的操作(让它们在比您的onSensorChanged更大的范围内 - 例如您的活动):
Handler sensorHandler = new Handler();
Runnable toggleRunnable = new Runnable() {
public void run() {
toggleMethod();
}
}
3 - 只要if语句的计算结果为true,就发布此runnable;但是,在n毫秒后发布。
@Override
public void onSensorChanged(SensorEvent event) {
float z_value = event.values[2];
if (z_value < 0) {
sensorHandler.postDelayed(toggleRunnable, TIME_MARGIN_FOR_USER_TO_UNDO);
}
}
4 - 由于传感器值更改后将调用onSensorChanged,因此如果用户修复了runnable,则可以停止运行。因此,您将需要一个将删除runnable的else语句。
@Override
public void onSensorChanged(SensorEvent event) {
float z_value = event.values[2];
if (z_value < 0) {
sensorHandler.postDelayed(toggleRunnable, TIME_MARGIN_FOR_USER_TO_UNDO);
}
else {
sensorHandler.removeCallbacks(toggleRunnable);
}
}
答案 2 :(得分:0)
另一种方法是随着时间的推移平滑输入,因此平均读数超过800毫秒。
(YAT的回答有正确的想法,但执行实际上没有考虑到一段时间内从一部手机到另一部手机不知道的样品交付量。它也永远不会忘记任何测量,10之后的行为1分钟后秒数可能会有很大差异。)
//the idea here is to store all the inputs over the last 800ms and average them
LinkedList<Entry<long, double>> inputs = new LinkedList<Entry<long,double>>
double totalZ = 0.0f; //keep a running total so we don't have to iterate through the list to calculate the average
@Override
public void onSensorChanged(SensorEvent event) {
//clear out values older than 800ms
while(inputs.size() > 0 && inputs.getLast().getKey() < System.currentTimeInMillis() - 800){
Entry<long, double> deletedEntry = inputs.removeLast();
totalZ -= deletedEntry.getValue(); //update the running total
}
//add the new measurement to the head
inputs.addFirst(new Entry<long, double>(System.currentTimeInMillis(),event.values[2]));
totalZ += event.values[2]; //update the running total with the new measurement
//this is the Z averaged over 800ms
float averagedZ = totalZ / inputs.size();
}
或者在检测到初始变化时,更简单的方法,更符合被问到的问题。
节省时间,保存新的潜在状态
然后当你得到另一个onSensorChanged()
该状态与潜在状态相同吗?如果不是,请清除时间并清除电位状态
如果已经过了800ms并且潜在状态仍然是当前状态,则启动该更改。
onSensorChanged在大多数精度模式下被调用多次,因此您应该接近800ms,只需等待正常的onSensorChanged调用即可。
下面粗糙的伪代码
long lastChange = 0;
int potentialState = 0;
@Override
public void onSensorChanged(SensorEvent event) {
int currentState = determineCurrentState(event); //whatever your logic is and states
if(potentialState != currentState){
lastChange = System.currentTimeInMillis();
potentialState = currentState;
}
else if(lastChange + 800 < System.currentTimeInMillis()){
//execute state change, 800ms have elapses with no further change in state
stateChange(currentState);
}