Освой Android играючи
/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000
В этой статье я покажу как сделать простейшую программу Справочник про котов. На этом примере можно сделать огромное количество полезных приложений — например, небольшой сборник рецептов или набор схем оригами, если использовать ListView с миниатюрами.
Создаём новый проект Manual (не путать с манулом). Начнём с интерфейса программы. Программа будет состоять из двух активностей. В первой выводится список тем, а во второй - полное описание выбранной темы. Откроем разметку первой активности res/layout/activity_main.xml и добавим компонент ListView для отображения списка тем:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<ListView
android:id="@+id/listView"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
</LinearLayout>
Сразу же создадим вторую активность DetailActivity (правой кнопкой мыши по имени пакета и выбираем New | Activity | Empty Activity). Создадим разметку для второй активности в файле res/layout/activity_detail.xml. Сюда мы добавим только компонент WebView. Скрытие строки состояния и заголовка сделаем в коде.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:id="@+id/webView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Дизайн приложения готов. Осталось написать код. Открываем файл класса MainActivity. В нём программно создадим список заголовков тем справочника и через адаптер добавим в список. Когда пользователь выбирает элемент списка, то получаем позицию выбранного элемента и запоминаем его. А затем запускаем вторую активность, в которую передаём номер позиции. Мы проходили подобные вещи раньше, поэтому просто освежите свою память.
// Kotlin
// Если этот код работает, его написал Александр Климов,
// а если нет, то не знаю, кто его писал.
// http://developer.alexanderklimov.ru/android/
package ru.alexanderklimov.manual
import android.content.Intent
import android.os.Bundle
import android.widget.AdapterView.OnItemClickListener
import android.widget.ArrayAdapter
import android.widget.ListView
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
//Создаем массив разделов:
private val titles = arrayOf(
"00. Начало",
"01. Чем кормить кота",
"02. Как гладить кота",
"03. Как спит кот",
"04. Как играть с котом",
"05. Как разговаривать с котом",
"06. Интересные факты из жизни котов",
"07. Как назвать кота"
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val listView: ListView = findViewById(R.id.listView)
listView.adapter = ArrayAdapter<Any?>(this, android.R.layout.simple_list_item_1, titles)
listView.isTextFilterEnabled = true
listView.onItemClickListener =
OnItemClickListener { a, v, position, id ->
val intent = Intent()
intent.setClass(this@MainActivity, DetailActivity::class.java)
intent.putExtra("title", position)
startActivity(intent)
}
}
}
// Java
package ru.alexanderklimov.manual;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class MainActivity extends AppCompatActivity {
//Создаём массив разделов:
private String titles[] = {
"00. Начало",
"01. Чем кормить кота",
"02. Как гладить кота",
"03. Как спит кот",
"04. Как играть с котом",
"05. Как разговаривать с котом",
"06. Интересные факты из жизни котов",
"07. Как назвать кота",
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Получим идентификатор ListView
ListView listView = findViewById(R.id.listView);
//устанавливаем массив в ListView
listView.setAdapter(
new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, titles));
listView.setTextFilterEnabled(true);
//Обрабатываем щелчки на элементах ListView:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> a, View v, int position, long id) {
Intent intent = new Intent();
intent.setClass(MainActivity.this, DetailActivity.class);
intent.putExtra("title", position);
//запускаем вторую активность
startActivity(intent);
}
});
}
}
Запустив пример, вы можете щёлкнуть по любому элементу списка, чтобы открыть вторую активность. Сейчас вторая активность пуста, так как мы не написали никакого кода. Но при этом она получает номер позиции выбранного элемента через метод putExtra(). В зависимости от полученного номера мы формируем содержание веб-страницы.
Для справочника удобнее держать заранее подготовленные локальные файлы, чтобы не зависеть от интернета. Создадим новую папку - выбираем res | New | Directory и в диалоговом окне вводим имя папки raw.
Самостоятельно подготовьте текстовые файлы с именами n0.txt, n1.txt, n2.txt и т.д. Символ n в начале имён файлов понадобился, чтобы избежать конфликта. Файлы ресурсов не должные начинаться на цифру.
Напишем код для второй активности. Во-первых, получим номер позиции, которую нам прислала первая активность. Во-вторых, открываем нужный файл для чтения и помещаем его содержимое в WebView.
// Kotlin
package ru.alexanderklimov.manual
import android.content.Context
import android.os.Bundle
import android.webkit.WebView
import androidx.appcompat.app.AppCompatActivity
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStream
import java.io.InputStreamReader
class DetailActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_detail)
val webView: WebView = findViewById(R.id.webView)
val intent = intent
//получаем строку и формируем имя ресурса
val resName = "n" + intent.getIntExtra("title", 0)
val context: Context = baseContext
//читаем текстовый файл из ресурсов по имени
val text: String = readRawTextFile(
context, resources.getIdentifier(
resName,
"raw", "ru.alexanderklimov.manual"
)
)
webView.loadDataWithBaseURL(null, text, "text/html", "en_US", null)
}
//читаем текст из raw-ресурсов
private fun readRawTextFile(context: Context, resId: Int): String {
val inputStream: InputStream = context.resources.openRawResource(resId)
val inputReader = InputStreamReader(inputStream)
val buffReader = BufferedReader(inputReader)
var line: String?
val builder = StringBuilder()
try {
while (buffReader.readLine().also { line = it } != null) {
builder.append(line)
builder.append("\n")
}
} catch (e: IOException) {
return e.localizedMessage
}
return builder.toString()
}
}
// Java
package ru.alexanderklimov.manual;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.webkit.WebView;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class DetailActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
WebView webView = findViewById(R.id.webView);
Intent intent = getIntent();
//получаем строку и формируем имя ресурса
String resName = "n" + intent.getIntExtra("title", 0);
Log.i("name", resName);
Context context = getBaseContext(); //получаем контекст
//читаем текстовый файл из ресурсов по имени
String text = readRawTextFile(context, getResources().getIdentifier(resName,
"raw", "ru.alexanderklimov.manual"));
webView.loadDataWithBaseURL(null, text, "text/html", "en_US", null);
}
//читаем текст из raw-ресурсов
private String readRawTextFile(Context context, int resId)
{
InputStream inputStream = context.getResources().openRawResource(resId);
InputStreamReader inputReader = new InputStreamReader(inputStream);
BufferedReader buffReader = new BufferedReader(inputReader);
String line;
StringBuilder builder = new StringBuilder();
try {
while (( line = buffReader.readLine()) != null) {
builder.append(line);
builder.append("\n");
}
} catch (IOException e) {
return null;
}
return builder.toString();
}
}
Запускаем проект и проверяем, что всё работает.
В примере показаны базовые функции, достаточные для понимания. Вы можете усложнить пример, добавив поддержку фрагментов. Также вы можете самостоятельно доработать пример. Например, добавить картинки к элементам списка, загружать готовые html-документы, а также загружать веб-страницы из интернета.
Обсуждение статьи на форуме.