English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Ahora hay más aplicaciones de ventana flotante en los teléfonos, para los usuarios, la aplicación de ventana flotante más común es el pequeño控件 de seguridad, tome36Para 0卫士, cuando se abre la ventana flotante, es una pelota que se puede arrastrar, al hacer clic en la pelota, aparece un控件 grande para realizar operaciones adicionales como liberar la memoria del teléfono. Por lo tanto, aprovechando el video de Mooc, realizando una imitación360 Ball de aceleración, se agregó la función de liberar memoria al hacer clic en la pelota.
Dado que es un teléfono, solo hay capturas de pantalla de la pantalla: como se muestra en la imagen después de la implementación: al hacer clic en el botón de encendido, aparece una pelota flotante en la parte superior del teléfono que muestra el porcentaje de memoria disponible; al arrastrar la pelota, la pelota se convierte en un icono de Android; al soltar la pelota, la pelota se adhiere a los lados de la pantalla; al hacer clic en la pelota, aparece un gran控件 en la parte inferior del teléfono, al hacer clic en la pelota dentro de él, se libera la memoria del teléfono; al hacer clic en otra área de la pantalla del teléfono, el gran控件 desaparece y la pelota reaparece.
El efecto es el siguiente:
A continuación, se detallan algunos pasos importantes:
1.Implementación de FloatCircleView (personalización de view)
El proceso de implementación de FloatCircleView es el proceso de personalización de view.1、Personalizar propiedades de View 2、Obtener propiedades personalizadas en el constructor de View 3、Sobrescribir onMesure 4、Sobrescribir onDraw. No hemos personalizado otras propiedades, por lo que hemos omitido muchos pasos.
Inicialización de varias variables, configuración del ícono que se mostrará al arrastrar la pelota, y cálculo de varios tipos de memoria. (Usado para mostrar en la pelota)
public int width=100; public int heigth=100; private Paint circlePaint;//Dibujar círculo private Paint textPaint; //Dibujar texto private float availMemory; //La memoria utilizada private float totalMemory; //La memoria total private String text; //El porcentaje de memoria utilizada mostrado private boolean isDraging=false; //¿Está en estado de arrastrar? private Bitmap src; private Bitmap scaledBitmap; //La imagen escalada. /** * Inicializar el pincel y calcular la memoria disponible, la memoria total y el porcentaje de memoria disponible. */ public void initPatints() { circlePaint = new Paint(); circlePaint.setColor(Color.CYAN); circlePaint.setAntiAlias(true); textPaint = new Paint(); textPaint.setColor(Color.WHITE); textPaint.setTextSize(25); textPaint.setFakeBoldText(true); textPaint.setAntiAlias(true); //Configurar la imagen src = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); //imagen escalada (poner el icono y la pelota flotante del mismo tamaño.). scaledBitmap = Bitmap.createScaledBitmap(src, width, heigth, true); //Calcular la memoria utilizada, la memoria total, el porcentaje de memoria utilizada, availMemory= (float) getAvailMemory(getContext()); totalMemory= (float) getTotalMemory(getContext()); text=(int)((availMemory/totalMemory)*100)+"%"; }
onMeasure(); es escribir las dimensiones fijas, a través de setMeasuredDimension(width, heigth); ingresar.
onDraw(); dibuja la pelota flotante. Definir una variable booleana para determinar si el estado actual es el estado de arrastre de la pelota. Si es el estado de arrastre de la pelota, dibuja el ícono de Android en esta posición, de lo contrario, realiza el dibujo de la pelota. El dibujo de la pelota no es difícil, lo más importante es el dibujo de texto. A continuación,2un dibujo puede profundizar en la comprensión de la escritura de texto.
1.El x-coordinate cuando se dibuja el texto (1.textPaint.measureText(text); obtener el ancho del texto2.El ancho de la pelota/2-El ancho del texto/2。)
2.El y-coordinate cuando se dibuja el texto (1.Paint.FontMetrics fontMetrics = textPaint.getFontMetrics(); obtener la clase de medición de atributos de fuente.2.(fontMetrics.ascent + fontMetrics.descent) / 2 Obtener la altura del texto.3.La altura de la pelota/2-La altura de la fuente/2)
Es bastante fácil entenderlo con un dibujo:
/** * Dibujar una pelota y texto. Si la pelota está en estado de arrastre, muestra el ícono de Android, de lo contrario, muestra la pelota. * @param canvas */ @Override protected void onDraw(Canvas canvas) { if (isDraging){ canvas.drawBitmap(scaledBitmap,0,0,null); } else { //1.Dibujar un círculo canvas.drawCircle(width) / 2, heigth / 2, width / 2, circlePaint); //2.画text float textwidth = textPaint.measureText(text);//文本宽度 float x = width / 2 - textwidth / 2; Paint.FontMetrics fontMetrics = textPaint.getFontMetrics(); float dy = -(fontMetrics.ascent + fontMetrics.descent) / 2; float y = heigth / 2 + dy; canvas.drawText(text, x, y, textPaint); } }
获得手机已用内存及总内存的方法:
public long getAvailMemory(Context context) { // 获取android当前可用内存大小 ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo(); am.getMemoryInfo(mi); //mi.availMem; 当前系统的可用内存 //return Formatter.formatFileSize(context, mi.availMem);// 将获取的内存大小规格化 return mi.availMem/(1024*1024); } public long getTotalMemory(Context context) { String str1 = "/proc/meminfo";// 系统内存信息文件 String str2; String[] arrayOfString; long initial_memory = 0; try { FileReader localFileReader = new FileReader(str1); BufferedReader localBufferedReader = new BufferedReader( localFileReader, 8192); str2 = localBufferedReader.readLine();// Lectura de la primera línea de meminfo, tamaño total de la memoria del sistema arrayOfString = str2.split("\\s+}); for (String num : arrayOfString) { Log.i(str2, num + "\t"); } initial_memory = Integer.valueOf(arrayOfString[1]).intValue() * 1024;// Se obtiene la memoria total del sistema, en KB, multiplicada por1024Conversión a Byte localBufferedReader.close(); } } //return Formatter.formatFileSize(context, initial_memory);// Conversión de Byte a KB o MB, normalización del tamaño de la memoria return initial_memory/(1024*1024); }
2.crea una clase de gestión de ventanas WindowManager, que gestiona la pelota flotante y la gran ventana inferior.
La clase WindowManager. Se utiliza para gestionar la visualización y ocultación de la pelota flotante y la gran ventana inferior del teléfono.
Debe agregarse <uses-permiso android:name="android.permission.SYSTEM_ALERT_WINDOW" /> Privilegios.
Se obtiene la clase de gestión de ventanas a través de WindowManager wm = (WindowManager)getSystemService(Context.WINDOW_SERVICE);
Se utiliza wm.addView(view, params) para agregar el view a la ventana.
Se utiliza wm.remove(view,params) para quitar el view de la ventana.
Se utiliza wm.updateViewLayout(view,params) para actualizar el view.
WindowManager.LayoutParams se utiliza para configurar varios atributos del view.
1.crea una instancia de FloatViewManager.
//模式单例创建 public static FloatViewManager getInstance(Context context){ if (inStance==null){ synchronized(FloatViewManager.class){ if (inStance==null){ inStance=new FloatViewManager(context); } } } return inStance; }
2.Métodos para mostrar la pelota flotante y mostrar la ventana inferior. (El método para mostrar la ventana es similar al para mostrar la pelota flotante.)
/** * Mostrar ventana flotante */ public void showFloatCircleView(){ //Configuración de parámetros if (params==null){ params = new WindowManager.LayoutParams(); //Ancho y alto params.width=circleView.width; params.height=circleView.heigth; //Alineación params.gravity= Gravity.TOP|Gravity.LEFT; //Desplazamiento params.x=0; params.y=0; //Tipo params.type=WindowManager.LayoutParams.TYPE_TOAST; //Configurar las propiedades de la ventana. params.flags= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; //Formato de píxeles params.format= PixelFormat.RGBA_8888; } //Agregar la pelota a la ventana. wm.addView(circleView, params); } public void showFloatCircleView(){ ...... }
3.Cuando se inicia el programa, primero crear la pelota flotante, la pelota puede arrastrarse, hacer clic en la pelota, la ventana inferior del teléfono muestra (FloatMenuView), la pelota se oculta. Por lo tanto, para la pelota (circleView), se debe realizar setOnTouchListener y setOnClickListener para escuchar eventos.
Analizar la distribución de eventos de la pelota; Para la pelota:
Cuando ACTION_DOWN, registrar downX, downY de la pelota, así como startX, startY
Cuando ACTION_MOVE, establecer el estado de arrastramiento de circleView en true, registrar moveX, moveY de la pelota, calcular la distancia de movimiento de la pelota (dx, dy), y luego actualizar la posición de la pelota según wm.updateViewLayout(circleView,params); Finalmente, asignar las últimas coordenadas de move a startX, startY.
Cuando ACTION_UP, establecer si circleView está arrastrando en false, registrar las coordenadas cuando se levanta, upx, según upx y el ancho de la pantalla del teléfono/2,realizar el juicio para determinar si el globo final está pegado a la izquierda o derecha de la pantalla. A continuación, se muestra el error de arrastre del globo. Cuando la distancia de arrastre del globo es menor10píxeles, puede desencadenar el evento de clic del globo. (El evento de toque del globo tiene prioridad sobre el evento de clic del globo. Cuando el evento de toque devuelve true, este evento se consume y ya no se transmite hacia abajo. Cuando el evento de toque devuelve false, este evento continúa transmitiéndose hacia abajo, lo que desencadena el evento de clic del globo.)
Evento de clic del globo: al hacer clic en el globo, ocultar el globo flotante, aparecer la ventana inferior del teléfono y establecer la animación de transición cuando la ventana inferior aparezca.
//asignar escucha de toque a circleView. private View.OnTouchListener circleViewOnTouchListener=new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: //Últimas coordenadas al presionar, según ACTION_MOVE. startX = event.getRawX(); startY = event.getRawY(); //coordenadas al presionar. downX = event.getRawX(); downY = event.getRawY(); break; case MotionEvent.ACTION_MOVE: circleView.setDrageState(true); moveX = event.getRawX(); moveY=event.getRawY(); float dx = moveX -startX; float dy=moveY-startY; params.x+=dx; params.y+=dy; wm.updateViewLayout(circleView,params); startX= moveX; startY=moveY; break; case MotionEvent.ACTION_UP: float upx=event.getRawX(); if (upx>getScreenWidth();/2) { params.x=getScreenWidth();-circleView.width; } else { params.x=0; } circleView.setDrageState(false); wm.updateViewLayout(circleView,params); if (Math.abs(moveX-downX>10) { return true; } else { return false; } default: break; } return false; } }); circleView.setOnTouchListener(circleViewOnTouchListener); circleView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //Toast.makeText(, "onclick", Toast.LENGTH_SHORT).show(); //ocultar circleView, mostrar la barra de menú. wm.removeView(circleView); showFloatMenuView(); floatMenuView.startAnimation(); } });
3.MyProgreeView (implementación del pequeño globo en la ventana inferior del teléfono).
1.Inicializar la pluma, escuchar gestos en el view. Escuchar eventos de clic y doble clic (debe establecer el view como clickeable).
private void initPaint() { //pluma de dibujo de círculo circlepaint = new Paint(); circlepaint.setColor(Color.argb(0xff, 0x3a, 0x8c, 0x6c)); circlepaint.setAntiAlias(true); //pluma de barra de progreso progerssPaint = new Paint(); progerssPaint.setAntiAlias(true); progerssPaint.setColor(Color.argb(0xff, 0x4e, 0xcc, 0x66)); progerssPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));//dibujar la parte superpuesta //pluma de progreso textPaint = new Paint(); textPaint.setAntiAlias(true); textPaint.setColor(Color.WHITE); textPaint.setTextSize(25); //lienzo bitmap = Bitmap.createBitmap(width, heigth, Bitmap.Config.ARGB_8888); bitmapCanvas = new Canvas(bitmap); //Escucha de gestos. gestureDetector = new GestureDetector(new MyGertureDetectorListener()); setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return gestureDetector.onTouchEvent(event); } }); //Establecer que la vista es clicable. setClickable(true); } class MyGertureDetectorListener extends GestureDetector.SimpleOnGestureListener{ @Override public boolean onDoubleTap(MotionEvent e) { ...... //Lógica del evento de doble toque return super.onDoubleTap(e); } @Override public boolean onSingleTapConfirmed(MotionEvent e) { ...... //Lógica del evento de toque simple return super.onSingleTapConfirmed(e); } }
2.Usar handler para interactuar y actualizar el estado de eventos de toque simple y doble. Al tocar una vez, usar la curva de Bézier para lograr el efecto de ondas. Al tocar dos veces, las ondas disminuyen continuamente, realizando la liberación de memoria, y finalmente mostrando el porcentaje de memoria utilizada después de la liberación de memoria. El handler envía mensajes periódicos para que los bolas de eventos de toque simple y doble se repinten continuamente. (La repintado se explicará en el siguiente capítulo).
//Single toque evento ciclo handler. private void startSingleTapAnimation() { handler.postDelayed(singleTapRunnable,200); } private SingleTapRunnable singleTapRunnable=new SingleTapRunnable(); class SingleTapRunnable implements Runnable{ @Override public void run() { count--; if (count>=0) { invalidate();//Continuar repintando. handler.postDelayed(singleTapRunnable,200); } else { handler.removeCallbacks(singleTapRunnable); count=50; } } } //Doble toque evento ciclo handler. private void startDoubleTapAnimation() { handler.postDelayed(runnbale,50); } private DoubleTapRunnable runnbale=new DoubleTapRunnable();} class DoubleTapRunnable implements Runnable{ @Override public void run() { num--; if (num>=0){ invalidate();//Continuar repintando. handler.postDelayed(runnbale,50); } else { handler.removeCallbacks(runnbale); //Liberar memoria. killprocess(); //Calcular el porcentaje de memoria utilizada después de liberar. num=(int)(((float)currentProgress/max)*100); } } }
3.Re pintar el evento de clic y el evento de doble clic.
Primero es el dibujo de la pelota, y el dibujo de la ruta de las ondas. //Dibujar la pelota bitmapCanvas.drawCircle(width / 2, heigth / 2, width / 2, circlepaint); //Basado en path, dibujar la ruta de las ondas. Antes de dibujar cada vez, restablecer el path anterior. path.reset(); float y =(1-(float)num/100)*heigth; path.moveTo(width, y); path.lineTo(width, heigth); path.lineTo(0, heigth); path.lineTo(0, y);
Luego, utilice las curvas de Bézier para dibujar la ruta de las ondas.
Android-Curvas de Bézier
Aplicación de las curvas de Bézier en android
Aquí hay una explicación detallada de las curvas de Bézier. En realidad, no es necesario entender profundamente. Basta con saber que se puede usar para implementar el efecto de las ondas del agua (las curvas de Bézier tienen muchos usos, el efecto de cambio de página también se puede implementar con ellas.) Principalmente se utiliza path.rQuadTo(x1, y1, x2, y2); Punto final (x2, y2), puntos de control auxiliares (x1, y1de Bézier. Por lo tanto, cambiar constantemente y1ubicación, podemos dibujar el efecto de las ondas del agua.
Primero, juzgar si es un evento de doble clic:
Si se hace doble clic: establecer una variable d, cambiar constantemente el valor de d (el cambio del valor de d se debe a num, mientras que num disminuye constantemente en el handler. num--;), para dibujar la curva de Bézier. Implementar el efecto de descenso de las ondas del agua.
Si se hace clic: establecer un valor de count, cambiar constantemente el valor de count (el cambio del valor de count se realiza en el handler. count--;), primero juzgar si count puede ser2múltiplo, alternar para dibujar estas dos curvas de Bézier. (Estas dos curvas de Bézier son exactamente opuestas), logrando el efecto de ondulación del agua.}}
(Utilizando un bucle for para implementar el número de ondas del agua, un par de path.rQuadTo(); solo puede implementar una onda. Puedes verificarlo por ti mismo)
if (!isSingleTap){ float d=(1-(float)num/(100/2))*10; for (int i=0;i<3;i++) { path.rQuadTo(10,-d,20,0); path.rQuadTo(10,d,20,0); } } else { float d=(float)count/50*10; if (count%2==0){ for (int i=0;i<=3;i++) { path.rQuadTo(10,-d,30,0); path.rQuadTo(10,d,30,0); } } else { for (int i=0;i<=3;i++) { path.rQuadTo(10,d,30,0); path.rQuadTo(10,-d,30,0); } } }
Finalmente, el método para liberar la memoria. Recuerda que debes agregar <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/> Privilegios.
public void killprocess(){ ActivityManager activityManger=(ActivityManager) getContext().getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningAppProcessInfo> list=activityManger.getRunningAppProcesses(); if(list!=null) for(int i=0;i<list.size();i++) { ActivityManager.RunningAppProcessInfo apinfo=list.get(i); String[] pkgList=apinfo.pkgList; if(apinfo.importance>ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE) { // Process.killProcess(apinfo.pid); for(int j=0;j<pkgList.length;j++) { boolean flag=pkgList[j].contains("com.example.yyh.animation")360");//Aquí se debe determinar si es la aplicación actual, de lo contrario también podría cerrar la aplicación actual. if(!flag){ activityManger.killBackgroundProcesses(pkgList[j]); } } } }
4.Implementación de FloatMenuView.
1.Crear un float_menuview.xml; que incluye un ImageView+TextView+MyProgreeView personalizado.
la ventana inferior debe poder ser clickeada. android:clickable="true";
<?xml version="1.0" encoding="utf"-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#"33000000" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:background="#F0"2F3942" android:layout_alignParentBottom="true" android:id="@"+id/ll" android:clickable="true" > <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <ImageView android:layout_width="50dp" android:layout_height="50dp" android:src="@mipmap/ic_launcher" android:layout_gravity="center_vertical" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="15sp" android:textColor="#c"93944" android:text="360aceleradorbola" android:layout_gravity="center_vertical" /> </LinearLayout> <com.example.yyh.animation360.view.MyProgreeView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="10dp" /> </LinearLayout> </RelativeLayout>
2.Añade FloatMenuView según las condiciones, utilizando (wm.addView(view, params); para agregar el view a la ventana.
Utiliza el método wm.remove(view,params); para quitar el view de la ventana.)Método, para mostrar y ocultar el view de la ventana inferior
TranslateAnimation se utiliza para configurar el efecto de animación de entrada de la ventana inferior. TranslateAnimation(int fromXType,float fromXValue,int toXType,float toXValue,int fromYType,float fromYValue,int toYType,float toYValue)
int fromXType: las referencias de valor de inicio en la dirección del eje X tienen3Opciones。(1.Animation.ABSOLUTE: valor de coordenada específico, que se refiere a unidades de píxeles de la pantalla absoluta。
2.Animation.RELATIVE_TO_SELF: valor de coordenada relativo a sí mismo。3.Animation.RELATIVE_TO_PARENT: valor de coordenada relativo al contenedor padre。)
float fromXValue: el segundo parámetro es el valor de inicio del tipo del primer parámetro (por ejemplo, si el primer parámetro se establece en Animation.RELATIVE_TO_SELF, el segundo parámetro es 0.1f, significa multiplicar el valor de coordenada propio por 0.1);
int toXType: las referencias de valor de final en la dirección del eje X tienen3Las opciones son las mismas que el primer parámetro.
float toValue: el cuarto parámetro es el valor de inicio del tipo del tercer parámetro.
Los parámetros en la dirección del eje Y son similares. Punto de partida+Punto final;(cada parámetro siguiente es el valor de inicio del parámetro anterior。)
Y se configura el OnTouchListener para este view, y el evento OnTouch debe retornar false al final, lo que indica que este evento aún necesita ser transmitido hacia abajo. De esta manera, al hacer clic en otras áreas del teléfono, la ventana inferior del teléfono se oculta y la pelota flotante se muestra, sin cambios al hacer clic en la ventana inferior, y al hacer clic en la pelota en la ventana inferior, se desencadena su evento de clic y doble clic.
View view = View.inflate(getContext(), R.layout.float_menuview, null); LinearLayout linearLayout = (LinearLayout) view.findViewById(R.id.ll); translateAnimation = new TranslateAnimation(Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF, 0, Animation.RELATIVE_TO_SELF,1.0f, Animation.RELATIVE_TO_SELF, 0); translateAnimation.setDuration(500); translateAnimation.setFillAfter(true); linearLayout.setAnimation(translateAnimation); view.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { FloatViewManager manager = FloatViewManager.getInstance(getContext()); manager.hideFloatMenuView(); manager.showFloatCircleView(); return false; } }); addView(view);
5.MyFloatService
用于创建FloatVIewManager单例,管理悬浮小球+创建和移除手机底部窗口。
public class MyFloatService extends Service { @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { //用于启动FloatViewManager FloatViewManager manager = FloatViewManager.getInstance(this); manager.showFloatCircleView(); super.onCreate(); } }
6.Implementación de MainActivity
Definir un intento, iniciar el servicio (en el servicio crear una instancia singleton de WindowManager para la gestión del balón flotante y la ventana inferior del teléfono), cerrar la actividad actual.
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void startService(View view){ Intent intent=new Intent(this, MyFloatService.class); startService(intent); finish(); } }
Esto es todo el contenido de este artículo, esperamos que haya sido útil para su aprendizaje y que todos apoyen y alienten el tutorial.
Declaración: El contenido de este artículo se ha obtenido de la red, es propiedad del autor original, el contenido se ha contribuido y subido por los usuarios de Internet, este sitio web no posee los derechos de propiedad, no se ha realizado una edición humana y no asume la responsabilidad legal relevante. Si encuentra contenido sospechoso de infracción de derechos de autor, le invitamos a enviar un correo electrónico a: notice#w proporcionando evidencia relevante, una vez verificada, este sitio eliminará inmediatamente el contenido sospechoso de infracción.3Declaración: El contenido de este artículo se ha obtenido de la red, es propiedad del autor original, el contenido se ha contribuido y subido por los usuarios de Internet, este sitio web no posee los derechos de propiedad, no se ha realizado una edición humana y no asume la responsabilidad legal relevante. Si encuentra contenido sospechoso de infracción de derechos de autor, le invitamos a enviar un correo electrónico a: notice#w proporcionando evidencia relevante, una vez verificada, este sitio eliminará inmediatamente el contenido sospechoso de infracción.