Android App starten
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
package="io.github.controlwear.virtual.joystick.android">
|
||||
|
||||
</manifest>
|
||||
+871
@@ -0,0 +1,871 @@
|
||||
package io.github.controlwear.virtual.joystick.android;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Handler;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
public class JoystickView extends View
|
||||
implements
|
||||
Runnable {
|
||||
|
||||
|
||||
/*
|
||||
INTERFACES
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when a
|
||||
* JoystickView's button is moved
|
||||
*/
|
||||
public interface OnMoveListener {
|
||||
|
||||
/**
|
||||
* Called when a JoystickView's button has been moved
|
||||
* @param angle current angle
|
||||
* @param strength current strength
|
||||
*/
|
||||
void onMove(int angle, int strength);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interface definition for a callback to be invoked when a JoystickView
|
||||
* is touched and held by multiple pointers.
|
||||
*/
|
||||
public interface OnMultipleLongPressListener {
|
||||
/**
|
||||
* Called when a JoystickView has been touch and held enough time by multiple pointers.
|
||||
*/
|
||||
void onMultipleLongPress();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
CONSTANTS
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default refresh rate as a time in milliseconds to send move values through callback
|
||||
*/
|
||||
private static final int DEFAULT_LOOP_INTERVAL = 50; // in milliseconds
|
||||
|
||||
/**
|
||||
* Used to allow a slight move without cancelling MultipleLongPress
|
||||
*/
|
||||
private static final int MOVE_TOLERANCE = 10;
|
||||
|
||||
/**
|
||||
* Default color for button
|
||||
*/
|
||||
private static final int DEFAULT_COLOR_BUTTON = Color.BLACK;
|
||||
|
||||
/**
|
||||
* Default color for border
|
||||
*/
|
||||
private static final int DEFAULT_COLOR_BORDER = Color.TRANSPARENT;
|
||||
|
||||
/**
|
||||
* Default alpha for border
|
||||
*/
|
||||
private static final int DEFAULT_ALPHA_BORDER = 255;
|
||||
|
||||
/**
|
||||
* Default background color
|
||||
*/
|
||||
private static final int DEFAULT_BACKGROUND_COLOR = Color.TRANSPARENT;
|
||||
|
||||
/**
|
||||
* Default View's size
|
||||
*/
|
||||
private static final int DEFAULT_SIZE = 200;
|
||||
|
||||
/**
|
||||
* Default border's width
|
||||
*/
|
||||
private static final int DEFAULT_WIDTH_BORDER = 3;
|
||||
|
||||
/**
|
||||
* Default behavior to fixed center (not auto-defined)
|
||||
*/
|
||||
private static final boolean DEFAULT_FIXED_CENTER = true;
|
||||
|
||||
|
||||
/**
|
||||
* Default behavior to auto re-center button (automatically recenter the button)
|
||||
*/
|
||||
private static final boolean DEFAULT_AUTO_RECENTER_BUTTON = true;
|
||||
|
||||
|
||||
/**
|
||||
* Default behavior to button stickToBorder (button stay on the border)
|
||||
*/
|
||||
private static final boolean DEFAULT_BUTTON_STICK_TO_BORDER = false;
|
||||
|
||||
|
||||
// DRAWING
|
||||
private Paint mPaintCircleButton;
|
||||
private Paint mPaintCircleBorder;
|
||||
private Paint mPaintBackground;
|
||||
|
||||
private Paint mPaintBitmapButton;
|
||||
private Bitmap mButtonBitmap;
|
||||
|
||||
|
||||
/**
|
||||
* Ratio use to define the size of the button
|
||||
*/
|
||||
private float mButtonSizeRatio;
|
||||
|
||||
|
||||
/**
|
||||
* Ratio use to define the size of the background
|
||||
*
|
||||
*/
|
||||
private float mBackgroundSizeRatio;
|
||||
|
||||
|
||||
// COORDINATE
|
||||
private int mPosX = 0;
|
||||
private int mPosY = 0;
|
||||
private int mCenterX = 0;
|
||||
private int mCenterY = 0;
|
||||
|
||||
private int mFixedCenterX = 0;
|
||||
private int mFixedCenterY = 0;
|
||||
|
||||
/**
|
||||
* Used to adapt behavior whether it is auto-defined center (false) or fixed center (true)
|
||||
*/
|
||||
private boolean mFixedCenter;
|
||||
|
||||
|
||||
/**
|
||||
* Used to adapt behavior whether the button is automatically re-centered (true)
|
||||
* when released or not (false)
|
||||
*/
|
||||
private boolean mAutoReCenterButton;
|
||||
|
||||
|
||||
/**
|
||||
* Used to adapt behavior whether the button is stick to border (true) or
|
||||
* could be anywhere (when false - similar to regular behavior)
|
||||
*/
|
||||
private boolean mButtonStickToBorder;
|
||||
|
||||
|
||||
/**
|
||||
* Used to enabled/disabled the Joystick. When disabled (enabled to false) the joystick button
|
||||
* can't move and onMove is not called.
|
||||
*/
|
||||
private boolean mEnabled;
|
||||
|
||||
|
||||
// SIZE
|
||||
private int mButtonRadius;
|
||||
private int mBorderRadius;
|
||||
|
||||
|
||||
/**
|
||||
* Alpha of the border (to use when changing color dynamically)
|
||||
*/
|
||||
private int mBorderAlpha;
|
||||
|
||||
|
||||
/**
|
||||
* Based on mBorderRadius but a bit smaller (minus half the stroke size of the border)
|
||||
*/
|
||||
private float mBackgroundRadius;
|
||||
|
||||
|
||||
/**
|
||||
* Listener used to dispatch OnMove event
|
||||
*/
|
||||
private OnMoveListener mCallback;
|
||||
|
||||
private long mLoopInterval = DEFAULT_LOOP_INTERVAL;
|
||||
private Thread mThread = new Thread(this);
|
||||
|
||||
|
||||
/**
|
||||
* Listener used to dispatch MultipleLongPress event
|
||||
*/
|
||||
private OnMultipleLongPressListener mOnMultipleLongPressListener;
|
||||
|
||||
private final Handler mHandlerMultipleLongPress = new Handler();
|
||||
private Runnable mRunnableMultipleLongPress;
|
||||
private int mMoveTolerance;
|
||||
|
||||
|
||||
/**
|
||||
* Default value.
|
||||
* Both direction correspond to horizontal and vertical movement
|
||||
*/
|
||||
public static int BUTTON_DIRECTION_BOTH = 0;
|
||||
|
||||
/**
|
||||
* The allowed direction of the button is define by the value of this parameter:
|
||||
* - a negative value for horizontal axe
|
||||
* - a positive value for vertical axe
|
||||
* - zero for both axes
|
||||
*/
|
||||
private int mButtonDirection = 0;
|
||||
|
||||
|
||||
/*
|
||||
CONSTRUCTORS
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Simple constructor to use when creating a JoystickView from code.
|
||||
* Call another constructor passing null to Attribute.
|
||||
* @param context The Context the JoystickView is running in, through which it can
|
||||
* access the current theme, resources, etc.
|
||||
*/
|
||||
public JoystickView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
|
||||
public JoystickView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
this(context, attrs);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Constructor that is called when inflating a JoystickView from XML. This is called
|
||||
* when a JoystickView is being constructed from an XML file, supplying attributes
|
||||
* that were specified in the XML file.
|
||||
* @param context The Context the JoystickView is running in, through which it can
|
||||
* access the current theme, resources, etc.
|
||||
* @param attrs The attributes of the XML tag that is inflating the JoystickView.
|
||||
*/
|
||||
public JoystickView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
TypedArray styledAttributes = context.getTheme().obtainStyledAttributes(
|
||||
attrs,
|
||||
R.styleable.JoystickView,
|
||||
0, 0
|
||||
);
|
||||
|
||||
int buttonColor;
|
||||
int borderColor;
|
||||
int backgroundColor;
|
||||
int borderWidth;
|
||||
Drawable buttonDrawable;
|
||||
try {
|
||||
buttonColor = styledAttributes.getColor(R.styleable.JoystickView_JV_buttonColor, DEFAULT_COLOR_BUTTON);
|
||||
borderColor = styledAttributes.getColor(R.styleable.JoystickView_JV_borderColor, DEFAULT_COLOR_BORDER);
|
||||
mBorderAlpha = styledAttributes.getInt(R.styleable.JoystickView_JV_borderAlpha, DEFAULT_ALPHA_BORDER);
|
||||
backgroundColor = styledAttributes.getColor(R.styleable.JoystickView_JV_backgroundColor, DEFAULT_BACKGROUND_COLOR);
|
||||
borderWidth = styledAttributes.getDimensionPixelSize(R.styleable.JoystickView_JV_borderWidth, DEFAULT_WIDTH_BORDER);
|
||||
mFixedCenter = styledAttributes.getBoolean(R.styleable.JoystickView_JV_fixedCenter, DEFAULT_FIXED_CENTER);
|
||||
mAutoReCenterButton = styledAttributes.getBoolean(R.styleable.JoystickView_JV_autoReCenterButton, DEFAULT_AUTO_RECENTER_BUTTON);
|
||||
mButtonStickToBorder = styledAttributes.getBoolean(R.styleable.JoystickView_JV_buttonStickToBorder, DEFAULT_BUTTON_STICK_TO_BORDER);
|
||||
buttonDrawable = styledAttributes.getDrawable(R.styleable.JoystickView_JV_buttonImage);
|
||||
mEnabled = styledAttributes.getBoolean(R.styleable.JoystickView_JV_enabled, true);
|
||||
mButtonSizeRatio = styledAttributes.getFraction(R.styleable.JoystickView_JV_buttonSizeRatio, 1, 1, 0.25f);
|
||||
mBackgroundSizeRatio = styledAttributes.getFraction(R.styleable.JoystickView_JV_backgroundSizeRatio, 1, 1, 0.75f);
|
||||
mButtonDirection = styledAttributes.getInteger(R.styleable.JoystickView_JV_buttonDirection, BUTTON_DIRECTION_BOTH);
|
||||
} finally {
|
||||
styledAttributes.recycle();
|
||||
}
|
||||
|
||||
// Initialize the drawing according to attributes
|
||||
|
||||
mPaintCircleButton = new Paint();
|
||||
mPaintCircleButton.setAntiAlias(true);
|
||||
mPaintCircleButton.setColor(buttonColor);
|
||||
mPaintCircleButton.setStyle(Paint.Style.FILL);
|
||||
|
||||
if (buttonDrawable != null) {
|
||||
if (buttonDrawable instanceof BitmapDrawable) {
|
||||
mButtonBitmap = ((BitmapDrawable) buttonDrawable).getBitmap();
|
||||
mPaintBitmapButton = new Paint();
|
||||
}
|
||||
}
|
||||
|
||||
mPaintCircleBorder = new Paint();
|
||||
mPaintCircleBorder.setAntiAlias(true);
|
||||
mPaintCircleBorder.setColor(borderColor);
|
||||
mPaintCircleBorder.setStyle(Paint.Style.STROKE);
|
||||
mPaintCircleBorder.setStrokeWidth(borderWidth);
|
||||
|
||||
if (borderColor != Color.TRANSPARENT) {
|
||||
mPaintCircleBorder.setAlpha(mBorderAlpha);
|
||||
}
|
||||
|
||||
mPaintBackground = new Paint();
|
||||
mPaintBackground.setAntiAlias(true);
|
||||
mPaintBackground.setColor(backgroundColor);
|
||||
mPaintBackground.setStyle(Paint.Style.FILL);
|
||||
|
||||
|
||||
// Init Runnable for MultiLongPress
|
||||
|
||||
mRunnableMultipleLongPress = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mOnMultipleLongPressListener != null)
|
||||
mOnMultipleLongPressListener.onMultipleLongPress();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private void initPosition() {
|
||||
// get the center of view to position circle
|
||||
mFixedCenterX = mCenterX = mPosX = getWidth() / 2;
|
||||
mFixedCenterY = mCenterY = mPosY = getWidth() / 2;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Draw the background, the border and the button
|
||||
* @param canvas the canvas on which the shapes will be drawn
|
||||
*/
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
// Draw the background
|
||||
canvas.drawCircle(mFixedCenterX, mFixedCenterY, mBackgroundRadius, mPaintBackground);
|
||||
|
||||
// Draw the circle border
|
||||
canvas.drawCircle(mFixedCenterX, mFixedCenterY, mBorderRadius, mPaintCircleBorder);
|
||||
|
||||
// Draw the button from image
|
||||
if (mButtonBitmap != null) {
|
||||
canvas.drawBitmap(
|
||||
mButtonBitmap,
|
||||
mPosX + mFixedCenterX - mCenterX - mButtonRadius,
|
||||
mPosY + mFixedCenterY - mCenterY - mButtonRadius,
|
||||
mPaintBitmapButton
|
||||
);
|
||||
}
|
||||
// Draw the button as simple circle
|
||||
else {
|
||||
canvas.drawCircle(
|
||||
mPosX + mFixedCenterX - mCenterX,
|
||||
mPosY + mFixedCenterY - mCenterY,
|
||||
mButtonRadius,
|
||||
mPaintCircleButton
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is called during layout when the size of this view has changed.
|
||||
* Here we get the center of the view and the radius to draw all the shapes.
|
||||
*
|
||||
* @param w Current width of this view.
|
||||
* @param h Current height of this view.
|
||||
* @param oldW Old width of this view.
|
||||
* @param oldH Old height of this view.
|
||||
*/
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldW, int oldH) {
|
||||
super.onSizeChanged(w, h, oldW, oldH);
|
||||
|
||||
initPosition();
|
||||
|
||||
// radius based on smallest size : height OR width
|
||||
int d = Math.min(w, h);
|
||||
mButtonRadius = (int) (d / 2 * mButtonSizeRatio);
|
||||
mBorderRadius = (int) (d / 2 * mBackgroundSizeRatio);
|
||||
mBackgroundRadius = mBorderRadius - (mPaintCircleBorder.getStrokeWidth() / 2);
|
||||
|
||||
if (mButtonBitmap != null)
|
||||
mButtonBitmap = Bitmap.createScaledBitmap(mButtonBitmap, mButtonRadius * 2, mButtonRadius * 2, true);
|
||||
}
|
||||
|
||||
|
||||
@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) {
|
||||
if (MeasureSpec.getMode(measureSpec) == MeasureSpec.UNSPECIFIED) {
|
||||
// if no bounds are specified return a default size (200)
|
||||
return DEFAULT_SIZE;
|
||||
} else {
|
||||
// As you want to fill the available space
|
||||
// always return the full available bounds.
|
||||
return MeasureSpec.getSize(measureSpec);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
USER EVENT
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Handle touch screen motion event. Move the button according to the
|
||||
* finger coordinate and detect longPress by multiple pointers only.
|
||||
*
|
||||
* @param event The motion event.
|
||||
* @return True if the event was handled, false otherwise.
|
||||
*/
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
// if disabled we don't move the
|
||||
if (!mEnabled) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// to move the button according to the finger coordinate
|
||||
// (or limited to one axe according to direction option
|
||||
mPosY = mButtonDirection < 0 ? mCenterY : (int) event.getY(); // direction negative is horizontal axe
|
||||
mPosX = mButtonDirection > 0 ? mCenterX : (int) event.getX(); // direction positive is vertical axe
|
||||
|
||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
|
||||
// stop listener because the finger left the touch screen
|
||||
mThread.interrupt();
|
||||
|
||||
// re-center the button or not (depending on settings)
|
||||
if (mAutoReCenterButton) {
|
||||
resetButtonPosition();
|
||||
|
||||
// update now the last strength and angle which should be zero after resetButton
|
||||
if (mCallback != null)
|
||||
mCallback.onMove(getAngle(), getStrength());
|
||||
}
|
||||
|
||||
// if mAutoReCenterButton is false we will send the last strength and angle a bit
|
||||
// later only after processing new position X and Y otherwise it could be above the border limit
|
||||
}
|
||||
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
if (mThread != null && mThread.isAlive()) {
|
||||
mThread.interrupt();
|
||||
}
|
||||
|
||||
mThread = new Thread(this);
|
||||
mThread.start();
|
||||
|
||||
if (mCallback != null)
|
||||
mCallback.onMove(getAngle(), getStrength());
|
||||
}
|
||||
|
||||
// handle first touch and long press with multiple touch only
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// when the first touch occurs we update the center (if set to auto-defined center)
|
||||
if (!mFixedCenter) {
|
||||
mCenterX = mPosX;
|
||||
mCenterY = mPosY;
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_DOWN: {
|
||||
// when the second finger touch
|
||||
if (event.getPointerCount() == 2) {
|
||||
mHandlerMultipleLongPress.postDelayed(mRunnableMultipleLongPress, ViewConfiguration.getLongPressTimeout()*2);
|
||||
mMoveTolerance = MOVE_TOLERANCE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
mMoveTolerance--;
|
||||
if (mMoveTolerance == 0) {
|
||||
mHandlerMultipleLongPress.removeCallbacks(mRunnableMultipleLongPress);
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_UP: {
|
||||
// when the last multiple touch is released
|
||||
if (event.getPointerCount() == 2) {
|
||||
mHandlerMultipleLongPress.removeCallbacks(mRunnableMultipleLongPress);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
double abs = Math.sqrt((mPosX - mCenterX) * (mPosX - mCenterX)
|
||||
+ (mPosY - mCenterY) * (mPosY - mCenterY));
|
||||
|
||||
// (abs > mBorderRadius) means button is too far therefore we limit to border
|
||||
// (buttonStickBorder && abs != 0) means wherever is the button we stick it to the border except when abs == 0
|
||||
if (abs > mBorderRadius || (mButtonStickToBorder && abs != 0)) {
|
||||
mPosX = (int) ((mPosX - mCenterX) * mBorderRadius / abs + mCenterX);
|
||||
mPosY = (int) ((mPosY - mCenterY) * mBorderRadius / abs + mCenterY);
|
||||
}
|
||||
|
||||
if (!mAutoReCenterButton) {
|
||||
// Now update the last strength and angle if not reset to center
|
||||
if (mCallback != null)
|
||||
mCallback.onMove(getAngle(), getStrength());
|
||||
}
|
||||
|
||||
|
||||
// to force a new draw
|
||||
invalidate();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
GETTERS
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Process the angle following the 360° counter-clock protractor rules.
|
||||
* @return the angle of the button
|
||||
*/
|
||||
private int getAngle() {
|
||||
int angle = (int) Math.toDegrees(Math.atan2(mCenterY - mPosY, mPosX - mCenterX));
|
||||
return angle < 0 ? angle + 360 : angle; // make it as a regular counter-clock protractor
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Process the strength as a percentage of the distance between the center and the border.
|
||||
* @return the strength of the button
|
||||
*/
|
||||
private int getStrength() {
|
||||
return (int) (100 * Math.sqrt((mPosX - mCenterX)
|
||||
* (mPosX - mCenterX) + (mPosY - mCenterY)
|
||||
* (mPosY - mCenterY)) / mBorderRadius);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reset the button position to the center.
|
||||
*/
|
||||
public void resetButtonPosition() {
|
||||
mPosX = mCenterX;
|
||||
mPosY = mCenterY;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the current direction allowed for the button to move
|
||||
* @return Actually return an integer corresponding to the direction:
|
||||
* - A negative value is horizontal axe,
|
||||
* - A positive value is vertical axe,
|
||||
* - Zero means both axes
|
||||
*/
|
||||
public int getButtonDirection() {
|
||||
return mButtonDirection;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the state of the joystick. False when the button don't move.
|
||||
* @return the state of the joystick
|
||||
*/
|
||||
public boolean isEnabled() {
|
||||
return mEnabled;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the size of the button (as a ratio of the total width/height)
|
||||
* Default is 0.25 (25%).
|
||||
* @return button size (value between 0.0 and 1.0)
|
||||
*/
|
||||
public float getButtonSizeRatio() {
|
||||
return mButtonSizeRatio;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the size of the background (as a ratio of the total width/height)
|
||||
* Default is 0.75 (75%).
|
||||
* @return background size (value between 0.0 and 1.0)
|
||||
*/
|
||||
public float getmBackgroundSizeRatio() {
|
||||
return mBackgroundSizeRatio;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the current behavior of the auto re-center button
|
||||
* @return True if automatically re-centered or False if not
|
||||
*/
|
||||
public boolean isAutoReCenterButton() {
|
||||
return mAutoReCenterButton;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the current behavior of the button stick to border
|
||||
* @return True if the button stick to the border otherwise False
|
||||
*/
|
||||
public boolean isButtonStickToBorder() {
|
||||
return mButtonStickToBorder;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the relative X coordinate of button center related
|
||||
* to top-left virtual corner of the border
|
||||
* @return coordinate of X (normalized between 0 and 100)
|
||||
*/
|
||||
public int getNormalizedX() {
|
||||
if (getWidth() == 0) {
|
||||
return 50;
|
||||
}
|
||||
return Math.round((mPosX-mButtonRadius)*100.0f/(getWidth()-mButtonRadius*2));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the relative Y coordinate of the button center related
|
||||
* to top-left virtual corner of the border
|
||||
* @return coordinate of Y (normalized between 0 and 100)
|
||||
*/
|
||||
public int getNormalizedY() {
|
||||
if (getHeight() == 0) {
|
||||
return 50;
|
||||
}
|
||||
return Math.round((mPosY-mButtonRadius)*100.0f/(getHeight()-mButtonRadius*2));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the alpha of the border
|
||||
* @return it should be an integer between 0 and 255 previously set
|
||||
*/
|
||||
public int getBorderAlpha() {
|
||||
return mBorderAlpha;
|
||||
}
|
||||
|
||||
/*
|
||||
SETTERS
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Set an image to the button with a drawable
|
||||
* @param d drawable to pick the image
|
||||
*/
|
||||
public void setButtonDrawable(Drawable d) {
|
||||
if (d != null) {
|
||||
if (d instanceof BitmapDrawable) {
|
||||
mButtonBitmap = ((BitmapDrawable) d).getBitmap();
|
||||
|
||||
if (mButtonRadius != 0) {
|
||||
mButtonBitmap = Bitmap.createScaledBitmap(
|
||||
mButtonBitmap,
|
||||
mButtonRadius * 2,
|
||||
mButtonRadius * 2,
|
||||
true);
|
||||
}
|
||||
|
||||
if (mPaintBitmapButton != null)
|
||||
mPaintBitmapButton = new Paint();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the button color for this JoystickView.
|
||||
* @param color the color of the button
|
||||
*/
|
||||
public void setButtonColor(int color) {
|
||||
mPaintCircleButton.setColor(color);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the border color for this JoystickView.
|
||||
* @param color the color of the border
|
||||
*/
|
||||
public void setBorderColor(int color) {
|
||||
mPaintCircleBorder.setColor(color);
|
||||
if (color != Color.TRANSPARENT) {
|
||||
mPaintCircleBorder.setAlpha(mBorderAlpha);
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the border alpha for this JoystickView.
|
||||
* @param alpha the transparency of the border between 0 and 255
|
||||
*/
|
||||
public void setBorderAlpha(int alpha) {
|
||||
mBorderAlpha = alpha;
|
||||
mPaintCircleBorder.setAlpha(alpha);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the background color for this JoystickView.
|
||||
* @param color the color of the background
|
||||
*/
|
||||
@Override
|
||||
public void setBackgroundColor(int color) {
|
||||
mPaintBackground.setColor(color);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the border width for this JoystickView.
|
||||
* @param width the width of the border
|
||||
*/
|
||||
public void setBorderWidth(int width) {
|
||||
mPaintCircleBorder.setStrokeWidth(width);
|
||||
mBackgroundRadius = mBorderRadius - (width / 2.0f);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register a callback to be invoked when this JoystickView's button is moved
|
||||
* @param l The callback that will run
|
||||
*/
|
||||
public void setOnMoveListener(OnMoveListener l) {
|
||||
setOnMoveListener(l, DEFAULT_LOOP_INTERVAL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register a callback to be invoked when this JoystickView's button is moved
|
||||
* @param l The callback that will run
|
||||
* @param loopInterval Refresh rate to be invoked in milliseconds
|
||||
*/
|
||||
public void setOnMoveListener(OnMoveListener l, int loopInterval) {
|
||||
mCallback = l;
|
||||
mLoopInterval = loopInterval;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Register a callback to be invoked when this JoystickView is touch and held by multiple pointers
|
||||
* @param l The callback that will run
|
||||
*/
|
||||
public void setOnMultiLongPressListener(OnMultipleLongPressListener l) {
|
||||
mOnMultipleLongPressListener = l;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the joystick center's behavior (fixed or auto-defined)
|
||||
* @param fixedCenter True for fixed center, False for auto-defined center based on touch down
|
||||
*/
|
||||
public void setFixedCenter(boolean fixedCenter) {
|
||||
// if we set to "fixed" we make sure to re-init position related to the width of the joystick
|
||||
if (fixedCenter) {
|
||||
initPosition();
|
||||
}
|
||||
mFixedCenter = fixedCenter;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enable or disable the joystick
|
||||
* @param enabled False mean the button won't move and onMove won't be called
|
||||
*/
|
||||
public void setEnabled(boolean enabled) {
|
||||
mEnabled = enabled;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the joystick button size (as a fraction of the real width/height)
|
||||
* By default it is 25% (0.25).
|
||||
* @param newRatio between 0.0 and 1.0
|
||||
*/
|
||||
public void setButtonSizeRatio(float newRatio) {
|
||||
if (newRatio > 0.0f & newRatio <= 1.0f) {
|
||||
mButtonSizeRatio = newRatio;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the joystick button size (as a fraction of the real width/height)
|
||||
* By default it is 75% (0.75).
|
||||
* Not working if the background is an image.
|
||||
* @param newRatio between 0.0 and 1.0
|
||||
*/
|
||||
public void setBackgroundSizeRatio(float newRatio) {
|
||||
if (newRatio > 0.0f & newRatio <= 1.0f) {
|
||||
mBackgroundSizeRatio = newRatio;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the current behavior of the auto re-center button
|
||||
* @param b True if automatically re-centered or False if not
|
||||
*/
|
||||
public void setAutoReCenterButton(boolean b) {
|
||||
mAutoReCenterButton = b;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the current behavior of the button stick to border
|
||||
* @param b True if the button stick to the border or False (default) if not
|
||||
*/
|
||||
public void setButtonStickToBorder(boolean b) {
|
||||
mButtonStickToBorder = b;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the current authorized direction for the button to move
|
||||
* @param direction the value will define the authorized direction:
|
||||
* - any negative value (such as -1) for horizontal axe
|
||||
* - any positive value (such as 1) for vertical axe
|
||||
* - zero (0) for the full direction (both axes)
|
||||
*/
|
||||
public void setButtonDirection(int direction) {
|
||||
mButtonDirection = direction;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
IMPLEMENTS
|
||||
*/
|
||||
|
||||
|
||||
@Override // Runnable
|
||||
public void run() {
|
||||
while (!Thread.interrupted()) {
|
||||
post(new Runnable() {
|
||||
public void run() {
|
||||
if (mCallback != null)
|
||||
mCallback.onMove(getAngle(), getStrength());
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
Thread.sleep(mLoopInterval);
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
|
||||
<declare-styleable name="JoystickView">
|
||||
|
||||
<attr name="JV_buttonImage" format="reference" />
|
||||
|
||||
<attr name="JV_buttonColor" format="color"/>
|
||||
|
||||
<attr name="JV_borderColor" format="color"/>
|
||||
|
||||
<attr name="JV_borderAlpha" format="integer"/>
|
||||
|
||||
<attr name="JV_backgroundColor" format="color"/>
|
||||
|
||||
<attr name="JV_borderWidth" format="dimension"/>
|
||||
|
||||
<attr name="JV_fixedCenter" format="boolean"/>
|
||||
|
||||
<attr name="JV_autoReCenterButton" format="boolean"/>
|
||||
|
||||
<attr name="JV_buttonStickToBorder" format="boolean"/>
|
||||
|
||||
<attr name="JV_enabled" format="boolean"/>
|
||||
|
||||
<attr name="JV_buttonSizeRatio" format="fraction"/>
|
||||
|
||||
<attr name="JV_backgroundSizeRatio" format="fraction"/>
|
||||
|
||||
<attr name="JV_buttonDirection">
|
||||
<flag name="horizontal" value="-1"/>
|
||||
<flag name="both" value="0"/>
|
||||
<flag name="vertical" value="1"/>
|
||||
|
||||
</attr>
|
||||
|
||||
</declare-styleable>
|
||||
|
||||
</resources>
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package io.github.controlwear.virtual.joystick.android;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* To work on unit tests, switch the Test Artifact in the Build Variants view.
|
||||
*/
|
||||
public class ExampleUnitTest {
|
||||
@Test
|
||||
public void addition_isCorrect() throws Exception {
|
||||
assertEquals(4, 2 + 2);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user