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

Implementação de View personalizado no Android para efeito de arco e animação semelhante ao QQ step counter

No desenvolvimento anterior do Contador de Passos Ultra Preciso do Android-Dylan Stepping Home Page usou um controle personalizado, semelhante à interface do QQ Movement, e efeitos de animação, vamos falar sobre como esse View é desenhado a seguir.

1.Primeiro olhemos para a imagem de efeito

2.Análise da imagem de efeito

Descrição da função: Amarelo representa o número total de passos planejados pelo usuário, vermelho representa o número de passos atuais do usuário.

Análise inicial: Personalizar completamente View, anular o método onDraw() para desenhar arcos.

3.Conhecimentos necessários para desenhar arcos

No Canvas há um método para desenhar arcos

drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)//desenhar arco

o primeiro parâmetro é o objeto RectF, uma área retangular elíptica que define os limites da forma, tamanho e arco.

o segundo parâmetro é o ângulo de início (em graus) no início do arco, o ângulo de início do arco, em graus.

o terceiro parâmetro é o ângulo do arco coberto, na direção horária, em graus, começando do meio direito como zero graus.

O quarto parâmetro é se for true (verdadeiro), ao desenhar o arco, incluirá o centro, geralmente usado para desenhar seções circulares; se for false (falso), isso será uma curva arredondada.

o quinto parâmetro é o objeto Paint;

Para este método, você pode ver o esboço que eu desenhei à mão, é muito ruim, expor o significado desses parâmetros e o processo de desenho, peço desculpas por minha má arte!

4.Preparativos de desenho

(1).Obter as coordenadas do ponto central

/**Coordenada x do ponto central*/
float centerX = (getWidth()) / 2;

(2).Criar um retângulo de referência externo ao arco

/**Especificar a área retangular contornada do arco circular*/
RectF rectF = new RectF(0 + borderWidth, borderWidth, 2 * centerX - borderWidth, 2 * centerX - borderWidth);

5.Os principais passos de desenho

(1).[Primeiro Passo] Desenhar o arco amarelo geral

/**
* 1.Desenhar o arco amarelo total
*
* @param canvas Caneta
* @param rectF Rectângulo de referência
*/
private void drawArcYellow(Canvas canvas, RectF rectF) {
Paint paint = new Paint();
/** Cor padrão da caneta, amarelo */
paint.setColor(getResources().getColor(R.color.yellow));
/** O ponto de junção é arco circular*/
paint.setStrokeJoin(Paint.Join.ROUND);
/** Definir o estilo da caneta Paint.Cap.Round, Cap.SQUARE, etc., como circular e quadrada*/
paint.setStrokeCap(Paint.Cap.ROUND);
/** definir estilo de preenchimento do pincel Paint.Style.FILL : preencher interno; Paint.Style.FILL_AND_STROKE : preencher interno e contorno; Paint.Style.STROKE : apenas contorno*/
paint.setStyle(Paint.Style.STROKE);
/**Função de anti-aliasamento*/
paint.setAntiAlias(true);
/**definir largura do pincel*/
paint.setStrokeWidth(borderWidth);
/**método para desenhar arco
* drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)//desenhar arco
o primeiro parâmetro é o objeto RectF, uma área retangular elíptica que define os limites da forma, tamanho e arco.
o segundo parâmetro é o ângulo de início (em graus) no início do arco, o ângulo de início do arco, em graus.
o terceiro parâmetro é o ângulo do arco coberto, na direção horária, em graus, começando do meio direito como zero graus.
o quarto parâmetro, se for true (verdadeiro), inclui o centro ao desenhar o arco, geralmente usado para desenhar seções circulares; se for false (falso), será uma curva de arco;
o quinto parâmetro é o objeto Paint;
*/
canvas.drawArc(rectF, startAngle, angleLength, false, paint);
}

(2).[Segundo Passo] Desenhar o arco vermelho do progresso atual

/**
* 2.desenhar arco vermelho atual
*/
private void drawArcRed(Canvas canvas, RectF rectF) {
Paint paintCurrent = new Paint();
paintCurrent.setStrokeJoin(Paint.Join.ROUND);
paintCurrent.setStrokeCap(Paint.Cap.ROUND);//curvatura arredondada
paintCurrent.setStyle(Paint.Style.STROKE);//definir estilo de preenchimento
paintCurrent.setAntiAlias(true);//Função de anti-aliasamento
paintCurrent.setStrokeWidth(borderWidth);//definir largura do pincel
paintCurrent.setColor(getResources().getColor(R.color.red));//Definir cor do pincel
canvas.drawArc(rectF, startAngle, currentAngleLength, false, paintCurrent);
}

(3).[Terceiro Passo] Desenhar o número vermelho do progresso atual

/**
* 3Número de passos no centro do anel
*/
private void drawTextNumber(Canvas canvas, float centerX) {
Paint vTextPaint = new Paint();
vTextPaint.setTextAlign(Paint.Align.CENTER);
vTextPaint.setAntiAlias(true);//Função de anti-aliasamento
vTextPaint.setTextSize(numberTextSize);
Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
vTextPaint.setTypeface(font);//Estilo de fonte
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).[Quarto Passo] Desenhar o número vermelho de 'passos'

/**
* 4Texto no centro do anel [número de passos]
*/
private void drawTextStepString(Canvas canvas, float centerX) {
Paint vTextPaint = new Paint();
vTextPaint.setTextSize(dipToPx(16));
vTextPaint.setTextAlign(Paint.Align.CENTER);
vTextPaint.setAntiAlias(true);//Função de anti-aliasamento
vTextPaint.setColor(getResources().getColor(R.color.grey));
String stepString = "步数";
Rect bounds = new Rect();
vTextPaint.getTextBounds(stepString, 0, stepString.length(), bounds);
canvas.drawText(stepString, centerX, getHeight()) / 2 + bounds.height() + getFontHeight(numberTextSize), vTextPaint);
}

6.Como a animação é implementada->ValueAnimator

ValueAnimator é uma classe fundamental no mecanismo de animação de atributos, e o mecanismo de execução da animação de atributos é realizado através de operações contínuas sobre os valores, enquanto a transição de animação entre o valor inicial e o valor final é calculada por essa classe ValueAnimator. Seu interior usa um mecanismo de loop de tempo para calcular a transição entre os valores, e tudo o que precisamos fazer é fornecer o valor inicial e o valor final para o ValueAnimator, e informá-lo do tempo necessário para a animação, então o ValueAnimator automaticamente nos ajudará a completar o efeito de transição suave do valor inicial ao valor final.

/*definir animação de progresso
* @param start O valor inicial
* @param current O valor final
* @param length A duração do animação
*/
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) {
/**Um valor de transição suave gerado entre o valor inicial e o valor final, atualizando gradualmente o progresso*/
currentAngleLength = (float) animation.getAnimatedValue();
invalidate();
}
});
progressAnimator.start();
}

7.O código-fonte completo do StepArcView personalizado

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;
/**
* Criado por DylanAndroid em 2016/5/26.
* O arco que exibe o número de passos
*/
public class StepArcView extends View {
/**
* A largura do arco
*/
private float borderWidth = 38f;
/**
* O tamanho da fonte para desenhar o número de passos
*/
private float numberTextSize = 0;
/**
* O número de passos
*/
private String stepNumber = "0";
/**
* O ângulo de início do desenho do arco
*/
private float startAngle = 135;
/**
* a diferença de ângulo entre o ângulo correspondente ao ponto final e o ângulo correspondente ao ponto de partida
*/
private float angleLength = 270;
/**
* O ângulo entre o ponto final do arco vermelho a ser desenhado e o ponto de partida
*/
private float currentAngleLength = 0;
/**
* Duração do animação
*/
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);
/**Coordenada x do ponto central*/
float centerX = (getWidth()) / 2;
/**Especificar a área retangular contornada do arco circular*/
RectF rectF = new RectF(0 + borderWidth, borderWidth, 2 * centerX - borderWidth, 2 * centerX - borderWidth);
/**【Passo 1】Desenhar o arco amarelo geral*/
drawArcYellow(canvas, rectF);
/**【Passo 2】Desenhar o arco vermelho do progresso atual*/
drawArcRed(canvas, rectF);
/**【Passo 3】Desenhar o número vermelho do progresso atual*/
drawTextNumber(canvas, centerX);
/**【Passo 4】Desenhar o número vermelho de "passos"*/
drawTextStepString(canvas, centerX);
}
/**
* 1.Desenhar o arco amarelo total
*
* @param canvas Caneta
* @param rectF Rectângulo de referência
*/
private void drawArcYellow(Canvas canvas, RectF rectF) {
Paint paint = new Paint();
/** Cor padrão da caneta, amarelo */
paint.setColor(getResources().getColor(R.color.yellow));
/** O ponto de junção é arco circular*/
paint.setStrokeJoin(Paint.Join.ROUND);
/** Definir o estilo da caneta Paint.Cap.Round, Cap.SQUARE, etc., como circular e quadrada*/
paint.setStrokeCap(Paint.Cap.ROUND);
/** definir estilo de preenchimento do pincel Paint.Style.FILL : preencher interno; Paint.Style.FILL_AND_STROKE : preencher interno e contorno; Paint.Style.STROKE : apenas contorno*/
paint.setStyle(Paint.Style.STROKE);
/**Função de anti-aliasamento*/
paint.setAntiAlias(true);
/**definir largura do pincel*/
paint.setStrokeWidth(borderWidth);
/**método para desenhar arco
* drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)//desenhar arco
o primeiro parâmetro é o objeto RectF, uma área retangular elíptica que define os limites da forma, tamanho e arco.
o segundo parâmetro é o ângulo de início (em graus) no início do arco, o ângulo de início do arco, em graus.
o terceiro parâmetro é o ângulo do arco coberto, na direção horária, em graus, começando do meio direito como zero graus.
o quarto parâmetro, se for true (verdadeiro), inclui o centro ao desenhar o arco, geralmente usado para desenhar seções circulares; se for false (falso), será uma curva de arco;
o quinto parâmetro é o objeto Paint;
*/
canvas.drawArc(rectF, startAngle, angleLength, false, paint);
}
/**
* 2.desenhar arco vermelho atual
*/
private void drawArcRed(Canvas canvas, RectF rectF) {
Paint paintCurrent = new Paint();
paintCurrent.setStrokeJoin(Paint.Join.ROUND);
paintCurrent.setStrokeCap(Paint.Cap.ROUND);//curvatura arredondada
paintCurrent.setStyle(Paint.Style.STROKE);//definir estilo de preenchimento
paintCurrent.setAntiAlias(true);//Função de anti-aliasamento
paintCurrent.setStrokeWidth(borderWidth);//definir largura do pincel
paintCurrent.setColor(getResources().getColor(R.color.red));//Definir cor do pincel
canvas.drawArc(rectF, startAngle, currentAngleLength, false, paintCurrent);
}
/**
* 3Número de passos no centro do anel
*/
private void drawTextNumber(Canvas canvas, float centerX) {
Paint vTextPaint = new Paint();
vTextPaint.setTextAlign(Paint.Align.CENTER);
vTextPaint.setAntiAlias(true);//Função de anti-aliasamento
vTextPaint.setTextSize(numberTextSize);
Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL);
vTextPaint.setTypeface(font);//Estilo de fonte
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);
}
/**
* 4Texto no centro do anel [número de passos]
*/
private void drawTextStepString(Canvas canvas, float centerX) {
Paint vTextPaint = new Paint();
vTextPaint.setTextSize(dipToPx(16));
vTextPaint.setTextAlign(Paint.Align.CENTER);
vTextPaint.setAntiAlias(true);//Função de anti-aliasamento
vTextPaint.setColor(getResources().getColor(R.color.grey));
String stepString = "步数";
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 the current step
*
* @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 converted to px
*
* @param dip
* @return
*/
private int dipToPx(float dip) {
float density = getContext().getResources().getDisplayMetrics().density;
return (int) (dip * density + 0.5f * (dip >= 0 &63; 1 : -1));
}
/**
* The progress of steps taken
*
* @param totalStepNum The set number of steps
* @param currentCounts The number of steps taken
*/
public void setCurrentCount(int totalStepNum, int currentCounts) {
stepNumber = currentCounts + "";
setTextSize(currentCounts);
/**If the number of steps taken exceeds the total number of steps, the arc is still270 degrees, cannot be a garden*/
if (currentCounts > totalStepNum) {
currentCounts = totalStepNum;
}
/**The percentage of steps taken in the total number of steps*/
float scale = (float) currentCounts / totalStepNum;
/**converter para o comprimento da angle final a ser alcançada em radianos--> comprimento da arco*/
float currentAngleLength = scale * angleLength;
/**iniciar execução da animação*/
setAnimation(0, currentAngleLength, animationLength);
}
/**
* definir animação de progresso
* O ValueAnimator é a classe mais central da mecânica de animação de atributos, a execução da animação de atributos é implementada através da operação contínua de valores,
* e a transição entre o valor inicial e final é calculada pelo ValueAnimator.
* Internamente, ele usa um mecanismo de ciclo de tempo para calcular a transição entre os valores,
* Precisamos fornecer o valor inicial e final ao ValueAnimator e informá-lo do tempo de execução do animação.
* Então o ValueAnimator nos ajudará automaticamente a completar a transição suave do valor inicial para o valor final.
*
* @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();
}
/**
* Define o tamanho do texto, para evitar que o texto não caiba após um número de passos particularmente grande, ajustando dinamicamente o tamanho da fonte
*
* @param num
*/
public void setTextSize(int num) {
String s = String.valueOf(num);
int length = s.length();
if (length <= 4) {
numberTextSize = dipToPx(50);
} else if (length > 4 && length <= 6) {
numberTextSize = dipToPx(40);
} else if (length > 6 && length <= 8) {
numberTextSize = dipToPx(30);
} else if (length > 8) {
numberTextSize = dipToPx(25);
}
}
}

8.Instruções de Uso

no 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" />

Dentro da Activity

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

O que foi mencionado acima é o que o editor apresentou aos amigos sobre o efeito circular e animação de contagem de passos do Android que imita o QQ, esperando que ajude a todos. Se você tiver alguma dúvida, por favor, deixe um comentário, o editor responderá a todos a tempo. Agradecemos também o apoio ao site Tutorial Yell.

Declaração: O conteúdo deste artigo é extraído da Internet, pertence ao autor original, foi uploaded por usuários da Internet de forma voluntária, o site não possui direitos de propriedade, não foi editado manualmente e não assume responsabilidade legal relevante. Se você encontrar conteúdo suspeito de violação de direitos autorais, por favor, envie um e-mail para: notice#oldtoolbag.com (ao enviar e-mail, substitua # por @ para denunciar, e forneça provas relevantes. Apenas após verificação, o site deletará o conteúdo suspeito de violação de direitos autorais.)

Você também pode gostar