次の画像はアプリのウィンドウです https://play.google.com/store/apps/details?id=jp.snowlife01.Android.clipboard 。メインウィンドウと名付けました。
メインウィンドウは通常のウィンドウではないようです。他のウィンドウの上にあるため、メインウィンドウを移動することはできません。
メインウィンドウの最小化されたボタンをクリックすると小さなポイントアイコンが表示され、小さなポイントアイコンを移動したり、小さなポイントアイコンをクリックするとメインウィンドウを復元したりできます。
小さな点のアイコンはSYSTEM_ALERT_WINDOW
だと思いますが、メインウィンドウはどうですか?
画像
権限を付与せずにWindowManager
を使用してフロートビューを作成できます。簡単に使えるサンプルコードを開発しました。
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;
}
}
}
視覚的結果:
タイプがTYPE_APPLICATION_OVERLAY
であると想定します。
Android:theme="@Android:style/Theme.Dialog"
と組み合わせる可能性があります。
Manifest.permission.SYSTEM_ALERT_WINDOW
が必要です。
playストアからインストールしない限り、手動で権限を有効にする必要があります。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION));
}
ドキュメント を参照してください(実際には、適切なものはそれほど多くありません)。