English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Ora ci sono sempre più applicazioni di finestre fluttuanti sui telefoni, per gli utenti, l'applicazione di finestra fluttuante più comune è il piccolo controllo fluttuante del software di sicurezza, prendiamo 360 Guardiano come esempio, quando si attiva la finestra fluttuante, è una sfera, la sfera può essere trascinata, quando si clicca sulla sfera viene visualizzato il controllo della finestra grande, è possibile eseguire ulteriori operazioni come liberare la memoria del telefono ecc. Così, con il video diimooc.com, ho realizzato una sfera di accelerazione 360, aggiungendo la funzione di liberare la memoria cliccando sulla sfera.
Poiché è un telefono, ci sono solo schermate di screenshot: come mostrato nell'immagine dopo l'implementazione, clicca sul pulsante di attivazione per visualizzare il控件 della sfera fluttuante in alto che mostra la percentuale di memoria disponibile del telefono; quando trascini la sfera, diventa un'icona Android; quando rilasci la sfera, si attacca ai lati dello schermo; clicca sulla sfera per visualizzare il controllo della finestra grande nella parte inferiore del telefono, clicca sulla sfera all'interno per liberare la memoria del telefono; clicca su altre aree dello schermo del telefono, la finestra grande scompare e la sfera riappare.
L'effetto è il seguente:
Quindi, i passaggi importanti da implementare sono i seguenti:
1. Implementazione di FloatCircleView (personalizzazione del view)
Il processo di implementazione di FloatCircleView è proprio il processo di personalizzazione del view. 1、Personalizzazione delle proprietà del View 2、Ottieni le proprietà personalizzate nel costruttore del View 3、Ridefinisci onMesure 4、Ridefinisci onDraw. Non abbiamo personalizzato altre proprietà, quindi abbiamo risparmiato molti passaggi.
varie variabili inizializzate, impostazione dell'icona da visualizzare quando si trascina la sfera, e calcolo di tutte le memorie. (Utilizzato per visualizzare sulla sfera.)
public int width = 100; public int heigth = 100; private Paint circlePaint; //画圆 private Paint textPaint; //画字 private float availMemory; //已用内存 private float totalMemory; //总内存 private String text; //显示的已用内存百分比 private boolean isDraging = false; //是否在拖动状态。 private Bitmap src; private Bitmap scaledBitmap; //缩放后的图片。 /** * 初始化画笔以及计算可用内存,总内存,和可用内存百分比。 */ 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); //设置图片 src = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); //缩放后的图片(将图标设置的和悬浮小球一样大小。) scaledBitmap = Bitmap.createScaledBitmap(src, width, heigth, true); //计算已用内存,总内存,已用内存百分比, availMemory = (float) getAvailMemory(getContext()); totalMemory = (float) getTotalMemory(getContext()); text = (int) ((availMemory/totalMemory) * 100) + "%"; }
onMeasure(); è scrivere le dimensioni fisse in modo statico, tramite setMeasuredDimension(width, heigth); passato.
onDraw(); disegna la pallina fluttuante. Definisci una variabile booleana per determinare lo stato corrente se è lo stato di trascinamento della pallina; se è lo stato di trascinamento della pallina, disegna l'icona android in questa posizione; se non è in stato di trascinamento, disegna la pallina. Disegnare la pallina non è difficile, la chiave è disegnare il testo. Le due immagini seguenti possono rafforzare la comprensione del disegno del testo.
1. Coordinate x del testo da disegnare (1. textPaint.measureText(text); ottieni la larghezza del carattere 2. Larghezza della pallina / 2 - larghezza del carattere / 2.)
2. Coordinate y del testo da disegnare (1. Paint.FontMetrics fontMetrics = textPaint.getFontMetrics(); ottieni la classe di misurazione delle proprietà del font. 2. (fontMetrics.ascent + fontMetrics.descent) / 2 ottieni l'altezza del carattere. 3. Altezza della pallina / 2 - altezza del carattere / 2)
Un'immagine disegnata è molto chiara:
/** * Disegna la pallina e il testo. Se la pallina è in stato di trascinamento, mostra l'icona android; se non è in stato di trascinamento, mostra la pallina. * @param canvas */ @Override protected void onDraw(Canvas canvas) { if (isDraging){ canvas.drawBitmap(scaledBitmap, 0, 0, null); } //1. Disegna il cerchio canvas.drawCircle(width / 2, heigth / 2, width / 2, circlePaint); //2. Disegna il testo float textwidth = textPaint.measureText(text);//larghezza del testo 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); } }
Metodo per ottenere la memoria utilizzata e la memoria totale del telefono:
public long getAvailMemory(Context context) { // Ottenere la dimensione della memoria disponibile corrente di android ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo(); am.getMemoryInfo(mi); //mi.availMem; Memoria disponibile del sistema corrente //return Formatter.formatFileSize(context, mi.availMem);// Specificare la dimensione della memoria ottenuta return mi.availMem/(1024*1024); } public long getTotalMemory(Context context) { String str1 = "/proc/meminfo";// File delle informazioni sulla memoria del sistema String str2; String[] arrayOfString; long initial_memory = 0; try { FileReader localFileReader = new FileReader(str1); BufferedReader localBufferedReader = new BufferedReader( localFileReader, 8192); str2 = localBufferedReader.readLine();// Leggere la prima riga di meminfo, dimensione della memoria totale del sistema arrayOfString = str2.split("\\s+"); for (String num : arrayOfString) { Log.i(str2, num + "\t"); } initial_memory = Integer.valueOf(arrayOfString[1]).intValue() * 1024;// Ottenere la memoria totale del sistema, in KB, moltiplicata per 1024 per convertire in Byte localBufferedReader.close(); catch (IOException e) { } //return Formatter.formatFileSize(context, initial_memory); // Conversione di Byte in KB o MB, specificazione della dimensione della memoria} return initial_memory / (1024 * 1024); }
2. Crea la classe di gestione delle finestre WindowManager per gestire il pallino sospeso e la grande finestra inferiore.
La classe WindowManager. Utilizzata per gestire la visualizzazione e l'oscuramento del pallino sospeso e della grande finestra inferiore del telefono.
Devi aggiungere la permissione <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> nel file Manifest.
Ottieni la classe di gestione delle finestre tramite WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Utilizza wm.addView(view, params) per aggiungere il view alla finestra.
Utilizza wm.remove(view, params) per rimuovere il view dalla finestra.
Utilizza wm.updateViewLayout(view, params) per aggiornare il view.
WindowManager.LayoutParams viene utilizzato per impostare vari attributi del view.
1. Crea un'istanza di FloatViewManager.
//Creazione del modello singleton public static FloatViewManager getInstance(Context context) { if (inStance == null) { synchronized(FloatViewManager.class) { if (inStance == null) { inStance = new FloatViewManager(context); } } } return inStance; }
2. Mostra il pallino sospeso e il metodo di visualizzazione della finestra inferiore. (Il metodo di visualizzazione della finestra è simile a quello del pallino sospeso.)
/** *Mostra la finestra fluttuante */ public void showFloatCircleView() { //Impostazioni dei parametri if (params == null) { params = new WindowManager.LayoutParams(); //Larghezza e altezza params.width = circleView.width; params.height = circleView.heigth; //Alignamento params.gravity= Gravity.TOP|Gravity.LEFT; //Offset params.x=0; params.y=0; //Tipo params.type=WindowManager.LayoutParams.TYPE_TOAST; //Impostare le proprietà della finestra. params.flags= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; //Formato pixel params.format= PixelFormat.RGBA_8888; } //Aggiungere il pallino alla finestra. wm.addView(circleView, params); } public void showFloatCircleView() { ...... }
3. Quando si avvia il programma, viene prima creato il pallino sospeso, il pallino può essere trascinato, cliccare il pallino, apparire la finestra inferiore del telefono (FloatMenuView), nascondere il pallino. Quindi, per il pallino (circleView), è necessario impostare setOnTouchListener e setOnClickListener per l'ascolto degli eventi.
Analizzare la distribuzione degli eventi del pallino; Per il pallino:
Quando ACTION_DOWN, registrare downX, downY del pallino, nonché startX, startY
Quando ACTION_MOVE, impostare lo stato di trascinamento di circleView a true, registrare moveX, moveY del pallino, calcolare la distanza di movimento del pallino (dx, dy), quindi aggiornare la posizione del pallino in base a wm.updateViewLayout(circleView,params); Infine, assegnare le coordinate dell'ultimo movimento move a startX, startY.
Quando ACTION_UP, impostare circleView se trascinato a false, registrare le coordinate di sollevamento, upx, in base a upx e la larghezza dello schermo del telefono / 2, per determinare se l'ultimo pallino è aderito al lato sinistro o destro dello schermo. Successivamente è l'errore di trascinamento del pallino. Quando la distanza di trascinamento del pallino è inferiore a 10 pixel, può essere attivato l'evento di clic del pallino. (L'evento di Touch del pallino ha la precedenza rispetto all'evento di clic del pallino, quando l'evento di Touch restituisce true, questo evento viene consumato e non viene più trasmesso verso il basso. Quando l'evento di Touch restituisce false, l'evento continua a essere trasmesso verso il basso, attivando così l'evento di clic del pallino.)
L'evento di clic del pallino: cliccare il pallino, nascondere il pallino sospeso, apparire la finestra inferiore del telefono. E impostare l'animazione di transizione quando appare la finestra inferiore.
//Aggiungi un listener di touch a circleView. private View.OnTouchListener circleViewOnTouchListener=new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: //最后按下时的坐标,根据ACTION_MOVE理解。 startX = event.getRawX(); startY = event.getRawY(); //按下时的坐标。 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(); //隐藏circleView,显示菜单栏。 wm.removeView(circleView); showFloatMenuView(); floatMenuView.startAnimation(); } });
3.MyProgreeView(手机底部窗体中小球的实现)。
1.初始化画笔,对view进行手势监听。监听单击和双击事件。(必须设置view是可以点击的)
private void initPaint() { //绘制圆画笔 circlepaint = new Paint(); circlepaint.setColor(Color.argb(0xff, 0x3a, 0x8c, 0x6c)); circlepaint.setAntiAlias(true); //绘制进度条画笔 progerssPaint = new Paint(); progerssPaint.setAntiAlias(true); progerssPaint.setColor(Color.argb(0xff, 0x4e, 0xcc, 0x66)); progerssPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));//绘制重叠部分 //绘制进度画笔 textPaint = new Paint(); textPaint.setAntiAlias(true); textPaint.setColor(Color.WHITE); textPaint.setTextSize(25); //画布 bitmap = Bitmap.createBitmap(width, heigth, Bitmap.Config.ARGB_8888); bitmapCanvas = new Canvas(bitmap); //监听手势。 gestureDetector = new GestureDetector(new MyGertureDetectorListener()); setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { return gestureDetector.onTouchEvent(event); } }); //Impostare la view cliccabile. setClickable(true); } class MyGertureDetectorListener extends GestureDetector.SimpleOnGestureListener{ @Override public boolean onDoubleTap(MotionEvent e) { ...... //Logica dell'evento di tap doppio return super.onDoubleTap(e); } @Override public boolean onSingleTapConfirmed(MotionEvent e) { ...... //Logica dell'evento di tap singolo return super.onSingleTapConfirmed(e); } }
2. Utilizzando handler per interagire con gli eventi di singolo e doppio tap. Durante il tap singolo, utilizzando la curva di Bézier, si realizza l'effetto di onde ondulate. Durante il tap doppio, le onde continuano a scendere, si effettua il rilascio della memoria e infine viene visualizzato il percentuale di memoria utilizzata dopo il rilascio della memoria. Il handler invia messaggi periodici, permettendo che le palline degli eventi di tap singolo e doppio vengano ridisegnate continuamente. (Il ridisegno viene trattato nel prossimo capitolo).
//单击事件发送周期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();//Continua a ridisegnare. handler.postDelayed(singleTapRunnable,200); } handler.removeCallbacks(singleTapRunnable); count=50; } } } //双击事件发送周期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();//Continua a ridisegnare. handler.postDelayed(runnbale,50); } handler.removeCallbacks(runnbale); //Libera la memoria. killprocess(); //Calcola la percentuale di memoria utilizzata dopo il rilascio. num=(int)(((float)currentProgress/max)*100); } } }
3. Ri-disegno degli eventi di clic singolo e doppio.
Prima di tutto, il disegno della pallina e il disegno del percorso delle onde. //Disegna la pallina bitmapCanvas.drawCircle(width / 2, heigth / 2, width / 2, circlepaint); //Secondo path, disegna il percorso delle onde. Prima di disegnare ogni volta, reset il path dell'ultima volta. 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);
Poi utilizza le curve Bezier per disegnare il percorso delle onde.
Android-Curve Bezier
Applicazione delle curve Bezier in Android
C'è una spiegazione dettagliata delle curve Bezier. In realtà non è necessario comprendere a fondo. Basta sapere che può essere utilizzato per realizzare l'effetto ondulazione dell'acqua (Le curve Bezier sono molto utili, anche l'effetto di sfogliatura può essere realizzato con loro.) Principalmente utilizza path.rQuadTo(x1,y1,x2,y2); il punto di arrivo (x2, y2), il punto di controllo ausiliario (x1, y1) della curva Bezier. Pertanto, cambiando continuamente la posizione di y1, possiamo disegnare l'effetto ondulazione dell'acqua.
Prima di tutto,判断它是否为双击击事件:
Se si fa un doppio clic: impostare una variabile d, cambiare il valore di d continuamente (il cambiamento del valore di d è causato da num, mentre num è in diminuzione nel handler. num--;), per disegnare curve Bezier. Realizzare l'effetto calante delle onde.
Se si fa un clic: impostare un valore count, cambiare il valore di count continuamente (il cambiamento del valore di count è realizzato nel handler. count--;), prima di判断count是否能被2整除, alternare la disegno di queste due curve Bezier. (Queste due curve Bezier sono esattamente opposte), quindi realizzare l'effetto ondulazione dell'acqua.
(L'uso del ciclo for è per realizzare il numero delle onde, un paio di path.rQuadTo(); può realizzare una ondulazione una volta. Puoi verificare da solo)
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); } } }
Infine, c'è il metodo di liberazione della memoria. Ricorda di aggiungere la permissione <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/> nel file Manifest.
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.animation360");//qui deve essere verificato se si tratta dell'applicazione corrente, altrimenti potrebbe anche terminare l'applicazione corrente. if(!flag){ activityManger.killBackgroundProcesses(pkgList[j]); } } } }
4.Implementazione di FloatMenuView.
1.Crea un file float_menuview.xml; che include un ImageView+TextView+un MyProgreeView personalizzato.
La finestra inferiore deve essere impostata per essere cliccabile. 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="#F02F3942" 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="#c93944" android:text="360accelerazionePalla" 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 seconda delle condizioni, aggiungere il FloatMenuView alla finestra utilizzando (wm.addView(view, params); per aggiungere il view alla finestra.
Utilizzando il metodo wm.remove(view,params); per rimuovere il view dalla finestra, si può eseguire la visualizzazione e l'oscuramento della view della finestra inferiore.
La classe TranslateAnimation viene utilizzata per impostare l'effetto animazione di entrata della finestra inferiore. TranslateAnimation(int fromXType,float fromXValue,int toXType,float toXValue,int fromYType,float fromYValue,int toYType,float toYValue)
int fromXType: ci sono tre opzioni per il valore di riferimento del punto di partenza dell'asse X. (1.Animation.ABSOLUTE: valore di coordinate specifico, che indica l'unità di pixel schermo assoluto.
2.Animation.RELATIVE_TO_SELF: valore di coordinate relativo a sé stessi. 3.Animation.RELATIVE_TO_PARENT: valore di coordinate relativo al contenitore padre.)
float fromXValue: il secondo parametro è l'unità di partenza del primo parametro (ad esempio, se il primo parametro è impostato su Animation.RELATIVE_TO_SELF, il secondo parametro è 0.1f, significa che il proprio valore di coordinate moltiplicato per 0.1);
int toXType: ci sono tre opzioni per il valore di riferimento del punto di arrivo dell'asse X, come il primo parametro.
float toValue: il quarto parametro è l'unità di partenza del terzo parametro.
Il parametro in direzione Y è analogo. Punto di partenza + punto di arrivo; (ogni parametro è l'unità di partenza del parametro successivo.)
Ecco come impostare OnTouchListener per questo view, l'evento OnTouch deve sempre restituire false, il che significa che l'evento deve essere trasmesso in basso. Così, quando si clicca su altre aree dello schermo del telefono, la finestra inferiore del telefono si nasconde e la sfera sospesa si visualizza, non ci sono cambiamenti quando si clicca sulla finestra inferiore, e quando si clicca sulla sfera nella finestra inferiore, si scatena l'evento di singolo clic e doppio 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
usato per creare un'istanza unica di FloatVIewManager, gestire la creazione e rimozione della sfera sospesa e della finestra inferiore del telefono.
public class MyFloatService extends Service { @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { //usato per abilitare FloatViewManager FloatViewManager manager=FloatViewManager.getInstance(this); manager.showFloatCircleView(); super.onCreate(); } }
Implementazione di MainActivity
Definire un intento, avviare il servizio (creare un'istanza singleton di WindowManager nel servizio, gestire la sfera sospesa e la finestra inferiore del telefono), chiudere l'activity corrente.
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(); } }
Questo è tutto il contenuto dell'articolo, speriamo che sia utile per la tua apprendimento e che tu sostenga fortemente la guida urla.
Dichiarazione: il contenuto di questo articolo è stato tratto da Internet, è di proprietà del rispettivo proprietario, il contenuto è stato contribuito e caricato autonomamente dagli utenti di Internet, questo sito non detiene i diritti di proprietà, non è stato elaborato manualmente e non assume alcuna responsabilità legale pertinente. Se trovi contenuti sospetti di violazione del copyright, ti preghiamo di inviare una e-mail a: notice#oldtoolbag.com (al momento dell'invio dell'e-mail, sostituisci # con @) per segnalare il problema e fornire prove pertinenti. Una volta verificata, questo sito rimuoverà immediatamente il contenuto sospetto di violazione del copyright.