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

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

Шкодим

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

CheckBox (Флажок)

Отслеживаем смену состояния флажка
Собственные стили
Собственный вид
AnimatedStateListDrawable. Анимация между переключением состояния

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

Компонент находится в группе Buttons.

Для управления состояниями флажка используйте методы setChecked() или togglе(). Чтобы узнать текущее состояние флажка, вызовите свойство isChecked.

Для экспериментов воспользуемся программой «Счётчик ворон», которую писали при изучении щелчка кнопки.

Как вы помните, в программе была кнопка и текстовое поле. Добавим ещё два элемента CheckBox, а также четыре текстовые метки TextView. Нам нужно постараться, чтобы элементы были аккуратно сгруппированы. Для этой цели воспользуемся вложенными компоновками LinearLayout. Заодно применим интересный приём - мы не будем использовать текст у флажков CheckBox, а воспользуемся текстовыми метками с разными размерами шрифтов. Верхняя метка с крупным шрифтом будет указывать на основную функциональность флажка, а нижняя метка с мелким шрифтом будет использоваться в качестве своеобразной подсказки, в которой содержится дополнительная информация для пользователя.


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="2dp">

    <EditText
        android:id="@+id/editText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/buttonCounter"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Считаем ворон" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="4dp">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="2dp">

            <TextView
                android:id="@+id/textViewColorLarge"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Выводить красным цветом"
                android:textSize="18sp" />

            <TextView
                android:id="@+id/textViewColorSmall"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Отметься для вывода красным цветом"
                android:textSize="12sp" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="right"
            android:padding="2dp">

            <CheckBox
                android:id="@+id/checkBoxColor"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </LinearLayout>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="4dp">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="2dp">

            <TextView
                android:id="@+id/textViewBoldLarge"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Использовать жирный текст"
                android:textSize="18sp" />

            <TextView
                android:id="@+id/textViewBoldSmall"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Отметьте для выделения жирным"
                android:textSize="12sp" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="right"
            android:padding="2dp">

            <CheckBox
                android:id="@+id/checkBoxBold"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

На самом деле вы можете попробовать другие способы разметки, не воспринимайте как догму. А мы идём дальше. Флажки в нашем приложении нужны для того, чтобы пользователь мог менять вывод текста в текстовом поле. По желанию, можно выводить текст красным цветом и жирным стилем по отдельности или в совокупности. Для этого нам нужно добавить дополнительные строчки кода в обработчик щелчка кнопки.


// Если этот код работает, его написал Александр Климов,
// а если нет, то не знаю, кто его писал.

package ru.alexanderklimov.checkbox;

import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;

public class MainActivity extends AppCompatActivity {

    private int mCount = 0;

    private EditText mMessageEditText;
    private CheckBox mColorCheckBox;
    private CheckBox mBoldCheckBox;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setTitle("CheckBox");

        mMessageEditText = (EditText) findViewById(R.id.editText);
        mColorCheckBox = (CheckBox)findViewById(R.id.checkBoxColor);
        mBoldCheckBox = (CheckBox)findViewById(R.id.checkBoxBold);

        Button mCrowsCounterButton = (Button) findViewById(R.id.buttonCounter);
        mCrowsCounterButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(mColorCheckBox.isChecked())
                    mMessageEditText.setTextColor(Color.RED);
                else
                    mMessageEditText.setTextColor(Color.BLACK);

                if(mBoldCheckBox.isChecked())
                    mMessageEditText.setTypeface(Typeface.DEFAULT_BOLD);
                else
                    mMessageEditText.setTypeface(Typeface.DEFAULT);

                mMessageEditText.setText("Я насчитал " + ++mCount + " ворон");
            }
        });
    }
}

Запустите проект и попробуйте снимать и ставить галочки у флажков в разных комбинациях, чтобы увидеть, как меняется текст после щелчка кнопки. Код очень простой - проверяется свойство isChecked. Если галочка у флажка установлена, то свойство возвращает true и мы меняем цвет (красный) или стиль текста (жирный). Если флажок не отмечен, то свойство возвращает false, и мы используем стандартные настройки текста.

CheckBox CheckBox CheckBox

Отслеживаем смену состояния флажка

С помощью слушателя-интерфейса OnCheckedChangeListener с его методом onCheckedChanged() можно отслеживать смену состояния флажка.


// Kotlin
val textView: TextView = findViewById(R.id.textView)
val checkBox: CheckBox = findViewById(R.id.checkBox)

checkBox.setOnCheckedChangeListener{ buttonView, isChecked ->
    if (isChecked){
        textView.text = "Флажок выбран"
    }else{
        textView.text = "Флажок не выбран"
    }
}

// Java TextView textView = findViewById(R.id.textView); CheckBox checkBox = findViewById(R.id.checkBox); checkBox.setOnCheckedChangeListener(new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if(isChecked) textView.setText("Флажок выбран"); else { textView.setText("Флажок не выбран"); } } });

Собственные стили

Если вы используете стандартный проект, то флажок будет использовать цвета Material Design, в частности цвет colorAccent для фона флажка.

В файле res/values/styles.xml добавим строки:


<style name="MyCheckBox" parent="Theme.AppCompat.Light">
    <item name="colorControlNormal">@android:color/holo_green_light</item>
    <item name="colorControlActivated">@android:color/holo_orange_dark</item>
</style>

Свойство colorControlNormal отвечает за прямоугольник в невыбранном состоянии, а colorControlActivated за закрашенный прямоугольник в выбранном состоянии.

Присваиваем созданный стиль атрибуту android:theme:


<CheckBox
    ...
    android:theme="@style/MyCheckBox"/>

Теперь цвета флажков изменились.

CheckBox Style

Собственный вид

Если вас не устраивает стандартный вид элементов CheckBox, то не составит никакого труда реализовать свои представления о дизайне.

В папке res/drawable создаём файл checkbox_selector.xml:


<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:drawable="@drawable/star_down" android:state_checked="true"/>
    <item android:drawable="@drawable/star" android:state_checked="false"/>

</selector>

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

Осталось прописать селектор в компоненте CheckBox (атрибут android:button):


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/linear"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Не выбран"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <CheckBox
        android:id="@+id/checkBox"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="CheckBox"
        android:button="@drawable/checkbox_selector" />

</LinearLayout>

Готово! Можете запускать проект и проверять работу флажков. Ниже код для реагирования на смену состояния флажков:


@Override
public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);
	
	final TextView textView = (TextView)findViewById(R.id.textView);
	CheckBox starCheckBox = (CheckBox) findViewById(R.id.checkBox);
	starCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
		@Override
		public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
			if(isChecked)
				textView.setText("Флажок выбран");
			else {
				textView.setText("Флажок не выбран");
			}
		}
	});
}

Unchecked Checked

AnimatedStateListDrawable. Анимация между переключением состояния

Когда мы создали собственный вид флажка, то переключение происходит сразу без анимации. В API 21 появилась возможность установить анимацию при помощи нового класса AnimatedStateListDrawable.

Пример написан на основе статьи AnimatedStateListDrawable – Styling Android.

Создадим как прежде файл для собственного вида флажка.

res/drawable/toggle.xml


<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/toggle_checked" android:state_checked="true" />
    <item android:drawable="@drawable/toggle_unchecked" android:state_checked="false" />
</selector>

Далее нужные два значка. Они сделаны в векторном виде.

res/drawable/toggle_checked.xml


<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportHeight="24"
    android:viewportWidth="24">

    <path
        android:name="tick"
        android:pathData="M7,12.448 L10.254,15.348 L17.265,9"
        android:strokeColor="#1E9618"
        android:strokeLineCap="round"
        android:strokeLineJoin="round"
        android:strokeWidth="1.5" />

    <path
        android:name="circle"
        android:pathData="M12,2 C17.5228,2,22,6.47715,22,12 C22,17.5228,17.5228,22,12,22 C6.47715,22,2,17.5228,2,12 C2,6.47715,6.47715,2,12,2 Z"
        android:strokeColor="#1E9618"
        android:strokeWidth="1.5" />
</vector>

res/drawable/toggle_unchecked.xml


<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportHeight="24"
    android:viewportWidth="24">

    <path
        android:name="circle"
        android:pathData="M12,2 C17.5228,2,22,6.47715,22,12 C22,17.5228,17.5228,22,12,22 C6.47715,22,2,17.5228,2,12 C2,6.47715,6.47715,2,12,2 Z"
        android:strokeColor="#A0A0A0"
        android:strokeWidth="1.5" />
</vector>

Присвоим созданный вид атрибуту android:button.


<CheckBox
    android:id="@+id/checkBox"
    android:button="@drawable/toggle"
    android:text="Кот накормлен" />

Код будет работать на устройствах, которые поддерживают векторную графику (API 14), но анимации не будет. Для анимации создадим альтернативный вариант файла в папке res/drawable-v21.

AnimatedStateListDrawable похож на обычный StateListDrawable, но позволяет указать анимацию перехода между двумя состояниями. Мы также указываем две картинки, но также добавляем элементы transition.

res/drawable-v21/toggle.xml


<?xml version="1.0" encoding="utf-8"?>
<animated-selector
    xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/checked"
        android:drawable="@drawable/toggle_checked"
        android:state_checked="true" />

    <item
        android:id="@+id/unchecked"
        android:drawable="@drawable/toggle_unchecked" />

    <transition
        android:drawable="@drawable/toggle_unchecked_checked"
        android:fromId="@id/unchecked"
        android:toId="@id/checked" />

    <transition
        android:drawable="@drawable/toggle_checked_unchecked"
        android:fromId="@+id/checked"
        android:toId="@+id/unchecked" />

</animated-selector>

res/drawable-v21/toggle_unchecked_checked.xml


<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:drawable="@drawable/toggle_checked">

    <target android:name="tick">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="@android:integer/config_shortAnimTime"
                android:interpolator="@android:interpolator/accelerate_cubic"
                android:propertyName="trimPathEnd"
                android:valueFrom="0"
                android:valueTo="1"
                android:valueType="floatType" />
        </aapt:attr>
    </target>

    <target android:name="circle">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="@android:integer/config_shortAnimTime"
                android:interpolator="@android:interpolator/accelerate_decelerate"
                android:propertyName="strokeColor"
                android:valueFrom="#A0A0A0"
                android:valueTo="#1E9618"
                android:valueType="intType" />
        </aapt:attr>
    </target>
</animated-vector>

res/drawable-v21/toggle_checked_unchecked.xml


<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:aapt="http://schemas.android.com/aapt"
    android:drawable="@drawable/toggle_checked">

    <target android:name="tick">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="@android:integer/config_shortAnimTime"
                android:interpolator="@android:interpolator/decelerate_cubic"
                android:propertyName="trimPathEnd"
                android:valueFrom="1"
                android:valueTo="0"
                android:valueType="floatType" />
        </aapt:attr>
    </target>

    <target android:name="circle">
        <aapt:attr name="android:animation">
            <objectAnimator
                android:duration="@android:integer/config_shortAnimTime"
                android:interpolator="@android:interpolator/accelerate_decelerate"
                android:propertyName="strokeColor"
                android:valueFrom="#1E9618"
                android:valueTo="#A0A0A0"
                android:valueType="intType" />
        </aapt:attr>
    </target>
</animated-vector>

Если запустить пример на старом устройстве, то никакой анимации не увидим, но код будет работать без ошибок. На новых устройствах анимация будет работать.

CheckBox

Реклама