Освой Android играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

Советы

Кот учёный

Compose

Compose

Surround with...

Допустим, у вас в студии уже есть код Text(text = "Hello Kitty"), который вы хотите обернуть контейнером. Если курсор находится на этой строке, то нажимаем комбинацию клавиш Ctrl+Alt+J и из контекстого меню выбираем нужный вариант Surround with widget (поместит текст в Box), Surround with Row, Surround with Column. Выбор из пунктов меню можно сделать не только мышкой, но и клавишей по первым буквам варианта: W (widget), R (row), C (column).

Пропустить параметры в composable-функции

Теперь в студии формируется другой шаблон без круглых скобок

Когда вы набираете, к примеру, Column, то студия создаёт следующий шаблон при автодополнении:


Column(|) {
    
}

В скобках мигает курсор (у меня проставлена вертикальная черта для наглядности) для ввода параметров функции. Но если вам не нужны параметры, то просто нажимаете клавишу Tab и круглые скобки исчезают и курсор переходит в тело функции.

Убрать ненужные классы при автодополнении

Очень часто при автодополнении студия пытается подставить ненужные классы с такими же именами. Наиболее частые примеры: Surface, Row, Alignment, Text.

Например, мы хотим использовать Text и студия предлагает вариант класса org.w3c.dom.Text. Скорее всего в Compose-среде этот класс вам никогда не понадобится и только мешается при выборе из списка классов. Мы можем настроить студию, чтобы ненужные классы не выводились в автодополнении. Идём в настройки File | Settings | Editor | General | Auto Import и выбираем раздел Exclude from auto-import and completion. Добавляем в список ненужные классы.


android.view.Surface

android.inputmethodservice.Keyboard.Row

android.text.layout.Alignment
android.widget.GridLayout.Alignment

org.w3c.dom.Text

Естественно, вы можете составить свой список исключений.

Необязательно идти сразу в настройки. Можно постепенно убирать ненужные классы. Когда в редакторе появится предложение автозаполнения, выберите нужный пункт, нажмите Alt + Enter (в Windows). После этого студия спросит, хотите ли вы исключить предложения из этого пакета. Далее появится всплывающее окно Auto Import, о котором говорилось выше. И там вы можете быстро исключить ненужный класс или пакет.

Вы также можете определить область действия исключения: IDE Scope или Project Scope. Лучше выбирать второй вариант для работы в текущем проекте. Первый вариант может вам помешать в будущих проектах, когда вам действительно понадобится нужный класс, который вы исключили на уровне всей среды разработки.

Кстати, если вы выбрали Project Scope для исключения определённых пакетов, то в папке .idea создаётся файл с именем codeInsightSettings.xml, содержащий исключённые пакеты для вашего проекта.

Вы можете скопировать данный файл и затем вставить его в свой новый проект в том же месте. Это позволить обеспечить вам переносимость настроек, независимо от обновлений IDE.

Разметка-контейнер

Не используйте в функции компоненты сами по себе, вложите их в разметку-контейнер.


// ❌
@Composable
fun Hello() {
    Text("Hello")
    Text("Kitty")
}

// ✅
@Composable
fun Hello(modifier: Modifier = Modifier) {
  Column(modifier = modifier) {
    Text("Hello")
    Text("Kitty")
  }
}

Если composable-функция применяет разметку (Column, Row и т.д.), то используйте модификатор в параметре.


// ❌
@Composable
fun Hello() {
    Column {
      Text("Hello")
      Text("Kitty")
    }
}

// ✅
@Composable
fun Hello(modifier: Modifier = Modifier) {
    Column(modifier = modifier) {
        Text("Hello")
        Text("Kitty)
    }
}

Live Template для создания Composable-функции

В студии есть заготовка для быстрого создания функции. Набираем comp, затем Enter и получаем код.


@Composable
fun |() {
    
}

Мигающий курсор стоит перед скобками (я поставил вертикальную черту). Набираем имя функции и нажимаем Tab, чтобы курсор переместился во внутрь фигурных скобок.

В настройках студии найдите раздел Live Template и просмотрите все шаблоны для AndroidCompose. Там вы найдёте сокращения для "Surround with Column", "Surround with Row" и другие варианты. Также вы можете создать свои собственные шаблоны.


XML + View

XML+View

Cleartext HTTP traffic not permitted (https)
Запретить делать скриншот экрана приложения
Генерируем случайные показания
Аналог html-кода  
Используем собственные шрифты
Доступ к ресурсам через URI
Убрать предупреждающие значки при использовании строк в атрибутах
Проверка правильности электронного адреса (Регулярное выражение)
Проверка на первый запуск приложения
Сделать скриншот экрана своего приложения
Раскодирование HTML-символов
Кодирование строк по схеме UTF-8
Кодирование в режиме Base64
Пишем собственную функцию md5() для вычисления хэша строки
Как сконвертировать строку или CharSequence в int?
Как включить вибрацию?
Определить системную локаль на устройстве
Как заблокировать экран в приложении?
Узнать размеры экрана
Как убрать заголовок у Activity?
Как использовать собственный значок для программы
Получить номер версии программы
Убить приложение
Сколько используется памяти
Разрешённый объём памяти для приложения
Запретить автоматическую смену ориентации при повороте устройства
Определить момент окончания перезагрузки устройства
Советы для Android Studio

Cleartext HTTP traffic not permitted (https)

При работе с Android 8+ примеры, которые обращаются к веб-адресам по протоколу http вызывают ошибку. Чтобы обойти данное ограничение, можно сделать следующее.

В папке res/xml размещаем файл network_security_config.xml:


<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
    </base-config>
</network-security-config>

В манифесте в блоке application добавляем новый атрибут.


android:networkSecurityConfig="@xml/network_security_config"

Есть ещё вариант добавления в application строки:


android:usesCleartextTraffic="true"

Но на форумах пишут, что это вариант не всегда срабатывает. Пробуйте самостоятельно.

Запретить делать скриншот экрана приложения

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


@Override
protected void onCreate(Bundle savedInstanceState) {
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, 
            WindowManager.LayoutParams.FLAG_SECURE);
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);
 
    .....
}

Аналог html-кода &nbsp;

При многострочных текстах вы можете воспрепятствовать переносу слов на разные строки, если их желательно держать вместе. В html есть специальный символ неразрывного пробела &nbsp;. В строковых ресурсах вы можете использовать символ &#160; или юникод-код \u00A0.


 <string name="cat">Кот&#160;Васька - отличный мышелов</string>

Доступ к ресурсам через URI

Обычно мы обращаемся к ресурсам через идентификатор. Существует альтернативный способ через Uri. Например, такой подход может пригодиться для загрузки изображения в WebView при помощи метода loadUrl(). Формат доступа будет следующим: android.resource://[package-name]/res-id. Например:


Uri.parse("android.resource://ru.alexanderklimov.sample/" + R.raw.cat_image);

Убрать предупреждающие значки при использовании строк в атрибутах

Если в некоторых атрибутах компонентов использовать строки, то появляются предупреждающие значки с всплывающей надписью [I18N] Hardcoded string "Котики рулят", should use @string resource. Надпись призывает использовать строковые ресурсы, а не писать текст прямо в атрибутах. На самом деле это всего лишь предупреждение, а не ошибка. Программа будет прекрасно запускаться, но новичков такие надписи пугают. Можете убрать данное предупреждение, если добавите дополнительный атрибут tools:ignore.


<TextView
    ... другие атрибуты
    android:text="Котики рулят"
    tools:ignore="HardcodedText" />

Проверка на первый запуск приложения

Для проверки первого запуска приложения можно использовать настройки SharedPreferences. Смотрите пример.

Сделать скриншот экрана своего приложения

Один из примеров снятия скриншота своего экрана - через методы рисования получить графический отпечаток корневой разметки, а затем сохранить его как картинку, которую можно вывести в ImageView. Выберите любой ваш экран для опытов и добавьте на неё кнопку для снятия скриншотов и ImageView для вывода сохранённого изображения. В моём случае корневым элементом была компоновка TableLayout.


TableLayout view;
ImageView view2;

@Override
public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);

	view = (TableLayout) findViewById(R.id.tableLayout);

}

// щелчок кнопки
public void onClick(View v) {
	View v1 = view.getRootView();
	v1.setDrawingCacheEnabled(true);

	Bitmap bm = v1.getDrawingCache();
	BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), bm);
	
	view2 = (ImageView) findViewById(R.id.imageView1);
	view2.setBackgroundDrawable(bitmapDrawable); // для старых версий
	//view2.setBackground(bitmapDrawable); // для API 16
}

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

Изменённый пример, когда скриншот сохраняется на SD-карту:


// set your location
private static final String SCREEN_SHOTS_LOCATION = "/media/screenshots"; 

public static void takeScreenShot(View view, String name) throws Exception {
    view.setDrawingCacheEnabled(true);
    view.buildDrawingCache();
    Bitmap b = view.getDrawingCache();
    FileOutputStream fos = null;
    
	try {
        File sddir = new File(SCREEN_SHOTS_LOCATION);
        if (!sddir.exists()) {
            sddir.mkdirs();
        }
        fos = new FileOutputStream(SCREEN_SHOTS_LOCATION + name + "_"
                + System.currentTimeMillis() + ".jpg");
    
	    if (fos != null) {
            b.compress(Bitmap.CompressFormat.JPEG, 90, fos);
            fos.close();
        }
    } catch (Exception e) {
    }
}

Не забываем про разрешения.

Раскодирование HTML-символов

Если вам нужно раскодировать HTML-символы (угловые скобки <>, знак амперсанда &, кавычки ""), то воспользуйтесь методом TextUtils.htmlEncode(). Добавим на форму две текстовые метки. И небольшой код для примера:


setContentView(R.layout.main);

TextView textSrc = (TextView) findViewById(R.id.tvSource);
TextView textResult = (TextView) findViewById(R.id.tvResult);

String src = "<p>Не тяни <b>кота</b> за хвост!</p>";
String result = TextUtils.htmlEncode(src);

textSrc.setText(src); // фраза в html до обработки
textResult.setText(result); // обработанный результат

htmlEncode

Примечание: Если вы пользуетесь Notepad++, то там есть такая же возможность: TextFX | TextFX Convert | Encode HTML.

Кодирование строк по схеме UTF-8

Вероятно, вы не раз замечали, что в адресной строке некоторые символы заменяются на последовательность других символов. Самый простой пример - символ пробела заменяется на %20. Немного изменим предыдущий пример:


String src = "One Two хвост";
String result = Uri.encode(src);

textSrc.setText(src); // фраза в html до обработки
textResult.setText(result); // обработанный результат

Uri.encode

В нашем примере были преобразованы символы пробела и русские символы.

Кодирование в режиме Base64


String testValue = "Hello Kitty";

byte[] encodeValue = Base64.encode(testValue.getBytes(), Base64.DEFAULT);
byte[] decodeValue = Base64.decode(encodeValue, Base64.DEFAULT);

Log.d("TEST", "defaultValue = " + testValue);
Log.d("TEST", "encodeValue = " + new String(encodeValue));
Log.d("TEST", "decodeValue = " + new String(decodeValue));

Пишем собственную функцию md5() для вычисления хэша строки

У PHP-программистов есть готовая функция md5(), которая вычисляет MD5 хэш строки с использованием алгоритма MD5 RSA Data Security и возвращает этот хэш. Хэш представляет собой 32-значное шестнадцатеричное число. Напишем собственную функцию на Java:


private String md5(String in) {
	MessageDigest digest;
	try {
		digest = MessageDigest.getInstance("MD5");
		digest.reset();
		digest.update(in.getBytes());
		byte[] a = digest.digest();
		int len = a.length;
		StringBuilder sb = new StringBuilder(len << 1);
		for (int i = 0; i < len; i++) {
			sb.append(Character.forDigit((a[i] & 0xf0) >> 4, 16));
			sb.append(Character.forDigit(a[i] & 0x0f, 16));
		}
		return sb.toString();
	} catch (NoSuchAlgorithmException e) {
		e.printStackTrace();
	}
	return null;
}

Осталось применить её где-нибудь:


public void onClick(View v) {
	String mypassword = "cat";
	String securepassword = md5(mypassword); // теперь содержит хэш
	tvInfo.setText(securepassword); // выводим результат в TextView
}

Как заблокировать экран в приложении?


KeyguardManager keyguardManager = (KeyguardManager) getSystemService(Activity.KEYGUARD_SERVICE); 
KeyguardLock lock = keyguardManager.newKeyguardLock(KEYGUARD_SERVICE); 
lock.disableKeyguard(); 

Также нужно установить разрешение android.permission.DISABLE_KEYGUARD.

Если стоит обратная задача - запретить блокировку экрана при долгом бездействии, то используйте метод setKeepScreenOn() или используйте XML-атрибут android:keepScreenOn="true".

Узнать размеры экрана

Для вычисления размеров экрана можно воспользоваться двумя способами. Второй способ более правильный.


TextView tvResult = (TextView)findViewById(R.id.textView1);

// Узнаем размеры экрана из ресурсов
DisplayMetrics displaymetrics = getResources().getDisplayMetrics();

// узнаем размеры экрана из класса Display
Display display = getWindowManager().getDefaultDisplay();
DisplayMetrics metricsB = new DisplayMetrics();
display.getMetrics(metricsB);

tvResult.setText(
        "[Используя ресурсы] \n" +
        "Ширина: " + displaymetrics.widthPixels + "\n" +
        "Высота: " + displaymetrics.heightPixels + "\n"
        + "\n" +
        "[Используя Display] \n" +
        "Ширина: " + metricsB.widthPixels + "\n" +
        "Высота: " + metricsB.heightPixels + "\n"
    );

Как убрать заголовок у Activity?

В некоторых случаях хочется спрятать заголовок (Title) у программы. Есть несколько способов. Например, применить специальную тему (прописать в манифесте файла):


<activity 
    android:name=".MainActivity" 
    android:label="My App"
    android:theme="@android:style/Theme.Black.NoTitleBar"
    android:screenOrientation="portrait">

Также попробуйте android:theme="@android:style/Theme.Black.NoTitleBar.Fullscreen" (убирает не только заголовок, но и панель уведомлений). Названия тем могут быть и другими, смотрите документацию. Несколько примеров

Если у вас используется своя тема, то используйте в ней параметр:

<item name="android:windowNoTitle">true</item>

Также существует программный способ (перед вызовом setContentView):


// Убираем заголовок
this.requestWindowFeature(Window.FEATURE_NO_TITLE);

// Убираем панель уведомлений
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);

Как использовать собственный значок для программы

Когда вы создаете учебные примеры, то у всех программ используется стандартный значок с изображением андроида. Как же использовать свой значок? Ответ прост. В папке проекта /res вы можете видеть подпапки drawable-ldpi, drawable-mdpi, drawable-hdpi и др., в которых и содержатся готовые значки в формате PNG под разные размеры экранов.

Вы можете просто открыть png-файл в графическом редакторе и нарисовать что-то свое и сохранить изменения.

Вы можете сохранить значки под своим именем, например, cat.png. В этом случае вам надо открыть файл манифеста, найти там строчку:

<application 
   android:icon="@drawable/icon" 
   ...

И отредактировать её, например, android:icon="@drawable/cat" (без расширения). Как вариант, вы можете сохранить один значок в папке drawable в нужном размере, если пишете программу под определенные типы телефонов.

Размеры значков в папках (часть):

  • Low density – ldpi = 36×36 px
  • Medium density – mdpi = 48×48 px
  • High density – hdpi = 72×72 px
  • Extra-high density screen - xhdpi (320 dpi) = 96 × 96 px

Получить номер версии программы

Смотри пример

Убить приложение

Смотри пример

Сколько используется памяти

Нужно от общей памяти отнять свободную память


// Get the Java runtime
Runtime runtime = Runtime.getRuntime();
// Run the garbage collector
runtime.gc();
// Calculate the used memory
long memoryUsed = runtime.totalMemory() - runtime.freeMemory();
textView1.setText(String.valueOf(memoryUsed));

Более общий пример:


TextView memInfo = findViewById(R.id.meminfo);

String info = "";

info += "Total memory: " + Runtime.getRuntime().totalMemory() + "\n";
info += "Free memory: " + Runtime.getRuntime().freeMemory() + "\n";
info += "Max memory: " + Runtime.getRuntime().maxMemory() + "\n";

memInfo.setText(info);

Разрешённый объём памяти для приложения

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


// Возвращаем лимит памяти в мегабайтах
int memoryClass = ((ActivityManager) getSystemService(Context.ACTIVITY_SERVICE))
        .getMemoryClass();
Toast.makeText(this, String.valueOf(memoryClass), Toast.LENGTH_LONG)
        .show();

Особенно это касается приложений, которые активно используют графику. Следите за размерами изображений, вызывайте метод recycle() для освобождения памяти при работе с классом BitmapFactory.

Начиная с Android 3.0 можно попросить у системы выделять чуть больше памяти, прописав в манифесте просьбу:


<application
    ....
    largerHeap = "true"> 

Запретить автоматическую смену ориентации при повороте устройства

Если вы хотите запретить автоматическую смену ориентацию при повороте устройства, то можно её отключить для нужного экрана. Например, если экран с настройками должен отображаться только в портретном режиме, то добавьте одну строчку с атрибутом screenOrientation в манифест файла для соответствующей активности


<activity android:name=".MyActivity"
    android:label="@string/app_name"
    android:screenOrientation="portrait">
Реклама