public class PaddyMenuView extends GLSurfaceView { public PaddyMenuView(Context context) { super(context); } public PaddyMenuView(Context context, AttributeSet attrs) { super(context, attrs); } @Override public void surfaceCreated(SurfaceHolder holder) { m_Paint = new Paint(Paint.ANTI_ALIAS_FLAG); m_Paint.setColor(Color.GREEN); m_Paint.setTextSize(60); m_Paint.setStyle(Paint.Style.STROKE); m_Paint.setStrokeWidth(2f); m_Paint.setDither(true);//防止文字抖动 m_Paint.setPathEffect(new CornerPathEffect(10));//增加圆角 m_DPoint = new PointF(0, 0); // m_MenuThd = new MenuThread("PaddingMenuThread"); m_MenuThd.start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { if (m_MenuThd != null) { m_MenuThd.setStopCmd(); m_MenuThd.Join(); m_MenuThd = null; } } @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN && event.getActionMasked() == MotionEvent.ACTION_DOWN) { //判断落点,处理点击事件 float x = event.getX(); float y = event.getY(); if (!(y >= getMeasuredHeight() / 2f - m_Radius && y <= getMeasuredHeight() / 2f + m_Radius)) return true; if (x >= m_POffset - m_Radius && x <= m_POffset + m_Radius) { m_Status = 0; if (m_OpenMenu.get()) m_OpenMenu.set(false); else m_OpenMenu.set(true); m_DPoint.set(x, y); rippleAnimate(); } if (!(m_PMoveSet == m_POffset || m_PMoveSet == 4 * m_POffset)) return true; if (m_PMoveSet != 4 * m_POffset) return true; if (x > 2 * m_POffset - m_Radius && x < 2 * m_POffset + m_Radius) { m_Status = 1; rippleAnimate(); } else if (x > 3 * m_POffset - m_Radius && x < 3 * m_POffset + m_Radius) { m_Status = 2; rippleAnimate(); } else if (x > 4 * m_POffset - m_Radius && x < 4 * m_POffset + m_Radius) { m_Status = 3; rippleAnimate(); } m_DPoint.set(x, y); } return true; } private class MenuThread extends ThreadEx { public MenuThread(String threadName) { super(threadName); } public void setStopCmd() { this.stopCmd = true; } @Override public void run() { super.run(); for (; ; ) { if (stopCmd) break; Canvas c = PaddyMenuView.this.getHolder().lockCanvas(); if (c != null) { m_Paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR)); m_Paint.setMaskFilter(null); c.drawPaint(m_Paint); m_Paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP)); c.drawColor(Color.DKGRAY); // 设置光源的方向 float[] direction = new float[]{1, 1, 1}; //环境光亮度 float light = 0.8f; //反射等级 float specular = 6f; //模糊级别 float blur = 0f; m_Paint.setMaskFilter(new EmbossMaskFilter(direction, light, specular, blur)); // m_Lock.lock(); drawMenuItem(c); drawInfoTxt(c); drawRipple(c); m_Lock.unlock(); PaddyMenuView.this.getHolder().unlockCanvasAndPost(c); } if (!ThreadEx.Sleep(8)) setStopCmd(); m_Lock.lock(); if (m_OpenMenu.get()) { //检查偏移量越界时的跳动现象 if (m_PMoveSet + m_OffsetSpeed <= 4 * m_POffset) m_PMoveSet += m_OffsetSpeed; else if (m_PMoveSet + m_OffsetSpeed > 4 * m_POffset) m_PMoveSet += 4 * m_POffset - m_PMoveSet; } else { //检查偏移量越界时的跳动现象 if (m_PMoveSet - m_OffsetSpeed >= m_POffset) m_PMoveSet -= m_OffsetSpeed; else if (m_PMoveSet - m_POffset < m_POffset) m_PMoveSet -= m_PMoveSet - m_POffset; } m_Lock.unlock(); } } private boolean stopCmd; } private void drawMenuItem(Canvas c) { //始终画第一个item if (m_PMoveSet <= 4 * m_POffset) { c.save(); c.drawCircle(1f * m_POffset, getHeight() / 2f, m_Radius, m_Paint); Rect rect = new Rect(m_POffset - m_Radius, getHeight() / 2 - m_Radius, m_POffset + m_Radius, getHeight() / 2 + m_Radius); Paint.FontMetricsInt fm = m_Paint.getFontMetricsInt(); int baseLine = rect.top + ((rect.bottom - rect.top) - (fm.bottom - fm.top)) / 2 - fm.top; m_Paint.setTextAlign(Paint.Align.CENTER); c.drawText("M", rect.centerX(), baseLine, m_Paint); c.restore(); } //画第二个item if (m_PMoveSet >= 2 * m_POffset) { c.save(); c.drawCircle(2f * m_POffset, getHeight() / 2f, m_Radius, m_Paint); Rect rect = new Rect(2 * m_POffset - m_Radius, getHeight() / 2 - m_Radius, 2 * m_POffset + m_Radius, getHeight() / 2 + m_Radius); Paint.FontMetricsInt fm = m_Paint.getFontMetricsInt(); int baseLine = rect.top + ((rect.bottom - rect.top) - (fm.bottom - fm.top)) / 2 - fm.top; m_Paint.setTextAlign(Paint.Align.CENTER); c.drawText("1", rect.centerX(), baseLine, m_Paint); c.restore(); } //画第三个item if (m_PMoveSet >= 3 * m_POffset) { c.save(); c.drawCircle(3f * m_POffset, getHeight() / 2f, m_Radius, m_Paint); Rect rect = new Rect(3 * m_POffset - m_Radius, getHeight() / 2 - m_Radius, 3 * m_POffset + m_Radius, getHeight() / 2 + m_Radius); Paint.FontMetricsInt fm = m_Paint.getFontMetricsInt(); int baseLine = rect.top + ((rect.bottom - rect.top) - (fm.bottom - fm.top)) / 2 - fm.top; m_Paint.setTextAlign(Paint.Align.CENTER); c.drawText("2", rect.centerX(), baseLine, m_Paint); c.restore(); } //画运动中的item(运动到最后,则既是第四个item) if (m_PMoveSet > m_POffset) { c.save(); float count = (float) m_PMoveSet / (float) m_POffset; int i = -1; float rDegree = 0; if (count >= 1 && count < 2) { i = 1; rDegree = 360f * ((m_PMoveSet - 1f * m_POffset) / ((float) m_POffset)); } if (count >= 2 && count < 3) { i = 2; rDegree = 360f * ((m_PMoveSet - 2f * m_POffset) / ((float) m_POffset)); } if (count >= 3 && count <= 4) { i = 3; rDegree = 360f * ((m_PMoveSet - 3f * m_POffset) / ((float) m_POffset)); } c.rotate(rDegree, m_PMoveSet, getHeight() / 2f); c.drawCircle(m_PMoveSet, getHeight() / 2f, m_Radius, m_Paint); // Rect rect = new Rect(m_PMoveSet - m_Radius, getHeight() / 2 - m_Radius, m_PMoveSet + m_Radius, getHeight() / 2 + m_Radius); Paint.FontMetricsInt fmi = m_Paint.getFontMetricsInt(); int baseLine1 = rect.top + ((rect.bottom - rect.top) - (fmi.bottom - fmi.top)) / 2 - fmi.top; m_Paint.setTextAlign(Paint.Align.CENTER); if (i != -1) c.drawText(String.valueOf(i), rect.centerX(), baseLine1, m_Paint); c.restore(); } } private void drawInfoTxt(Canvas c) { //center txt c.save(); String str = ""; if (m_PMoveSet == m_POffset) str = "close"; else if (m_PMoveSet == 4 * m_POffset && m_Status != 0) str = String.valueOf(m_Status); //make text in center Rect targetRect = new Rect(0, getHeight() / 2 - m_Radius, getWidth(), getHeight() / 2 + m_Radius); Paint.FontMetricsInt fm = m_Paint.getFontMetricsInt(); int baseLine = (targetRect.top + ((targetRect.bottom - targetRect.top) - (fm.bottom - fm.top)) / 2 - fm.top); m_Paint.setTextAlign(Paint.Align.RIGHT); c.drawText(str, targetRect.width(), baseLine, m_Paint); c.restore(); } private void drawRipple(Canvas c) { m_Paint.setStyle(Paint.Style.FILL); m_Paint.setAlpha(180); c.save(); c.drawCircle(m_DPoint.x, m_DPoint.y, m_RipRadius, m_Paint); c.restore(); m_Paint.setStyle(Paint.Style.STROKE); m_Paint.setAlpha(255); } private void rippleAnimate() { if (m_RippleAnimator != null) { m_RippleAnimator.cancel(); m_RippleAnimator = null; m_RipRadius = 0; } m_RippleAnimator = new Animation() { @Override protected void applyTransformation(float interpolatedTime, Transformation t) { m_RipRadius = (int) (.6f * m_Radius * interpolatedTime); if (interpolatedTime == 1) { m_RipRadius = 0; } } }; m_RippleAnimator.setDuration(500); m_RippleAnimator.setInterpolator(new LinearInterpolator()); startAnimation(m_RippleAnimator); } private MenuThread m_MenuThd; private ReentrantLock m_Lock = new ReentrantLock(); private Paint m_Paint; private final static int m_Radius = 60; private final static int m_POffset = 150; private int m_PMoveSet = m_POffset; private final static int m_OffsetSpeed = 18; private AtomicBoolean m_OpenMenu = new AtomicBoolean(false); private int m_Status = -1; private PointF m_DPoint; private int m_RipRadius; private Animation m_RippleAnimator; }