Android Custom Floating Button Effect
- 2021-12-19 06:52:58
- OfStack
This article example for everyone to share the Android custom floating button effect of the specific code, for your reference, the specific content is as follows
The following: The content has no reference, and it is also a relatively simple example, which mainly shows/hides the floating window when switching the front and back office. Content is only used for self-recording learning.
When the project is developed, a floating window should be displayed after logging in, and other information such as online number should be displayed at the same time. Click the floating button to display the global pop-up list. After the development is completed, I feel that I need to record this implementation. So write a simple Demo.
The idea of Demo is to add/remove floating windows by starting Service. Because it is a global floating window, it is selected to attach to Service.
In MyAppliction, listen for the application's foreground and foreground switching to add or hide floating windows.
Its custom floating window View is manually drawn through Canvas in the project, where it is convenient to load a layout file directly. The main purpose is to understand this way of adding global floating windows, so Demo style and functions can be simplified.
MyAppliction
package com.example.qxb_810.floatbuttondemo.application;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager;
import com.example.qxb_810.floatbuttondemo.service.FloatingActionService;
import java.util.List;
import static android.content.ContentValues.TAG;
/**
* create 2018/12/1 13:31
* desc Customize Application
*/
public class MyApplication extends Application {
private Intent mIntent;
private WindowManager.LayoutParams mFloatingLayoutParams = new WindowManager.LayoutParams();
public WindowManager.LayoutParams getmFloatingLayoutParams() {
return mFloatingLayoutParams;
}
@Override
public void onCreate() {
super.onCreate();
this.monitorActivityLifecycle();
}
/**
* Listener program Activity Declaration period
*/
private void monitorActivityLifecycle() {
this.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityStarted(Activity activity) {
if (isRunningForeground()){
mIntent = new Intent(MyApplication.this, FloatingActionService.class);
startService(mIntent);
}
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
if (!isRunningForeground()){
stopService(mIntent);
}
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
});
}
/**
* Judge that the application runs in the front and back office
*/
public boolean isRunningForeground() {
ActivityManager activityManager = (ActivityManager) this.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appProcessInfos = activityManager.getRunningAppProcesses(); // Enumerate processes
for (ActivityManager.RunningAppProcessInfo appProcessInfo : appProcessInfos) {
if (appProcessInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
if (appProcessInfo.processName.equals(this.getApplicationInfo().processName)) {
Log.d(TAG, "EntryActivity isRunningForeGround");
return true;
}
}
}
Log.d(TAG, "EntryActivity isRunningBackGround");
return false;
}
}
FloatingActionService. java-Service to which the floating window is attached
package com.example.qxb_810.floatbuttondemo.service;
import android.app.Service;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.Gravity;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.Toast;
import com.example.qxb_810.floatbuttondemo.application.MyApplication;
import com.example.qxb_810.floatbuttondemo.button.FloatingActionButton;
/**
* create 2018/12/1 13:34
* desc Floating button Service
*/
public class FloatingActionService extends Service {
private WindowManager mWindowManager;
private FloatingActionButton mButton;
private int mScreenWidth;
private int mScreenHeight;
@Override
public void onCreate() {
super.onCreate();
this.initView();
this.initEvent();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
this.mWindowManager.removeViewImmediate(this.mButton);
}
/**
* Add button
*/
private void initView() {
// Pass WindowManager To add a floating window
this.mWindowManager = (WindowManager) this.getApplicationContext().getSystemService(WINDOW_SERVICE);
Display display = this.mWindowManager.getDefaultDisplay();
DisplayMetrics metrics = new DisplayMetrics();
display.getMetrics(metrics);
this.mScreenWidth = metrics.widthPixels;
this.mScreenHeight = metrics.heightPixels;
WindowManager.LayoutParams params = ((MyApplication) this.getApplication()).getmFloatingLayoutParams();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mFloatingLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
} else {
mFloatingLayoutParams.type = WindowManager.LayoutParams.TYPE_TOAST;
}
params.format = PixelFormat.RGBA_8888;
params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
params.gravity = Gravity.LEFT | Gravity.TOP; // The upper left is the coordinate point
// Settings View Size
params.height = 50;
params.width = 50;
// Settings View Coordinate position
params.x = 0;
params.y = mScreenHeight / 2;
this.mButton = FloatingActionButton.getInstance(this);
this.mWindowManager.addView(mButton, params);
}
/**
* Initialization event
*/
private void initEvent() {
this.mButton.setOnClickListener(v -> {
// Project is added here List pop-up window And remove the list pop-up window, but the initialization of the list pop-up window is not here
Toast.makeText(this, " Clicked Button", Toast.LENGTH_SHORT).show();
});
}
}
FloatingActionButton-Popup button
package com.example.qxb_810.floatbuttondemo.button;
import android.content.Context;
import android.graphics.Canvas;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import com.example.qxb_810.floatbuttondemo.R;
import com.example.qxb_810.floatbuttondemo.application.MyApplication;
import static android.content.Context.WINDOW_SERVICE;
/**
* create 2018/12/1 13:35
* desc Custom float button -- It's written here casually 1 Layout files
*/
public class FloatingActionButton extends FrameLayout {
private Context mContext;
private float mStartPointX;
private float mStartPointY;
private WindowManager mWindowManager;
private WindowManager.LayoutParams mFloatingLayoutParams;
private boolean isIntercept = false;
private int mStatusHeight;
public static FloatingActionButton getInstance(Context context) {
FloatingActionButton button = new FloatingActionButton(context);
return button;
}
public FloatingActionButton(Context context) {
this(context, null);
}
public FloatingActionButton(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, -1);
}
public FloatingActionButton(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
initView();
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
/**
* Initialization View
*/
private void initView(){
// Add casually 1 A simple one View Layout as a floating window
View view = LayoutInflater.from(mContext).inflate(R.layout.layout_floating_button, null, false);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,LinearLayout.LayoutParams.WRAP_CONTENT);
this.addView(view, params);
this.mWindowManager = (WindowManager) getContext().getApplicationContext().getSystemService(WINDOW_SERVICE);
this.mFloatingLayoutParams = ((MyApplication) getContext().getApplicationContext()).getmFloatingLayoutParams();
this.mStatusHeight = getStatusHeight(mContext);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// Gets the coordinates relative to the screen, that is, starting from the upper left corner of the screen
float rawX = event.getRawX();
float rawY = event.getRawY() - mStatusHeight; //statusHeight Is the height of the system status bar
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
this.mStartPointX = event.getX();
this.mStartPointY = event.getY();
isIntercept = false;
break;
case MotionEvent.ACTION_MOVE:
mFloatingLayoutParams.x = (int)(rawX - mStartPointX);
mFloatingLayoutParams.y = (int)(rawY - mStartPointY);
this.mWindowManager.updateViewLayout(this, mFloatingLayoutParams);
isIntercept = true;
break;
case MotionEvent.ACTION_UP:
mFloatingLayoutParams.x = 0; // The strategy here is to let go of adsorption to the left by default If it needs to be adsorbed to the right side, change it to mFloatingLayoutParams.x = mScreenWidth; mScreenWidth Is the screen width, if you don't want to absorb it, comment this 1 Sentence is sufficient
this.mWindowManager.updateViewLayout(this, mFloatingLayoutParams);
break;
}
return isIntercept ? isIntercept : super.onTouchEvent(event);
}
/**
* Height of status bar
*/
public static int getStatusHeight(Context context) {
int statusHeight = -1;
try {
Class clazz = Class.forName("com.android.internal.R$dimen"); // Getting instances using reflection
Object object = clazz.newInstance();
int height = Integer.parseInt(clazz.getField("status_bar_height")
.get(object).toString());
statusHeight = context.getResources().getDimensionPixelSize(height);
} catch (Exception e) {
e.printStackTrace();
}
return statusHeight;
}
}
Layout file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:background="@color/colorPrimary">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="12312313131313"
android:textSize="20sp" />
</LinearLayout>