Статьи

Android Shake для обновления учебник

В этом посте мы хотим исследовать другой способ обновления пользовательского интерфейса приложения Shake to Refresh . Все мы знаем шаблон «pull-to-refresh», который реализован в нескольких приложениях. В этом паттерне мы проводим пальцем вдоль экрана, и пользовательский интерфейс обновляется:

android_pull_to_refresh4

Даже этот шаблон очень полезен, мы можем использовать другой шаблон для обновления нашего пользовательского интерфейса на основе датчиков смартфона, мы можем назвать его Shake для обновления . Вместо того, чтобы тянуть палец вниз, мы встряхиваем смартфон, чтобы обновить интерфейс:

adroid_shake_to_refresh4

Реализация

Для того, чтобы наше приложение могло поддерживать функцию встряхивания для обновления, мы должны использовать сенсоры смартфонов и, в частности, датчики движения : акселерометр. Если вы хотите получить больше информации о том, как использовать датчик, вы можете посмотреть здесь .

Как уже было сказано, мы хотим, чтобы пользователь встряхивал смартфон, чтобы обновить, и в то же время мы не хотим, чтобы процесс обновления начинался случайно или когда пользователь просто перемещал свой смартфон. Поэтому мы должны реализовать некоторые элементы управления, чтобы быть уверенным, что пользователь намеренно трясет смартфон. С другой стороны, мы не хотим реализовывать эту логику в классе, который обрабатывает пользовательский интерфейс, потому что не рекомендуется смешивать логику пользовательского интерфейса с другими вещами, и используя другой класс, мы можем повторно использовать этот «шаблон» в других контекстах. ,

Затем мы создадим еще один класс с именем ShakeEventManager . Этот класс должен прослушивать сенсорные события:

1
2
3
public class ShakeEventManager implements SensorEventListener {
..
}

так что он будет реализовывать SensorEventListener . Затем мы должны найти датчик акселерометра и зарегистрировать наш класс в качестве прослушивателя событий:

1
2
3
4
5
public void init(Context ctx) {
    sManager = (SensorManager)  ctx.getSystemService(Context.SENSOR_SERVICE);
    s = sManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
    register();
}

а потом:

1
2
3
public void register() {
    sManager.registerListener(this, s, SensorManager.SENSOR_DELAY_NORMAL);
}

Для запуска события обновления в пользовательском интерфейсе необходимо проверить некоторые условия, эти условия гарантируют, что пользователь намеренно встряхивает свой смартфон. Условия:

  1. Ускорение должно быть больше порогового уровня
  2. Фиксированное количество событий ускорения должно произойти
  3. Время между этими событиями должно быть в фиксированном временном окне

Мы реализуем эту логику в методе onSensorChanged который вызывается каждый раз, когда доступно новое значение. Первым шагом является вычисление ускорения, нам интересно узнать максимальное значение ускорения на трех осях, и мы хотим очистить значение датчика от силы тяжести. Итак, как указано в официальной документации Android, мы сначала применяем фильтр низких частот, чтобы изолировать силу тяжести, а затем фильтр высоких частот:

01
02
03
04
05
06
07
08
09
10
11
12
private float calcMaxAcceleration(SensorEvent event) {
    gravity[0] = calcGravityForce(event.values[0], 0);
    gravity[1] = calcGravityForce(event.values[1], 1);
    gravity[2] = calcGravityForce(event.values[2], 2);
 
    float accX = event.values[0] - gravity[0];
    float accY = event.values[1] - gravity[1];
    float accZ = event.values[2] - gravity[2];
 
    float max1 = Math.max(accX, accY);
    return Math.max(max1, accZ);
}

где

1
2
3
4
// Low pass filter
private float calcGravityForce(float currentVal, int index) {
    return  ALPHA * gravity[index] + (1 - ALPHA) * currentVal;
}

Как только мы узнаем максимальное ускорение, мы реализуем нашу логику:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
    float maxAcc = calcMaxAcceleration(sensorEvent);
    Log.d("SwA", "Max Acc ["+maxAcc+"]");
    if (maxAcc >= MOV_THRESHOLD) {
        if (counter == 0) {
            counter++;
            firstMovTime = System.currentTimeMillis();
            Log.d("SwA", "First mov..");
        } else {
            long now = System.currentTimeMillis();
            if ((now - firstMovTime) < SHAKE_WINDOW_TIME_INTERVAL)
                counter++;
            else {
                resetAllData();
                counter++;
                return;
            }
            Log.d("SwA", "Mov counter ["+counter+"]");
 
            if (counter >= MOV_COUNTS)
                if (listener != null)
                    listener.onShake();
        }
    }
 
}

Анализируя код в строке 3, мы просто вычисляем ускорение, а затем проверяем, не превышает ли оно пороговое значение (условие 1) (строка 5). Если это первое движение (строка 7-8), мы сохраняем временную метку, чтобы проверить, происходят ли другие события в указанном временном окне. Если все условия выполнены, мы вызываем метод обратного вызова, определенный в интерфейсе обратного вызова:

1
2
3
public static interface ShakeListener {
    public void onShake();
}

Тестовое приложение

Теперь мы реализовали менеджер событий Shake, мы готовы создать простое приложение, которое его использует. Мы можем создать простое действие с ListView, которое обновляется при возникновении события встряхивания:

1
2
3
4
5
6
7
8
public class MainActivity extends ActionBarActivity implements ShakeEventManager.ShakeListener {
....
 
  @Override
    public void onShake() {
       // We update the ListView
    }
}

Где в строке 5 мы обновляем интерфейс, потому что этот метод вызывается только тогда, когда пользователь трясет свой смартфон.

Несколько заключительных соображений: когда приложение приостановлено, мы должны отменить регистрацию слушателя датчика, чтобы он больше не слушал события, и таким образом мы сэкономим батарею. С другой стороны, когда приложение будет возобновлено, мы снова зарегистрируем слушателя:

01
02
03
04
05
06
07
08
09
10
11
Override
protected void onResume() {
    super.onResume();
    sd.register();
}
 
@Override
protected void onPause() {
    super.onPause();
    sd.deregister();
}
Ссылка: Учебник по Android Shake to Refresh от нашего партнера по JCG Франческо Аццолы из блога Surviving w / Android .