之前在项目里面绘制摇杆圆盘使用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; } }
显示效果如下图所示:
看效果图明显:
1.左上角的Button没有被覆盖,而右下角的Button被覆盖掉
2.这两个圆盘都是使用相同的图片,但是右下角有明显的锯齿,而左上角的没有
3.唯一不足的就是左上角流畅度不如右下角的
所以如果绘图区域要求矩形最好选用SurfaceVIew,因为这样提高程序流畅度,如果 要求圆盘而且不能覆盖其他的组件,选用继承View,重新实现。
源码下载地址:http://download.csdn.net/detail/jwzhangjie/5816135 这里同时实现了圆盘的功能