问题
The following image is a window of the app https://play.google.com/store/apps/details?id=jp.snowlife01.android.clipboard. i named it as main window.
It's seem that main window is not a normal window. it's on the top of other windows, the main window can't be moved.
A small point icon will be displayed when I click the minimized button in the main window, the small point icon can be moved, the main window can be restored when I click the small point icon.
I think the small point icon is a SYSTEM_ALERT_WINDOW
, but how about the main window?
Image
回答1:
You can create a float view using WindowManager
without granting any permissions. I have developed a sample code that you can use it simply.
FloatViewManager.java:
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.graphics.PixelFormat;
import android.os.Build;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
import static android.content.Context.WINDOW_SERVICE;
/**
* Created by aminography on 11/1/2018.
*/
public class FloatViewManager {
private static boolean mIsFloatViewShowing;
private Activity mActivity;
private WindowManager mWindowManager;
private View mFloatView;
private WindowManager.LayoutParams mFloatViewLayoutParams;
private boolean mFloatViewTouchConsumedByMove = false;
private int mFloatViewLastX;
private int mFloatViewLastY;
private int mFloatViewFirstX;
private int mFloatViewFirstY;
@SuppressLint("InflateParams")
public FloatViewManager(Activity activity) {
mActivity = activity;
LayoutInflater inflater = LayoutInflater.from(activity);
mFloatView = inflater.inflate(R.layout.float_view_layout, null);
TextView textView = mFloatView.findViewById(R.id.textView);
textView.setText("I'm a float view!");
ImageButton imageButton = mFloatView.findViewById(R.id.imageButton);
imageButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismissFloatView();
}
});
mFloatView.setOnClickListener(mFloatViewOnClickListener);
mFloatView.setOnTouchListener(mFloatViewOnTouchListener);
mFloatViewLayoutParams = new WindowManager.LayoutParams();
mFloatViewLayoutParams.format = PixelFormat.TRANSLUCENT;
mFloatViewLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mFloatViewLayoutParams.type = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
: WindowManager.LayoutParams.TYPE_TOAST;
mFloatViewLayoutParams.gravity = Gravity.CENTER;
mFloatViewLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
mFloatViewLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
}
public void dismissFloatView() {
if (mIsFloatViewShowing) {
mIsFloatViewShowing = false;
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (mWindowManager != null) {
mWindowManager.removeViewImmediate(mFloatView);
}
}
});
}
}
public void showFloatView() {
if (!mIsFloatViewShowing) {
mIsFloatViewShowing = true;
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (!mActivity.isFinishing()) {
mWindowManager = (WindowManager) mActivity.getSystemService(WINDOW_SERVICE);
if (mWindowManager != null) {
mWindowManager.addView(mFloatView, mFloatViewLayoutParams);
}
}
}
});
}
}
@SuppressWarnings("FieldCanBeLocal")
private View.OnClickListener mFloatViewOnClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(mActivity, "Float view is clicked!", Toast.LENGTH_SHORT).show();
}
});
}
};
@SuppressWarnings("FieldCanBeLocal")
private View.OnTouchListener mFloatViewOnTouchListener = new View.OnTouchListener() {
@SuppressLint("ClickableViewAccessibility")
@TargetApi(Build.VERSION_CODES.FROYO)
@Override
public boolean onTouch(View v, MotionEvent event) {
WindowManager.LayoutParams prm = mFloatViewLayoutParams;
int totalDeltaX = mFloatViewLastX - mFloatViewFirstX;
int totalDeltaY = mFloatViewLastY - mFloatViewFirstY;
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mFloatViewLastX = (int) event.getRawX();
mFloatViewLastY = (int) event.getRawY();
mFloatViewFirstX = mFloatViewLastX;
mFloatViewFirstY = mFloatViewLastY;
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_MOVE:
int deltaX = (int) event.getRawX() - mFloatViewLastX;
int deltaY = (int) event.getRawY() - mFloatViewLastY;
mFloatViewLastX = (int) event.getRawX();
mFloatViewLastY = (int) event.getRawY();
if (Math.abs(totalDeltaX) >= 5 || Math.abs(totalDeltaY) >= 5) {
if (event.getPointerCount() == 1) {
prm.x += deltaX;
prm.y += deltaY;
mFloatViewTouchConsumedByMove = true;
if (mWindowManager != null) {
mWindowManager.updateViewLayout(mFloatView, prm);
}
} else {
mFloatViewTouchConsumedByMove = false;
}
} else {
mFloatViewTouchConsumedByMove = false;
}
break;
default:
break;
}
return mFloatViewTouchConsumedByMove;
}
};
}
float_view_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">
<android.support.v7.widget.AppCompatTextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ff4444"
android:padding="32dp"
android:text="text"
android:textColor="#ffffff"
android:textSize="24sp" />
<android.support.v7.widget.AppCompatImageButton
android:id="@+id/imageButton"
style="@style/Base.Widget.AppCompat.Button.Borderless"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="top|right"
android:src="@drawable/ic_close_white_24dp" />
</FrameLayout>
MainActivity.java:
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;
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_CODE_DRAW_OVERLAY_PERMISSION = 5;
private FloatViewManager mFloatViewManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mFloatViewManager = new FloatViewManager(MainActivity.this);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (checkDrawOverlayPermission()) {
mFloatViewManager.showFloatView();
}
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_DRAW_OVERLAY_PERMISSION) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.canDrawOverlays(this)) {
mFloatViewManager.showFloatView();
}
}
}
private boolean checkDrawOverlayPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, REQUEST_CODE_DRAW_OVERLAY_PERMISSION);
return false;
} else {
return true;
}
}
}
Visual Result:
回答2:
would assume, that it's of type TYPE_APPLICATION_OVERLAY
.
possibly combined with android:theme="@android:style/Theme.Dialog"
.
and it requires Manifest.permission.SYSTEM_ALERT_WINDOW
.
unless installing from the Play Store, you'd have to manually enable the permission:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION));
}
see the documentation (there's really not too many which would suit).
来源:https://stackoverflow.com/questions/52980478/how-to-create-a-floating-window-like-clipboard-pro-app