¿Cómo hacer una aplicación de ventana flotante en Android?

Bueno, en nuestras computadoras de escritorio podemos restaurar fácilmente las ventanas, hacer algo en segundo plano y maximizar la ventana cuando queramos. Pero no vemos mucho esta función en las aplicaciones de Android. Hoy en día podemos ver que Android ofrece pantalla dividida, pero esa es una función proporcionada por el sistema operativo, no la función individual de la aplicación. Hagamos una aplicación que pueda minimizarse y maximizarse con solo hacer clic en un botón. Esta función puede ayudar a los usuarios de muchas maneras. Suponga que está leyendo un documento pdf con algunos cálculos matemáticos y luego una calculadora minimizada sobre la aplicación de visor de pdf será muy útil. Hay muchas aplicaciones que usan esta función, como el Portapapeles, la Calculadora MI y muchas más. Aquí hay una demostración de la aplicación final de este artículo.

Make a Floating Window Application in Android Sample GIF

Implementación paso a paso

Paso 1: crear un nuevo proyecto

Para crear un nuevo proyecto en Android Studio, consulte Cómo crear/iniciar un nuevo proyecto en Android Studio . Tenga en cuenta que seleccione Java como lenguaje de programación.

Paso 2: Hagamos la plataforma de trabajo

  • Agregue nuevos colores para la aplicación : vaya a valores -> colores.xml . Cualquier color personalizado se puede agregar aquí. Hemos añadido estos dos colores.

XML

<color name="gfgTheme">#FF2F8D46</color>
<color name="gfgThemeTwo">#FF098043</color>
  • Elimina la barra de acciones : en Android Studio 4.1, ve a valores -> temas . Hay dos archivos XML de temas, uno para el modo claro y otro para el modo oscuro . En ambos XML, en el bloque de estilo, cambie el atributo principal a Theme.MaterialComponents.DayNight.NoActionBar .
  • Cambiar el color principal del tema para la aplicación : en el mismo archivo, el primer bloque de elementos debe ser sobre el color principal de la aplicación. Aquí se agregan los colores recién agregados. En el bloque de elementos, agregue @color/gfgTheme o @color/gfgThemeTwo .

Paso 3: primero hagamos todos los diseños

3.a: Empezar a trabajar en el archivo activity_main.xml

Este archivo XML hace el diseño de la actividad principal de la aplicación. El diseño no es tan complicado. Solo hay un Button , TextView , EditText y otro Button uno tras otro dentro de un ConstraintLayout. Aquí está el código XML.

XML

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white"
    tools:context=".MainActivity">
  
    <Button
        android:id="@+id/buttonMinimize"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="MINIMIZE"
        android:textColor="@android:color/white"
        android:textSize="25sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
  
    <TextView
        android:id="@+id/titleText"
        android:layout_width="match_parent"
        android:layout_height="70dp"
        android:gravity="center"
        android:text="GEEKS FOR GEEKS"
        android:textColor="@color/gfgTheme"
        android:textSize="30sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/buttonMinimize" />
  
    <EditText
        android:id="@+id/descEditText"
        android:layout_width="match_parent"
        android:layout_height="330dp"
        android:layout_marginTop="10dp"
        android:gravity="start"
        android:hint="Description"
        android:paddingLeft="20dp"
        android:paddingTop="10dp"
        android:paddingRight="20dp"
        android:paddingBottom="10dp"
        android:textColor="@android:color/black"
        android:textSize="22sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/titleText" />
  
    <Button
        android:id="@+id/saveBtn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:text="SAVE"
        android:textColor="@android:color/white"
        android:textSize="25sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/descEditText" />
  
</androidx.constraintlayout.widget.ConstraintLayout>

3.b: Comience a trabajar en el archivo floating_layout.xml

Vaya a res -> diseño . Haga clic con el botón derecho en diseño -> Nuevo -> Archivo de recursos de diseño . Agregue el nombre del diseño ( floating_layout aquí). Este archivo XML hace el diseño de la ventana flotante. Tiene los mismos componentes que el diseño principal pero con restricciones de tamaño ligeramente diferentes. Aquí está el código XML.

XML

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white">
  
    <Button
        android:id="@+id/buttonMaximize"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_marginTop="20dp"
        android:background="@color/gfgThemeTwo"
        android:text="MAXIMIZE"
        android:textColor="@android:color/white"
        android:textSize="15sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
  
    <TextView
        android:id="@+id/titleText"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:layout_marginTop="10dp"
        android:gravity="center"
        android:text="GEEKS FOR GEEKS"
        android:textColor="@color/gfgThemeTwo"
        android:textSize="18sp"
        android:textStyle="bold"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/buttonMaximize" />
  
    <EditText
        android:id="@+id/descEditText"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:gravity="start"
        android:hint="Description"
        android:paddingLeft="20dp"
        android:paddingTop="10dp"
        android:paddingRight="20dp"
        android:paddingBottom="10dp"
        android:textColor="@android:color/black"
        android:textSize="16sp"
        app:layout_constraintBottom_toTopOf="@+id/saveBtn"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/titleText" />
  
    <Button
        android:id="@+id/saveBtn"
        android:layout_width="wrap_content"
        android:layout_height="40dp"
        android:layout_marginBottom="10dp"
        android:background="@color/gfgThemeTwo"
        android:text="SAVE"
        android:textColor="@android:color/white"
        android:textSize="12sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />
  
</androidx.constraintlayout.widget.ConstraintLayout>

Paso 4: Comience a trabajar en los programas de Java

Hicimos 3 clases aquí. Obviamente, el primero es MainActivity . El segundo es FloatingWindowGFG para el servicio de ventana flotante y el último es una clase común para dos variables comunes. 

4.a: Hagamos la clase para las variables comunes

  • Para esto primero, hagamos un paquete llamado Common . Haga clic derecho en la ruta del paquete del proyecto ( com.wheic.floatingedittext aquí) -> Nuevo -> Paquete .
  • Una ventana aparecerá. El nombre del paquete previsto está escrito ( común para mí). Se creará un nuevo paquete.
  • Haga clic derecho en el paquete recién creado -> Nuevo -> Clase Java . El nombre de la clase prevista está escrito ( Común para aquí).
  • Se crean dos variables de string públicas, una es currentDesc y otra se guarda Desc . Ambos se inician con strings vacías.
  • Aquí está el código para la clase Common.java .

Java

package com.wheic.floatingedittext.Common;
  
public class Common {
    // The EditText String will be 
    // stored in this variable
    // when MINIMIZE or MAXIMIZE 
    // button is pressed
    public static String currentDesc = "";
      
    // The EditText String will be 
    // stored in this variable
    // when SAVE button is pressed
    public static String savedDesc = "";
}

4.b: Comience a trabajar en el archivo MainActivity.java

  • Primero, se crea la referencia para las clases de componentes. Se crean dos botones , un AlertDialog y una referencia EditText .
  • Antes de ir a onCreate() , se crean algunos otros métodos.
  • isMyServiceRunning() : este método ayuda a determinar si se está ejecutando el servicio de ventana flotante de esta aplicación. Esta función es necesaria cuando se abre la misma aplicación cuando ya se ve la ventana flotante porque entonces se necesita detener el servicio de ventana flotante. Aquí está el código con la explicación línea por línea:

Java

private boolean isMyServiceRunning() {
       // The ACTIVITY_SERVICE is needed to retrieve a 
          // ActivityManager for interacting with the global system
       // It has a constant String value "activity".
       ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
         
          // A loop is needed to get Service information that are currently running in the System.
       // So ActivityManager.RunningServiceInfo is used. It helps to retrieve a
       // particular service information, here its this service.
       // getRunningServices() method returns a list of the services that are currently running
       // and MAX_VALUE is 2147483647. So at most this many services can be returned by this method.
       for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
             
             // If this service is found as a running, it will return true or else false.
           if (FloatingWindowGFG.class.getName().equals(service.service.getClassName())) {
               return true;
           }
       }
       return false;
   }
  • requestOverlayDisplayPermission() : el método ayuda a redirigir la aplicación a la Configuración para habilitar ‘ Mostrar sobre otras aplicaciones ‘. Aunque para eso se debe agregar una línea adicional al archivo AndroidManifest.xml. Para eso, vaya a la aplicación -> manifiestos -> AndroidManifest.xml . Agregue esta línea antes del bloque de aplicación:

<usos-permiso android:name=”android.permission.SYSTEM_ALERT_WINDOW”/>

Java

private void requestOverlayDisplayPermission() {
        // An AlertDialog is created
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
          
          // This dialog can be closed, just by 
          // taping outside the dialog-box
        builder.setCancelable(true);
    
        // The title of the Dialog-box is set
        builder.setTitle("Screen Overlay Permission Needed");
    
        // The message of the Dialog-box is set
        builder.setMessage("Enable 'Display over other apps' from System Settings.");
    
        // The event of the Positive-Button is set
        builder.setPositiveButton("Open Settings", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // The app will redirect to the 'Display over other apps' in Settings.
                // This is an Implicit Intent. This is needed when any Action is needed 
                  // to perform, here it is
                // redirecting to an other app(Settings).
                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
                  
                  // This method will start the intent. It takes two parameter, 
                  // one is the Intent and the other is
                // an requestCode Integer. Here it is -1.
                startActivityForResult(intent, RESULT_OK);
            }
        });
        dialog = builder.create();
        // The Dialog will show in the screen
        dialog.show();
    }
  • checkOverlayDisplayPermission() : este método en realidad verifica que si el nivel de la API es superior a 23, entonces si la ‘ Mostrar sobre otras aplicaciones ‘ está habilitada en la configuración. Aquí está el código de esta función:

Java

private boolean checkOverlayDisplayPermission() {
        // Android Version is lesser than Marshmallow 
          // or the API is lesser than 23
        // doesn't need 'Display over other apps' permission enabling.
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
            // If 'Display over other apps' is not enabled it 
              // will return false or else true
            if (!Settings.canDrawOverlays(this)) {
                return false;
            } else {
                return true;
            }
        } else {
            return true;
        }
    }
  • Ahora aquí está el código completo para el archivo MainActivity.java .

Java

package com.wheic.floatingedittext;
  
import android.app.ActivityManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
  
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
  
import com.wheic.floatingedittext.Common.Common;
  
public class MainActivity extends AppCompatActivity {
  
    // The reference variables for the
    // Button, AlertDialog, EditText 
    // classes are created
    private Button minimizeBtn;
    private AlertDialog dialog;
    private EditText descEditArea;
    private Button save;
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
  
        // The Buttons and the EditText are connected with
        // the corresponding component id used in layout file
        minimizeBtn = findViewById(R.id.buttonMinimize);
        descEditArea = findViewById(R.id.descEditText);
        save = findViewById(R.id.saveBtn);
  
        // If the app is started again while the 
        // floating window service is running
        // then the floating window service will stop
        if (isMyServiceRunning()) {
            // onDestroy() method in FloatingWindowGFG
            // class will be called here
            stopService(new Intent(MainActivity.this, FloatingWindowGFG.class));
        }
  
        // currentDesc String will be empty 
        // at first time launch
        // but the text written in floating 
        // window will not gone
        descEditArea.setText(Common.currentDesc);
        descEditArea.setSelection(descEditArea.getText().toString().length());
  
        // The EditText string will be stored in 
        // currentDesc while writing
        descEditArea.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                // Not Necessary
            }
  
            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                Common.currentDesc = descEditArea.getText().toString();
            }
  
            @Override
            public void afterTextChanged(Editable editable) {
                // Not Necessary
            }
        });
  
        // Here the save button is used just to store the 
        // EditText string in saveDesc variable
        save.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Common.savedDesc = descEditArea.getText().toString();
                descEditArea.setCursorVisible(false);
                descEditArea.clearFocus();
                Toast.makeText(MainActivity.this, "Text Saved!!!", Toast.LENGTH_SHORT).show();
            }
        });
  
        // The Main Button that helps to minimize the app
        minimizeBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // First it confirms whether the
                // 'Display over other apps' permission in given
                if (checkOverlayDisplayPermission()) {
                    // FloatingWindowGFG service is started
                    startService(new Intent(MainActivity.this, FloatingWindowGFG.class));
                    // The MainActivity closes here
                    finish();
                } else {
                    // If permission is not given,
                    // it shows the AlertDialog box and
                    // redirects to the Settings
                    requestOverlayDisplayPermission();
                }
            }
        });
    }
  
    private boolean isMyServiceRunning() {
        // The ACTIVITY_SERVICE is needed to retrieve a
        // ActivityManager for interacting with the global system
        // It has a constant String value "activity".
        ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
          
        // A loop is needed to get Service information that 
        // are currently running in the System.
        // So ActivityManager.RunningServiceInfo is used. 
        // It helps to retrieve a
        // particular service information, here its this service.
        // getRunningServices() method returns a list of the 
        // services that are currently running
        // and MAX_VALUE is 2147483647. So at most this many services
        // can be returned by this method.
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            // If this service is found as a running, 
            // it will return true or else false.
            if (FloatingWindowGFG.class.getName().equals(service.service.getClassName())) {
                return true;
            }
        }
        return false;
    }
  
    private void requestOverlayDisplayPermission() {
        // An AlertDialog is created
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
          
        // This dialog can be closed, just by taping
        // anywhere outside the dialog-box
        builder.setCancelable(true);
          
        // The title of the Dialog-box is set
        builder.setTitle("Screen Overlay Permission Needed");
          
        // The message of the Dialog-box is set
        builder.setMessage("Enable 'Display over other apps' from System Settings.");
          
        // The event of the Positive-Button is set
        builder.setPositiveButton("Open Settings", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                // The app will redirect to the 'Display over other apps' in Settings.
                // This is an Implicit Intent. This is needed when any Action is needed
                // to perform, here it is
                // redirecting to an other app(Settings).
                Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
                  
                // This method will start the intent. It takes two parameter, one is the Intent and the other is
                // an requestCode Integer. Here it is -1.
                startActivityForResult(intent, RESULT_OK);
            }
        });
        dialog = builder.create();
        // The Dialog will 
        // show in the screen
        dialog.show();
    }
  
    private boolean checkOverlayDisplayPermission() {
        // Android Version is lesser than Marshmallow or
        // the API is lesser than 23
        // doesn't need 'Display over other apps' permission enabling.
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
            // If 'Display over other apps' is not enabled
            // it will return false or else true
            if (!Settings.canDrawOverlays(this)) {
                return false;
            } else {
                return true;
            }
        } else {
            return true;
        }
    }
}

4.c: Comience a trabajar en el archivo FloatingWindowGFG.java

  • Se crea otra clase denominada FloatingWindowGFG . Vaya a la ruta del paquete del proyecto ( com.wheic.floatingedittext para mí) -> Nuevo -> Clase Java y luego se le da cualquier nombre previsto ( FloatingWindowGFG para mí).
  • Esta clase hereda la clase Servicio.
  • Ahora, como esta clase se hereda de la clase Servicio, esta clase se puede usar como un servicio en el archivo Manifiesto. Entonces, en AndroidManifest.xml , agregue esta línea después del bloque de actividad y antes de que finalice el bloque de aplicación.

<servicio android:name=”.FloatingWindowGFG”/>

  • Ahora aquí está el código para FloatingWindowGFG.java . Se han añadido los comentarios para una mejor explicación:

Java

package com.wheic.floatingedittext;
  
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.IBinder;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
  
import androidx.annotation.Nullable;
  
import com.wheic.floatingedittext.Common.Common;
  
public class FloatingWindowGFG extends Service {
  
    // The reference variables for the
    // ViewGroup, WindowManager.LayoutParams, 
    // WindowManager, Button, EditText classes are created
    private ViewGroup floatView;
    private int LAYOUT_TYPE;
    private WindowManager.LayoutParams floatWindowLayoutParam;
    private WindowManager windowManager;
    private Button maximizeBtn;
    private EditText descEditArea;
    private Button saveBtn;
  
    // As FloatingWindowGFG inherits Service class, 
    // it actually overrides the onBind method
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
  
    @Override
    public void onCreate() {
        super.onCreate();
  
        // The screen height and width are calculated, cause
        // the height and width of the floating window is set depending on this
        DisplayMetrics metrics = getApplicationContext().getResources().getDisplayMetrics();
        int width = metrics.widthPixels;
        int height = metrics.heightPixels;
  
        // To obtain a WindowManager of a different Display,
        // we need a Context for that display, so WINDOW_SERVICE is used
        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
  
        // A LayoutInflater instance is created to retrieve the 
        // LayoutInflater for the floating_layout xml
        LayoutInflater inflater = (LayoutInflater) getBaseContext().getSystemService(LAYOUT_INFLATER_SERVICE);
          
        // inflate a new view hierarchy from the floating_layout xml
        floatView = (ViewGroup) inflater.inflate(R.layout.floating_layout, null);
  
        // The Buttons and the EditText are connected with
        // the corresponding component id used in floating_layout xml file
        maximizeBtn = floatView.findViewById(R.id.buttonMaximize);
        descEditArea = floatView.findViewById(R.id.descEditText);
        saveBtn = floatView.findViewById(R.id.saveBtn);
  
        // Just like MainActivity, the text written 
        // in Maximized will stay
        descEditArea.setText(Common.currentDesc);
        descEditArea.setSelection(descEditArea.getText().toString().length());
        descEditArea.setCursorVisible(false);
  
        // WindowManager.LayoutParams takes a lot of parameters to set the
        // the parameters of the layout. One of them is Layout_type.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            // If API Level is more than 26, we need TYPE_APPLICATION_OVERLAY
            LAYOUT_TYPE = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
            // If API Level is lesser than 26, then we can 
            // use TYPE_SYSTEM_ERROR,
            // TYPE_SYSTEM_OVERLAY, TYPE_PHONE, TYPE_PRIORITY_PHONE.
            // But these are all
            // deprecated in API 26 and later. Here TYPE_TOAST works best.
            LAYOUT_TYPE = WindowManager.LayoutParams.TYPE_TOAST;
        }
  
        // Now the Parameter of the floating-window layout is set.
        // 1) The Width of the window will be 55% of the phone width.
        // 2) The Height of the window will be 58% of the phone height.
        // 3) Layout_Type is already set.
        // 4) Next Parameter is Window_Flag. Here FLAG_NOT_FOCUSABLE is used. But
        // problem with this flag is key inputs can't be given to the EditText.
        // This problem is solved later.
        // 5) Next parameter is Layout_Format. System chooses a format that supports 
        // translucency by PixelFormat.TRANSLUCENT
        floatWindowLayoutParam = new WindowManager.LayoutParams(
                (int) (width * (0.55f)),
                (int) (height * (0.58f)),
                LAYOUT_TYPE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT
        );
  
        // The Gravity of the Floating Window is set. 
        // The Window will appear in the center of the screen
        floatWindowLayoutParam.gravity = Gravity.CENTER;
          
        // X and Y value of the window is set
        floatWindowLayoutParam.x = 0;
        floatWindowLayoutParam.y = 0;
  
        // The ViewGroup that inflates the floating_layout.xml is
        // added to the WindowManager with all the parameters
        windowManager.addView(floatView, floatWindowLayoutParam);
  
        // The button that helps to maximize the app
        maximizeBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // stopSelf() method is used to stop the service if
                // it was previously started
                stopSelf();
                  
                // The window is removed from the screen
                windowManager.removeView(floatView);
                  
                // The app will maximize again. So the MainActivity 
                // class will be called again.
                Intent backToHome = new Intent(FloatingWindowGFG.this, MainActivity.class);
                  
                // 1) FLAG_ACTIVITY_NEW_TASK flag helps activity to start a new task on the history stack.
                // If a task is already running like the floating window service, a new activity will not be started.
                // Instead the task will be brought back to the front just like the MainActivity here
                // 2) FLAG_ACTIVITY_CLEAR_TASK can be used in the conjunction with FLAG_ACTIVITY_NEW_TASK. This flag will
                // kill the existing task first and then new activity is started.
                backToHome.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
                startActivity(backToHome);
            }
        });
  
        // The EditText string will be stored 
        // in currentDesc while writing
        descEditArea.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                // Not Necessary
            }
  
            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                Common.currentDesc = descEditArea.getText().toString();
            }
  
            @Override
            public void afterTextChanged(Editable editable) {
                // Not Necessary
            }
        });
  
        // Another feature of the floating window is, the window is movable.
        // The window can be moved at any position on the screen.
        floatView.setOnTouchListener(new View.OnTouchListener() {
            final WindowManager.LayoutParams floatWindowLayoutUpdateParam = floatWindowLayoutParam;
            double x;
            double y;
            double px;
            double py;
  
            @Override
            public boolean onTouch(View v, MotionEvent event) {
  
                switch (event.getAction()) {
                    // When the window will be touched, 
                    // the x and y position of that position
                    // will be retrieved
                    case MotionEvent.ACTION_DOWN:
                        x = floatWindowLayoutUpdateParam.x;
                        y = floatWindowLayoutUpdateParam.y;
                          
                        // returns the original raw X 
                        // coordinate of this event
                        px = event.getRawX();
                          
                        // returns the original raw Y 
                        // coordinate of this event
                        py = event.getRawY();
                        break;
                    // When the window will be dragged around, 
                    // it will update the x, y of the Window Layout Parameter
                    case MotionEvent.ACTION_MOVE:
                        floatWindowLayoutUpdateParam.x = (int) ((x + event.getRawX()) - px);
                        floatWindowLayoutUpdateParam.y = (int) ((y + event.getRawY()) - py);
  
                        // updated parameter is applied to the WindowManager
                        windowManager.updateViewLayout(floatView, floatWindowLayoutUpdateParam);
                        break;
                }
                return false;
            }
        });
  
        // Floating Window Layout Flag is set to FLAG_NOT_FOCUSABLE, 
        // so no input is possible to the EditText. But that's a problem.
        // So, the problem is solved here. The Layout Flag is 
        // changed when the EditText is touched.
        descEditArea.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                descEditArea.setCursorVisible(true);
                WindowManager.LayoutParams floatWindowLayoutParamUpdateFlag = floatWindowLayoutParam;
                // Layout Flag is changed to FLAG_NOT_TOUCH_MODAL which 
                // helps to take inputs inside floating window, but
                // while in EditText the back button won't work and 
                // FLAG_LAYOUT_IN_SCREEN flag helps to keep the window
                // always over the keyboard
                floatWindowLayoutParamUpdateFlag.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
                  
                // WindowManager is updated with the Updated Parameters
                windowManager.updateViewLayout(floatView, floatWindowLayoutParamUpdateFlag);
                return false;
            }
        });
  
  
        saveBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // saves the text in savedDesc variable
                Common.savedDesc = descEditArea.getText().toString();
                descEditArea.setCursorVisible(false);
                WindowManager.LayoutParams floatWindowLayoutParamUpdateFlag = floatWindowLayoutParam;
                floatWindowLayoutParamUpdateFlag.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
                  
                // The Layout Flag is changed back to FLAG_NOT_FOCUSABLE. and the Layout is updated with new Flag
                windowManager.updateViewLayout(floatView, floatWindowLayoutParamUpdateFlag);
                  
                // INPUT_METHOD_SERVICE with Context is used 
                // to retrieve a InputMethodManager for
                // accessing input methods which is the soft keyboard here
                InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                  
                // The soft keyboard slides back in
                inputMethodManager.hideSoftInputFromWindow(floatView.getApplicationWindowToken(), 0);
                  
                // A Toast is shown when the text is saved
                Toast.makeText(FloatingWindowGFG.this, "Text Saved!!!", Toast.LENGTH_SHORT).show();
            }
        });
    }
  
    // It is called when stopService() 
    // method is called in MainActivity
    @Override
    public void onDestroy() {
        super.onDestroy();
        stopSelf();
        // Window is removed from the screen
        windowManager.removeView(floatView);
    }
}

Producción:

Finalmente, el proyecto está listo. Puedes consultar este proyecto en este enlace de GitHub .

Publicación traducida automáticamente

Artículo escrito por jeetnath y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *