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

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

Шкодим

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

ProgressBar (Horizontal) - горизонтальный индикатор прогресса

Progress Cat

Основы
Отдельный поток
Пунктирный ProgressBar
Режим Indeterminate
Вертикальный индикатор прогресса
Круговые индикаторы прогресса
Индикатор с текстом
Программное создание индикатора

Основы

Компонент ProgressBar (Horizontal) (Индикатор прогресса) применяется в тех случаях, когда пользователю нужно показать, что программа не зависла, а выполняет продолжительную работу.

Находится в категории Widgets.

Студия предлагает несколько видов индикаторов: ProgressBar и ProgressBar (Horizontal). В свою очередь компонент имеет несколько модификаций и их отличия заложены в стилях, если посмотреть XML-описание компонентов. Стили постоянно меняются, поэтому проще выбрать атрибут style и посмотреть предлагаемые варианты из выпадающего списка на текущий момент.

Методы для работы с ProgressBar:

  • setProgress() — устанавливает заданное значение индикатора прогресса;
  • getProgress() — возвращает текущее значение индикатора прогресса;
  • incrementProgressBy() — устанавливает величину дискретизации приращения значения индикатора;
  • setMax() — устанавливает максимальное значение величины прогресса.

Горизонтальный индикатор обычно используют, чтобы показать индикатор в действии. При длительной операции пользователь видит, что полоска индикатора постоянно увеличивается.

Нужно задать верхнюю границу диапазона max и начальное значение progress и затем программно работать с этими данными.

Атрибут android:secondaryProgress="50" создаёт эффект буфера, когда рисуется ещё одна полоска, опережающая основной цвет. Ей можно управлять через метод setSecondaryProgress().

Отдельный поток

Выполнение длительных операций предпочтительнее производить в отдельном потоке. Android имеет специальный класс Handler для создания фоновых потоков и их взаимодействия с пользовательским интерфейсом. Либо используется AsyncTask и другие способы выполнения в другом потоке.

Запустим индикатор через кнопку.


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="32dp"
        android:padding="24dp"
        android:text="0"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:text="Start"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/progressBar" />

</androidx.constraintlayout.widget.ConstraintLayout>

Будем усыплять поток на небольшой промежуток времени.


// Kotlin
package ru.alexanderklimov.progress

import android.os.Bundle
import android.os.Handler
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    private var progressStatus = 0
    private var handler = Handler()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_main)

        button.setOnClickListener {
            Thread(Runnable {
                while (progressStatus < 100){
                    // update progress status
                    progressStatus +=1

                    // sleep the thread for 100 milliseconds
                    Thread.sleep(100)

                    // update the progress bar
                    handler.post {
                        progressBar.progress = progressStatus
                        textView.text = "$progressStatus"
                    }
                }
            }).start()
        }
    }
}
ProgressBar

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

Допустим размер файла 365 Мб. Запускаем и смотрим - вся жизнь пробежала перед глазами, ну или год.


// Kotlin
button.setOnClickListener {
    button.isEnabled = false

    val fileSize = 365
    progressBar.max = fileSize

    Thread(Runnable {
        while (progressStatus < fileSize){
            // update progress status
            progressStatus +=1

            // sleep the thread for 100 milliseconds
            Thread.sleep(100)

            // update the progress bar
            handler.post {
                progressBar.progress = progressStatus

                val percentage = (progressStatus.toDouble() / fileSize * 100).toInt()

                textView.text = "Downloaded $progressStatus ($percentage%)"

                if (progressStatus == fileSize) {
                    button.isEnabled = true
                }
            }
        }
    }).start()
}
ProgressBar

Пунктирный ProgressBar

Изменим обычную линию на пунктир. Создадим файл res/drawable/dash_progressbar.xml.


<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/background">
        <shape android:shape="line">
            <stroke
                android:width="3dp"
                android:color="#FFDDAA"
                android:dashWidth="20dp"
                android:dashGap="7dp" />
        </shape>
    </item>

    <item android:id="@+id/progress">
        <clip
            android:clipOrientation="horizontal"
            android:gravity="start">
            <shape android:shape="line">
                <stroke
                    android:width="3dp"
                    android:color="#2299FF"
                    android:dashWidth="21dp"
                    android:dashGap="7dp" />
            </shape>
        </clip>
    </item>
</layer-list>

Подключим через атрибут progressDrawable.


<ProgressBar
    ...
    android:progressDrawable="@drawable/dash_progressbar" />
ProgressBar

Режим Indeterminate

Существует режим неопределённости (indeterminate), когда индикатор работает постоянно. Для этого нужно установить соответствующие атрибуты.


<ProgressBar
    ...
    android:indeterminate="true"
    android:indeterminateBehavior="cycle"
    android:indeterminateOnly="true"
    android:indeterminateTint="#8BC34A"
    android:indeterminateTintMode="src_in"

При запуске индикатор прогресса без всякого кода будет выводить бесконечную анимацию.

Переключение между режимами программно - метод setIndeterminate(boolean indeterminate).

Аккуратнее с атрибутом indeterminateOnly, если он будет установлен, то анимацию программно уже не приостановить. В этом случае просто не используйте атрибут. Несмотря на анимацию, индикатор по прежнему меняет свои показания. Если брать предыдущий пример с процентами и установить атрибуты, указанные выше (кроме indeterminateOnly), то программно можно остановить анимацию следующим образом.


// Kotlin
button.setOnClickListener {
    button.isEnabled = false

    val fileSize = 65
    progressBar.max = fileSize

    Thread(Runnable {
        while (progressStatus < fileSize){
             progressStatus +=1

            Thread.sleep(100)

            handler.post {
                progressBar.progress = progressStatus

                val percentage = (progressStatus.toDouble() / fileSize * 100).toInt()

                textView.text = "Downloaded $progressStatus ($percentage%)"

                if (progressStatus == fileSize) {
                    button.isEnabled = true

                }
            }
        }
    }).start()
    progressStatus = 0
    progressBar.isIndeterminate = false
}

Также часто используют изменение видимости - когда индикатор не нужен, его просто прячут.

Настройка через XML

Индикатор прогресса работает в стиле Material Design на Lollipop-устройствах и выше. На старых устройствах выводится другой цвет.

Можно задать свои настройки для цвета.


<ProgressBar
    ...
    android:progressTint="#FFEB3B"
    android:progressBackgroundTint="#8BC34A"
    ... />

Или программно.


progressBar.progressTintList = ColorStateList.valueOf(Color.CYAN)
progressBar.progressBackgroundTintList = ColorStateList.valueOf(Color.BLUE)

// новый способ
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    progressBar.progressDrawable.colorFilter =
        BlendModeColorFilter(Color.GREEN,BlendMode.SRC_IN)
}else {
    progressBar.progressDrawable
        .setColorFilter(Color.GREEN,PorterDuff.Mode.SRC_IN)
}

Для тонкой настройки компонента под себя следует использовать файл в ресурсах с использованием LayerDrawable. Файл ресурсов должен использовать системный идентификатор @android:id/background для фона, а также идентификатор @android:id/progress для самого индикатора. Также можно использовать ещё один необязательный идентификатор @android:id/secondaryProgress для дополнительного индикатора. Пример ниже.

Вертикальный индикатор прогресса

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

Рассмотрим пример с обычной вертикальной линией. Создадим файл res/drawable/vert_progressbar.xml.


<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/background">
        <rotate
            android:fromDegrees="90"
            android:toDegrees="90">
            <shape android:shape="line">
                <stroke
                    android:width="5dp"
                    android:color="#FFEB3B" />
            </shape>
        </rotate>
    </item>

    <item android:id="@+id/progress">
        <clip
            android:clipOrientation="vertical"
            android:gravity="bottom">
            <rotate
                android:fromDegrees="90"
                android:toDegrees="90">
                <shape android:shape="line">
                    <stroke
                        android:width="5dp"
                        android:color="#4CAF50" />
                </shape>
            </rotate>
        </clip>
    </item>
</layer-list>

Задействуем атрибут android:progressDrawable. Есть определённая сложность размещения компонента на экране. Хотя мы хотим видеть вертикальный индикатор, студия воспринимает его по-прежнему горизонтальным и в режиме дизайна отображает его соответствующим образом. Попробуем следующий вариант.


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginTop="32dp"
        android:padding="24dp"
        android:text="0"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="396dp"
        android:layout_height="509dp"
        android:layout_marginStart="8dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="8dp"
        android:progressDrawable="@drawable/vert_progressbar"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:text="Start"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textView" />

</androidx.constraintlayout.widget.ConstraintLayout>

Код можно взять из примера с процентами. Там всё без изменений.

ProgressBar

Вариант не слишком удобный, так как не интуитивен при визуальном размещении.

Другой вариант вертикального индикатора, который гораздо лучше справляется со своей задачей. Кроме того, индикатор будет не виде линии, а полноценным жирным прямоугольником. В папке res/drawable подготовим два файла:

verticalprogressbar.xml


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

    <item android:id="@android:id/background">
        <shape>
            <corners android:radius="5dip" />

            <solid android:color="#FFFFFF" />

            <stroke
                android:width="1dp"
                android:color="#FF000000" />
        </shape>
    </item>
    <item android:id="@android:id/progress">
        <clip
            android:clipOrientation="vertical"
            android:gravity="bottom" >
            <shape>
                <corners android:radius="5dip" />

                <solid android:color="#FFAADD" />

                <stroke
                    android:width="1dp"
                    android:color="#FF000000" />
            </shape>
        </clip>
    </item>

</layer-list>

verticalprogressbar2.xml


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

    <item android:id="@android:id/background">
        <shape>
            <corners android:radius="5dip" />

            <solid android:color="#FFFFFF" />

            <stroke
                android:width="1dp"
                android:color="#FF000000" />
        </shape>
    </item>
    <item android:id="@android:id/progress">
        <clip
            android:clipOrientation="vertical"
            android:gravity="bottom" >
            <shape>
                <corners android:radius="5dip" />

                <solid android:color="#00FF55" />

                <stroke
                    android:width="1dp"
                    android:color="#FF000000" />
            </shape>
        </clip>
    </item>

</layer-list>	

Разница между двумя файлами только в цвете, которым закрашивается индикатор во время работы. Я создал второй файл только для демонстрации программного изменения индикатора - когда значение индикатора достигнет отметки 50, то цвет поменяется на зелёный.

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


// Kotlin
// код из предыдущего примера
...
// update the progress bar
handler.post {
    progressBar.progress = progressStatus

    val percentage = (progressStatus.toDouble() / fileSize * 100).toInt()

    textView.text = "Downloaded $progressStatus ($percentage%)"

    if(percentage > 50){
        progressBar.progressDrawable = getDrawable(R.drawable.verticalprogressbar2)
    }
    if (progressStatus == fileSize) {
        button.isEnabled = true

    }
}

// Java
if(myProgress >= 50) myProgressBar.setProgressDrawable(getResources().getDrawable(R.drawable.verticalprogressbar2));

ProgressBar ProgressBar

Также можно использовать атрибут XML.


<ProgressBar
    ...
    android:progressDrawable="@drawable/myprogressbar"/>

Круговые индикаторы прогресса

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

Сначала покажу неудачный пример, который попался в интернете. Создадим файл res/drawable/circular_progressbar.xml.


<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@android:id/background">
        <shape android:shape="oval">
            <stroke
                android:width="10dp"
                android:color="#DADADA" />
        </shape>
    </item>
    <item android:id="@android:id/progress">
        <rotate
            android:fromDegrees="180"
            android:pivotX="50%"
            android:pivotY="50%"
            android:toDegrees="180">
            <shape
                android:innerRadiusRatio="2.2"
                android:shape="ring"
                android:thickness="10dp"
                android:thicknessRatio="25.0">
                <solid android:color="#2299FF" />
            </shape>
        </rotate>
    </item>
</layer-list>

Снова используем атрибут android:progressDrawable. Чтобы пример работал должным образом, нужно следить за размерами индикатора - ширина и высота должны быть одинаковыми. Если забыть об этом, то получим следующее.

Circular ProgressBar

Попробуем установить одинаковые размеры, а также добавить пунктир и градиент.


<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@android:id/background">
        <shape android:shape="oval">
            <stroke
                android:width="8dp"
                android:color="#DADADA"
                android:dashWidth="5dp"
                android:dashGap="5dp" />
        </shape>
    </item>
    <item android:id="@android:id/progress">
        <rotate
            android:fromDegrees="180"
            android:pivotX="50%"
            android:pivotY="50%"
            android:toDegrees="180">
            <shape
                android:innerRadiusRatio="2.1"
                android:shape="ring"
                android:thickness="9dp"
                android:thicknessRatio="25.0">
                <solid android:color="#2299FF" />
                <gradient
                    android:angle="90"
                    android:centerColor="#FFEB3B"
                    android:endColor="#00BCD4"
                    android:startColor="#FF5722"
                    android:type="sweep"
                    android:useLevel="true" />
            </shape>
        </rotate>
    </item>
</layer-list>

<ProgressBar android:id="@+id/progressBar" style="?android:attr/progressBarStyleHorizontal" android:layout_width="300dp" android:layout_height="300dp" />
Circular ProgressBar

Для примера с обратным отсчётом я использовал индикатор с двумя drawable-ресурсами, используя shape с другими параметрами.

EggTimer

Индикатор с текстом

К сожалению, индикатор не выводит текст во время работы, что не очень удобно. Можно создать свой ProgressBar из TextView или создать свой компонент на основе ProgressBar. Другой простой способ - наложить текстовый блок прямо на индикатор, используя RelativeLayout. Пример для пояснения.


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content" >

    <RelativeLayout
        android:id="@+id/rl_progress_bar_set"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/btn_start"
        android:visibility="visible" >

        <ProgressBar
            android:id="@+id/pb_horizontal"
            style="@android:style/Widget.ProgressBar.Horizontal"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:max="100" />

        <TextView
            android:id="@+id/tv_progress_horizontal"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_alignBottom="@id/pb_horizontal"
            android:background="@android:color/transparent"
            android:gravity="center" />

        <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_below="@id/pb_horizontal"
            android:layout_marginLeft="10dp"
            android:layout_marginRight="10dp"
            android:layout_marginTop="10dp" >

            <ProgressBar
                style="@android:style/Widget.ProgressBar.Large"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:background="@android:color/background_dark" />

            <TextView
                android:id="@+id/tv_progress_circle"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:background="@android:color/transparent"
                android:gravity="center"
                android:textColor="@android:color/white" />
        </RelativeLayout>
    </RelativeLayout>

    <Button
        android:id="@+id/btn_start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="15dp"
        android:onClick="onClick"
        android:text="Поехали"
        android:textSize="25dp" />

</RelativeLayout>

Код, чтобы увидеть работу индикатора. При каждом щелчке кнопки значение индикаторов увеличиваются на 10 процентов.


// Java
package ru.alexanderklimov.progress;

import ...

public class MainActivity extends Activity {

	private int progress = 0;
	private ProgressBar pbHorizontal;
	private TextView tvProgressHorizontal;
	private TextView tvProgressCircle;

	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		setContentView(R.layout.activity_main);

		pbHorizontal = findViewById(R.id.pb_horizontal);
		tvProgressHorizontal = findViewById(R.id.tv_progress_horizontal);
		tvProgressCircle  = findViewById(R.id.tv_progress_circle); 
	}

	public void onClick(View v) {
		progress = progress + 10;
		postProgress(progress);
	}

	private void postProgress(int progress) {
		String strProgress = String.valueOf(progress) + " %";
		pbHorizontal.setProgress(progress);
		
		if (progress == 0) {
			pbHorizontal.setSecondaryProgress(0);
		} else {
			pbHorizontal.setSecondaryProgress(progress + 5);
		}
		tvProgressHorizontal.setText(strProgress);
		tvProgressCircle.setText(strProgress);
	}
}

ProgressBar

Программное создание индикатора

Данный способ уже устарел, так как через XML намного удобнее. Для общего развития.


// Java

// Зададим цвета для фона и самого индикатора
Drawable background = new ColorDrawable(0xFF373737);
Drawable progress = new ColorDrawable(0xFF00B51C);
ClipDrawable clipProgress = new ClipDrawable(progress, Gravity.LEFT,
		ClipDrawable.HORIZONTAL);

// Слои для индикатора
LayerDrawable layerlist = new LayerDrawable(new Drawable[] {
		background, clipProgress });
layerlist.setId(0, android.R.id.background);
layerlist.setId(1, android.R.id.progress);

// Создаем новый индикатор с горизонтальным стилем и заданным фоном
ProgressBar progressBar = new ProgressBar(this, null,
		android.R.attr.progressBarStyleHorizontal);
progressBar.setLayoutParams(new LinearLayout.LayoutParams(
		LinearLayout.LayoutParams.MATCH_PARENT,
		LinearLayout.LayoutParams.WRAP_CONTENT));
progressBar.setProgressDrawable(layerlist);
progressBar.setProgress(76);

LinearLayout linearlayout = (LinearLayout) findViewById(R.id.linearlayout);

linearlayout.addView(progressBar);

Индикаторы прогресса также можно использовать в заголовках приложения у старых устройств, а также в ActionBar и ToolBar у новых устройств.

Компонент постоянно дорабатывается. Например, в Android 8 появились новые методы isAnimating(), getMin(), setMin().

Анимация ObjectAnimator

Можно подключить готовую анимацию для визуализации компонента.


private static final TimeInterpolator GAUGE_ANIMATION_INTERPOLATOR = new DecelerateInterpolator(2);
private static final int MAX_LEVEL = 100;
private static final long GAUGE_ANIMATION_DURATION = 5000;
private ProgressBar mProgressBar;


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

    mProgressBar = findViewById(R.id.progressBar);
}

// По щелчку кнопки
public void onClick(View view) {
    ObjectAnimator animator = ObjectAnimator.ofInt(mProgressBar, "progress", 0, MAX_LEVEL);
    animator.setInterpolator(GAUGE_ANIMATION_INTERPOLATOR);
    animator.setDuration(GAUGE_ANIMATION_DURATION);
    animator.start();
}

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

Меняем стандартный индикатор через drawable - простой и быстрый способ.

Material ProgressBar - статья про создание собственного индикатора в стиле Material Design

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

How to create your own progressBar in Android. – Medium

Make an android custom view, publish and open source.

Библиотеки

GitHub - MackHartley/RoundedProgressBar: A customizable, animated progress bar that features rounded corners - индикатор с закруглёнными углами

skydoves/ProgressView: A polished and flexible ProgressView, fully customizable with animations.

GitHub - nipun-birla/BoxLoaderView

GitHub - VladimirWrites/Lemniscate: An easy way to make your progress view nice and sleek.

yuriy-budiyev/circular-progress-bar: Circular progress bar

timqi/SectorProgressView: a simple progress prompt or chart widget of android using circle and a sector

kofigyan/StateProgressBar: Android library to realize the various states and transitions in a ProgressBar.

JustZak/DilatingDotsProgressBar: A customizable indeterminate progress bar

DreaminginCodeZH/MaterialProgressBar: Material Design ProgressBar with consistent appearance

castorflex/SmoothProgressBar

Todd-Davies/ProgressWheel

passsy/android-HoloCircularProgressBar

daimajia/NumberProgressBar

lsjwzh/MaterialLoadingProgressBar

amanjeetsingh150/CoolProgressViews: Android library with collection of cool progress views.

Подборка индикаторов

glomadrian/loading-balls: A highly configurable library to do loading progress with animated balls - анимация для длительных операций.
loading-balls

droidchef/tinglingsquares: A delightful progress animation

Индикатор прогресса в виде пирога. Домашняя страница FilipPudak/ProgressPieView

cdeange/RopeProgressBar: Android ProgressBar that "bends" under its own weight. Inspired by http://drbl.in/nwih

biodunalfet/SlidingSquaresLoader: A simple progress loader

Реклама