

Android Floating Widget like Facebook Chat Head
source link: https://www.androidhive.info/2016/11/android-floating-widget-like-facebook-chat-head/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Floating widgets are the views that float over the screen. This view will remain floating on the screen no matter whichever screen you are in. They are very convenient for multitasking as a user can work on other application and control your application for the same time. That means if you are in the calculator application and a widget from the music player is floating over the screen, you can control your music at the same time. Pretty cool!
In this tutorial, we are going to learn how to create simple floating widget and allow the user to drag them across the screen. So that user can adjust the position of the floating widget on the screen. We are going to develop a floating view that is having buttons to control the music. (To keep this article focused around floating widget, we will not look into how to control music)
Final Preview:
So, let’s get started.
1. Understanding the basics
Floating widget is nothing but the view that is drawn over other applications. Android system allows applications to draw over other application if the application has android.permission.SYSTEM_ALERT_WINDOW permission. We are going to use the background service to add the floating widget into the view hierarchy of the current screen. So, this floating view is always on top of application windows.
To drag the view across the screen we are going to override OnTouchListener() to listen to drag events and change the position of the view has in the screen.
2. Creating New Project
1. Create a new project in Android Studio from File ⇒ New Project and fill all the necessary details.
2. Download this resource drawables and add them to project’s res directory. This folder contains all the necessary icons & images required for this app.
3. Add android.permission.SYSTEM_ALERT_WINDOW permission to the AndroidManifest.xml file. This permission allows an app to create windows, shown on top of all other apps. We’ll also add a service named FloatingViewService that will be created shortly.
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
package
=
"info.androidhive.floatingview"
>
<
uses-permission
android:name
=
"android.permission.SYSTEM_ALERT_WINDOW"
/>
<
application
android:allowBackup
=
"true"
android:icon
=
"@mipmap/ic_launcher"
android:label
=
"@string/app_name"
android:supportsRtl
=
"true"
android:theme
=
"@style/AppTheme"
>
<
activity
android:name
=
"info.androidhive.floatingview.MainActivity"
>
<
intent-filter
>
<
action
android:name
=
"android.intent.action.MAIN"
/>
<
category
android:name
=
"android.intent.category.LAUNCHER"
/>
</
intent-filter
>
</
activity
>
<
service
android:name
=
"info.androidhive.floatingview.FloatingViewService"
android:enabled
=
"true"
android:exported
=
"false"
/>
</
application
>
</
manifest
>
4. Create a new layout file named layout_floating_widget.xml for the floating view. This layout will contain two main views.
Collapsed view:
The floating widget will remain collapsed when the view is launched. When the user clicks on this view, an expanded view will open.
Expanded View:
This view will contain buttons to play music, change the song to next/previous and open the application.
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
FrameLayout
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
>
<!--Root container-->
<
RelativeLayout
android:id
=
"@+id/root_container"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
tools:ignore
=
"UselessParent"
>
<!--View while view is collapsed-->
<
RelativeLayout
android:id
=
"@+id/collapse_view"
android:layout_width
=
"wrap_content"
android:visibility
=
"visible"
android:layout_height
=
"wrap_content"
android:orientation
=
"vertical"
>
<!--Icon of floating widget -->
<
ImageView
android:id
=
"@+id/collapsed_iv"
android:layout_width
=
"60dp"
android:layout_height
=
"60dp"
android:layout_marginTop
=
"8dp"
android:src
=
"@drawable/ic_android_circle"
tools:ignore
=
"ContentDescription"
/>
<!--Close button-->
<
ImageView
android:id
=
"@+id/close_btn"
android:layout_width
=
"20dp"
android:layout_height
=
"20dp"
android:layout_marginLeft
=
"40dp"
android:src
=
"@drawable/ic_close"
tools:ignore
=
"ContentDescription"
/>
</
RelativeLayout
>
<!--View while view is expanded-->
<
LinearLayout
android:id
=
"@+id/expanded_container"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:background
=
"#F8BBD0"
android:visibility
=
"gone"
android:orientation
=
"horizontal"
android:padding
=
"8dp"
>
<!--Album image for the song currently playing.-->
<
ImageView
android:layout_width
=
"80dp"
android:layout_height
=
"80dp"
android:src
=
"@drawable/music_player"
tools:ignore
=
"ContentDescription"
/>
<!--Previous button-->
<
ImageView
android:id
=
"@+id/prev_btn"
android:layout_width
=
"30dp"
android:layout_height
=
"30dp"
android:layout_gravity
=
"center_vertical"
android:layout_marginLeft
=
"20dp"
android:src
=
"@mipmap/ic_previous"
tools:ignore
=
"ContentDescription"
/>
<!--Play button-->
<
ImageView
android:id
=
"@+id/play_btn"
android:layout_width
=
"50dp"
android:layout_height
=
"50dp"
android:layout_gravity
=
"center_vertical"
android:layout_marginLeft
=
"10dp"
android:src
=
"@mipmap/ic_play"
tools:ignore
=
"ContentDescription"
/>
<!--Next button-->
<
ImageView
android:id
=
"@+id/next_btn"
android:layout_width
=
"30dp"
android:layout_height
=
"30dp"
android:layout_gravity
=
"center_vertical"
android:layout_marginLeft
=
"10dp"
android:src
=
"@mipmap/ic_play_next"
tools:ignore
=
"ContentDescription"
/>
<
RelativeLayout
android:layout_width
=
"wrap_content"
android:layout_height
=
"match_parent"
android:orientation
=
"vertical"
>
<
ImageView
android:id
=
"@+id/close_button"
android:layout_width
=
"20dp"
android:layout_height
=
"20dp"
android:src
=
"@drawable/ic_close"
/>
<
ImageView
android:id
=
"@+id/open_button"
android:layout_width
=
"20dp"
android:layout_height
=
"20dp"
android:layout_alignParentBottom
=
"true"
android:src
=
"@drawable/ic_open"
/>
</
RelativeLayout
>
</
LinearLayout
>
</
RelativeLayout
>
</
FrameLayout
>
2.1 Adding Floating Widget (Music Controls) and handle Dragging
Now create a service called FloatingViewService.java. Whenever you want to display a floating view, start the service using startService() command. In onCreate() of the service, we will add the layout of the floating view at the top-left corner of the window.
public
class
FloatingViewService
extends
Service {
private
WindowManager mWindowManager;
private
View mFloatingView;
public
FloatingViewService() {
}
@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.layout_floating_widget,
null
);
//Add the view to the window.
final
WindowManager.LayoutParams params =
new
WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
//Specify the view position
params.gravity = Gravity.TOP | Gravity.LEFT;
//Initially view will be added to top-left corner
params.x =
0
;
params.y =
100
;
//Add the view to the window
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
mWindowManager.addView(mFloatingView, params);
//….
//….
}
@Override
public
void
onDestroy() {
super
.onDestroy();
if
(mFloatingView !=
null
) mWindowManager.removeView(mFloatingView);
}
}
Also, we are implementing OnClickListner() for all the buttons like play, pause, and open application. You can implement a function to play pause music on button click using MediaPlayer. When the user clicks on the close button in the collapsed view, FloatingViewService will get destroyed and floating view will be removed from the view hierarchy.
Add the below code to FloatingViewService.java in onCrate() method.
//The root element of the collapsed view layout
final
View collapsedView = mFloatingView.findViewById(R.id.collapse_view);
//The root element of the expanded view layout
final
View expandedView = mFloatingView.findViewById(R.id.expanded_container);
//Set the close button
ImageView closeButtonCollapsed = (ImageView) mFloatingView.findViewById(R.id.close_btn);
closeButtonCollapsed.setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View view) {
//close the service and remove the from from the window
stopSelf();
}
});
//Set the view while floating view is expanded.
//Set the play button.
ImageView playButton = (ImageView) mFloatingView.findViewById(R.id.play_btn);
playButton.setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View v) {
Toast.makeText(FloatingViewService.
this
,
"Playing the song."
, Toast.LENGTH_LONG).show();
}
});
//Set the next button.
ImageView nextButton = (ImageView) mFloatingView.findViewById(R.id.next_btn);
nextButton.setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View v) {
Toast.makeText(FloatingViewService.
this
,
"Playing next song."
, Toast.LENGTH_LONG).show();
}
});
//Set the pause button.
ImageView prevButton = (ImageView) mFloatingView.findViewById(R.id.prev_btn);
prevButton.setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View v) {
Toast.makeText(FloatingViewService.
this
,
"Playing previous song."
, Toast.LENGTH_LONG).show();
}
});
//Set the close button
ImageView closeButton = (ImageView) mFloatingView.findViewById(R.id.close_button);
closeButton.setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View view) {
collapsedView.setVisibility(View.VISIBLE);
expandedView.setVisibility(View.GONE);
}
});
//Open the application on thi button click
ImageView openButton = (ImageView) mFloatingView.findViewById(R.id.open_button);
openButton.setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View view) {
//Open the application click.
Intent intent =
new
Intent(FloatingViewService.
this
, MainActivity.
class
);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
//close the service and remove view from the view hierarchy
stopSelf();
}
});
To drag the floating view along with the user’s touch, we have to override OnTouchListener(). Whenever the user touches the root of the view, we will record the initial x and y coordinates, and when the user moves the finger, the application will calculate the new X and Y coordinate and move the float view.
//Drag and move floating view using user's touch action.
mFloatingView.findViewById(R.id.root_container).setOnTouchListener(
new
View.OnTouchListener() {
private
int
initialX;
private
int
initialY;
private
float
initialTouchX;
private
float
initialTouchY;
@Override
public
boolean
onTouch(View v, MotionEvent event) {
switch
(event.getAction()) {
case
MotionEvent.ACTION_DOWN:
//remember the initial position.
initialX = params.x;
initialY = params.y;
//get the touch location
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
return
true
;
case
MotionEvent.ACTION_MOVE:
//Calculate the X and Y coordinates of the view.
params.x = initialX + (
int
) (event.getRawX() - initialTouchX);
params.y = initialY + (
int
) (event.getRawY() - initialTouchY);
//Update the layout with new X & Y coordinate
mWindowManager.updateViewLayout(mFloatingView, params);
return
true
;
}
return
false
;
}
});
2.2 Handling Widget Collapsing / Expanding
When user clicks on the image view of the collapsed layout, visibility of the collapsed layout should changed to View.GONE and expanded view should become visible. Like below:
To achieve this we need to implement OnClickListner() to the imageview of the collapsed layout. But, as we implemented OnTouchListener() to the root view, OnClickListner() won’t work. So, to detect clicks we will detect clicks in MotionEvent.ACTION_MOVE in the OnTouchListener() using below code:
case
MotionEvent.ACTION_UP:
int
Xdiff = (
int
) (event.getRawX() - initialTouchX);
int
Ydiff = (
int
) (event.getRawY() - initialTouchY);
//The check for Xdiff <10 && YDiff< 10 because sometime elements moves a little while clicking.
//So that is click event.
if
(Xdiff <
10
&& Ydiff <
10
) {
if
(isViewCollapsed()) {
//When user clicks on the image view of the collapsed layout,
//visibility of the collapsed layout will be changed to "View.GONE"
//and expanded view will become visible.
collapsedView.setVisibility(View.GONE);
expandedView.setVisibility(View.VISIBLE);
}
}
return
true
;
So finally your FloatingViewService.java will look like below:
import
android.app.Service;
import
android.content.Intent;
import
android.graphics.PixelFormat;
import
android.opengl.Visibility;
import
android.os.IBinder;
import
android.view.Gravity;
import
android.view.LayoutInflater;
import
android.view.MotionEvent;
import
android.view.View;
import
android.view.WindowManager;
import
android.widget.ImageView;
import
android.widget.Toast;
public
class
FloatingViewService
extends
Service {
private
WindowManager mWindowManager;
private
View mFloatingView;
public
FloatingViewService() {
}
@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.layout_floating_widget,
null
);
//Add the view to the window.
final
WindowManager.LayoutParams params =
new
WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
//Specify the view position
params.gravity = Gravity.TOP | Gravity.LEFT;
//Initially view will be added to top-left corner
params.x =
0
;
params.y =
100
;
//Add the view to the window
mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
mWindowManager.addView(mFloatingView, params);
//The root element of the collapsed view layout
final
View collapsedView = mFloatingView.findViewById(R.id.collapse_view);
//The root element of the expanded view layout
final
View expandedView = mFloatingView.findViewById(R.id.expanded_container);
//Set the close button
ImageView closeButtonCollapsed = (ImageView) mFloatingView.findViewById(R.id.close_btn);
closeButtonCollapsed.setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View view) {
//close the service and remove the from from the window
stopSelf();
}
});
//Set the view while floating view is expanded.
//Set the play button.
ImageView playButton = (ImageView) mFloatingView.findViewById(R.id.play_btn);
playButton.setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View v) {
Toast.makeText(FloatingViewService.
this
,
"Playing the song."
, Toast.LENGTH_LONG).show();
}
});
//Set the next button.
ImageView nextButton = (ImageView) mFloatingView.findViewById(R.id.next_btn);
nextButton.setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View v) {
Toast.makeText(FloatingViewService.
this
,
"Playing next song."
, Toast.LENGTH_LONG).show();
}
});
//Set the pause button.
ImageView prevButton = (ImageView) mFloatingView.findViewById(R.id.prev_btn);
prevButton.setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View v) {
Toast.makeText(FloatingViewService.
this
,
"Playing previous song."
, Toast.LENGTH_LONG).show();
}
});
//Set the close button
ImageView closeButton = (ImageView) mFloatingView.findViewById(R.id.close_button);
closeButton.setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View view) {
collapsedView.setVisibility(View.VISIBLE);
expandedView.setVisibility(View.GONE);
}
});
//Open the application on thi button click
ImageView openButton = (ImageView) mFloatingView.findViewById(R.id.open_button);
openButton.setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View view) {
//Open the application click.
Intent intent =
new
Intent(FloatingViewService.
this
, MainActivity.
class
);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
//close the service and remove view from the view hierarchy
stopSelf();
}
});
//Drag and move floating view using user's touch action.
mFloatingView.findViewById(R.id.root_container).setOnTouchListener(
new
View.OnTouchListener() {
private
int
initialX;
private
int
initialY;
private
float
initialTouchX;
private
float
initialTouchY;
@Override
public
boolean
onTouch(View v, MotionEvent event) {
switch
(event.getAction()) {
case
MotionEvent.ACTION_DOWN:
//remember the initial position.
initialX = params.x;
initialY = params.y;
//get the touch location
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
return
true
;
case
MotionEvent.ACTION_UP:
int
Xdiff = (
int
) (event.getRawX() - initialTouchX);
int
Ydiff = (
int
) (event.getRawY() - initialTouchY);
//The check for Xdiff <10 && YDiff< 10 because sometime elements moves a little while clicking.
//So that is click event.
if
(Xdiff <
10
&& Ydiff <
10
) {
if
(isViewCollapsed()) {
//When user clicks on the image view of the collapsed layout,
//visibility of the collapsed layout will be changed to "View.GONE"
//and expanded view will become visible.
collapsedView.setVisibility(View.GONE);
expandedView.setVisibility(View.VISIBLE);
}
}
return
true
;
case
MotionEvent.ACTION_MOVE:
//Calculate the X and Y coordinates of the view.
params.x = initialX + (
int
) (event.getRawX() - initialTouchX);
params.y = initialY + (
int
) (event.getRawY() - initialTouchY);
//Update the layout with new X & Y coordinate
mWindowManager.updateViewLayout(mFloatingView, params);
return
true
;
}
return
false
;
}
});
}
/**
* Detect if the floating view is collapsed or expanded.
*
* @return true if the floating view is collapsed.
*/
private
boolean
isViewCollapsed() {
return
mFloatingView ==
null
|| mFloatingView.findViewById(R.id.collapse_view).getVisibility() == View.VISIBLE;
}
@Override
public
void
onDestroy() {
super
.onDestroy();
if
(mFloatingView !=
null
) mWindowManager.removeView(mFloatingView);
}
}
2.3 Handle Overdraw permission
Now one final step is remaining is adding floating view by starting the FloatingViewService. For that, we need to check if the application has android.permission.SYSTEM_ALERT_WINDOW permission or not? For android version <= API22, this permission is granted by default. But for the android versions running API > 22, we need to check for the permission runtime.
If the permission is not available, we will open permission management screen to allow the user to grant permission using Settings.ACTION_MANAGE_OVERLAY_PERMISSION intent action. This will open below screen facilitate user to grant android.permission.SYSTEM_ALERT_WINDOW permission.
Here is the code snippet for the MainActivity that will display the floating view when button is clicked by checking the SYSTEM_ALERT_WINDOW permission.
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
CODE_DRAW_OVER_OTHER_APP_PERMISSION =
2084
;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Check if the application has draw over other apps permission or not?
//This permission is by default available for API<23. But for API > 23
//you have to ask for the permission in runtime.
if
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(
this
)) {
//If the draw over permission is not available open the settings screen
//to grant the permission.
Intent intent =
new
Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse(
"package:"
+ getPackageName()));
startActivityForResult(intent, CODE_DRAW_OVER_OTHER_APP_PERMISSION);
}
else
{
initializeView();
}
}
/**
* Set and initialize the view elements.
*/
private
void
initializeView() {
findViewById(R.id.notify_me).setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View view) {
startService(
new
Intent(MainActivity.
this
, FloatingViewService.
class
));
finish();
}
});
}
@Override
protected
void
onActivityResult(
int
requestCode,
int
resultCode, Intent data) {
if
(requestCode == CODE_DRAW_OVER_OTHER_APP_PERMISSION) {
//Check if the permission is granted or not.
if
(resultCode == RESULT_OK) {
initializeView();
}
else
{
//Permission is not available
Toast.makeText(
this
,
"Draw over other app permission not available. Closing the application"
,
Toast.LENGTH_SHORT).show();
finish();
}
}
else
{
super
.onActivityResult(requestCode, resultCode, data);
}
}
}
That’s it. Now build and run the project to see the results. You can check out the below image to see the sample in action.
Don’t worry if you have any problems while building the project. If you have any queries let me know in comments below.
I am an Android Developer and Machine Learner, based on Ahmedabad. I love to writing complex articles on android. I have a passion for building new things by continuously learning new things.
Recommend
-
12
Facebook comments plugin lets users to comment on any web url using their Facebook account. Right now Facebook doesn’t provide native android support for the plugin, but we...
-
10
Chat widget accessible to everyoneCreate a multifunctional chat widget, accessible to everyone. Make your online business friendly to people with disabilities, allowing customers to talk to you more freely.
-
6
react-native-floating-bubble A simple Facebook Chat Head like bubble for react native. Special thanks to bubble-for-android because this is just react native wrapper for that library.
-
7
Connect with website visitors via popular messaging apps
-
7
Microsoft is testing iOS-like widget picker for Windows 11, here is how to enable it...
-
4
Microsoft’s Bing Chat gets deeper Edge mobile integration, a widget, and much more / Microsoft isn’t slowing down in its battle with Google for AI features.By
-
11
Bing Chat Is Getting A Bunch Of New Features, Including An AI Widget ...
-
6
Microsoft has released a Bing Chat AI widget for Android and iOS smartphones. Users can continue viewing Bing Chat chats on mobile from a PC using this functionality to get their answers faster and with precision.
-
8
Bing Chat launches an iOS widget as part of this week's dev update...
-
1
Sony PlayStation head Jim Ryan claims publishers don't like Xbox Game Pass...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK