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