English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Реализация пользовательского View в Android для имитации эффекта кругового шагомера QQ и анимации

В предыдущем проекте Android Ultra Precise Pedometer - Dylan Pedometer на главной странице использовался пользовательский виджет, который somewhat напоминает интерфейс QQ Sports, и имеет анимационный эффект. Вот как этот View был нарисован.

1. Начнем с изображения

2. Анализ изображений

Описание функции: Желтый цвет represents the total number of steps planned by the user, while red represents the number of steps the user has taken at the moment.

Начальный анализ: полная自定义 View, перезапись метода onDraw(), рисование дуги.

3. Необходимые знания для рисования дуги

В Canvas есть метод для рисования дуги

drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)// рисовать дугу,

Параметр один - это объект RectF, это граница эллипса в прямоугольной области, которая используется для определения формы, размера, дуги,

Параметр два - это начальный угол (градусы) в начале дуги, начальный угол дуги, единица измерения - градусы.

Параметр три - это угол дуги, проходящий по часовой стрелке, единица измерения - градусы, от середины справа к нулю.

Параметр四是, если true (истина), включает в себя центр при рисовании дуги, обычно используется для рисования секторов; если false (ложь), это будет дуга.

Параметр пять - это объект Paint;

Для этого метода вы можете посмотреть на мой ручной эскиз, он не очень хорош, объясните смысл этих параметров и процесс рисования, извините за плохую работу!

4. Подготовка к рисованию

(1). Получение координат центра

/**中心点的x坐标 */
float centerX = (getWidth()) / 2;

(2). Создание отложенного прямоугольника для дуги

/**指定圆弧的外轮廓矩形区域 */
RectF rectF = new RectF(0 + borderWidth, borderWidth, 2 * centerX - borderWidth, 2 * centerX - borderWidth);

5. Основные шаги рисования

(1).【第一步】Нанесение целого желтого дуги

/**
* 1.绘制总步数的黄色圆弧
*
* @param canvas 画笔
* @param rectF 参考的矩形
*/
private void drawArcYellow(Canvas canvas, RectF rectF) {
Paint paint = new Paint();
/** 默认画笔颜色,黄色 */
paint.setColor(getResources().getColor(R.color.yellow));
/** 结合处为圆弧 */
paint.setStrokeJoin(Paint.Join.ROUND);
/** 设置画笔的样式 Paint.Cap.Round ,Cap.SQUARE等分别为圆形、方形 */
paint.setStrokeCap(Paint.Cap.ROUND);
/** 设置画笔的填充样式 Paint.Style.FILL :填充内部;Paint.Style.FILL_AND_STROKE :填充内部和描边; Paint.Style.STROKE :仅描边 */
paint.setStyle(Paint.Style.STROKE);
/** 抗锯齿功能 */
paint.setAntiAlias(true);
/** 设置画笔宽度 */
paint.setStrokeWidth(borderWidth);
/**Метод рисования дуги
* drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)//рисовать дугу,
Параметр один - это объект RectF, это граница эллипса в прямоугольной области, которая используется для определения формы, размера, дуги,
Параметр два - это начальный угол (градусы) в начале дуги, начальный угол дуги, единица измерения - градусы.
Параметр три - это угол дуги, проходящий по часовой стрелке, единица измерения - градусы, от середины справа к нулю.
Параметр четыре: если это true (истина), то при рисовании дуги центр включается, обычно используется для рисования секторов; если это false (ложь), то это будет дуга,
Параметр пять - это объект Paint;
*/
canvas.drawArc(rectF, startAngle, angleLength, false, paint);
}

(2).【第二步】Нанесение красного дуги текущего прогресса

/**
* 2. Рисование красного дуги текущего шага
*/
private void drawArcRed(Canvas canvas, RectF rectF) {
Paint paintCurrent = new Paint();
paintCurrent.setStrokeJoin(Paint.Join.ROUND);
paintCurrent.setStrokeCap(Paint.Cap.ROUND);//Круглый край
paintCurrent.setStyle(Paint.Style.STROKE);//Установить стиль заливки
paintCurrent.setAntiAlias(true);//Функция的抗锯齿
paintCurrent.setStrokeWidth(borderWidth);//Установить ширину кисти
paintCurrent.setColor(getResources().getColor(R.color.red));//Установить цвет кисти
canvas.drawArc(rectF, startAngle, currentAngleLength, false, paintCurrent);
}

(3).【第三步】Нанесение красного числа текущего прогресса

/**
* 3. Круглый центр шагов
*/
private void drawTextNumber(Canvas canvas, float centerX) {
Paint vTextPaint = new Paint();
vTextPaint.setTextAlign(Paint.Align.CENTER);
vTextPaint.setAntiAlias(true); // Anti-aliasing function
vTextPaint.setTextSize(numberTextSize);
Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
vTextPaint.setTypeface(font); // Font style
vTextPaint.setColor(getResources().getColor(R.color.red));
Rect bounds_Number = new Rect();
vTextPaint.getTextBounds(stepNumber, 0, stepNumber.length(), bounds_Number);
canvas.drawText(stepNumber, centerX, getHeight() / 2 + bounds_Number.height() / 2, vTextPaint);
}

(4).【第四步】Нанесение красного числа «шагов»

/**
* 4. Text at the center of the ring [number of steps]
*/
private void drawTextStepString(Canvas canvas, float centerX) {
Paint vTextPaint = new Paint();
vTextPaint.setTextSize(dipToPx(16));
vTextPaint.setTextAlign(Paint.Align.CENTER);
vTextPaint.setAntiAlias(true); // Anti-aliasing function
vTextPaint.setColor(getResources().getColor(R.color.grey));
String stepString = "Number of steps";
Rect bounds = new Rect();
vTextPaint.getTextBounds(stepString, 0, stepString.length(), bounds);
canvas.drawText(stepString, centerX, getHeight() / 2 + bounds.height() + getFontHeight(numberTextSize), vTextPaint);
}

6. Как реализуется анимация -> ValueAnimator

ValueAnimator — это один из самых основных классов в механизме анимации свойств, который реализует анимацию свойств, постоянно манипулируя значениями. Анимационный переход между начальным и конечным значениями реализуется классом ValueAnimator. Внутри класса используется механизм времени для вычисления перехода между значениями, и нам нужно только предоставить начальное и конечное значения, а также сообщить продолжительность анимации, и ValueAnimator автоматически поможет нам выполнить эффект平滑 перехода от начального значения к конечному.

/* Настройка анимации для прогресса */
* @param start Начальное значение
* @param current Конечное значение
* @param length Продолжительность анимации
*/
private void setAnimation(float start, float current, int length) {
ValueAnimator progressAnimator = ValueAnimator.ofFloat(start, current);
progressAnimator.setDuration(length);
progressAnimator.setTarget(currentAngleLength);
progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
/** Каждая гладкая переходная величина между начальным и конечным значениями, постепенно обновляющая прогресс */
currentAngleLength = (float) animation.getAnimatedValue();
invalidate();
}
});
progressAnimator.start();
}

7. Полный код的自定义 StepArcView

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.View;
import cn.bluemobi.dylan.step.R;
/**
* Создано DylanAndroid 26 мая 2016 года.
* Дуга для отображения шагов
*/
public class StepArcView extends View {
/**
* Ширина дуги
*/
private float borderWidth = 38f;
/**
* Размер шрифта для рисования шага
*/
private float numberTextSize = 0;
/**
* Шаг
*/
private String stepNumber = "0";
/**
* Угол начала рисования дуги
*/
private float startAngle = 135;
/**
* Угол между концом и началом угла
*/
private float angleLength = 270;
/**
* Угол между концом красной дуги текущего шага и началом
*/
private float currentAngleLength = 0;
/**
* Время анимации
*/
private int animationLength = 3000;
public StepArcView(Context context) {
super(context);
}
public StepArcView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public StepArcView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/**中心点的x坐标 */
float centerX = (getWidth()) / 2;
/**指定圆弧的外轮廓矩形区域 */
RectF rectF = new RectF(0 + borderWidth, borderWidth, 2 * centerX - borderWidth, 2 * centerX - borderWidth);
/**【第一步】绘制整体的黄色圆弧 */
drawArcYellow(canvas, rectF);
/**【第二步】绘制当前进度的红色圆弧 */
drawArcRed(canvas, rectF);
/**【第三步】绘制当前进度的红色数字 */
drawTextNumber(canvas, centerX);
/**【第四步】绘制"步数"的红色数字 */
drawTextStepString(canvas, centerX);
}
/**
* 1.绘制总步数的黄色圆弧
*
* @param canvas 画笔
* @param rectF 参考的矩形
*/
private void drawArcYellow(Canvas canvas, RectF rectF) {
Paint paint = new Paint();
/** 默认画笔颜色,黄色 */
paint.setColor(getResources().getColor(R.color.yellow));
/** 结合处为圆弧 */
paint.setStrokeJoin(Paint.Join.ROUND);
/** 设置画笔的样式 Paint.Cap.Round ,Cap.SQUARE等分别为圆形、方形 */
paint.setStrokeCap(Paint.Cap.ROUND);
/** 设置画笔的填充样式 Paint.Style.FILL :填充内部;Paint.Style.FILL_AND_STROKE :填充内部和描边; Paint.Style.STROKE :仅描边 */
paint.setStyle(Paint.Style.STROKE);
/** 抗锯齿功能 */
paint.setAntiAlias(true);
/** 设置画笔宽度 */
paint.setStrokeWidth(borderWidth);
/**Метод рисования дуги
* drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)//рисовать дугу,
Параметр один - это объект RectF, это граница эллипса в прямоугольной области, которая используется для определения формы, размера, дуги,
Параметр два - это начальный угол (градусы) в начале дуги, начальный угол дуги, единица измерения - градусы.
Параметр три - это угол дуги, проходящий по часовой стрелке, единица измерения - градусы, от середины справа к нулю.
Параметр четыре: если это true (истина), то при рисовании дуги центр включается, обычно используется для рисования секторов; если это false (ложь), то это будет дуга,
Параметр пять - это объект Paint;
*/
canvas.drawArc(rectF, startAngle, angleLength, false, paint);
}
/**
* 2. Рисование красного дуги текущего шага
*/
private void drawArcRed(Canvas canvas, RectF rectF) {
Paint paintCurrent = new Paint();
paintCurrent.setStrokeJoin(Paint.Join.ROUND);
paintCurrent.setStrokeCap(Paint.Cap.ROUND);//Круглый край
paintCurrent.setStyle(Paint.Style.STROKE);//Установить стиль заливки
paintCurrent.setAntiAlias(true);//Функция的抗锯齿
paintCurrent.setStrokeWidth(borderWidth);//Установить ширину кисти
paintCurrent.setColor(getResources().getColor(R.color.red));//Установить цвет кисти
canvas.drawArc(rectF, startAngle, currentAngleLength, false, paintCurrent);
}
/**
* 3. Круглый центр шагов
*/
private void drawTextNumber(Canvas canvas, float centerX) {
Paint vTextPaint = new Paint();
vTextPaint.setTextAlign(Paint.Align.CENTER);
vTextPaint.setAntiAlias(true); // Anti-aliasing function
vTextPaint.setTextSize(numberTextSize);
Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
vTextPaint.setTypeface(font); // Font style
vTextPaint.setColor(getResources().getColor(R.color.red));
Rect bounds_Number = new Rect();
vTextPaint.getTextBounds(stepNumber, 0, stepNumber.length(), bounds_Number);
canvas.drawText(stepNumber, centerX, getHeight() / 2 + bounds_Number.height() / 2, vTextPaint);
}
/**
* 4. Text at the center of the ring [number of steps]
*/
private void drawTextStepString(Canvas canvas, float centerX) {
Paint vTextPaint = new Paint();
vTextPaint.setTextSize(dipToPx(16));
vTextPaint.setTextAlign(Paint.Align.CENTER);
vTextPaint.setAntiAlias(true); // Anti-aliasing function
vTextPaint.setColor(getResources().getColor(R.color.grey));
String stepString = "Number of steps";
Rect bounds = new Rect();
vTextPaint.getTextBounds(stepString, 0, stepString.length(), bounds);
canvas.drawText(stepString, centerX, getHeight() / 2 + bounds.height() + getFontHeight(numberTextSize), vTextPaint);
}
/**
* Get the height of the number of steps
*
* @param fontSize Font size
* @return Font height
*/
public int getFontHeight(float fontSize) {
Paint paint = new Paint();
paint.setTextSize(fontSize);
Rect bounds_Number = new Rect();
paint.getTextBounds(stepNumber, 0, stepNumber.length(), bounds_Number);
return bounds_Number.height();
}
/**
* dip преобразуется в px
*
* @param dip
* @return
*/
private int dipToPx(float dip) {
float density = getContext().getResources().getDisplayMetrics().density;
return (int) (dip * density + 0.5f * (dip >= 0 ? 1 : -1));
}
/**
* Прогресс выполненных шагов
*
* @param totalStepNum Установленное количество шагов
* @param currentCounts Количество выполненных шагов
*/
public void setCurrentCount(int totalStepNum, int currentCounts) {
stepNumber = currentCounts + "";
setTextSize(currentCounts);
/** Если текущее количество шагов превышает общее количество шагов, дуга все еще составляет 270 градусов, она не может быть кругом */
if (currentCounts > totalStepNum) {
currentCounts = totalStepNum;
}
/** Процент шагов, занимаемых из общего числа шагов */
float scale = (float) currentCounts / totalStepNum;
/** Преобразование в длину дуги, которую нужно достичь в конце, в радианы --> длина дуги */
float currentAngleLength = scale * angleLength;
/** Начать выполнение анимации */
setAnimation(0, currentAngleLength, animationLength);
}
/**
* Анимация прогресса
* ValueAnimator является одним из самых ключевых классов в механизме анимации свойств, механизм работы анимации свойств реализуется через постоянное манипулирование значениями.
* А анимационный переход между начальным и конечным значениями отвечает за ValueAnimator этот класс.
* Внутри он использует механизм времени для вычисления анимационного перехода между значениями,
* Нам нужно предоставить начальное и конечное значения ValueAnimator и сообщить ему продолжительность анимации,
* Тогда ValueAnimator автоматически поможет нам создать эффект平滑过渡 от начального значения к конечному значению.
*
* @param last
* @param current
*/
private void setAnimation(float last, float current, int length) {
ValueAnimator progressAnimator = ValueAnimator.ofFloat(last, current);
progressAnimator.setDuration(length);
progressAnimator.setTarget(currentAngleLength);
progressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentAngleLength = (float) animation.getAnimatedValue();
invalidate();
}
});
progressAnimator.start();
}
/**
* Устанавливает размер текста, чтобы предотвратить ситуации, когда текст становится слишком велик и не помещается, и динамически устанавливает размер шрифта
*
* @param num
*/
public void setTextSize(int num) {
String s = String.valueOf(num);
int length = s.length();
if (length <= 4) {
numberTextSize = dipToPx(50);
} иными словами, если length > 4 и length <= 6 {
numberTextSize = dipToPx(40);
} else if (length > 6 && length <= 8) {
numberTextSize = dipToPx(30);
} else if (length > 8) {
numberTextSize = dipToPx(25);
}
}
}

8. Объяснение использования

в xml

<cn.bluemobi.dylan.step.view.StepArcView
android:id="@+id/sv "
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp" />

В Activity

StepArcView sv = (StepArcView) findViewById(R.id.sv);
sv.setCurrentCount(7000, 1000);

Названные выше являются аналогичными QQ运动步数圆弧及动画效果,介绍的Android,希望大家有所帮助,如果大家有任何疑问请给我留言,我会及时回复大家的。在此也非常感谢大家对呐喊教程网站的支持!

Заявление: содержимое этой статьи было взято из Интернета, авторские права принадлежат соответствующему автору. Контент был предоставлен пользователями Интернета, сайт не имеет права собственности, не был отредактирован вручную и не несет ответственности за соответствующие юридические вопросы. Если вы обнаружите содержимое,涉嫌侵犯版权, пожалуйста, отправьте письмо по адресу: notice#oldtoolbag.com (при отправке письма, пожалуйста, замените # на @) для жалоб, и предоставьте соответствующие доказательства. Если факт будет установлен, сайт немедленно удалил涉嫌侵权的内容。

Дайте мне знать, что вам нравится