forum.boolean.name

forum.boolean.name (http://forum.boolean.name/index.php)
-   Android (http://forum.boolean.name/forumdisplay.php?f=150)
-   -   Полезные находки (http://forum.boolean.name/showthread.php?t=20135)

Жека 24.12.2015 10:16

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

Жека 24.12.2015 10:24

Ответ: Полезные находки
 
Недавно делал восстановление открытых окошек после поворота экрана.
Самодельный велосипед с сохранением данных в преференсы.
А тут нашёл простое решение - http://developer.android.com/referen...#configChanges
Прописываем в манифесте для активити строку
PHP код:

android:configChanges="orientation|screenSize|locale|keyboardHidden" 

И теперь при повороте экрана активити не пересоздаётся.
Если нужно обработать изменения (н-р, обновить лэйауты для гориз. ориент.), то добавляем колбэк
PHP код:

@Override
public void onConfigurationChanged(Configuration newConfig) {
    
super.onConfigurationChanged(newConfig);
    
/** обработка конфига ниже **/


Профит - поворот делается почти мгновенно, все вьюхи остаются видимые, включая popupMenu, лоадеры для списков заново не стартуют и т.д.

Если кто-то знает проблемные места этого подхода, расскажите.

Жека 24.12.2015 15:28

Ответ: Полезные находки
 
Свойство android:clipToPadding="false"

Позволяет при наличии паддингов у элемента видеть содержимое такого размера, будто паддингов нет.
Может быть полезно в списках - когда нужен отступ сверху / снизу, и чтобы содержимое не выглядело обрезанным.

Жека 26.12.2015 11:24

Ответ: Полезные находки
 
Использование AlertDialog.Builder

1. Использовать android.support.v7.app.AlertDialog - для получения стиля material design, ничего делать не нужно, диалог сразу будет в стиле материал при импорте суппорт-версии.

2. Создание диалога
AlertDialog dialog = builder.create();

a) если после создания назначать кнопки через setXXXButton, то их не будет в диалоге. это очевидно.

б) назначение лисенеров, например,
builder.setOnDismissListener
после создания диалога - лисенер не применится. тоже очевидно, но можно не доглядеть. как?
например, какая-то вьюха внутри диалога должна по нажатию закрывать диалог. значит, нам нужно создать диалог раньше, чем это вьюшку. и значит, лисенеры нужно навесить ещё раньше. есть вероятность несоблюдения этого порядка.

в) при нажатии на диалоговские кнопки, диалог закрывается. это не всегда уместно. часто нужна проверка введённых данных.
для этого ставим кнопке listener в null, и далее после создания диалога
PHP код:

Button b dialog.getButton(AlertDialog.BUTTON_POSITIVE);
b.setOnClickListener(new View.OnClickListener() {
    @
Override
    
public void onClick(View v) {
        
/** делаем что-то **/
    
}
}); 

также можно скрывать / показывать эти кнопки как любые другие вьюхи через setVisibility.

г) Назначение dialog.setOnShowListener() после вызова dialog.show() - слушатель не сработает.

FireOwl 26.12.2015 15:16

Ответ: Полезные находки
 
Цитата:

Сообщение от Жека (Сообщение 302873)
Недавно делал восстановление открытых окошек после поворота экрана.
Самодельный велосипед с сохранением данных в преференсы.
А тут нашёл простое решение - http://developer.android.com/referen...#configChanges
Прописываем в манифесте для активити строку
PHP код:

android:configChanges="orientation|screenSize|locale|keyboardHidden" 

И теперь при повороте экрана активити не пересоздаётся.

Вообще, насколько я знаю, "самопальный велосипед" с сохранением данных (в savedInstance Bundle) это и есть рекомендуемый путь.
Перехватом configChanges надо пользоваться осторожно, т.к. андроид все равно найдет способ уничтожить твой activity и пересоздать его заново.

Например - юзер сворачивает приложение в фон -> чем-то там занят -> Андроид решил что ему не хватает оперативной памяти -> убил твой activity -> юзер закончил свои дела, и вернулся к твоей программе -> Андроид создает activity заново.

Жека 27.12.2015 05:26

Ответ: Полезные находки
 
Пересоздание активити после его закрытия/сворачивания - не проблема, создаём всё с чистого листа и всё.
Проблема именно в повороте экрана, когда юзер ожидает, что прога должна оставаться такой, какая была до поворота.

Жека 31.01.2016 17:02

Ответ: Полезные находки
 
Фильтрация данных на уровне адаптера VS на уровне sql-запроса.

При работе с базой данных иногда нужно отсеять часть информации.

Например, в моей проге по долгам есть опция "показывать нулевой баланс" - т.е. все записи с нулевым балансом либо отображать либо нет.
У меня это было сделано на уровне запроса к базе - в зависимости от опции добавлял параметр.
Однако, мне также нужно хранить информацию обо всех контактах, включая тех, кого скрыли из-за нулевого баланса. (Например, все контакты должны быть доступны в фильтре контактов в истории и в автоподстановке при вводе имени.)
В итоге пришлось делать ещё один запрос, выгребающий все контакты.

Но сегодня пришла идея - отсеивать "нулевые" записи в адаптере, который выводит информацию на экран (CursorAdapter).

Для этого добавляем в адаптер 2 метода:
PHP код:

@Override
public int getViewTypeCount() {
    return 
2;
}

@
Override
public int getItemViewType(int position) {
    if (
isShowZeroBalance)
        return 
TYPE_NORMAL;
    
long balance extractBalanceAt(position); // достаём баланс из курсора
    
return (balance == 0) ? TYPE_EMPTY TYPE_NORMAL;


далее в методе
PHP код:

public View newView(Context contextCursor cursorViewGroup parent

делаем проверку типа, если TYPE_NORMAL, то создаём элемент как и раньше, а иначе создаём пустой лэйаут размером 0х0 dp.
И в методе
PHP код:

public void bindView(View viewContext context, final Cursor cursor

в самом начале делаем проверку
PHP код:

int type getItemViewType(cursor.getPosition());
if (
type == TYPE_EMPTY) {
    return;


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

Жека 03.06.2016 10:07

Ответ: Полезные находки
 
меню Analyze | Inspect Code...

В Android Studio есть полезная штука - анализ кода, который подсказывает потенциальные ошибки типа "присваивание переменной в саму себя" (после рефактора можно не заметить) и неиспользуемые методы, полезно для чистки большого проекта от шлака.

Жека 23.01.2017 12:36

Ответ: Полезные находки
 
Gradle Retrolambda Plugin (ссылка)

Что: Плагин, который позволяет юзать лямбды в старых версиях джавы (нативно они доступны в java 8 ).

Зачем: повышает читабельность кода, избавляет от необходимости писать "лишний" шлак код.

Примеры:

было
PHP код:

view.setOnClickListener(new View.OnClickListener() {
    @
Override
    
public void onClick(View v) {
        
/** делаем что-то **/
    
}
}); 

стало
PHP код:

view.setOnClickListener(-> /** делаем что-то, однострочный блок. **/); 

или
PHP код:

view.setOnClickListener(-> {
    
/** делаем что-то, многострочный блок. **/
}); 

Ещё частый гость - Runnable с единственным методом run.

слушатель для кнопки "оставить отзыв".
было
PHP код:

View.OnClickListener listener = new View.OnClickListener() {
    @
Override
    
public void onClick(View v) {
        
startActivity(new Intent(Intent.ACTION_VIEWUri.parse("market://details?id=" getPackageName())));
        
Metrica.reportEvent("rate us dialog : rate");
        
rateHelper.setState(RateHelper.STATE_RATED);
        
dialog.dismiss();

        
/** показываем диалог "спасибо за отзыв" через 1,2 сек */
        
handler.postDelayed(new Runnable() {
            @
Override
            
public void run() {
                
showDialog_RateUsRated();
            }
        }, 
1200);
    }
}; 

стало
PHP код:

View.OnClickListener listener -> { // упрощение #1
    
    
startActivity(new Intent(Intent.ACTION_VIEWUri.parse("market://details?id=" getPackageName())));
    
Metrica.reportEvent("rate us dialog : rate");
    
rateHelper.setState(RateHelper.STATE_RATED);
    
dialog.dismiss();

    
/** показываем диалог "спасибо за отзыв" через 1,2 сек */
    
handler.postDelayed(() -> showDialog_RateUsRated(), 1200); // упрощение #2
}; 


Жека 13.02.2017 08:31

Ответ: Полезные находки
 
Массивы в шаблонных типах в Java.

Интро. Я глядя на RxJava решил сделать некое подобие, но попроще.
Полученную штуку назвал CallChain - это цепочки вызовов методов.
Суть: составляем последовательность операций в цепочку "операторов", на вход очередного оператора подаётся выход из предыдущего.
Профит - удобно составлять логику, ошибки всегда прокидываются до получателя, можно остановить выполнение на любом этапе.
(я это сделал в виде worker - operator(s) - receiver (операторы обычно тоже worker'ы).

Кароче, суть "проблемы" открылась при добавлении оператора, который бы превратил массив в последовательность единичных элементов (flatMap).

Оператор наследуется от базового Chunk<TIn,TOut>{ ... }
т.е. у него есть тип входных данных и тип выходных.
Прицепляемый к нему оператор должен на вход получить выход от текущего, а на выходе может иметь любой тип, т.е. Chunk<TOut,TOut2>

Так вот, как из шаблонных типов вытащить массив?

Я не долго думая написал для нашего flatMap'а такой вариант:
Chunk<TOut[],TOut>
В надежде, что если мы на вход даём Integer[], то TOut распознается как Integer.

И это так, но только в рамках самого класса!

В каждом операторе есть входной метод
Код:

void onReceive<TIn data>{ ... }
в рамках этого метода по описанному выше типу данных я смог нормально пробежать по массиву:
Код:

void onReceive<TOut data>{ // здесь TOut, т.к. на вход берём быход от предыдущего оператора
  for (TOut i : data){
    sendResult(i); // пересылаем каждое значение в nextChunk
  }
}

Всё хорошо, тут наш Integer.

Однако, когда к этому оператору хотим прицепить следующий, у которого на входе должен быть штучный Integer,
то получаем ошибку нестыковки типов - "на входе ожидается массив Integer[]".

Т.е. наш TOut вне самого класса оператора расползнаётся как массив.
Но тогда логично, что конструкция <TOut[],TOut> должна развернуться в <Integer[][],Integer[]> внутри класса, но этого не происходит.
И это немного сломало мне мозг. :)
^ Ради этой инфы и написан этот пост.

В итоге я добавил шаблонный тип TOut2 к этому оператору
Код:

public <TOut2> Chunk<TOut,TOut2> flatMap() { ... }
и убрал признак массива в шаблонном типе TOut
(проверка на массив или итератор делается через instanceof;
не фэншуй? зато работает, и я пока на экспорт не планирую отдавать, а мне норм такой вариант).

при использовании теперь нужно явно указывать тип
Код:

new CallChain()
  .someWorker()
  .<Integer>flatMap()
  .someOperator()
  .....

чуток неудобно, но я лучшего решения не придумал.

кому интересен весь код - гитхаб.

Да, я смотрел исходники RxJava, но нихрена не понял, умеют же люди писать такие заморочки. :)

Жека 16.03.2017 19:09

Ответ: Полезные находки
 
Проверка нажатия кнопки HOME.

Переопределяем метод активити:
PHP код:

@Override
protected void onUserLeaveHint(){
    
log("Home Button Pressed");
    
super.onUserLeaveHint();



Жека 18.03.2017 18:02

Ответ: Полезные находки
 
Переопределение методов в момент создания класса

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

Например, нужно запретить клик и выделение по клику в дефолтном андроидовском адаптере.
Переопределяем нужный метод непосредственно при создании класса:
PHP код:

String[] lines message.split(".\n");

ArrayAdapter<Stringadapter = new ArrayAdapter<String>(Main.getContext(), // контекст
    
android.R.layout.simple_list_item_1// resId
    
lines){ // данные

    
@Override
    
public boolean isEnabled(int position) { // запрещаем выделять нажатие
        
return false;
    }
}; 

Естественно, для финальных классов так делать нельзя.


Часовой пояс GMT +4, время: 08:02.

vBulletin® Version 3.6.5.
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Перевод: zCarot