我正在尝试使用android.permission.SYSTEM_ALERT_WINDOW
实现一个可在其他应用程序之上绘制的应用程序。它在屏幕上显示一条直线(ImageView
),可以拖动(一个手指),缩放(捏缩放)和旋转(两个手指)。
在此chatHead code的帮助下,我能够在屏幕上拖动ImageView
。但是我无法在此服务的ImageView
上应用缩放和旋转,但是缩放和旋转在应用于扩展了AppCompatActivity
的常规活动(使用来自this question的第二个答案)时是完美的。
我在下面提供完整的代码以供参考。
HomeActivity.java
我从HomeActivity
呼叫Service(LineService)
和AppCompatActivity(LineActivity)
。我无法从viewTransformation()
的{{1}}实现功能LineActivity
,因为它是LineService
。还有其他方法可以在Service
上执行这些转换吗?
LineService
activity_home.xml
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.view.View;
import android.widget.Button;
public class HomeActivity extends Activity {
public final static int REQUEST_CODE = 5463&0xffffff00;
Button buttonLineService;
Button buttonLineActivity;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
buttonLineService = findViewById(R.id.buttonLineService);
buttonLineActivity = findViewById(R.id.buttonLineActivity);
checkDrawOverlayPermission();
buttonLineService.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(getApplicationContext(), LineService.class);
startService(i);
finish();
}
});
buttonLineActivity.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i = new Intent(getApplicationContext(), LineActivity.class);
startActivity(i);
finish();
}
});
}
public void checkDrawOverlayPermission() {
/** check if we already have permission to draw over other apps */
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(HomeActivity.this)) {
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O &&
// (AppOpsManager.OPSTR_SYSTEM_ALERT_WINDOW.equals(op) &&
// packageName.equals(mContext.getPackageName()))) {
/** if not construct intent to request permission */
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
/** request permission via start activity for result */
startActivityForResult(intent, REQUEST_CODE);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
/** check if received result code
is equal our requested code for draw permission */
if (requestCode == REQUEST_CODE) {
// ** if so check once again if we have permission */
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && Settings.canDrawOverlays(this)) {
// continue here - permission was granted
}
}
}
}
LineService.java
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".HomeActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="100dp"
android:orientation="vertical"
android:layout_centerHorizontal="true">
<Button
android:id="@+id/buttonPermission"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Provide Overlay Permission"/>
<Button
android:id="@+id/buttonLineService"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Open Line Service"/>
<Button
android:id="@+id/buttonLineActivity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Open Line Activity"/>
</LinearLayout>
</RelativeLayout>
LineActivity.java
import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.IBinder;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
public class LineService extends Service {
private WindowManager windowManager;
private ImageView line;
@Override
public IBinder onBind(Intent intent) {
// Not used
return null;
}
@SuppressLint("ClickableViewAccessibility")
@Override
public void onCreate() {
super.onCreate();
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
line = new ImageView(this);
line.setImageResource(R.drawable.line);
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.CENTER;
params.x = 0;
params.y = 100;
windowManager.addView(line, params);
line.setOnTouchListener(new View.OnTouchListener() {
private int initialX;
private int initialY;
private float initialTouchX;
private float initialTouchY;
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getPointerCount() == 2) {
} else {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
initialX = params.x;
initialY = params.y;
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
return true;
case MotionEvent.ACTION_UP:
return true;
case MotionEvent.ACTION_MOVE:
params.x = initialX + (int) (event.getRawX() - initialTouchX);
params.y = initialY + (int) (event.getRawY() - initialTouchY);
windowManager.updateViewLayout(line, params);
return true;
}
}
return false;
}
});
}
@Override
public void onDestroy() {
super.onDestroy();
if (line != null) windowManager.removeView(line);
}
}
activity_test_line.xml
import android.annotation.SuppressLint;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.PointF;
import android.os.Build;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
public class LineActivity extends AppCompatActivity {
private ImageView line;
float[] lastEvent = null;
float d = 0f;
float newRot = 0f;
private boolean isZoomAndRotate;
private boolean isOutSide;
private static final int NONE = 0;
private static final int DRAG = 1;
private static final int ZOOM = 2;
private int mode = NONE;
private PointF start = new PointF();
private PointF mid = new PointF();
float oldDist = 1f;
private float xCoOrdinate, yCoOrdinate;
@SuppressLint("ClickableViewAccessibility")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_line);
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY : WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.CENTER;
params.x = 0;
params.y = 100;
line = findViewById(R.id.imageview_trash);
line.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
ImageView view = (ImageView) v;
view.bringToFront();
viewTransformation(view, event);
return true;
}
});
}
private void viewTransformation(View view, MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
xCoOrdinate = view.getX() - event.getRawX();
yCoOrdinate = view.getY() - event.getRawY();
start.set(event.getX(), event.getY());
isOutSide = false;
mode = DRAG;
lastEvent = null;
break;
case MotionEvent.ACTION_POINTER_DOWN:
oldDist = spacing(event);
if (oldDist > 10f) {
midPoint(mid, event);
mode = ZOOM;
}
lastEvent = new float[4];
lastEvent[0] = event.getX(0);
lastEvent[1] = event.getX(1);
lastEvent[2] = event.getY(0);
lastEvent[3] = event.getY(1);
d = rotation(event);
break;
case MotionEvent.ACTION_UP:
isZoomAndRotate = false;
if (mode == DRAG) {
float x = event.getX();
float y = event.getY();
}
case MotionEvent.ACTION_OUTSIDE:
isOutSide = true;
mode = NONE;
lastEvent = null;
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
lastEvent = null;
break;
case MotionEvent.ACTION_MOVE:
if (!isOutSide) {
if (mode == DRAG) {
isZoomAndRotate = false;
view.animate().x(event.getRawX() + xCoOrdinate).y(event.getRawY() + yCoOrdinate).setDuration(0).start();
}
if (mode == ZOOM && event.getPointerCount() == 2) {
float newDist1 = spacing(event);
if (newDist1 > 10f) {
float scale = newDist1 / oldDist * view.getScaleX();
view.setScaleX(scale);
view.setScaleY(scale);
}
if (lastEvent != null) {
newRot = rotation(event);
view.setRotation((float) (view.getRotation() + (newRot - d)));
}
}
}
break;
}
}
private float rotation(MotionEvent event) {
double delta_x = (event.getX(0) - event.getX(1));
double delta_y = (event.getY(0) - event.getY(1));
double radians = Math.atan2(delta_y, delta_x);
return (float) Math.toDegrees(radians);
}
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return (int) Math.sqrt(x * x + y * y);
}
private void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
@Override
public void onBackPressed() {
super.onBackPressed();
Intent i = new Intent(getApplicationContext(), HomeActivity.class);
startActivity(i);
finish();
}
}
line.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".LineActivity">
<ImageView
android:id="@+id/imageview_trash"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/line" />
</LinearLayout>