解决SurfaceView设置透明造成覆盖其他组件的替代方案

时间:2023-03-10 05:19:41
解决SurfaceView设置透明造成覆盖其他组件的替代方案

之前在项目里面绘制摇杆圆盘使用SurfaceView来实现,同时设置SurfaceView透明,但是这样会造成SurfaceView的组件会覆盖其他的组件,一般情况没有关系,而不一般的情况就是有类似上拉和下拉功能,需要拉出的布局位于最顶部,覆盖其他的组件,而由于之前设置SurfaceView透明使用是:

setZOrderOnTop(true);
mHolder.setFormat(PixelFormat.TRANSPARENT);//设置背景透明

这样会使SurfaceView位于布局的最顶部,即使你设置了bringToTop也没有用,解决这类问题有两种方案:

第一种:使用PopupWindow或者类似浮动小窗体的功能,我测试过他们不会被SurfaceView覆盖,但是这种方案只适用于点击实现组件的弹出,不能实现上拉拖动来显示组建(这里的上拉布局是指有一部分在可见窗体之外),不幸的是项目里面需求指定要上拉,而不是点击来实现组件的弹出功能,所以这种方案不适用于我的情况。所以我就找到了第二种情况。

第二种:

自己写一个类来继承View,然后利用onTouchEvent和OnDraw这两个方法来实现绘制图像,具体的见代码:

Jockey_Left类:继承View

package com.example.test;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View; public class Jockey_Left extends View{ private Bitmap bitmap;
public Point mRockerPosition;
public Point mCtrlPoint;
private int mRudderRadius = 25;
public int mWheelRadius = 80;
private float scale;
public int isHide = 0;
private Paint mPaint;
public Jockey_Left(Context context) {
super(context);
} public Jockey_Left(Context context, AttributeSet attrs) {
super(context, attrs);
} public void init(float scale){
this.scale = scale;
mRudderRadius = dip2px(15);
mWheelRadius = dip2px(45);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.print2);
bitmap = Bitmap.createScaledBitmap(bitmap, mRudderRadius*2, mRudderRadius*2, false);
mCtrlPoint = new Point((mRudderRadius + mWheelRadius), (mRudderRadius + mWheelRadius));
mPaint = new Paint();
mPaint.setAntiAlias(true);
mRockerPosition = new Point(mCtrlPoint);
}
public int dip2px(float dpValue) {
return (int)(dpValue * scale + 0.5f);
}
public Canvas canvas;
@Override
protected void onDraw(Canvas canvas) {
if (bitmap != null) {
//canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);//�����Ļ
this.canvas = canvas;
canvas.drawBitmap(bitmap, mRockerPosition.x - mRudderRadius, mRockerPosition.y - mRudderRadius, mPaint);
}
super.onDraw(canvas);
} int len;
@Override
public boolean onTouchEvent(MotionEvent event) {
try {
if (isHide == 0) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
len = MathUtils.getLength(mCtrlPoint.x, mCtrlPoint.y, event.getX(), event.getY());
//如果屏幕接触点不在摇杆挥动范围内,则不处理
if(len > mWheelRadius) {
return true;
}
break;
case MotionEvent.ACTION_MOVE:
len = MathUtils.getLength(mCtrlPoint.x, mCtrlPoint.y, event.getX(), event.getY());
if(len <= mWheelRadius) {
//如果手指在摇杆活动范围内,则摇杆处于手指触摸位置
mRockerPosition.set((int)event.getX(), (int)event.getY());
}else{
//设置摇杆位置,使其处于手指触摸方向的 摇杆活动范围边缘
mRockerPosition = MathUtils.getBorderPoint(mCtrlPoint, new Point((int)event.getX(), (int)event.getY()), mWheelRadius);
}
break;
case MotionEvent.ACTION_UP:
mRockerPosition = new Point(mCtrlPoint);
break;
}
Thread.sleep(40);
}else {
Thread.sleep(200);
}
} catch (Exception e) { }
invalidate();//更新布局
return true;
} }

使用Touch来检测手的触摸点,然后更新中心圆点的位置,再调用invalidate();来更新VIew的背景,实现OnDraw方法,来绘制图像

AppSingleRocker类:作为对比我用一个类继承SurfaceView然后实现背景透明

package com.example.test;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.PorterDuff.Mode;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.SurfaceHolder.Callback; @SuppressLint("ViewConstructor")
public class AppSingleRocker extends SurfaceView implements Callback{
private SurfaceHolder mHolder;
private Paint mPaint;
public Point mRockerPosition; // 摇杆位置
private Point mCtrlPoint;// 摇杆起始位置
private int mRudderRadius = 25;// 摇杆半径
private int mWheelRadius = 80;// 摇杆活动范围半径
private int batmapHW = 160;
private int batmap2HW = 40;
int isHide = 0;
Bitmap bitmap,bitmap2;
float scale;
private SingleRudderListener listener = null; //事件回调接口
public static final int ACTION_RUDDER = 1, ACTION_ATTACK_DEVICEMOVE = 2, ACTION_STOP = 3, ACTION_ATTACK_CAMERAMOVE = 4;
public AppSingleRocker(Context context, AttributeSet attrs) {
super(context, attrs);
this.setKeepScreenOn(true);
scale = context.getResources().getDisplayMetrics().density;
mRudderRadius = dip2px(15);// 摇杆半径
mWheelRadius = dip2px(45);// 摇杆活动范围半径
mCtrlPoint = new Point((mRudderRadius + mWheelRadius), (mRudderRadius + mWheelRadius));// 摇杆起始位置
batmapHW = (mWheelRadius+mRudderRadius) * 2;
batmap2HW = mRudderRadius * 2;
mHolder = getHolder();
mHolder.addCallback(this);
mPaint = new Paint();
mPaint.setAntiAlias(true);
setFocusable(true);
setFocusableInTouchMode(true);
mRockerPosition = new Point(mCtrlPoint);
setZOrderOnTop(true);
mHolder.setFormat(PixelFormat.TRANSPARENT);//设置背景透明
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.joystick_l_pad);
bitmap = Bitmap.createScaledBitmap(bitmap, batmapHW, batmapHW, false);
bitmap2 = BitmapFactory.decodeResource(getResources(), R.drawable.print2);
bitmap2 = Bitmap.createScaledBitmap(bitmap2,batmap2HW,batmap2HW, false);
} //获取屏幕的宽度,高度和密度以及dp / px
public void getDisplayMetrics() { }
public int dip2px(float dpValue) {
return (int)(dpValue * scale + 0.5f);
}
//回调接口
public interface SingleRudderListener {
void onSteeringWheelChanged(int action,int angle);
} //设置回调接口
public void setSingleRudderListener(SingleRudderListener rockerListener) {
listener = rockerListener;
} int len;
@Override
public boolean onTouchEvent(MotionEvent event) {
try {
if (isHide == 0) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
len = MathUtils.getLength(mCtrlPoint.x, mCtrlPoint.y, event.getX(), event.getY());
//如果屏幕接触点不在摇杆挥动范围内,则不处理
if(len > mWheelRadius) {
return true;
}
break;
case MotionEvent.ACTION_MOVE:
len = MathUtils.getLength(mCtrlPoint.x, mCtrlPoint.y, event.getX(), event.getY());
if(len <= mWheelRadius) {
//如果手指在摇杆活动范围内,则摇杆处于手指触摸位置
mRockerPosition.set((int)event.getX(), (int)event.getY());
}else{
//设置摇杆位置,使其处于手指触摸方向的 摇杆活动范围边缘
mRockerPosition = MathUtils.getBorderPoint(mCtrlPoint, new Point((int)event.getX(), (int)event.getY()), mWheelRadius);
}
if(listener != null) {
float radian = MathUtils.getRadian(mCtrlPoint, new Point((int)event.getX(), (int)event.getY()));
listener.onSteeringWheelChanged(ACTION_RUDDER, getAngleCouvert(radian));
}
break;
case MotionEvent.ACTION_UP:
mRockerPosition = new Point(mCtrlPoint);
if (listener != null) {
listener.onSteeringWheelChanged(ACTION_STOP, 0);
}
break;
}
Canvas_OK();
Thread.sleep(60);
}else {
Thread.sleep(200);
}
} catch (Exception e) { }
return true;
} public void singleRockerUp(){
mRockerPosition = new Point(mCtrlPoint);
listener.onSteeringWheelChanged(ACTION_STOP, 0);
}
//获取摇杆偏移角度 0-360°
private int getAngleCouvert(float radian) {
int tmp = (int)Math.round(radian/Math.PI * 180);
if(tmp < 0) {
return -tmp;
}else{
return 180 + (180 - tmp);
}
} public void surfaceCreated(SurfaceHolder holder) {
Canvas_OK();
} public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) { } public void surfaceDestroyed(SurfaceHolder holder) { }
// 设置是否隐藏
public void Hided(int opt)
{
isHide = opt;
Canvas_OK();
} public void setHided(int opt){
isHide = opt;
}
/**
* 返回圆盘是否隐藏
* @return
*/
public int getIsHided(){
return isHide;
}
//绘制图像
public void Canvas_OK(){
Canvas canvas = null;
try {
if (isHide == 0) {
canvas = mHolder.lockCanvas();
canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);//清除屏幕
canvas.drawBitmap(bitmap, mCtrlPoint.x - mWheelRadius - mRudderRadius, mCtrlPoint.y - mWheelRadius - mRudderRadius, mPaint);
canvas.drawBitmap(bitmap2, mRockerPosition.x - mRudderRadius, mRockerPosition.y - mRudderRadius, mPaint);
}else {
canvas = mHolder.lockCanvas();
canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);//清除屏幕
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if(canvas != null) {
mHolder.unlockCanvasAndPost(canvas);
}
}
}
}

activity_main这个是我的布局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" > <com.example.test.Jockey_Left
android:layout_width="120dp"
android:layout_height="120dp"
android:id="@+id/left_jockey"
android:background="@drawable/joystick_l_pad"
></com.example.test.Jockey_Left> <Button
android:layout_width="60dp"
android:layout_height="60dp"
/> <com.example.test.AppSingleRocker
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
></com.example.test.AppSingleRocker>
<Button
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true"
/>
</RelativeLayout>

MainActivity这个是显示的Activity

package com.example.test;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu; public class MainActivity extends Activity { Jockey_Left jockey_left; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
jockey_left = (Jockey_Left)findViewById(R.id.left_jockey);
jockey_left.init(getResources().getDisplayMetrics().density);
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
} }

显示效果如下图所示:

解决SurfaceView设置透明造成覆盖其他组件的替代方案

看效果图明显:

1.左上角的Button没有被覆盖,而右下角的Button被覆盖掉

2.这两个圆盘都是使用相同的图片,但是右下角有明显的锯齿,而左上角的没有

3.唯一不足的就是左上角流畅度不如右下角的

所以如果绘图区域要求矩形最好选用SurfaceVIew,因为这样提高程序流畅度,如果 要求圆盘而且不能覆盖其他的组件,选用继承View,重新实现。

源码下载地址:http://download.csdn.net/detail/jwzhangjie/5816135 这里同时实现了圆盘的功能