/**
* 利用系统 UI flag特性模拟身临其境的game
*
* @description:
* @author ldm
* @date 2016-5-26 下午4:13:04
*/
public class GameActivity extends Activity {
/**
* 自定义游戏视图,填充整个屏幕。
*
* @description:
* @author ldm
* @date 2016-5-26 下午4:16:17
*/
public static class Content extends TouchPaint.PaintView implements
View.OnSystemUiVisibilityChangeListener, View.OnClickListener {
Activity mActivity;
Button mPlayButton;
boolean mPaused;
int mLastSystemUiVis;
boolean mUpdateSystemUi;
Runnable mFader = new Runnable() {
@Override
public void run() {
fade();
if (mUpdateSystemUi) {
updateNavVisibility();
}
if (!mPaused) {
getHandler().postDelayed(mFader, 1000 / 30);
}
}
};
public Content(Context context, AttributeSet attrs) {
super(context, attrs);
setOnSystemUiVisibilityChangeListener(this);
}
public void init(Activity activity, Button playButton) {
mActivity = activity;
mPlayButton = playButton;
mPlayButton.setOnClickListener(this);
setGamePaused(true);
}
@Override
public void onSystemUiVisibilityChange(int visibility) {
int diff = mLastSystemUiVis ^ visibility;
mLastSystemUiVis = visibility;
if (!mPaused && (diff & SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
&& (visibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0) {
mUpdateSystemUi = true;
}
}
@Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
// 游戏暂停
setGamePaused(true);
}
// 窗口焦点发生变化时
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (!hasWindowFocus) {
setGamePaused(true);
}
}
@Override
public void onClick(View v) {
if (v == mPlayButton) {
// 开始与暂停按钮
setGamePaused(!mPaused);
}
}
void setGamePaused(boolean paused) {
mPaused = paused;
mPlayButton.setText(paused ? R.string.play : R.string.pause);// 设置按钮开关
setKeepScreenOn(!paused);
updateNavVisibility();
Handler h = getHandler();
if (h != null) {
getHandler().removeCallbacks(mFader);
if (!paused) {
mFader.run();
text("Draw!");// 在屏幕上绘制文字
}
}
}
// 更新系统UI的FLAG特性
void updateNavVisibility() {
int newVis = SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
| SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
| SYSTEM_UI_FLAG_LAYOUT_STABLE;
if (!mPaused) {
newVis |= SYSTEM_UI_FLAG_LOW_PROFILE
| SYSTEM_UI_FLAG_FULLSCREEN
| SYSTEM_UI_FLAG_HIDE_NAVIGATION
| SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
}
// 设备UI
setSystemUiVisibility(newVis);
mUpdateSystemUi = false;
}
}
Content mContent;
public GameActivity() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.game);
mContent = (Content) findViewById(R.id.content);
mContent.init(this, (Button) findViewById(R.id.play));
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
}
@Override
protected void onPause() {
super.onPause();
// Pause game when its activity is paused.
mContent.setGamePaused(true);
}
}
/**
* 自定义手绘View,处理所有的绘图及手势操作
*
* @description:
* @author ldm
* @date 2016-5-26 下午4:56:18
*/
public static class PaintView extends View {
private static final int FADE_ALPHA = 0x06;
private static final int MAX_FADE_STEPS = 256 / (FADE_ALPHA / 2) + 4;
private static final int TRACKBALL_SCALE = 10;
private static final int SPLAT_VECTORS = 40;
private final Random mRandom = new Random();
private Bitmap mBitmap;
private Canvas mCanvas;
private final Paint mPaint = new Paint();
private final Paint mFadePaint = new Paint();
private float mCurX;
private float mCurY;
private int mOldButtonState;
private int mFadeSteps = MAX_FADE_STEPS;
/** 当前使用的颜色下标 */
int mColorIndex;
public PaintView(Context c) {
super(c);
init();
}
public PaintView(Context c, AttributeSet attrs) {
super(c, attrs);
init();
}
/**
* 初始化画笔
*
* @description:
* @author ldm
* @date 2016-5-26 下午4:58:26
*/
private void init() {
setFocusable(true);
mPaint.setAntiAlias(true);
mFadePaint.setColor(BACKGROUND_COLOR);
mFadePaint.setAlpha(FADE_ALPHA);
}
/**
* 清屏操作
*
* @description:
* @author ldm
* @date 2016-5-26 下午5:01:54
*/
public void clear() {
if (mCanvas != null) {
mPaint.setColor(BACKGROUND_COLOR);
mCanvas.drawPaint(mPaint);
invalidate();
mFadeSteps = MAX_FADE_STEPS;
}
}
/**
* 设置透明度变化
*
* @description:
* @author ldm
* @date 2016-5-26 下午5:02:12
*/
public void fade() {
if (mCanvas != null && mFadeSteps < MAX_FADE_STEPS) {
mCanvas.drawPaint(mFadePaint);
invalidate();
mFadeSteps++;
}
}
/**
* 绘制文字
*
* @description:
* @author ldm
* @date 2016-5-26 下午4:39:07
*/
public void text(String text) {
if (mBitmap != null) {
// 图片大小
final int width = mBitmap.getWidth();
final int height = mBitmap.getHeight();
mPaint.setColor(COLORS[mColorIndex]);
mPaint.setAlpha(255);
int size = height;
// 设置文字 大小
mPaint.setTextSize(size);
Rect bounds = new Rect();
// 获取文字的边界范围
mPaint.getTextBounds(text, 0, text.length(), bounds);
int twidth = bounds.width();
twidth += (twidth / 4);
if (twidth > width) {
size = (size * width) / twidth;
mPaint.setTextSize(size);
mPaint.getTextBounds(text, 0, text.length(), bounds);
}
Paint.FontMetrics fm = mPaint.getFontMetrics();
// 绘制文字
mCanvas.drawText(text, (width - bounds.width()) / 2,
((height - size) / 2) - fm.ascent, mPaint);
mFadeSteps = 0;
invalidate();
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
int curW = mBitmap != null ? mBitmap.getWidth() : 0;
int curH = mBitmap != null ? mBitmap.getHeight() : 0;
if (curW >= w && curH >= h) {
return;
}
if (curW < w)
curW = w;
if (curH < h)
curH = h;
Bitmap newBitmap = Bitmap.createBitmap(curW, curH,
Bitmap.Config.ARGB_8888);
Canvas newCanvas = new Canvas();
newCanvas.setBitmap(newBitmap);
if (mBitmap != null) {
newCanvas.drawBitmap(mBitmap, 0, 0, null);
}
mBitmap = newBitmap;
mCanvas = newCanvas;
mFadeSteps = MAX_FADE_STEPS;
}
@Override
protected void onDraw(Canvas canvas) {
if (mBitmap != null) {
canvas.drawBitmap(mBitmap, 0, 0, null);
}
}
@Override
public boolean onTrackballEvent(MotionEvent event) {
final int action = event.getActionMasked();
if (action == MotionEvent.ACTION_DOWN) {
// Advance color when the trackball button is pressed.
advanceColor();
}
if (action == MotionEvent.ACTION_DOWN
|| action == MotionEvent.ACTION_MOVE) {
final int N = event.getHistorySize();
final float scaleX = event.getXPrecision() * TRACKBALL_SCALE;
final float scaleY = event.getYPrecision() * TRACKBALL_SCALE;
for (int i = 0; i < N; i++) {
moveTrackball(event.getHistoricalX(i) * scaleX,
event.getHistoricalY(i) * scaleY);
}
moveTrackball(event.getX() * scaleX, event.getY() * scaleY);
}
return true;
}
private void moveTrackball(float deltaX, float deltaY) {
final int curW = mBitmap != null ? mBitmap.getWidth() : 0;
final int curH = mBitmap != null ? mBitmap.getHeight() : 0;
mCurX = Math.max(Math.min(mCurX + deltaX, curW - 1), 0);
mCurY = Math.max(Math.min(mCurY + deltaY, curH - 1), 0);
paint(PaintMode.Draw, mCurX, mCurY);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return onTouchOrHoverEvent(event, true /* isTouch */);
}
@Override
public boolean onHoverEvent(MotionEvent event) {
return onTouchOrHoverEvent(event, false /* isTouch */);
}
private boolean onTouchOrHoverEvent(MotionEvent event, boolean isTouch) {
final int buttonState = event.getButtonState();
int pressedButtons = buttonState & ~mOldButtonState;
mOldButtonState = buttonState;
if ((pressedButtons & MotionEvent.BUTTON_SECONDARY) != 0) {
// Advance color when the right mouse button or first stylus
// button
// is pressed.
advanceColor();
}
PaintMode mode;
if ((buttonState & MotionEvent.BUTTON_TERTIARY) != 0) {
// Splat paint when the middle mouse button or second stylus
// button is pressed.
mode = PaintMode.Splat;
} else if (isTouch
|| (buttonState & MotionEvent.BUTTON_PRIMARY) != 0) {
// Draw paint when touching or if the primary button is pressed.
mode = PaintMode.Draw;
} else {
// Otherwise, do not paint anything.
return false;
}
final int action = event.getActionMasked();
if (action == MotionEvent.ACTION_DOWN
|| action == MotionEvent.ACTION_MOVE
|| action == MotionEvent.ACTION_HOVER_MOVE) {
final int N = event.getHistorySize();
final int P = event.getPointerCount();
for (int i = 0; i < N; i++) {
for (int j = 0; j < P; j++) {
paint(getPaintModeForTool(event.getToolType(j), mode),
event.getHistoricalX(j, i),
event.getHistoricalY(j, i),
event.getHistoricalPressure(j, i),
event.getHistoricalTouchMajor(j, i),
event.getHistoricalTouchMinor(j, i),
event.getHistoricalOrientation(j, i),
event.getHistoricalAxisValue(
MotionEvent.AXIS_DISTANCE, j, i),
event.getHistoricalAxisValue(
MotionEvent.AXIS_TILT, j, i));
}
}
for (int j = 0; j < P; j++) {
paint(getPaintModeForTool(event.getToolType(j), mode),
event.getX(j), event.getY(j), event.getPressure(j),
event.getTouchMajor(j), event.getTouchMinor(j),
event.getOrientation(j),
event.getAxisValue(MotionEvent.AXIS_DISTANCE, j),
event.getAxisValue(MotionEvent.AXIS_TILT, j));
}
mCurX = event.getX();
mCurY = event.getY();
}
return true;
}
private PaintMode getPaintModeForTool(int toolType,
PaintMode defaultMode) {
if (toolType == MotionEvent.TOOL_TYPE_ERASER) {
return PaintMode.Erase;
}
return defaultMode;
}
private void advanceColor() {
mColorIndex = (mColorIndex + 1) % COLORS.length;
}
private void paint(PaintMode mode, float x, float y) {
paint(mode, x, y, 1.0f, 0, 0, 0, 0, 0);
}
private void paint(PaintMode mode, float x, float y, float pressure,
float major, float minor, float orientation, float distance,
float tilt) {
if (mBitmap != null) {
if (major <= 0 || minor <= 0) {
// If size is not available, use a default value.
major = minor = 16;
}
switch (mode) {
case Draw:
mPaint.setColor(COLORS[mColorIndex]);
mPaint.setAlpha(Math.min((int) (pressure * 128), 255));
drawOval(mCanvas, x, y, major, minor, orientation, mPaint);
break;
case Erase:
mPaint.setColor(BACKGROUND_COLOR);
mPaint.setAlpha(Math.min((int) (pressure * 128), 255));
drawOval(mCanvas, x, y, major, minor, orientation, mPaint);
break;
case Splat:
mPaint.setColor(COLORS[mColorIndex]);
mPaint.setAlpha(64);
drawSplat(mCanvas, x, y, orientation, distance, tilt,
mPaint);
break;
}
}
mFadeSteps = 0;
invalidate();
}
/**
* Draw an oval.
*
* When the orienation is 0 radians, orients the major axis vertically,
* angles less than or greater than 0 radians rotate the major axis left
* or right.
*/
private final RectF mReusableOvalRect = new RectF();
private void drawOval(Canvas canvas, float x, float y, float major,
float minor, float orientation, Paint paint) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.rotate((float) (orientation * 180 / Math.PI), x, y);
mReusableOvalRect.left = x - minor / 2;
mReusableOvalRect.right = x + minor / 2;
mReusableOvalRect.top = y - major / 2;
mReusableOvalRect.bottom = y + major / 2;
canvas.drawOval(mReusableOvalRect, paint);
canvas.restore();
}
/**
* Splatter paint in an area.
*
* Chooses random vectors describing the flow of paint from a round
* nozzle across a range of a few degrees. Then adds this vector to the
* direction indicated by the orientation and tilt of the tool and
* throws paint at the canvas along that vector.
*
* Repeats the process until a masterpiece is born.
*/
private void drawSplat(Canvas canvas, float x, float y,
float orientation, float distance, float tilt, Paint paint) {
float z = distance * 2 + 10;
// Calculate the center of the spray.
float nx = (float) (Math.sin(orientation) * Math.sin(tilt));
float ny = (float) (-Math.cos(orientation) * Math.sin(tilt));
float nz = (float) Math.cos(tilt);
if (nz < 0.05) {
return;
}
float cd = z / nz;
float cx = nx * cd;
float cy = ny * cd;
for (int i = 0; i < SPLAT_VECTORS; i++) {
// Make a random 2D vector that describes the direction of a
// speck of paint
// ejected by the nozzle in the nozzle's plane, assuming the
// tool is
// perpendicular to the surface.
double direction = mRandom.nextDouble() * Math.PI * 2;
double dispersion = mRandom.nextGaussian() * 0.2;
double vx = Math.cos(direction) * dispersion;
double vy = Math.sin(direction) * dispersion;
double vz = 1;
// Apply the nozzle tilt angle.
double temp = vy;
vy = temp * Math.cos(tilt) - vz * Math.sin(tilt);
vz = temp * Math.sin(tilt) + vz * Math.cos(tilt);
// Apply the nozzle orientation angle.
temp = vx;
vx = temp * Math.cos(orientation) - vy * Math.sin(orientation);
vy = temp * Math.sin(orientation) + vy * Math.cos(orientation);
// Determine where the paint will hit the surface.
if (vz < 0.05) {
continue;
}
float pd = (float) (z / vz);
float px = (float) (vx * pd);
float py = (float) (vy * pd);
// Throw some paint at this location, relative to the center of
// the spray.
mCanvas.drawCircle(x + px - cx, y + py - cy, 1.0f, paint);
}
}
}
https://github.com/ldm520/ANDROID_API_DEMOS等Android官方API开源代码。