English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Il y a de plus en plus d'applications de fenêtres flottantes sur les téléphones mobiles, pour les utilisateurs, les applications de fenêtres flottantes les plus courantes sont les petits contrôles flottants des logiciels de sécurité, prenez36Pour 0 Guard, lorsque la fenêtre flottante est ouverte, c'est une balle qui peut être glissée; lorsque l'on clique sur la balle, un contrôle de fenêtre de grandeur apparaît, permettant des opérations supplémentaires telles que : libérer la mémoire du téléphone, etc. Alors, en s'inspirant de la vidéo de Mooc, en imitant l'implémentation360 Acceleration Ball, a ajouté la fonction de libération de la mémoire en cliquant sur la balle.
Comme c'est un téléphone, seules des captures d'écran d'écran : après l'implémentation, comme montré ci-dessous : cliquer sur le bouton d'activation pour afficher le pourcentage de mémoire disponible du téléphone sur le contrôle de la balle flottante; glisser la balle, la balle devient un icône Android; relâcher la balle, la balle est attachée aux côtés de l'écran; cliquer sur la balle, une grande fenêtre de contrôle apparaît en bas du téléphone, cliquer sur la balle à l'intérieur pour libérer la mémoire du téléphone; cliquer sur une autre région de l'écran du téléphone, la grande fenêtre disparaît et la balle réapparaît.
L'effet est le suivant :
Les prochaines étapes importantes à réaliser :
1.Implémentation de FloatCircleView (personnalisation de View)
Le processus d'implémentation de FloatCircleView est le processus de personnalisation de View.1、Personnaliser les attributs de View 2、Obtenir nos attributs personnalisés dans le constructeur de View 3、Redéfinir onMesure 4、Redéfinir onDraw. Nous n'avons pas personnalisé d'autres attributs, donc nous avons économisé beaucoup d'étapes.
Initialisation de diverses variables, configuration de l'icône à afficher lors du glisser-déposer de la balle, et calcul des différentes mémoires. (Utilisé pour afficher sur la balle)
public int width=100; public int heigth=100; private Paint circlePaint;//Dessiner un cercle private Paint textPaint; //Écrire du texte private float availMemory; //Mémoire utilisée private float totalMemory; //Mémoire totale private String text; //Pourcentage de mémoire utilisée affiché private boolean isDraging=false; //Est-ce que l'état de glisser-déposer est actif. private Bitmap src; private Bitmap scaledBitmap; //Image redimensionnée. /** * Initialiser le pinceau et calculer la mémoire disponible, la mémoire totale et le pourcentage de mémoire 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); //configurer l'image src = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); //image redimensionnée (rendre l'icône et la balle flottante de la même taille.) scaledBitmap = Bitmap.createScaledBitmap(src, width, heigth, true); //calculer la mémoire utilisée, la mémoire totale, le pourcentage de mémoire utilisée, availMemory= (float) getAvailMemory(getContext()); totalMemory= (float) getTotalMemory(getContext()); text=(int)((availMemory/totalMemory)*100)+"%"; }
onMeasure(); c'est écrire la largeur et la hauteur fixes, en utilisant setMeasuredDimension(width, heigth); pour transmettre.
onDraw(); pour dessiner la balle flottante. Définissez une variable booleane pour déterminer si l'état actuel est le mode glisser de la balle. Si c'est le cas, dessinez l'icône android à cet endroit, sinon dessinez la balle. Dessiner la balle n'est pas difficile, la clé est de dessiner le texte. Ensuite,2une image peut renforcer la compréhension du dessin du texte.
1.la coordonnée x lors du dessin du texte (1.textPaint.measureText(text); obtenir la largeur du texte2.la largeur de la balle/2-la largeur du texte/2).
2.la coordonnée y lors du dessin du texte (1.Paint.FontMetrics fontMetrics = textPaint.getFontMetrics(); obtenir les propriétés de mesure de la police.2.(fontMetrics.ascent + fontMetrics.descent) / 2 obtenir la hauteur du texte.3.la hauteur de la balle/2-la hauteur de la police/2)
dessiner une image est très clair :
/** * dessiner une balle et du texte. Si la balle est en mode glisser, affichez l'icône android, sinon affichez la balle. * @param canvas */ @Override protected void onDraw(Canvas canvas) { if (isDraging){ canvas.drawBitmap(scaledBitmap, 0, 0, null); } //1.dessiner un cercle canvas.drawCircle(width) / 2, heigth / 2, width / 2, circlePaint); //2.dessiner text float textwidth = textPaint.measureText(text);//largeur du texte 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); } }
méthode pour obtenir la mémoire utilisée et totale du téléphone :
public long getAvailMemory(Context context) { // obtenir la taille de la mémoire disponible actuelle d'android ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo(); am.getMemoryInfo(mi); //mi.availMem; mémoire disponible du système actuel //return Formatter.formatFileSize(context, mi.availMem);// normaliser la taille de la mémoire obtenue return mi.availMem/(1024*1024); } public long getTotalMemory(Context context) { String str1 = "/proc/meminfo";// fichier d'information de la mémoire système String str2; String[] arrayOfString; long initial_memory = 0; try { FileReader localFileReader = new FileReader(str1); BufferedReader localBufferedReader = new BufferedReader( localFileReader, 8192); str2 = localBufferedReader.readLine();// 读取meminfo第一行,系统总内存大小 arrayOfString = str2.split("\\s+); for (String num : arrayOfString) { Log.i(str2, num + "\t"); } initial_memory = Integer.valueOf(arrayOfString[1}).intValue() * 1024;// 获得系统总内存,单位是KB,乘以1024转换为Byte localBufferedReader.close(); } catch (IOException e) { } //return Formatter.formatFileSize(context, initial_memory);// Byte转换为KB或MB,内存大小规范化 return initial_memory/(1024*1024); }
2.创建WindowManager窗口管理类,管理悬浮小球和底部大窗口。
WindowManager类。用于管理整个悬浮小球和手机底部大窗口的显示和隐藏。
必须在Manifest文件中增加<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> Droits.
通过WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE)获取窗口管理类;
使用wm.addView(view, params)将视图添加到窗口中。
使用wm.remove(view, params)将视图从窗口中移除。
使用wm.updateViewLayout(view, params)来更新视图。
WindowManager.LayoutParams用于设置视图的各种属性。
1.创建FloatViewManager实例。
//模式单一实例创建 public static FloatViewManager getInstance(Context context) { if (inStance == null) { synchronized (FloatViewManager.class) { if (inStance == null) { inStance = new FloatViewManager(context); } } } return inStance; }
2.Méthodes d'affichage de la balle flottante et de l'affichage de la fenêtre inférieure. (La méthode d'affichage de la fenêtre est similaire à celle de l'affichage de la balle flottante.)
/** * Affichage de la fenêtre flottante */ public void showFloatCircleView() { //Paramètres de configuration if (params == null) { params = new WindowManager.LayoutParams(); //Largeur et hauteur params.width = circleView.width; params.height = circleView.heigth; //Alignement params.gravity = Gravity.TOP | Gravity.LEFT; //Décalage params.x=0; params.y = 0; //Type params.type = WindowManager.LayoutParams.TYPE_TOAST; //Définissez les propriétés de cette fenêtre. params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; //Format des pixels params.format = PixelFormat.RGBA_8888; } //Ajoutez la balle à la fenêtre. wm.addView(circleView, params); } public void showFloatCircleView() { ...... }
3.Lorsque le programme est lancé, créez d'abord la balle flottante, la balle peut être glissée, cliquez sur la balle, la fenêtre inférieure de l'appareil mobile s'affiche (FloatMenuView), la balle est cachée. Par conséquent, pour la balle (circleView), il faut lui ajouter des écouteurs d'événements setOnTouchListener et setOnClickListener.
Analysez la distribution des événements de la balle; Pour la balle:
Lorsque ACTION_DOWN est déclenché, enregistrez les downX, downY de la balle, ainsi que startX, startY
Lorsque ACTION_MOVE est déclenché, mettez l'état de drag de circleView à true, enregistrez les moveX et moveY de la balle, calculez la distance de déplacement de la balle (dx, dy), puis mettez à jour la position de la balle en utilisant wm.updateViewLayout(circleView, params); Enfin, assignez les dernières coordonnées de move à startX, startY.
Lorsque ACTION_UP est déclenché, mettez le drag de circleView à false, enregistrez les coordonnées de levée, upx, en fonction de upx et de la largeur de l'écran du téléphone/2,faire la jugement, pour déterminer finalement si la balle est collée à gauche ou à droite de l'écran. Ensuite, il s'agit de l'erreur de glissement de la balle. Lorsque la distance de glissement de la balle est inférieure à10un pixel, déclenche l'événement de clic de la balle. (Les événements de Touch sont prioritaires par rapport aux événements de clic. Lorsque l'événement de Touch retourne true, cet événement est consommé et n'est plus transmis vers le bas. Lorsque l'événement de Touch retourne false, cet événement continue de se propager vers le bas, déclenchant ainsi l'événement de clic de la balle.)
Événement de clic de la balle: cliquer sur la balle, cacher la balle flottante, faire apparaître la fenêtre inférieure du téléphone, et définir l'animation de transition lorsqu'une fenêtre inférieure apparaît.
//Ajouter un écouteur de toucher à circleView. private View.OnTouchListener circleViewOnTouchListener=new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: //Dernières coordonnées de la pression, comprises dans ACTION_MOVE. startX = event.getRawX(); startY = event.getRawY(); //coordonnées de la pression. 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; } params.x=0; } circleView.setDrageState(false); wm.updateViewLayout(circleView,params); if (Math.abs(moveX-downX)>10){ return true; } 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(); //cacher circleView, afficher la barre de menu. wm.removeView(circleView); showFloatMenuView(); floatMenuView.startAnimation(); } });
3.MyProgreeView (implémentation de la balle dans le panneau inférieur du téléphone).
1.Initialiser les pinceaux, écouter les gestes sur le view. Écouter les événements de clic simple et double (doit être configuré le view comme cliquable).
private void initPaint() { //pinceau de dessin de cercle circlepaint = new Paint(); circlepaint.setColor(Color.argb(0xff, 0x3a, 0x8c, 0x6c)); circlepaint.setAntiAlias(true); //pinceau de barre de progression progerssPaint = new Paint(); progerssPaint.setAntiAlias(true); progerssPaint.setColor(Color.argb(0xff, 0x4e, 0xcc, 0x66)); progerssPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));//dessiner la partie superposée //pinceau de progression textPaint = new Paint(); textPaint.setAntiAlias(true); textPaint.setColor(Color.WHITE); textPaint.setTextSize(25); //canva bitmap = Bitmap.createBitmap(width, heigth, Bitmap.Config.ARGB_8888); bitmapCanvas = new Canvas(bitmap); //Ecouteur de gestes. gestureDetector = new GestureDetector(new MyGertureDetectorListener()); setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return gestureDetector.onTouchEvent(event); } }); //Régler la vue pour être cliquable. setClickable(true); } class MyGertureDetectorListener extends GestureDetector.SimpleOnGestureListener{ @Override public boolean onDoubleTap(MotionEvent e) { ...... //Logique de l'événement de double clic return super.onDoubleTap(e); } @Override public boolean onSingleTapConfirmed(MotionEvent e) { ...... //Logique de l'événement de clic return super.onSingleTapConfirmed(e); } }
2.Utilisez handler pour interagir avec l'état de l'événement de clic et de double clic. Lors du clic, utilisez la courbe de Bézier pour réaliser l'effet ondulatoire. Lors du double clic, les ondes descendent constamment, libèrent la mémoire, puis affichent le pourcentage d'utilisation de la mémoire après libération. Le handler envoie des messages cycliques pour que les billes des événements de clic et de double clic se redessinent constamment. (La redessin est expliquée dans la section suivante).
//Clique événement envoie cycle 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();//Répéter constamment le redessin. handler.postDelayed(singleTapRunnable,200); } handler.removeCallbacks(singleTapRunnable); count=50; } } } //Clique double événement envoie cycle 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();//Répéter constamment le redessin. handler.postDelayed(runnbale,50); } handler.removeCallbacks(runnbale); //Libérer la mémoire. killprocess(); //Calculer le pourcentage d'utilisation de la mémoire après libération. num=(int)(((float)currentProgress/max)*100); } } }
3. Redessiner les événements de clic simple et double-clic.
Tout d'abord, dessiner la balle, et le chemin des ondes. //Dessiner la balle bitmapCanvas.drawCircle(width / 2, heigth / 2, width / 2, circlepaint); //Selon path, dessiner le chemin des ondes. Avant chaque dessin, réinitialiser le path précédent, path.reset. 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);
Ensuite, utilisez les courbes de Bézier pour dessiner le chemin des ondes.
Android-Courbes de Bézier
Application des courbes de Bézier dans Android
Il y a une explication détaillée des courbes de Bézier. En réalité, il n'est pas nécessaire de comprendre en profondeur. Il suffit de savoir qu'elles peuvent être utilisées pour réaliser l'effet des ondes de l'eau (les courbes de Bézier ont de nombreuses applications, l'effet de tourne-page peut également être réalisé avec elles.) Il utilise principalement path.rQuadTo(x1, y1, x2, y2); Point final (x2, y2), les points de contrôle d'assistance (x1, y1) une courbe de Bézier. Par conséquent, en changeant constamment y1de position, on peut dessiner l'effet des ondes de l'eau.
D'abord vérifier s'il s'agit d'un double-clic :
Si l'utilisateur double-clique : définir une variable d, en changeant constamment cette valeur (le changement de la valeur de d est causé par num, tandis que num diminue constamment dans le handler. num--;), pour dessiner une courbe de Bézier. Réaliser l'effet de descente des ondes de l'eau.
Si l'utilisateur clique : définir une valeur de count, en changeant constamment cette valeur (le changement de count est réalisé dans le handler. count--;), d'abord vérifier si count peut être divisé par2divisible, dessiner alternativement ces deux courbes de Bézier. (Ces deux courbes de Bézier sont exactement inversées), pour réaliser l'effet ondulatoire des vagues.}}
(Utiliser une boucle for pour implémenter le nombre d'ondes de l'eau, un couple de path.rQuadTo(); ne peut implémenter qu'une onde. Vous pouvez vous-même le vérifier)
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); } } 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); } } for (int i=0;i<=3;i++){ path.rQuadTo(10,d,30,0); path.rQuadTo(10,-d,30,0); } } }
Enfin, la méthode de libération de la mémoire. N'oubliez pas d'ajouter <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/> Droits.
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");//Ici, il faut vérifier s'il s'agit de l'application actuelle, sinon elle pourrait également mettre fin à l'application actuelle. if(!flag){ activityManger.killBackgroundProcesses(pkgList[j]); } } } }
4.Implémentation de FloatMenuView.
1.Créer un float_menuview.xml ; y compris un ImageView+TextView+MyProgreeView personnalisé.
Le corps de la fenêtre en bas doit être configuré pour être cliquable. 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="360加速球" 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.Ajouter FloatMenuView en fonction des conditions, en utilisant(wm.addView(view, params);Pour ajouter le view à la fenêtre.
Utilisez la méthode wm.remove(view,params); pour supprimer le view de la fenêtre.)Pour afficher et masquer la vue de la fenêtre inférieure
La classe TranslateAnimation est utilisée pour définir l'effet d'animation d'entrée de la fenêtre inférieure. TranslateAnimation(int fromXType,float fromXValue,int toXType,float toXValue,int fromYType,float fromYValue,int toYType,float toYValue)
int fromXType: Les valeurs de référence pour le point de départ en direction de l'axe des abscisses sont3Les options sont les suivantes.(1.Animation.ABSOLUTE: Les valeurs de coordonnées spécifiques, indiquant des unités de pixels d'écran absolus.
2.Animation.RELATIVE_TO_SELF: Les valeurs de coordonnées par rapport à soi-même.3.Animation.RELATIVE_TO_PARENT: Les valeurs de coordonnées par rapport au conteneur parent.)
float fromXValue: Le deuxième paramètre est la valeur de départ du type du premier paramètre (par exemple, si le premier paramètre est défini sur Animation.RELATIVE_TO_SELF, le deuxième paramètre est 0.1f, ce qui signifie multiplier la valeur de coordonnées par 0.1);
int toXType: Les valeurs de référence pour le point final en direction de l'axe des abscisses sont3Les options sont les mêmes que pour le premier paramètre.
float toValue: Le quatrième paramètre est la valeur de départ du type du troisième paramètre.
Les paramètres dans la direction Y sont similaires. Point de départ+Point final;(Chaque paramètre suivant est la valeur de départ du paramètre précédent.)
Etape 1 : Définir OnTouchListener pour ce view, l'événement OnTouch doit finalement renvoyer false, ce qui signifie que cet événement doit être transmis en continu. De cette manière, lorsque vous cliquez sur une autre zone de l'appareil mobile, la fenêtre inférieure de l'appareil disparaît et la balle flottante s'affiche, sans changement lorsqu'on clique sur la fenêtre inférieure, et lorsqu'on clique sur la balle dans la fenêtre inférieure, il déclenche les événements de clic simple et double.
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.Implémentation de MainActivity
Définir un intent pour ouvrir le service (créer une instance Singleton de WindowManager dans le service pour gérer la balle flottante et le cadre inférieur du téléphone), fermer l'activity actuelle.
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(); } }
C'est tout pour cet article, j'espère qu'il vous aidera dans vos études, et j'espère que vous soutiendrez également le tutoriel criant.
Déclaration : le contenu de cet article est issu du réseau, propriété de ses auteurs respectifs, contribué et téléversé par les utilisateurs d'Internet de manière spontanée. Ce site n'en possède pas la propriété, n'a pas fait l'objet d'une rédaction humaine et n'assume aucune responsabilité juridique. Si vous trouvez du contenu présumé violer les droits d'auteur, n'hésitez pas à envoyer un e-mail à : notice#oldtoolbag.com (veuillez remplacer # par @ lors de l'envoi d'un e-mail pour signaler une violation, et fournir des preuves pertinentes. Une fois confirmée, ce site supprimera immédiatement le contenu présumé enfreindre les droits d'auteur.