![image](https://habrastorage.org/getpro/habr/post_images/bdd/e6f/34d/bdde6f34d15f08142b6b1a653c0d9ad6.jpg)
Пробовали ли Вы читать книгу или статью вроде этой в автобусе или идя по улице? Могу поспорить пробовали! В этом случае Вы должны были заметить что чтение текста таким образом является не лучшей идеей из-за постоянной тряски. Похоже что тряска экрана является достаточно серьезной проблемой и устранение ее может дать очень хорошее улучшение UX. Моя идея состоит в том, чтоб использовать датчики ускорения для компенсации тряски так-же как зеркальные камеры стабилизируют сенсор или линзы. Технически это возможно так что почему бы не попробовать сделать это самому!
Существующие решения
Для начала давайте посмотрим на существующие решения. В Сети есть несколько интересных статей на такую-же тематику.
- NoShake: Content Stabilization for Shaking Screens of Mobile Devices от Lin Zhong, Ahmad Rahmati и Clayton Shepard об стабилизации экрана для iPhone (3) опубликованная в 2009 г. Статья подытоживает что стабилизация экрана работает и дает заметные результаты, но алгоритм потребляет “в среднем 30% мощности у 620 МГц ARM процессора”. Это делает эту реализацию непрактичной для реального применения. И хотя современные айфоны могут легко справится с данной задачей авторы не предоставили ни исходников ни собранного приложения чтоб можно было попробовать это в деле.
- Walking with your Smartphone: Stabilizing Screen Content от Kevin Jeisy. Эта статься была опубликована в 2014 и имеет хорошее математическое обоснование. Статья подытоживает что «используя скрытую марковскую модель мы получили хорошую стабилизацию в теории». К сожалению не предоставлено ни исходные кодов, ни собранного приложения, так что посмотреть не получится.
- Shake-Free Screen. Исследуется тот же самый вопрос, но нету готовых результатов чтоб попробовать.
Эти статьи дают хорошее объяснение темы нашей статьи но к сожалению не дают ни исходных кодов, ни скомпилированных приложений чтоб посмотреть на это в живую. Давайте попробуем изобрести колесо заново и реализуем стабилизацию экрана по своему.
Теория
Датчик ускорения может быть использован для определения перемещения устройства. Но судя по названию этот датчик предназначен все таки для определения ускорения. Чтоб ответить на вопрос «как определить перемещение имея ускорение», давайте посмотрим на устройства с датчиками:
![image](https://habrastorage.org/getpro/habr/post_images/72e/0b2/f99/72e0b2f994525a0cc31c42696b6e65cc.png)
Как видно там есть есть три оси, соответственно датчик дает три значения на выходе. Технически датчик состоит из трех датчиков расположенных по разным осям, но давайте воспринимать его как единое целое.
Три значения на выходе обозначают ускорение вдоль соответствующей оси:
![image](https://habrastorage.org/getpro/habr/post_images/84c/63e/7b8/84c63e7b85267275ea4e42b1619f42f9.png)
Ускорение меряется в “м/с2”. Как можно видеть там есть некоторое ускорение вдоль оси Y. На самом деле это ускорение свободного падения и любой поворот устройства изменит все три значения:
![image](https://habrastorage.org/getpro/habr/post_images/883/04f/f6e/88304ff6e8023aab4cb39c7e666c081d.png)
Вы можете представить себе его как шар привязанный к устройству веревкой. Это достаточно хорошее объяснение так как если замените шар стрелкой, то получите вектор ускорения.
Хорошо, но что насчет определения перемещения?
Я не могу показать какой-то наглядный пример, но если Вы немного переместите устройство, то вектор изменится: на самом деле он будет состоять из двух векторов: 1) вектор земного притяжения как и раньше; 2) вектор ускорения устройства из-за перемещения вдоль соответствующих осей. Самое интересное для нас это «чистый» вектор перемещения. Его достаточно просто просто получить путем вычитания вектора земного притяжения из результирующего вектора, но как определить истинный вектор земного притяжения? Эта задача может быть решена разными путями, но к счастью Андроид имеет специальный датчик линейного ускорения который делает как раз то, что нам нужно. В нормальных условиях выходные значения у датчика 0, и только перемещая устройство можно получить не нулевые значения. Здесь его исходный код если интересно. Мы на один шаг ближе к определению перемещения устройства. Давайте начнем программировать что нибудь.
Реализация
Чтоб найти как высчитать перемещение устройства давайте разработаем одно простое приложение с одной активити. Это приложение будет мониторить изменение ускорения и двигать специальный вью элемент соответствующим образом. Также оно будет показывать «сырые» значения ускорения на графике:
![image](https://habrastorage.org/getpro/habr/post_images/c96/51a/6ca/c9651a6ca46974699e11ae0ea4323db0.png)
Я покажу только ключевые примеры кода. Полностью весь код есть в GIT репозитории. Ключевые вещи следующие:
1. Специальный элемент который мы будем двигать. Это синий блок с текстом внутри контейнера:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/graph1"
android:background="@drawable/dots_repeat_bg"
android:clipChildren="false">
<LinearLayout
android:id="@+id/layout_sensor"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp"
android:orientation="vertical"
android:background="#5050FF">
<ImageView
android:id="@+id/img_test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"/>
<TextView
android:id="@+id/txt_test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/img_test"
android:textSize="15sp"
android:text="@string/test"/>
</LinearLayout>
</FrameLayout>
Для перемещения layout_sensor мы будем использовать методы View.setTranslationX и View.setTranslationY.
Также подпишемся на событие нажатия на какой-либо элемент для сброса внутренних значений в 0 потому что на первых порах они могут быть очень непослушными:
private void reset()
{
position[0] = position[1] = position[2] = 0;
velocity[0] = velocity[1] = velocity[2] = 0;
timestamp = 0;
layoutSensor.setTranslationX(0);
layoutSensor.setTranslationY(0);
}
2. Подпишемся на события датчика ускорения:
sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION);
sensorManager.registerListener(sensorEventListener, accelerometer, SensorManager.SENSOR_DELAY_FASTEST);
3. И самое главное: слушатель изменений. Его базовая реализация:
private final float[] velocity = new float[3];
private final float[] position = new float[3];
private long timestamp = 0;
private final SensorEventListener sensorEventListener = new SensorEventListener()
{
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {}
@Override
public void onSensorChanged(SensorEvent event)
{
if (timestamp != 0)
{
float dt = (event.timestamp - timestamp) * Constants.NS2S;
for(int index = 0; index < 3; ++index)
{
velocity[index] += event.values[index] * dt;
position[index] += velocity[index] * dt * 10000;
}
}
else
{
velocity[0] = velocity[1] = velocity[2] = 0f;
position[0] = position[1] = position[2] = 0f;
}
}
};
Давайте разберемся что здесь происходит. Метод onSensorChanged вызывается каждый раз когда значение ускорения изменяется (прим. переводчика: ну на самом деле он вызывается по таймеру не зависимо от того какие значения ускорения). Первым делом вы проверяем инициализирована ли переменная timestamp. В этом случае мы просто инициализируем основные переменные. В случае если метод вызван повторно, мы производим вычисления использую следующую формулу:
deltaT = time() - lastTime;
velocity += acceleration * deltaT;
position += velocity * deltaT;
lastTime = time();
Вы должны были заметить интересную константу 10000. Воспринимайте ее как некое магическое число.
И результат:
Как видно текущая реализация имеет две проблемы:
- Дрифтинг и уползание значений
- Контрольный элемент не возвращается в 0
На самом деле решение для обоих проблем есть общее — нужно ввести в формулу торможение. Измененная формула выглядит так:
deltaT = time() - lastTime;
velocity += acceleration * deltaT - VEL_FRICTION * velocity;
position += velocity * deltaT - POS_FRICTION * position;
lastTime = time();
Хорошо. Текущая реализация выглядит неплохо. Я бы добавил некоторые косметические улучшения типа низкочастотного фильтра для сглаживания, отрезание недопустимых значений и настройки программы.
Готовое приложение находится в репозитории в ветке “standalone_app”.
AOSP
Мы разработали базовый алгоритм стабилизации и сделали демонстрационное приложение которое показывает, что стабилизация экрана возможна. Теперь мы можем применить нашу работу к устройству в целом. Это непростая задача, но тем интереснее будет ее решать.
Эта задача требует некоторого опыта в сборке AOSP. Google предоставляет всю необходимую документацию. В общем нужно скачать исходные коды Андроид для выбранного Nexus устройства. Собрать прошивку для Nexus и прошить ее. Не забывайте включить все необходимые драйвера перед сборкой.
Как только получится собрать стоковую прошивку, можно приступать к разработке и интеграции стабилизации экрана.
План реализации следующий:
- Найти способ смещения экрана в устройстве
- Разработать API во внутренностях AOSP чтоб дать возможность задавать смещение в стандартном Андроид приложении
- Разработать службу в демо приложении которая будет обрабатывать данные с датчика ускорения и задавать смещение используя API выше. Служба будет запускаться автоматически при включении устройства так что стабилизация будет работать сразу после включения
Сейчас я просто расскажу как я решил эти задачи.
1. Первый файл для исследования DisplayDevice.cpp который контролирует параметры экрана. Метод на который нужно смотреть void DisplayDevice::setProjection(int orientation, const Rect& newViewport, const Rect& newFrame). Самое интересное находится в строке 483:
![image](http://blog.lemberg.co.uk/sites/blog/files/imce/%5E5B40D0C94EF11B16B24388346D2A919B161F7E3CD79AA64B65%5Epimgpsh_fullsize_distr.png)
где финальная матрица преобразований образуется из других компонентов. Все эти переменные являются экземплярами класса Transform. Этот класс предназначен для обработки преобразований и имеет несколько перегруженных операторов (например *). Чтоб добавить сдвиг добавим новый элемент:
![image](http://blog.lemberg.co.uk/sites/blog/files/imce/%5E6ED59935F3E29F91CB4C2E5C40D0E9B550CB311E2035329842%5Epimgpsh_fullsize_distr.png)
Если Вы скомпилируете и прошьете Ваше устройство, экран там будет смещен на translateX пикселей по горизонтали и translateY пикселей по вертикали. В конечном итоге нам нужно добавить новый метод void setTranslate(int x, int y); который будет отвечать за матрицу сдвига.
2. Второй интересный файл SurfaceFlinger.cpp. Этот файл есть ключевым в создании API для доступа к параметрам экрана. Просто добавим новый метод:
![image](http://blog.lemberg.co.uk/sites/blog/files/imce/%5E399E74EA695720FF615C7583C47FBA87BCA68419850DDA8BE2%5Epimgpsh_fullsize_distr.png)
который будет вызывать метод setTranslate для всех дисплеев. Другая часть выглядит немного странной, но я объясню это позже. Нам нужно модифицировать метод status_t SurfaceFlinger::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) добавив новую секцию в конструкцию switch:
![image](http://blog.lemberg.co.uk/sites/blog/files/imce/%5E631012B98649CFB3D27B1A05DB3839D01144591CC166528080%5Epimgpsh_fullsize_distr.png)
Этот код является точкой входа в наше улучшение.
3. Служба обработки данных достаточно простая: она использует алгоритм разработанный ранее для получения значений смещения. Дальше эти значения через IPC передаются в SurfaceFlinger:
![image](http://blog.lemberg.co.uk/sites/blog/files/imce/%5EF2D56DE3BE87BBC36A1881CAC9F9220909D1D9032F11211425%5Epimgpsh_fullsize_distr.png)
ServiceManager не распознается Android Studio потому что он недоступен для не системных приложений. Системные приложения должны собираться вместе с AOSP с помощью системы сборки makefile. Это позволит нашему приложению получить необходимые права доступа в скрытым API Андроид. Чтоб получить доступ к службе SurfaceFlinger приложение должно обладать правами “android.permission.ACCESS_SURFACE_FLINGER”. Эти права могут иметь только системные приложения (см. далее). Чтоб иметь право вызывать наше API с кодом 2020, приложение должно иметь правами “android.permission.HARDWARE_TEST”. Эти права также могут иметь только системные приложения. И что в конце концов сделать наше приложение системным, модифицируйте его манифест следующим образом:
![image](http://blog.lemberg.co.uk/sites/blog/files/imce/%5E522BA25288110CEAA82538180FDEB74A1896C0E11A3F0BB7E8%5Epimgpsh_fullsize_distr.png)
Также создаете соответствующий makefile:
![image](http://blog.lemberg.co.uk/sites/blog/files/imce/%5EE6BD5F61A5FB94590B8F4E7B350E3CF7E6D20B2703CBBC549B%5Epimgpsh_fullsize_distr.png)
Остальные вещи в приложении (broadcast receiver загрузки, настройки, другое) достаточно стандартные и я не буду из касаться здесь. Осталась показать как сделать это приложение предустановленным (т.е. вшитым в прошивку). Просто разместите исходный код в каталоге {aosp}/packages/apps и измените файл core.mk так чтоб он включал наше приложение:
![image](http://blog.lemberg.co.uk/sites/blog/files/imce/%5E858784F72E8B81A5FED6F1CB31D045675CB20B805081A797B3%5Epimgpsh_fullsize_distr.png)
Финальная демонстрация:
Вы можете найти детальную информацию и исходный код на GitHub
Там есть приложение ScreenStabilization которое должно быть размещено в каталоге {aosp}/packages/apps, AOSP патч-файлы: 0001-ScreenStabilization-application-added.patch должен быть применен к каталогу {aosp}/build, 0001-Translate-methods-added.patch должен быть применен к каталогу {aosp}/frameworks/native.
Прошивка для Nexus 2013 Mobile собрана в конфигурации “userdebug” так что она больше подходит для тестирования. Чтоб прошить прошивку загрузитесь в режим загружчика удерживая кнопку “volume down” и нажимая кнопку “power” одновременно. Дальше введите:
fastboot -w update aosp_deb_screen_stabilization.zip
Эта процедура удалит все существующие данные на Вашем устройстве. Имейте ввиду что для того чтоб прошить любую нестандартную прошивку Вы должны разблокировать загружчик командой:
fastboot oem unlock
Заключение
Эта статья показывает как реализовать простой алгоритм стабилизации экрана и применить его ко всему устройству путем модификации исходных кодов Андроид и сборки пользовательской прошивки. Алгоритм не идеальный но достаточный для целей демонстрации. Мы создали модифицированную прошивку для устройства Nexus 2013 Mobile, но наш исходный код может быть применен к любому Nexus устройству и даже к любой AOSP системе типа CyanogenMod что делает возможным интеграцию стабилизации экрана в новые устройства.
P.S. На самом деле я также являюсь и автором оригинальной англоязычной версии статьи, которая была опубликована на blog.lemberg.co.uk, так что могу ответить на технические вопросы.
Комментарии (38)
vjjvr
13.12.2016 00:22-4Матрицы фотоаппаратов уж лет 10 как стабилизируют.
Но там миллиметров достаточно.
Для хорошей стабилизации экрана не хватает места.vjjvr
13.12.2016 16:35-2Господа минусаторы — еще раз повторяю для идиотов:
Формально — это возможно.
1. Фактически — нет. Нужна гораздо большая амплитуда
2. И еще одно — мешает шлейф от перерисовки на экране.
Программно это сделать можно.
Но без конструктивно заложенных возможностей — толку от этого 0.
Только по практиковаться в программировании датчиков.
justmara
13.12.2016 00:30иииии в конце-концов мы встречаем шлейфы при перемещении текста по фону. на разных экранах в разной степени, но везде есть. т.е. в результате "более стабильная", но размазанная картинка :)
justmara
13.12.2016 09:23+5Не, правда. Я не к тому, что "всё равно найдутся недовольные".
Идея клёвая. Реально вот направление мысли, реализация — молодцом.
Вот только конечный результат абсолютно беспомощен и бесполезен :) Но так что уже аппаратные ограничения.
slayerhabr
13.12.2016 03:04+6Не сочтите за не конструктивную критику.
1. Для глаз было 2 движущихся слоя, стало 3.
2. Датчики и реакция заметно отстают от реального движения.
В связи с этим нагрузка на глаза может стать еще большеStan_1
13.12.2016 08:13У меня после просмотра видео — такое же ощущение. Стаблизация камеры, на которую ссылается автор — построена вообще по другому — на гироскопах. Здесь же есть задержка обработки, и изображение все-равно двигается, просто более плавно, чем «дергает» транспорт. Но, например, было ли для меня комфортнее читать текст таким образом — скорее всего нет.
aamonster
13.12.2016 11:39-1«Более плавно» оно двигается только относительно экрана. Относительно глаз — рывками (ВЧ-составляющие движения не убираются из-за недостаточного быстродействия).
Ну и да, даже если бы удалось сделать идеально (скажем, датчик движения -> аппаратная обработка -> аппаратное смещение изображения на экране), и, допустим, сам экран достаточно быстродействующий — всё равно получили бы две проблемы:
1. Двигающаяся в поле зрения рамка экрана (и держащая её рука)
2. Голову-то при тряске тоже мотает! Т.е. как ни стабилизируй — картинка в поле зрения будет дёргаться. Привязываться надо к глазам, для этого крепить что-то на голову — что естественным образом приводит к идее очков.
zikolach
13.12.2016 11:28Автор, сделайте пожалуйста видео демонстрирующее одновременно 2 устройства — со стабилизацией и без. По последнему видео кажется, что изображение замыливается, но может быть без стабилизации вообще нечитаемо.
ad1Dima
13.12.2016 14:08Вы точнее меня сформулировали мою мысль.
За собой заметил что в транспорте прислонившись к стеклю читать становится сложнее: голова начинает двигаться с частотой опоры, а рука с частотой туловища, которое прекрасно справляется со стабилизацией. Фактически получается тот же лишний «слой»
Мне интересно. Те, кому сложно читать в транспорте, прижимают локти к туловищу или нет?
alan008
13.12.2016 09:40Сравнение между демо-1 и демо-2 вызывает желание создать демо-3 (идеальный вариант), когда текст просто жёстко зафиксирован :-) (т.е. обычный режим, без приложения :-))
perfect_genius
13.12.2016 10:58Потому что вы не трясётесь. А вы посмотрите видео в транспорте =)
ad1Dima
13.12.2016 14:10Вот, вы мне скажите, вы когда видео в транспорте смотрите локти к туловищу прижимаете, или на коленях/вытянутых руках смотрите?
perfect_genius
13.12.2016 16:33Я не смотрю и не читаю в транспорте, но когда было — на вытянутых, полусогнутых. Синхронизировал с головой.
ad1Dima
13.12.2016 16:54И я тоже жесткой фиксацией рук и головы к плечам вполне комфортно читаю. Толи дороги ровнее, толи что.
mickvav
13.12.2016 11:05-1Дорогой автор, закрепите в штативе камеру, которой снимаете демо. Прицепите к вашему девайсу какой-нибудь контролируемый источник вибрации (микроконтроллер с вибродвижком). Запилите в качестве опорной картинки какую-нибудь референсную — например, тупо клетчатое поле. Реализуйте на OpenCV вычисление функционала «качества сглаживания» — например, евклидову норму разницы между картинкой в статике и с подключенным источником вибрации, осредненную за некий период времени. Дальше можно любым алгоритмом многомерной оптимизации автоматически подкрутить ваши волшебные константы до минимума функционала, проходясь каждый раз по спектру. Двое-трое суток работы такой установки — и у вас профиль из десятка циферок для конкретного телефона, который можно (попробовать) продать производителю телефона за денюжку. Но для референсных моделей придется выложить в паблик, чтобы народ начал массово хотеть эту фишку ;)
St321
13.12.2016 11:38В оболочку ОС я бы добавил такую функцию! Ведь люди читают текст не только в электронных книгах.
edogs
13.12.2016 14:31По последнему видео есть ощущение, что текст не стоит как вкопанный (как должно быть при стабилизации хорошей), а привязан на резинке (реагирует, но медленнее и меньше).
При таком раскладе становится только хуже читать, т.к. добавляется еще одно непредсказуемое движение в уравнения для мозга.
Хотелось бы узнать скорость реакции стабилизации. Т, е. вот начали Вы движение экрана — через сколько времени Ваш софт перерисует экран на новом месте? Наверняка же можете тех. данные по дебагу посмотреть?
По ощущениям там не больше 15к/с, для качественной стабилизации надо не меньше 120 к/с.r_ii
13.12.2016 15:01Вы правы в том, что изображение все таки не стоит на месте. В этом плане алгоритм требует доработки.
Здесь требуется разработка очень точной инерциальной системы позиционирования.
Вот здесь например обсуждение: http://stackoverflow.com/questions/7829097/android-accelerometer-accuracy-inertial-navigation
Насчет скорости реакции — она зависит от частоты опроса датчика, задержки между-процессного взаимодействия и задержки обновления UI. Основной параметр все таки частота опроса датчика — и она задана как SENSOR_DELAY_FASTEST, что должно давать 100 Гц.
Здесь также есть возможности для улучшений путем переноса реализации алгоритма из отдельного приложения в драйвер датчика.
Какая реальная задержка — нужно еще подумать как это можно измерить.aamonster
13.12.2016 22:27+1К сожалению, проблема — не дрожание телефона относительно Земли, а дрожание телефона относительно глаз. Т.е. стабилизировать изображение на экране — мало, надо стабилизировать движение головы. Как это можно сделать — лично мне непонятно, единственная идея — фронтальной камерой ловить положение глаз (что-то не соображу, даст ли это достаточно информации).
Но к подходу поставленной задачки вы подошли серьёзно, приятно было посмотреть =)
MaxxONE
14.12.2016 00:53+1«Резинка» должна присутствовать в любом случае. Представьте себе — вы с устройством шагнули вперед, а изображение «осталось» в метре за вашей спиной. Это случай идеальной стабилизации. И ведь экран после воздействия колебаний никогда не вернется в прежнее положение, даже если у вас просто дрогнула рука. Так что в реальном устройстве изображение всегда будет стремиться занять центр экрана с тем или иным ускорением. А это ускорение будет восприниматься как та же «резинка».
Да, я понял, что вас больше напрягает скорость реакции прототипа на возмущение. Но даже с хорошей скоростью стабилизации эффект останется.edogs
14.12.2016 02:08Да, я понял, что вас больше напрягает скорость реакции прототипа на возмущение.
Нам представляется это единственной проблемой.
При чем к тряске можно привыкнуть и адаптироваться, потому что организм как-то ускорения отслеживает головой и движения рук тоже, а вот добавление непредсказуемого движения экрана это жесть, организму не только за тряской придется следить (хоть как-то предсказуемой), но и за постоянно ползущим куда-то текстом.
Но даже с хорошей скоростью стабилизации эффект останется.
Отчего же?
«Резинка» должна присутствовать в любом случае. Представьте себе — вы с устройством шагнули вперед, а изображение «осталось» в метре за вашей спиной.
А не фиг ходить пялясь в экран, это надо вбивать ремнем с младенчества.MaxxONE
14.12.2016 11:39Отчего же?
Собственно, я об этом написал.
в реальном устройстве изображение всегда будет стремиться занять центр экрана с тем или иным ускорением. А это ускорение будет восприниматься как та же «резинка».
То есть — либо изображение постоянно улетает за пределы экрана, «ползёт», либо вы получаете «резинку». Но уже не по причине низкой FPS.
А не фиг ходить пялясь в экран, это надо вбивать ремнем с младенчества.
Да я, собственно, абстрактный пример привел, для иллюстрации явления.edogs
14.12.2016 17:03Собственно, я об этом написал.
Так мы об этом и спросили:) Отчего же будет стремиться? Само не будет. Алгоритмически это не нужно, надо просто прибить изображение к точке в реальном мире. Можно сделать кнопочку для центровки изображения, что бы иметь какой-то контроль и/или делать это при перелистывании страницы. Но постоянно ползающий текст это ппц.MaxxONE
14.12.2016 23:15Алгоритмически это нужно. Представьте: едете вы в автобусе, читаете книгу. Автобус тормозит, и текст на экране смартфона бодро улетает далеко за пределы экрана. Будете при каждом ускорении кнопку центрирования жмакать?
edogs
15.12.2016 01:44За пределы экрана — можно автоматом перерисовывать в 0 позицию если улетает дальше 20% экрана или можно дальше пределов экрана вообще не отпускать — достигло границ и все.
Лучше мы один раз при торможении нажмем кнопку, чем постоянно будем пытаться отследить уползающий текст. А он будет уползать именно постоянно, если сам режим уползания сделать.
antonksa
Интересная идея! Финальное видео подтверждает жизнеспособность! Только ходить уткнувшись в девайс опасно для здоровья :D
lgorSL
Этой возможности иногда не хватает при чтении в транспорте.
Ещё к компенсации перемещений можно попробовать добавить вращения — они могут возникать, если, например, держать телефон одной рукой сбоку в ландшафтном режиме. Но возможно, появится куча некрасивых эффектов сглаживания из-за вертикальных и горизонтальных элементов интерфейса.
DrZlodberg
Прикрутить
кирпичбатарейку побольше и в полу-расслабленой руке будет стабилизороваться нативно :)А вообще обычно достаточно придерживать второй рукой или даже просто опереть на другую руку/сумку/что-нибудь ещё.
Но идея интересная.