package com.zerokol.views;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
public class JoystickView extends View implements Runnable {
// Constants
private final double RAD = 57.2957795;
public final static long DEFAULT_LOOP_INTERVAL = 200; // 100 ms
public final static int FRONT = 3;
public final static int FRONT_RIGHT = 4;
public final static int RIGHT = 5;
public final static int RIGHT_BOTTOM = 6;
public final static int BOTTOM = 7;
public final static int BOTTOM_LEFT = 8;
public final static int LEFT = 1;
public final static int LEFT_FRONT = 2;
// Variables
private OnJoystickMoveListener onJoystickMoveListener; // Listener
private Thread thread = new Thread(this);
private long loopInterval = DEFAULT_LOOP_INTERVAL;
private int xPosition = 0; // Touch x position
private int yPosition = 0; // Touch y position
private double centerX = 0; // Center view x position
private double centerY = 0; // Center view y position
private Paint mainCircle;
private Paint secondaryCircle;
private Paint outerCircle;
private Paint button;
private Paint horizontalLine;
private Paint verticalLine;
private int joystickRadius;
private int buttonRadius;
private int lastAngle = 0;
private int lastPower = 0;
public JoystickView(Context context) {
super(context);
}
public JoystickView(Context context, AttributeSet attrs) {
super(context, attrs);
initJoystickView();
}
public JoystickView(Context context, AttributeSet attrs, int defaultStyle) {
super(context, attrs, defaultStyle);
initJoystickView();
}
protected void initJoystickView() {
mainCircle = new Paint(Paint.ANTI_ALIAS_FLAG);
// mainCircle.setColor(Color.WHITE);
mainCircle.setColor(Color.parseColor("#ced3d6"));
mainCircle.setStyle(Paint.Style.FILL_AND_STROKE);
secondaryCircle = new Paint();
secondaryCircle.setColor(Color.LTGRAY);
secondaryCircle.setStyle(Paint.Style.FILL);
outerCircle = new Paint();
outerCircle.setColor(Color.LTGRAY);
outerCircle.setStrokeWidth(7);
outerCircle.setStyle(Paint.Style.STROKE);
verticalLine = new Paint();
verticalLine.setStrokeWidth(10);
verticalLine.setColor(Color.LTGRAY);
horizontalLine = new Paint();
horizontalLine.setStrokeWidth(10);
horizontalLine.setColor(Color.LTGRAY);
button = new Paint(Paint.ANTI_ALIAS_FLAG);
button.setColor(Color.parseColor("#F26C6E"));
button.setStyle(Paint.Style.FILL);
}
@Override
protected void onFinishInflate() {
}
@Override
protected void onSizeChanged(int xNew, int yNew, int xOld, int yOld) {
super.onSizeChanged(xNew, yNew, xOld, yOld);
// before measure, get the center of view
xPosition = (int) getWidth() / 2;
yPosition = (int) getWidth() / 2;
int d = Math.min(xNew, yNew);
buttonRadius = (int) (d / 4 * 0.25);
joystickRadius = (int) (d / 2 * 0.75);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// setting the measured values to resize the view to a certain width and
// height
int d = Math.min(measure(widthMeasureSpec), measure(heightMeasureSpec));
setMeasuredDimension(d, d);
}
private int measure(int measureSpec) {
int result = 0;
// Decode the measurement specifications.
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.UNSPECIFIED) {
// Return a default size of 200 if no bounds are specified.
result = 200;
} else {
// As you want to fill the available space
// always return the full available bounds.
result = specSize;
}
return result;
}
@Override
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas);
centerX = (getWidth()) / 2;
centerY = (getHeight()) / 2;
// painting the main circle
canvas.drawCircle((int) centerX, (int) centerY, joystickRadius,
mainCircle);
// painting the secondary circle
canvas.drawCircle((int) centerX, (int) centerY, joystickRadius / 2.7f,
secondaryCircle);
// paint the outer circle
canvas.drawCircle((int) centerX, (int) centerY, joystickRadius / 1.1f,
outerCircle);
// paint lines
canvas.drawLine((float) centerX, (float) centerY, (float) centerX,
(float) (centerY - joystickRadius), verticalLine);
canvas.drawLine((float) (centerX - joystickRadius), (float) centerY,
(float) (centerX + joystickRadius), (float) centerY,
horizontalLine);
canvas.drawLine((float) centerX, (float) (centerY + joystickRadius),
(float) centerX, (float) centerY, horizontalLine);
// painting the move button
canvas.drawCircle(xPosition, yPosition, buttonRadius, button);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
xPosition = (int) event.getX();
yPosition = (int) event.getY();
double abs = Math.sqrt((xPosition - centerX) * (xPosition - centerX)
+ (yPosition - centerY) * (yPosition - centerY));
if (abs > joystickRadius) {
xPosition = (int) ((xPosition - centerX) * joystickRadius / abs + centerX);
yPosition = (int) ((yPosition - centerY) * joystickRadius / abs + centerY);
}
invalidate();
if (event.getAction() == MotionEvent.ACTION_UP) {
xPosition = (int) centerX;
yPosition = (int) centerY;
Log.d("JOYSTICK", "ActionUp!");
thread.interrupt();
if (onJoystickMoveListener != null)
onJoystickMoveListener.onValueChanged(getAngle(), getPower(),
getDirection());
}
if (onJoystickMoveListener != null
&& event.getAction() == MotionEvent.ACTION_DOWN) {
if (thread != null && thread.isAlive()) {
thread.interrupt();
}
Log.d("JOYSTICK", "ActionDown!");
thread = new Thread(this);
thread.start();
if (onJoystickMoveListener != null)
onJoystickMoveListener.onValueChanged(getAngle(), getPower(),
getDirection());
}
return true;
}
private int getAngle() {
if (xPosition > centerX) {
if (yPosition < centerY) {
return lastAngle = (int) (Math.atan((yPosition - centerY)
/ (xPosition - centerX))
* RAD + 90);
} else if (yPosition > centerY) {
return lastAngle = (int) (Math.atan((yPosition - centerY)
/ (xPosition - centerX)) * RAD) + 90;
} else {
return lastAngle = 90;
}
} else if (xPosition < centerX) {
if (yPosition < centerY) {
return lastAngle = (int) (Math.atan((yPosition - centerY)
/ (xPosition - centerX))
* RAD - 90);
} else if (yPosition > centerY) {
return lastAngle = (int) (Math.atan((yPosition - centerY)
/ (xPosition - centerX)) * RAD) - 90;
} else {
return lastAngle = -90;
}
} else {
if (yPosition <= centerY) {
return lastAngle = 0;
} else {
if (lastAngle < 0) {
return lastAngle = -180;
} else {
return lastAngle = 180;
}
}
}
}
private int getPower() {
return (int) (100 * Math.sqrt((xPosition - centerX)
* (xPosition - centerX) + (yPosition - centerY)
* (yPosition - centerY)) / joystickRadius);
}
private int getDirection() {
if (lastPower == 0 && lastAngle == 0) {
return 0;
}
int a = 0;
if (lastAngle <= 0) {
a = (lastAngle * -1) + 90;
} else if (lastAngle > 0) {
if (lastAngle <= 90) {
a = 90 - lastAngle;
} else {
a = 360 - (lastAngle - 90);
}
}
int direction = (int) (((a + 22) / 45) + 1);
if (direction > 8) {
direction = 1;
}
return direction;
}
public void setOnJoystickMoveListener(OnJoystickMoveListener listener,
long repeatInterval) {
this.onJoystickMoveListener = listener;
this.loopInterval = repeatInterval;
}
public static interface OnJoystickMoveListener {
public void onValueChanged(int angle, int power, int direction);
}
@Override
public void run() {
while (!Thread.interrupted()) {
post(new Runnable() {
public void run() {
if (onJoystickMoveListener != null)
onJoystickMoveListener.onValueChanged(getAngle(),
getPower(), getDirection());
}
});
try {
Thread.sleep(loopInterval);
} catch (InterruptedException e) {
break;
}
}
}
}