Долгое время я думал о создании библиотеки Android с открытым исходным кодом. Я твердо верю в то, что вклад в открытый исходный код и чтение открытого исходного кода делает человека лучшим инженером. Но у меня никогда не было опыта в создании собственной библиотеки.
Увлеченный тем фактом, что как библиотека Android публикуется в JCenter и как она загружается путем помещения одной строки в gradle, я подумал о публикации простой библиотеки Android.
Идея
Так как я впервые писал библиотеку Android с открытым исходным кодом, я подумал, что было бы лучше сделать ее максимально простой. Я хотел очень быстро создать библиотеку и опубликовать ее, чтобы освоить процесс.
Я знал, что создание какого-то SDK займет много времени. Следовательно, я решил возиться с пользовательским интерфейсом. Я хотел создать простой анимационный вид. Это был случай использования, с которым я столкнулся в своей работе, когда я должен был привлечь внимание пользователя к кнопке.
Я думал о добавлении к нему какой-то анимации . Импульсная анимация (растущая и уменьшающаяся) казалась наиболее эффективной и быстрой. Поэтому я решил продолжить эту линию со своей библиотекой. Просто создаю что-то, что я мог бы использовать в своей повседневной работе.
Кроме того, я был в некоторой степени осведомлен о том, как я мог бы сделать это просто с помощью ValueAnimator. Вишня сверху ?
Выполнение
Шаг 1
Я создал новый проект Android Studio с пустой деятельностью.
После создания проекта я создал класс с именем Pulsating Button и расширил его из LinearLayout. Это будет мой CustomView / Button, который я буду анимировать.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
class PulsatingButton : LinearLayout { constructor(context: Context?) : super(context) { init(context) } constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) { parseAttributes(context, attrs) init(context) } constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { parseAttributes(context, attrs) init(context) }} |
С этого момента я должен был присвоить моей кнопке такие атрибуты, как, например, частота пульса / частота пульса. Какой будет размер импульса, количество повторений и т. Д.
Я задавался вопросом, мог ли бы я использовать атрибуты для этого, и как оказалось, я мог. Потратив некоторое время на изучение того, как добавить пользовательские атрибуты xml, я создал 7 пользовательских атрибутов для своей кнопки:
- Частота пульса
- Счетчик импульсов
- Горизонтальное смещение
- Вертикальное смещение
- Текст
- Цвет текста
- Фон
Я создал файл с именем: attribute.xml в пакете значений и поместил в него свои атрибуты.
|
01
02
03
04
05
06
07
08
09
10
11
12
|
<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="PulsatingButton"> <attr name="pulseDuration" format="integer" /> <attr name="horizontalOffset" format="integer" /> <attr name="verticalOffset" format="integer" /> <attr name="pulseCount" format="integer" /> <attr name="buttonColor" format="color"/> <attr name="textColor" format="color"/> <attr name="text" format="string"/> </declare-styleable></resources> |
Шаг 2
При наличии атрибутов пришло время применить их к кнопке. Я добавил несколько глобальных переменных и проанализировал атрибуты.
Пришло время создать файл макета для кнопки. Файл макета содержал кнопку как дочерний элемент. Это было бы корневым представлением.
Я хотел бы извлечь кнопку в глобальной переменной. Закончив метод инициализации пульсирующей кнопки, я продолжил и установил атрибуты. Также добавлены некоторые методы для динамической установки этих переменных с помощью кода.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
class PulsatingButton : LinearLayout { private var buttonText: CharSequence? = "" private var textColor: Int = ContextCompat.getColor(context, android.R.color.black) private var buttonColor: Int = ContextCompat.getColor(context, R.color.colorAccent) private var verticalOffset: Int = 40 private var horizontalOffset: Int = 40 private var repeatCount: Int = Int.MAX_VALUE private var animationDuration: Int = 1000 val set = AnimatorSet() constructor(context: Context?) : super(context) { init(context) } constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) { parseAttributes(context, attrs) init(context) } constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { parseAttributes(context, attrs) init(context) } private fun init(context: Context?) { LayoutInflater.from(context).inflate(R.layout.pulsating_button, this, true) setColors() setText(buttonText) } private fun parseAttributes(context: Context, attributes: AttributeSet) { val attrs = context.theme.obtainStyledAttributes(attributes, R.styleable.PulsatingButton, 0, 0) try { this.animationDuration = attrs.getInt(R.styleable.PulsatingButton_pulseDuration, 100) this.verticalOffset = attrs.getInt(R.styleable.PulsatingButton_verticalOffset, 4) this.horizontalOffset = attrs.getInt(R.styleable.PulsatingButton_horizontalOffset, 4) this.repeatCount = attrs.getInt(R.styleable.PulsatingButton_pulseCount, Int.MAX_VALUE) this.buttonColor = attrs.getColor(R.styleable.PulsatingButton_buttonColor, ContextCompat.getColor(context, R.color.colorAccent)) this.textColor = attrs.getColor(R.styleable.PulsatingButton_textColor, ContextCompat.getColor(context, R.color.colorAccent)) this.buttonText = attrs.getText(R.styleable.PulsatingButton_text) } finally { attrs.recycle() } } fun setHorizontalOffset(horizontalOffset: Int) { this.horizontalOffset = horizontalOffset } fun setVerticalOffset(verticalOffset: Int) { this.verticalOffset = verticalOffset } fun setRepeatCount(count: Int) { this.repeatCount = count } fun setAnimationDuration(duration: Int) { this.animationDuration = duration } private fun setColors() { button.setBackgroundColor(buttonColor) button.setTextColor(textColor) } fun setButtonDrawable(drawable: Drawable) { button.background = drawable } fun setText(text: CharSequence?) { if (!TextUtils.isEmpty(text)) { button.text = text } }} |
Шаг 3
Теперь пришло время создать настоящую анимацию. Я планировал использовать ValueAnimator для многократного генерирования значений, которые будут использоваться для изменения ширины и высоты моей кнопки.
Я создал функцию с именем startAnimation и два объекта-аниматора значений для горизонтального и вертикального масштабирования:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
class PulsatingButton : LinearLayout {... fun startAnimation() { val animator = ValueAnimator.ofInt(0, verticalOffset) animator.interpolator = AccelerateDecelerateInterpolator() animator.duration = animationDuration.toLong() val animator2 = ValueAnimator.ofInt(0, horizontalOffset) animator2.interpolator = AccelerateDecelerateInterpolator() animator2.duration = animationDuration.toLong() }...} |
Затем к ним добавлены слушатели обновлений, которые будут получать промежуточные значения и обновлять границы моей кнопки для каждого значения.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
class PulsatingButton : LinearLayout {... fun startAnimation() { val animator = ValueAnimator.ofInt(0, verticalOffset) animator.repeatMode = ValueAnimator.REVERSE animator.interpolator = AccelerateDecelerateInterpolator() animator.duration = animationDuration.toLong() val animator2 = ValueAnimator.ofInt(0, horizontalOffset) animator2.repeatMode = ValueAnimator.REVERSE animator2.interpolator = AccelerateDecelerateInterpolator() animator2.duration = animationDuration.toLong() val originalheight = button.layoutParams.height val originalWidth = button.layoutParams.width animator.addUpdateListener { valueAnimator -> val params = button.layoutParams val animatedValue = valueAnimator.animatedValue as Int params.height = (originalheight + animatedValue) button.layoutParams = params } animator2.addUpdateListener { valueAnimator -> val params = button.layoutParams val animatedValue = valueAnimator.animatedValue as Int params.width = (originalWidth + animatedValue) button.layoutParams = params } }...} |
Затем я проверил, установлен ли счетчик повторов. Если нет, то по умолчанию повторяется бесконечно. Наконец, пришло время объединить эти два объекта анимации и воспроизвести их вместе. Для этого я использовал AnimatorSet.
Это должно работать правильно? Когда все выглядело хорошо, я на это надеялся, но, к моему удивлению, вот что происходит:

Есть подвох в использовании ValueAnimator . Потратив несколько часов на переполнение стека и официальные документы по Android, я узнал о свойстве аниматора значений под названием repeatMode. Должно быть установлено значение ValueAnimator.REVERSE
Что здесь произошло, это:
По умолчанию valueAnimator будет выдавать значения, скажем, от 0 до 40, например, 0, 1, 2, 3 …… ..40, а затем снова вернется к 0 и выдаст 0, 1, …… 40.
То, что я хотел, было 0, 1, 2 …… 40, а затем 40, 39, 38 …… ..1, 0. Это было необходимо для того, чтобы анимация работала, иначе она просто сократилась в мгновение ока.
Это был, вероятно, самый интенсивный и трудоемкий шаг в развитии этой библиотеки. Но, в конце концов, у меня наконец было это:

Релиз
Хорошо, теперь я закончила свою библиотеку. Но ждать! На самом деле это была не библиотека, а обычный проект Android Studio, который я создал.
Мне нужно было выяснить, как легко перенести проект Android Studio в библиотеку, иначе мне пришлось бы создать новый проект библиотеки и скопировать код.
Мне нужно было просто изменить одну строку внутри файла app / build.gradle. Мне нужно было удалить com.android.application и добавить com.android.library.
Обратите внимание, что после этого вы не сможете запустить свой проект, так как это библиотека.
После этого я хотел выяснить, как опубликовать библиотеку на какой-либо онлайн-платформе, чтобы ее можно было легко использовать для разработчиков Android. Самый простой способ сделать это — использовать JitPack.io
Все, что вам нужно сделать, это создать релиз вашего репозитория GitHub на вкладке релиз.

Затем скопируйте URL-адрес вашего репозитория и вставьте его в текстовое поле на JitPack.io. Это создаст ссылку «» для добавления в gradle.
Но подождите, это не может быть так просто, верно?
На самом деле это было до тех пор, пока я не выяснил, что также нужно включить maven url для jitpack в их файл build.gradle уровня проекта.
Проекты Android не имеют этого по умолчанию и включают только google () и jcenter (). Поэтому, чтобы минимизировать трения, я решил опубликовать свою библиотеку в JCenter.
Как я опубликовал свою библиотеку Android на JCenter? потребовал бы полной статьи самостоятельно. Так что следите за обновлениями для части 2.
Вывод
В этой статье рассказывается о создании моей первой библиотеки Android с открытым исходным кодом. Лучший способ учиться — это делать. И лучший способ начать писать лучший код — начать читать и писать больше открытого исходного кода, используемого другими!
Поэтому я призываю вас пойти дальше и внести свой вклад в этот простой проект Android с открытым исходным кодом. Это может быть что-то столь же простое, как обновление документации. Это первый шаг к началу работы с открытым исходным кодом.
|
Смотрите оригинальную статью здесь: Как я создал свою первую библиотеку Android с открытым исходным кодом — Часть 1 Мнения, высказанные участниками Java Code Geeks, являются их собственными. |