Floating widgets float over the screen over any app that is currently running.
Here we will make a floating action button that you can drag around the screen to position and do actions on it.
Add Permission
For drawing over the other apps, you need “android.permission.SYSTEM_ALERT_WINDOW” permission.
So add this entry in your manifest.
< uses-permission android:name = "android.permission.SYSTEM_ALERT_WINDOW" /> |
Our Manifest will look like this
<? xml version = "1.0" encoding = "utf-8" ?> < manifest xmlns:android = "http://schemas.android.com/apk/res/android" package = "floating_demo.coderzheaven.com.floatingdemo" > < uses-permission android:name = "android.permission.SYSTEM_ALERT_WINDOW" /> < application android:allowBackup = "true" android:icon = "@mipmap/ic_launcher" android:label = "@string/app_name" android:roundIcon = "@mipmap/ic_launcher_round" android:supportsRtl = "true" android:theme = "@style/AppTheme" > < activity android:name = ".MainActivity" > < intent-filter > < action android:name = "android.intent.action.MAIN" /> < category android:name = "android.intent.category.LAUNCHER" /> </ intent-filter > </ activity > < service android:name = ".FloatingService" android:enabled = "true" android:exported = "false" /> </ application > </ manifest > |
Layout
<? xml version = "1.0" encoding = "utf-8" ?> < RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:tools = "http://schemas.android.com/tools" android:layout_width = "match_parent" android:layout_height = "match_parent" tools:context = "floating_demo.coderzheaven.com.floatingdemo.MainActivity" > < TextView android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_centerInParent = "true" android:gravity = "center" android:text = "Floating action Demo\n(coderzheaven.com)" /> < Button android:id = "@+id/createBtn" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:text = "Create Floating Button" android:layout_centerInParent = "true" android:textColor = "@android:color/white" android:layout_marginBottom = "30dp" android:padding = "10dp" android:background = "@color/colorAccent" android:layout_alignParentBottom = "true" /> </ RelativeLayout > |
The next layout is shown when the floating button is clicked.
We will name it floating_layout.xml.
<? xml version = "1.0" encoding = "utf-8" ?> < FrameLayout xmlns:android = "http://schemas.android.com/apk/res/android" android:layout_width = "wrap_content" android:layout_height = "wrap_content" > < RelativeLayout android:id = "@+id/parentRel" android:layout_width = "wrap_content" android:layout_height = "wrap_content" > < RelativeLayout android:id = "@+id/r1" android:layout_width = "wrap_content" android:layout_height = "wrap_content" > < ImageView android:id = "@+id/mainButton" android:layout_width = "50dp" android:layout_height = "50dp" android:layout_alignParentTop = "true" android:src = "@mipmap/ic_launcher" /> < ImageView android:id = "@+id/closeBtn" android:layout_width = "30dp" android:layout_height = "30dp" android:layout_marginLeft = "30dp" android:src = "@android:drawable/ic_dialog_info" /> </ RelativeLayout > < LinearLayout android:id = "@+id/showLin" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_margin = "10dp" android:layout_toRightOf = "@+id/r1" android:background = "@color/colorAccent" android:gravity = "center" android:padding = "20dp" android:visibility = "gone" > < ImageView android:id = "@+id/btnInfo" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:src = "@android:drawable/ic_dialog_info" /> </ LinearLayout > </ RelativeLayout > </ FrameLayout > |
Activity and the Floating Service
MainActivity
package floating_demo.coderzheaven.com.floatingdemo; import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.Settings; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Toast; public class MainActivity extends AppCompatActivity { private static final int APP_OVERLAY_PERMISSION = 1000 ; private Context context; @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_main); context = this ; // Asking for permission from user... if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(context)) { Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse( "package:" + getPackageName())); startActivityForResult(intent, APP_OVERLAY_PERMISSION); } findViewById(R.id.createBtn).setOnClickListener( new View.OnClickListener() { @Override public void onClick(View view) { if (!(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(context))) { // Permission was already granted..starting service for creating the Floating Button UI... startService( new Intent(context, FloatingService. class )); finish(); } } }); findViewById(R.id.createBtn).setVisibility(checkIfOverlayPermissionGranted() ? View.VISIBLE : View.GONE); } Boolean checkIfOverlayPermissionGranted() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.canDrawOverlays(context); } @Override protected void onDestroy() { super .onDestroy(); if (!(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(context))) { // Permission was already granted..starting service for creating the Floating Button UI... startService( new Intent(context, FloatingService. class )); } } @Override protected void onActivityResult( int requestCode, int resultCode, Intent data) { if (requestCode == APP_OVERLAY_PERMISSION) { showMessage(checkIfOverlayPermissionGranted() ? "Overlay Permission Granted :)" : "Overlay Permission Denied :(" ); findViewById(R.id.createBtn).setVisibility(checkIfOverlayPermissionGranted() ? View.VISIBLE : View.GONE); } else { super .onActivityResult(requestCode, resultCode, data); } } // Shows message to the user... void showMessage(String message) { Toast.makeText(context, message, Toast.LENGTH_LONG).show(); } } |
We will use a service to draw the floating button over other apps.
Create a java file named FloatingService.java which extends Service.
package floating_demo.coderzheaven.com.floatingdemo; import android.app.Service; import android.content.Intent; import android.graphics.PixelFormat; import android.os.IBinder; import android.support.annotation.Nullable; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.WindowManager; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.Toast; public class FloatingService extends Service implements OnClickListener, View.OnTouchListener { private WindowManager mWindowManager; private View mFloatingView; private ImageView mainButton; private LinearLayout showLin; private ImageView btnInfo; private WindowManager.LayoutParams params; private ImageView btnClose; int initialX = 0 ; int initialY = 0 ; float initialTouchX = 0 ; float initialTouchY = 0 ; @Nullable @Override public IBinder onBind(Intent intent) { return null ; } @Override public void onCreate() { super .onCreate(); //Inflate the floating view layout we created mFloatingView = LayoutInflater.from( this ).inflate(R.layout.floating_layout, null ); //Add the view to the window. params = new WindowManager.LayoutParams( WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_PHONE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT); // Set the position to the top right corner of the screen params.gravity = Gravity.TOP | Gravity.LEFT; params.x = 50 ; params.y = 50 ; //Add the view to the window mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE); mWindowManager.addView(mFloatingView, params); mainButton = (ImageView) mFloatingView.findViewById(R.id.mainButton); btnClose = (ImageView) mFloatingView.findViewById(R.id.closeBtn); btnInfo = (ImageView) mFloatingView.findViewById(R.id.btnInfo); showLin = (LinearLayout) mFloatingView.findViewById(R.id.showLin); mainButton.setOnClickListener( this ); btnInfo.setOnClickListener( this ); btnClose.setOnClickListener( this ); //Drag and move floating view using user's touch action. mainButton.setOnTouchListener( this ); } @Override public void onClick(View view) { if (view == btnClose) { int vis = (showLin.getVisibility() == View.VISIBLE) ? View.GONE : View.VISIBLE; showLin.setVisibility(vis); } if (view == btnInfo) { Intent intent = new Intent(FloatingService. this , MainActivity. class ); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); // Stop the service and Remove the Floating Button when our app opens... stopSelf(); } } @Override public void onDestroy() { super .onDestroy(); if ( null != mFloatingView && null != mWindowManager) mWindowManager.removeView(mFloatingView); } // Shows message to the user... void showMessage(String message) { Toast.makeText( this , message, Toast.LENGTH_LONG).show(); } @Override public boolean onTouch(View view, MotionEvent motionEvent) { showLin.setVisibility(View.GONE); switch (motionEvent.getAction()) { case MotionEvent.ACTION_DOWN: //remember the initial position. //initialX = params.x; //initialY = params.y; //get the touch location initialTouchX = motionEvent.getRawX(); initialTouchY = motionEvent.getRawY(); return true ; case MotionEvent.ACTION_UP: return true ; case MotionEvent.ACTION_MOVE: //Calculate the X and Y coordinates of the view. params.x = ( int ) (motionEvent.getRawX() - initialTouchX) - initialX; params.y = ( int ) (motionEvent.getRawY() - initialTouchY) - initialY; //Update the layout with new X & Y coordinate mWindowManager.updateViewLayout(mFloatingView, params); return true ; } return false ; } } |
Source Code
You can download the complete source code from here.