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

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

Шкодим

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

Пишем справочник про котов

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

Что мы узнаем:

  • Как сделать простой список из массива, используя ListView
  • Как загрузить текст из ресурсов
  • Как загрузить html-текст в WebView
  • Как передать данные из одной активности в другую
Пример морально устарел. Теперь уже не используют ListView и отдельные активности для разделов. Вместо них следует использовать RecyclerView и фрагменты. Но для общего развития всё-равно полезно изучить пример.

Создаём новый проект 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(); } }

Запускаем проект и проверяем, что всё работает.

Справочник про котов

Раньше программу можно было скачать в Google Play. На данный момент я отказался от дальнейшей поддержки приложения и Гугл удалила мою программу. Взамен я создал веб-версию.

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

Дополнительное чтение

Обсуждение статьи на форуме.

Реклама