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>

Related articles: