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

Implementación de Android Custom View para el efecto de aceleración de la pelota de limpieza de memoria

Introducción

Al utilizar Clean Master o software de seguridad similar, todos sabemos que tienen una función, es decir, la limpieza de la memoria, que se muestra a través de una pequeña esfera circular que representa el tamaño de la memoria, y se muestra el progreso de la limpieza mediante porcentajes numéricos y barras de progreso. Este artículo detallará el proceso de implementación de este efecto, pero no aborda la implementación de la limpieza de la memoria.

Vista previa

Veamos primero cómo es el efecto de implementación final (el efecto gif es un poco malo):

A partir de la imagen anterior, podemos ver:

Cuando el View de la pelota de aceleración se muestra, la barra de progreso y el número de porcentaje comenzarán a aumentar desde 0% hasta un valor determinado (6(0%).
Después de que la barra de progreso detenga el aumento, el círculo central comienza a girar a lo largo del eje Y, se girará180 grados, el número de porcentaje en la parte superior no aparecerá el efecto de espejo (se mencionará más adelante).
Después de que el usuario haga clic en la pelota, comienza a limpiar la memoria, la barra de progreso y el número de porcentaje pasarán por el proceso de disminuir a 0 y luego aumentar a un valor determinado.

Detalles del proceso de implementación

En realidad, el efecto anterior se implementó a partir del acelerador de la aplicación Clean Master, ligeramente diferente, pero la forma general es la misma. Si los lectores están interesados en el efecto anterior, continúen leyendo, a continuación es la parte principal.

Paso 1.Inicialización

Primero, creamos un LieBaoView.java, que hereda de View, y reescribimos su constructor como sigue:

public LieBaoView(Context context) {
  super(context);
  init();
 }
 public LieBaoView(Context context, AttributeSet attrs) {
  super(context, attrs);
  init();
 }
 public LieBaoView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  init();
 }

Independientemente de cómo se instancie este View, se llamará al método init(), que se utiliza principalmente para manejar la inicialización de varios variables miembro, entonces ¿qué variables miembro o instancias necesitamos para ayudarnos?

La idea del autor es esta: a través de un bitmap en blanco, dibujamos círculos, texto y otros, y luego dibujamos este bitmap en nuestro view.

Por lo tanto, en el momento de la inicialización, necesitamos obtener instancias de varios Paint (pinceles), Bitmap (imágenes en blanco), Canvas (lienzo) y otros. Piense en esto: el círculo central es giratorio, por lo tanto, el círculo giratorio central no puede estar en el mismo bitmap que otros círculos, de lo contrario, traerá麻烦 en la implementación posterior de la rotación, por lo tanto, podemos preparar dos bitmaps en blanco. Entonces, podemos hacerlo así:

public void init(){
  //pincel para dibujar el círculo de fondo
  mBackgroundCirclePaint = new Paint();
  mBackgroundCirclePaint.setAntiAlias(true);
  mBackgroundCirclePaint.setColor(Color.argb(0xff, 0x10, 0x53, 0xff));
  //pincel para dibujar el círculo giratorio
  mFrontCirclePaint = new Paint();
  mFrontCirclePaint.setAntiAlias(true);
  mFrontCirclePaint.setColor(Color.argb(0xff, 0x5e, 0xae, 0xff));
  //pincel para dibujar texto
  mTextPaint = new Paint();
  mTextPaint.setAntiAlias(true);
  mTextPaint.setTextSize(80);
  mTextPaint.setColor(Color.WHITE);
  //pincel para dibujar la barra de progreso
  mArcPaint = new Paint();
  mArcPaint.setAntiAlias(true);
  mArcPaint.setColor(Color.WHITE);
  mArcPaint.setStrokeWidth(12);
  mArcPaint.setStyle(Paint.Style.STROKE);
  mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
  mBitmapCanvas = new Canvas(mBitmap); //asocia el lienzo y el Bitmap
  //gira el bitmap con el lienzo
  mOverturnBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
  mOverturnBitmapCanvas = new Canvas(mOverturnBitmap);
  //Se ha omitido parte de...
  //Camera, Matrix, Runnable y otros se explicarán a continuación
  mMatrix = new Matrix();
  mCamera = new Camera();
}

El contenido anterior se centró principalmente en la inicialización de varios tipos de pinceles diferentes, y se prepararon dos Bitmap y sus lienzos asociados. Podemos dibujar en estos lienzos asociados, de esta manera podemos obtener dos Bitmap con contenido.

Continuemos pensando: ¿Qué más necesitamos para lograr el efecto de inversión? Android SDK nos proporciona una serie de herramientas: Camera y Matrix. Utilizando estas herramientas, podemos realizar fácilmente varias transformaciones en Bitmap, como escalado, desplazamiento, inversión, etc. Sobre Camera y Matrix, los lectores pueden buscar información más detallada, aquí no se detallará más. Finalmente, también necesitamos Runnable, ya que es necesario implementar la inversión automática y el aumento y disminución automático de la barra de progreso, que se explicará en detalle a continuación, no es necesario apresurarse, por supuesto, también es necesario configurar un escuchador de clics.

Paso 2.Draw the image

The pen and canvas have been prepared for us, so let's draw the required images next. This can be done by overriding the View's onDraw() method.

①Draw the background circle, that is, the deepest blue circle at the outermost layer in the figure above:

mBitmapCanvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2, mBackgroundCirclePaint);

②Draw the middle white background circle, that is, the white part of the background during the flip process of the rotating circle:

mBitmapCanvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2 - , mPadding, mTextPaint);

③Draw the progress bar, how to implement the arc-shaped progress bar? Here is the author's idea: implement it through the canvas's drawArc() method, which can draw the largest circle (or ellipse) within a rectangle, set the pen to hollow and the pen line width to12It's just left and right, which can realize a thick arc line. Then, by continuously calling the onDraw() method and modifying the drawArc() angle, the progress bar effect can be achieved. If you have any other implementation methods, welcome to discuss.

 mBitmapCanvas.save();
//Instanciar un rectángulo, cuyas coordenadas superior izquierda y inferior derecha no coinciden con el Bitmap original, porque se necesita
//La barra de progreso tiene un espacio determinado con el círculo más externo
RectF rectF = new RectF(10,10,mWidth-10,mHeight-10);
//Primero girar el lienzo en sentido contrario a las agujas del reloj90 grados, de esta manera el ángulo de inicio de drawArc puede comenzar en 0 grados, evitando problemas innecesarios
mBitmapCanvas.rotate(-90, mWidth / 2, mHeight / 2);
mBitmapCanvas.drawArc(rectF, 0, ((float)mProgress/mMaxProgress)*360, false, mArcPaint);
mBitmapCanvas.restore();
canvas.drawBitmap(mBitmap, 0, 0, null);

④Draw the middle rotating circle. As mentioned above, since we need to achieve the flip effect, we can no longer draw on the same Bitmap, so we use another blank Bitmap. The drawing of the rotating circle is very simple, as long as its radius is smaller than the sum of the outer circle radius and the progress bar width:

mOverturnBitmapCanvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2 - , mPadding, mFrontCirclePaint);

⑤The last step is to draw the percentage numbers on the rotating circle. To draw text, we need to use the Canvas's drawText method, let's focus on this method:

 /**
  * Draw the text, with origin at (x,y), using the specified paint. The
  * origin is interpreted based on the Align setting in the paint.
  *
  * @param text The text to be drawn
  * @param x  The x-coordinate of the origin of the text being drawn
  * @param y  The y-coordinate of the baseline of the text being drawn
  * @param paint The paint used for the text (e.g. color, size, style)
  */
 public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
  //...
 }

Los primeros y cuartos parámetros no tienen mucho que decir, el segundo parámetro representa la coordenada x de inicio del texto, el tercer parámetro representa la coordenada y del baseline. Para que el texto se muestre centrado, solo necesitamos establecer las coordenadas x, y adecuadas, ¿entonces qué es el baseline? Es el punto de referencia del texto, veamos una imagen:

Desde la imagen podemos ver que desde el baseline hasta el punto más alto del texto es Ascent, es un valor negativo; desde el baseline hasta el punto más bajo del texto es Descent, es un valor positivo. Por lo tanto, si queremos que el texto se muestre en el controlador, podemos utilizar-(ascent-descent)/2Calcular la mitad de la altura del texto, luego utilizar mHeight/2(la mitad de la altura del controlador) más este valor, podemos obtener el valor de baseline en el controlador, en este momento también hemos logrado la visualización centrada, el código es el siguiente:

String text = (int) (((float)mProgress / mMaxProgress) *100) + "%";
//Obtener el ancho del texto
 float textWidth = mTextPaint.measureText(text);
//Obtener especificaciones de texto
Paint.FontMetrics metrics = mTextPaint.getFontMetrics();
float baseLine = mHeight / 2 - (metrics.ascent + metrics.descent) /2;
mOverturnBitmapCanvas.drawText(text, mWidth / 2 - textWidth / 2, baseLine, mTextPaint);

Por último, dibujamos el bitmap en la view:

canvas.drawBitmap(mOverturnBitmap, mMatrix, null);

Después de la dibujo anterior, veamos cómo es el efecto:

Entonces, todos los efectos básicos ya se han implementado. A continuación, implementaremos los efectos dinámicos.

Paso 3.Implementar el efecto de rotación automática

Desde el efecto de animación anterior, primero aumentamos la barra de progreso desde 0 hasta un valor determinado, luego realizamos la rotación automática. La implementación del aumento del valor es很简单, solo se necesita habilitar un Runnable, dentro del Runnable aumentar constantemente el valor de mProgress y llamar al método invalidate() para refrescar la View. Una vez que la barra de progreso haya aumentado, comenzamos la rotación, para lograr la rotación utilizamos Camera y Matrix para operar en el bitmap central, cambiar constantemente el ángulo para lograr, veamos el código:
Dentro del método onDraw():

 @Override
 protected void onDraw(Canvas canvas) {
  //....
  //Si está rotando actualmente
  if(isRotating) {
   mCamera.save();
   //ángulo de rotación
   mCamera.rotateY(mRotateAngle);
   //Si el ángulo de rotación es mayor o igual que18Cuando es 0 grados, restar180 grados
   if (mRotateAngle >= 180) {
    mRotateAngle -= 180;
   }
   //Obtener la matriz correspondiente según las operaciones del Camera
   mCamera.getMatrix(mMatrix);
   mCamera.restore();
   mMatrix.preTranslate(-mWidth / 2, -, mHeight / 2);
   mMatrix.postTranslate(mWidth / 2, mHeight / 2);
  }
  canvas.drawBitmap(mOverturnBitmap, mMatrix, null);
  //Si el control actual no ha realizado el proceso de volteo
  if(!isRotating && !isInital){
   //Establecer isIncreasing, lo que indica que primero se iniciará el proceso de aumento de la barra de progreso
   isIncreasing = true;
   isRotating = true;
   postDelayed(mRotateRunnable,10);
}

A continuación, escribimos mRotateRunnable, la inicialización de Runnable se realiza dentro del método init():

mRotateRunnable = new Runnable() {
 @Override
 public void run() {
  //Si el proceso actual está en aumento
  if(isIncreasing){
   Log.d("cylog","mProgress:")+mProgress);
   //Cuando el progreso增加到某个数值时,停止增加
   if(mProgress >= 59){
    isIncreasing = false;
   }
   mProgress++;
  } else {
   //Si el proceso de aumento termina, comenzar a invertir
   //Si mRotateAngle es mayor que90 grados, lo que indica que bitmap ya se ha invertido90 grados,
   //En este momento, el contenido de bitmap se convierte en contenido espejo, para evitar efectos de espejo, necesitamos girar adicionalmente180 grados,
   //Entonces se convertirá en una visualización normal, y la que se ha girado adicionalmente180 grados se restará dentro de onDraw.
   if (mRotateAngle > 90 y mRotateAngle < 180)
    mRotateAngle = mRotateAngle + 3 + 180;
   //Si mRotateAngle excede180 grados, el proceso de inversión completado
   else if (mRotateAngle >= 180) {
    isRotating = false;
    isInital = true;
    mRotateAngle = 0;
    return;
   } else
    //cada vez que el ángulo aumenta3esto puede ser ajustado, lo suficiente es suficiente
    mRotateAngle += 3;
  }
  invalidate();
  //25ms después de volver a llamar a este método
  postDelayed(this,25);
 }
};

Con los Runnable anteriores y la colaboración de onDraw() ya se puede lograr el efecto de rotación automática.

Paso 4.Implementar el efecto de limpieza al hacer clic

Bien, vamos a implementar el último efecto, al igual que utilizamos un Runnable, ya que el efecto de limpieza requiere que el usuario haga clic en la pelota antes de comenzar la limpieza, por lo que necesitamos un escucha de eventos, cada vez que el usuario haga clic, en el método onClick publicar un Runnable.
Primero implementar mCleaningRunnable, dentro del método init():

mCleaningRunnable = new Runnable() {
 @Override
 public void run() {
  //Si el progreso actual supera un valor determinado, detener la limpieza
  if (mProgress >= 60) {
   isCleaning = false;
   return;
  }
  //Si el proceso actual está en disminución, mProgress disminuye constantemente hasta alcanzar 0
  if (isDescending) {
   mProgress--;
   if (mProgress <= 0)
    isDescending = false;
  } else {
   mProgress++;
  }
  invalidate();
  postDelayed(this,40);
 }
};
setOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
  if(isCleaning) return;
   //Si el proceso de limpieza está en curso, devolver directamente para evitar que se envíen muchos posts
   //Configurar flag para realizar la limpieza
   isDescending = true;
   isCleaning = true;
   mProgress--;
   postDelayed(mCleaningRunnable, 40);
  }
);

La lógica se ha implementado, cada vez que se hace clic, primero se reduce el valor de progreso constantemente hasta 0, luego se aumenta nuevamente hasta un valor fijo, utilizando cada llamada a invalidate() para notificar al componente de actualizarse, de esta manera se logra el efecto dinámico.

Hasta aquí, todos los efectos han sido implementados. Aquí está todo el código. Gracias por leer~

public class LieBaoView extends View {
 private Paint mBackgroundCirclePaint;
 private Paint mFrontCirclePaint;
 private Paint mTextPaint;
 private Paint mArcPaint;
 private Bitmap mBitmap;
 private Bitmap mOverturnBitmap;
 private Canvas mBitmapCanvas;
 private Canvas mOverturnBitmapCanvas;
 private Matrix mMatrix;
 private Camera mCamera;
 private int mWidth = 400;
 private int mHeight = 400;
 private int mPadding = 20;
 private int mProgress = 0;
 private int mMaxProgress = 100;
 private int mRotateAngle = 0;
 private Runnable mRotateRunnable;
 private Runnable mCleaningRunnable;
 private boolean isRotating;
 private boolean isInital = false;
 private boolean isDescending;
 private boolean isIncreasing;
 private boolean isCleaning;
 public LieBaoView(Context context) {
  super(context);
  init();
 }
 public LieBaoView(Context context, AttributeSet attrs) {
  super(context, attrs);
  init();
 }
 public LieBaoView(Context context, AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  init();
 }
 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  setMeasuredDimension(mWidth,mHeight);
 }
 public void init(){
  //pincel para dibujar el círculo de fondo
  mBackgroundCirclePaint = new Paint();
  mBackgroundCirclePaint.setAntiAlias(true);
  mBackgroundCirclePaint.setColor(Color.argb(0xff, 0x10, 0x53, 0xff));
  //pincel para dibujar el círculo giratorio
  mFrontCirclePaint = new Paint();
  mFrontCirclePaint.setAntiAlias(true);
  mFrontCirclePaint.setColor(Color.argb(0xff, 0x5e, 0xae, 0xff));
  //pincel para dibujar texto
  mTextPaint = new Paint();
  mTextPaint.setAntiAlias(true);
  mTextPaint.setTextSize(80);
  mTextPaint.setColor(Color.WHITE);
  //pincel para dibujar la barra de progreso
  mArcPaint = new Paint();
  mArcPaint.setAntiAlias(true);
  mArcPaint.setColor(Color.WHITE);
  mArcPaint.setStrokeWidth(12);
  mArcPaint.setStyle(Paint.Style.STROKE);
  mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
  mBitmapCanvas = new Canvas(mBitmap); //asocia el lienzo y el Bitmap
  //gira el bitmap con el lienzo
  mOverturnBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
  mOverturnBitmapCanvas = new Canvas(mOverturnBitmap);
  mMatrix = new Matrix();
  mCamera = new Camera();
  mRotateRunnable = new Runnable() {
   @Override
   public void run() {
    //Si el proceso actual está en aumento
    if(isIncreasing){
     Log.d("cylog","mProgress:")+mProgress);
     //Cuando el progreso增加到某个数值时,停止增加
     if(mProgress >= 59){
      isIncreasing = false;
     }
     mProgress++;
    } else {
     //Si el proceso de aumento termina, comenzar a invertir
     //Si mRotateAngle es mayor que90 grados, lo que indica que bitmap ya se ha invertido90 grados,
     //En este momento, el contenido de bitmap se convierte en contenido espejo, para evitar efectos de espejo, necesitamos girar adicionalmente180 grados,
     //Entonces se convertirá en una visualización normal, y la que se ha girado adicionalmente180 grados se restará dentro de onDraw.
     if (mRotateAngle > 90 y mRotateAngle < 180)
      mRotateAngle = mRotateAngle + 3 + 180;
     //Si mRotateAngle excede180 grados, el proceso de inversión completado
     else if (mRotateAngle >= 180) {
      isRotating = false;
      isInital = true;
      mRotateAngle = 0;
      return;
     } else
      //cada vez que el ángulo aumenta3esto puede ser ajustado, lo suficiente es suficiente
      mRotateAngle += 3;
    }
    invalidate();
    //25ms después de volver a llamar a este método
    postDelayed(this,25);
   }
  };
  mCleaningRunnable = new Runnable() {
   @Override
   public void run() {
    //Si el progreso actual supera un valor determinado, detener la limpieza
    if (mProgress >= 60) {
     isCleaning = false;
     return;
    }
    //Si el proceso actual está en disminución, mProgress disminuye constantemente hasta alcanzar 0
    if (isDescending) {
     mProgress--;
     if (mProgress <= 0)
      isDescending = false;
    } else {
     mProgress++;
    }
    invalidate();
    postDelayed(this,40);
   }
  };
  setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
    if(isCleaning) return;
    isDescending = true;
    isCleaning = true;
    mProgress--;
    postDelayed(mCleaningRunnable, 40);
   }
  );
 }
 @Override
 protected void onDraw(Canvas canvas) {
  mBitmapCanvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2, mBackgroundCirclePaint);
  mBitmapCanvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2 - , mPadding, mTextPaint);
   mBitmapCanvas.save();
  //Instanciar un rectángulo, cuyas coordenadas superior izquierda y inferior derecha no coinciden con el Bitmap original, porque se necesita
  //La barra de progreso tiene un espacio determinado con el círculo más externo
  RectF rectF = new RectF(10,10,mWidth-10,mHeight-10);
  //Primero girar el lienzo en sentido contrario a las agujas del reloj90 grados, de esta manera el ángulo de inicio de drawArc puede comenzar en 0 grados, evitando problemas innecesarios
  mBitmapCanvas.rotate(-90, mWidth / 2, mHeight / 2);
  mBitmapCanvas.drawArc(rectF, 0, ((float)mProgress/mMaxProgress)*360, false, mArcPaint);
  mBitmapCanvas.restore();
  canvas.drawBitmap(mBitmap, 0, 0, null);
  mOverturnBitmapCanvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2 - , mPadding, mFrontCirclePaint);
  String text = (int) (((float)mProgress / mMaxProgress) *100) + "%";
  //Obtener el ancho del texto
  float textWidth = mTextPaint.measureText(text);
  //Obtener especificaciones de texto
  Paint.FontMetrics metrics = mTextPaint.getFontMetrics();
  float baseLine = mHeight / 2 - (metrics.ascent + metrics.descent) /2;
  mOverturnBitmapCanvas.drawText(text, mWidth / 2 - textWidth / 2, baseLine, mTextPaint);
  //Si está rotando actualmente
  if(isRotating) {
   mCamera.save();
   //ángulo de rotación
   mCamera.rotateY(mRotateAngle);
   //Si el ángulo de rotación es mayor o igual que18Cuando es 0 grados, restar180 grados
   if (mRotateAngle >= 180) {
    mRotateAngle -= 180;
   }
   //Obtener la matriz correspondiente según las operaciones del Camera
   mCamera.getMatrix(mMatrix);
   mCamera.restore();
   mMatrix.preTranslate(-mWidth / 2, -, mHeight / 2);
   mMatrix.postTranslate(mWidth / 2, mHeight / 2);
  }
  canvas.drawBitmap(mOverturnBitmap, mMatrix, null);
  //Si el control actual no ha realizado el proceso de volteo
  if(!isRotating && !isInital){
   //Establecer isIncreasing, lo que indica que primero se iniciará el proceso de aumento de la barra de progreso
   isIncreasing = true;
   isRotating = true;
   postDelayed(mRotateRunnable,10);
  }
 }
}

Esto es todo el contenido del artículo, esperamos que sea útil para su aprendizaje y que todos apoyen el tutorial de alarido.

Declaración: El contenido de este artículo se ha obtenido de la red, pertenece al autor original, se ha subido por los usuarios de Internet de manera autónoma, este sitio no posee los derechos de propiedad, no se ha realizado una edición humana y no asume ninguna responsabilidad legal relacionada. Si encuentra contenido sospechoso de copyright, le invitamos a enviar un correo electrónico a: notice#oldtoolbag.com (al enviar un correo electrónico, reemplace # con @) para denunciar y proporcionar evidencia relevante. Una vez confirmado, este sitio eliminará inmediatamente el contenido sospechoso de infracción.

Te gustará