I need to add multiple mark on seekbar like above image (But on run time).
e.g
May be i need to add two marks one on 10sec and other on 44sec.
So how can i add mark on specific position using time??
Use this Class:
import java.math.BigDecimal;
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.Paint.Style;
import android.graphics.RectF;
import android.os.Bundle;
import android.os.Parcelable;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.widget.ImageView;
import com.hkhaksar.shikat.R;
/**
* Widget that lets users select a minimum and maximum value on a given
* numerical range. The range value types can be one of Long, Double, Integer,
* Float, Short, Byte or BigDecimal.<br />
* <br />
* Improved {#link MotionEvent} handling for smoother use, anti-aliased painting
* for improved aesthetics.
*
* #author Stephan Tittel (stephan.tittel#kom.tu-darmstadt.de)
* #author Peter Sinnott (psinnott#gmail.com)
* #author Thomas Barrasso (tbarrasso#sevenplusandroid.org)
*
* #param <T>
* The Number type of the range values. One of Long, Double, Integer,
* Float, Short, Byte or BigDecimal.
*/
public class RangeSeekBar<T extends Number> extends ImageView {
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Bitmap thumbImage = BitmapFactory.decodeResource(
getResources(), R.drawable.leftithumb);
private final Bitmap thumbPressedImage = BitmapFactory.decodeResource(
getResources(), R.drawable.leftithumb);
private final float thumbWidth = thumbImage.getWidth();
private final float thumbHalfWidth = 0.5f * thumbWidth;
private final float thumbHalfHeight = 0.5f * thumbImage.getHeight();
private final float lineHeight = 0.3f * thumbHalfHeight;
private final float padding = thumbHalfWidth;
private final T absoluteMinValue, absoluteMaxValue;
private final NumberType numberType;
private final double absoluteMinValuePrim, absoluteMaxValuePrim;
private double normalizedMinValue = 0d;
private double normalizedMaxValue = 1d;
private Thumb pressedThumb = null;
private boolean notifyWhileDragging = false;
private OnRangeSeekBarChangeListener<T> listener;
/**
* Default color of a {#link RangeSeekBar}, #FF33B5E5. This is also known as
* "Ice Cream Sandwich" blue.
*/
public static final int DEFAULT_COLOR = Color.argb(0xFF, 0x2F, 0xB5, 0x9B);
/**
* An invalid pointer id.
*/
public static final int INVALID_POINTER_ID = 255;
// Localized constants from MotionEvent for compatibility
// with API < 8 "Froyo".
public static final int ACTION_POINTER_UP = 0x6,
ACTION_POINTER_INDEX_MASK = 0x0000ff00,
ACTION_POINTER_INDEX_SHIFT = 8;
private float mDownMotionX;
private int mActivePointerId = INVALID_POINTER_ID;
/**
* On touch, this offset plus the scaled value from the position of the
* touch will form the progress value. Usually 0.
*/
float mTouchProgressOffset;
private int mScaledTouchSlop;
private boolean mIsDragging;
/**
* Creates a new RangeSeekBar.
*
* #param absoluteMinValue
* The minimum value of the selectable range.
* #param absoluteMaxValue
* The maximum value of the selectable range.
* #param context
* #throws IllegalArgumentException
* Will be thrown if min/max value type is not one of Long,
* Double, Integer, Float, Short, Byte or BigDecimal.
*/
public RangeSeekBar(T absoluteMinValue, T absoluteMaxValue, Context context)
throws IllegalArgumentException {
super(context);
this.absoluteMinValue = absoluteMinValue;
this.absoluteMaxValue = absoluteMaxValue;
absoluteMinValuePrim = absoluteMinValue.doubleValue();
absoluteMaxValuePrim = absoluteMaxValue.doubleValue();
numberType = NumberType.fromNumber(absoluteMinValue);
// make RangeSeekBar focusable. This solves focus handling issues in
// case EditText widgets are being used along with the RangeSeekBar
// within ScollViews.
setFocusable(true);
setFocusableInTouchMode(true);
init();
}
private final void init() {
mScaledTouchSlop = ViewConfiguration.get(getContext())
.getScaledTouchSlop();
}
public boolean isNotifyWhileDragging() {
return notifyWhileDragging;
}
/**
* Should the widget notify the listener callback while the user is still
* dragging a thumb? Default is false.
*
* #param flag
*/
public void setNotifyWhileDragging(boolean flag) {
this.notifyWhileDragging = flag;
}
/**
* Returns the absolute minimum value of the range that has been set at
* construction time.
*
* #return The absolute minimum value of the range.
*/
public T getAbsoluteMinValue() {
return absoluteMinValue;
}
/**
* Returns the absolute maximum value of the range that has been set at
* construction time.
*
* #return The absolute maximum value of the range.
*/
public T getAbsoluteMaxValue() {
return absoluteMaxValue;
}
/**
* Returns the currently selected min value.
*
* #return The currently selected min value.
*/
public T getSelectedMinValue() {
return normalizedToValue(normalizedMinValue);
}
/**
* Sets the currently selected minimum value. The widget will be invalidated
* and redrawn.
*
* #param value
* The Number value to set the minimum value to. Will be clamped
* to given absolute minimum/maximum range.
*/
public void setSelectedMinValue(T value) {
// in case absoluteMinValue == absoluteMaxValue, avoid division by zero
// when normalizing.
if (0 == (absoluteMaxValuePrim - absoluteMinValuePrim)) {
setNormalizedMinValue(0d);
} else {
setNormalizedMinValue(valueToNormalized(value));
}
}
/**
* Returns the currently selected max value.
*
* #return The currently selected max value.
*/
public T getSelectedMaxValue() {
return normalizedToValue(normalizedMaxValue);
}
/**
* Sets the currently selected maximum value. The widget will be invalidated
* and redrawn.
*
* #param value
* The Number value to set the maximum value to. Will be clamped
* to given absolute minimum/maximum range.
*/
public void setSelectedMaxValue(T value) {
// in case absoluteMinValue == absoluteMaxValue, avoid division by zero
// when normalizing.
if (0 == (absoluteMaxValuePrim - absoluteMinValuePrim)) {
setNormalizedMaxValue(1d);
} else {
setNormalizedMaxValue(valueToNormalized(value));
}
}
/**
* Registers given listener callback to notify about changed selected
* values.
*
* #param listener
* The listener to notify about changed selected values.
*/
public void setOnRangeSeekBarChangeListener(
OnRangeSeekBarChangeListener<T> listener) {
this.listener = listener;
}
/**
* Handles thumb selection and movement. Notifies listener callback on
* certain events.
*/
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled())
return false;
int pointerIndex;
final int action = event.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
// Remember where the motion event started
mActivePointerId = event.getPointerId(event.getPointerCount() - 1);
pointerIndex = event.findPointerIndex(mActivePointerId);
mDownMotionX = event.getX(pointerIndex);
pressedThumb = evalPressedThumb(mDownMotionX);
// Only handle thumb presses.
if (pressedThumb == null)
return super.onTouchEvent(event);
setPressed(true);
invalidate();
onStartTrackingTouch();
trackTouchEvent(event);
attemptClaimDrag();
break;
case MotionEvent.ACTION_MOVE:
if (pressedThumb != null) {
if (mIsDragging) {
trackTouchEvent(event);
} else {
// Scroll to follow the motion event
pointerIndex = event.findPointerIndex(mActivePointerId);
final float x = event.getX(pointerIndex);
if (Math.abs(x - mDownMotionX) > mScaledTouchSlop) {
setPressed(true);
invalidate();
onStartTrackingTouch();
trackTouchEvent(event);
attemptClaimDrag();
}
}
if (notifyWhileDragging && listener != null) {
listener.onRangeSeekBarValuesChanged(this,
getSelectedMinValue(), getSelectedMaxValue());
}
}
break;
case MotionEvent.ACTION_UP:
if (mIsDragging) {
trackTouchEvent(event);
onStopTrackingTouch();
setPressed(false);
} else {
// Touch up when we never crossed the touch slop threshold
// should be interpreted as a tap-seek to that location.
onStartTrackingTouch();
trackTouchEvent(event);
onStopTrackingTouch();
}
pressedThumb = null;
invalidate();
if (listener != null) {
listener.onRangeSeekBarValuesChanged(this,
getSelectedMinValue(), getSelectedMaxValue());
}
break;
case MotionEvent.ACTION_POINTER_DOWN: {
final int index = event.getPointerCount() - 1;
// final int index = ev.getActionIndex();
mDownMotionX = event.getX(index);
mActivePointerId = event.getPointerId(index);
invalidate();
break;
}
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(event);
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
if (mIsDragging) {
onStopTrackingTouch();
setPressed(false);
}
invalidate(); // see above explanation
break;
}
return true;
}
private final void onSecondaryPointerUp(MotionEvent ev) {
final int pointerIndex = (ev.getAction() & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose
// a new active pointer and adjust accordingly.
// TODO: Make this decision more intelligent.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mDownMotionX = ev.getX(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
}
private final void trackTouchEvent(MotionEvent event) {
final int pointerIndex = event.findPointerIndex(mActivePointerId);
final float x = event.getX(pointerIndex);
if (Thumb.MIN.equals(pressedThumb)) {
setNormalizedMinValue(screenToNormalized(x));
} else if (Thumb.MAX.equals(pressedThumb)) {
setNormalizedMaxValue(screenToNormalized(x));
}
}
/**
* Tries to claim the user's drag motion, and requests disallowing any
* ancestors from stealing events in the drag.
*/
private void attemptClaimDrag() {
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
}
/**
* This is called when the user has started touching this widget.
*/
void onStartTrackingTouch() {
mIsDragging = true;
}
/**
* This is called when the user either releases his touch or the touch is
* canceled.
*/
void onStopTrackingTouch() {
mIsDragging = false;
}
/**
* Ensures correct size of the widget.
*/
#Override
protected synchronized void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
int width = 200;
if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(widthMeasureSpec)) {
width = MeasureSpec.getSize(widthMeasureSpec);
}
int height = thumbImage.getHeight();
if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(heightMeasureSpec)) {
height = Math.min(height, MeasureSpec.getSize(heightMeasureSpec));
}
setMeasuredDimension(width, height);
}
/**
* Draws the widget on the given canvas.
*/
#Override
protected synchronized void onDraw(Canvas canvas) {
super.onDraw(canvas);
// draw seek bar background line
final RectF rect = new RectF(padding,
0.5f * (getHeight() - lineHeight), getWidth() - padding,
0.5f * (getHeight() + lineHeight));
paint.setStyle(Style.FILL);
paint.setColor(Color.GRAY);
paint.setAntiAlias(true);
canvas.drawRect(rect, paint);
// draw seek bar active range line
rect.left = normalizedToScreen(normalizedMinValue);
rect.right = normalizedToScreen(normalizedMaxValue);
// orange color
paint.setColor(DEFAULT_COLOR);
canvas.drawRect(rect, paint);
// draw minimum thumb
drawThumb(normalizedToScreen(normalizedMinValue),
Thumb.MIN.equals(pressedThumb), canvas);
// draw maximum thumb
drawThumb(normalizedToScreen(normalizedMaxValue),
Thumb.MAX.equals(pressedThumb), canvas);
}
/**
* Overridden to save instance state when device orientation changes. This
* method is called automatically if you assign an id to the RangeSeekBar
* widget using the {#link #setId(int)} method. Other members of this class
* than the normalized min and max values don't need to be saved.
*/
#Override
protected Parcelable onSaveInstanceState() {
final Bundle bundle = new Bundle();
bundle.putParcelable("SUPER", super.onSaveInstanceState());
bundle.putDouble("MIN", normalizedMinValue);
bundle.putDouble("MAX", normalizedMaxValue);
return bundle;
}
/**
* Overridden to restore instance state when device orientation changes.
* This method is called automatically if you assign an id to the
* RangeSeekBar widget using the {#link #setId(int)} method.
*/
#Override
protected void onRestoreInstanceState(Parcelable parcel) {
final Bundle bundle = (Bundle) parcel;
super.onRestoreInstanceState(bundle.getParcelable("SUPER"));
normalizedMinValue = bundle.getDouble("MIN");
normalizedMaxValue = bundle.getDouble("MAX");
}
/**
* Draws the "normal" resp. "pressed" thumb image on specified x-coordinate.
*
* #param screenCoord
* The x-coordinate in screen space where to draw the image.
* #param pressed
* Is the thumb currently in "pressed" state?
* #param canvas
* The canvas to draw upon.
*/
private void drawThumb(float screenCoord, boolean pressed, Canvas canvas) {
canvas.drawBitmap(pressed ? thumbPressedImage : thumbImage, screenCoord
- thumbHalfWidth,
(float) ((0.5f * getHeight()) - thumbHalfHeight), paint);
}
/**
* Decides which (if any) thumb is touched by the given x-coordinate.
*
* #param touchX
* The x-coordinate of a touch event in screen space.
* #return The pressed thumb or null if none has been touched.
*/
private Thumb evalPressedThumb(float touchX) {
Thumb result = null;
boolean minThumbPressed = isInThumbRange(touchX, normalizedMinValue);
boolean maxThumbPressed = isInThumbRange(touchX, normalizedMaxValue);
if (minThumbPressed && maxThumbPressed) {
// if both thumbs are pressed (they lie on top of each other),
// choose the one with more room to drag. this avoids "stalling" the
// thumbs in a corner, not being able to drag them apart anymore.
result = (touchX / getWidth() > 0.5f) ? Thumb.MIN : Thumb.MAX;
} else if (minThumbPressed) {
result = Thumb.MIN;
} else if (maxThumbPressed) {
result = Thumb.MAX;
}
return result;
}
/**
* Decides if given x-coordinate in screen space needs to be interpreted as
* "within" the normalized thumb x-coordinate.
*
* #param touchX
* The x-coordinate in screen space to check.
* #param normalizedThumbValue
* The normalized x-coordinate of the thumb to check.
* #return true if x-coordinate is in thumb range, false otherwise.
*/
private boolean isInThumbRange(float touchX, double normalizedThumbValue) {
return Math.abs(touchX - normalizedToScreen(normalizedThumbValue)) <= thumbHalfWidth;
}
/**
* Sets normalized min value to value so that 0 <= value <= normalized max
* value <= 1. The View will get invalidated when calling this method.
*
* #param value
* The new normalized min value to set.
*/
public void setNormalizedMinValue(double value) {
normalizedMinValue = Math.max(0d,
Math.min(1d, Math.min(value, normalizedMaxValue)));
invalidate();
}
/**
* Sets normalized max value to value so that 0 <= normalized min value <=
* value <= 1. The View will get invalidated when calling this method.
*
* #param value
* The new normalized max value to set.
*/
public void setNormalizedMaxValue(double value) {
normalizedMaxValue = Math.max(0d,
Math.min(1d, Math.max(value, normalizedMinValue)));
invalidate();
}
/**
* Converts a normalized value to a Number object in the value space between
* absolute minimum and maximum.
*
* #param normalized
* #return
*/
#SuppressWarnings("unchecked")
private T normalizedToValue(double normalized) {
return (T) numberType.toNumber(absoluteMinValuePrim + normalized
* (absoluteMaxValuePrim - absoluteMinValuePrim));
}
/**
* Converts the given Number value to a normalized double.
*
* #param value
* The Number value to normalize.
* #return The normalized double.
*/
private double valueToNormalized(T value) {
if (0 == absoluteMaxValuePrim - absoluteMinValuePrim) {
// prevent division by zero, simply return 0.
return 0d;
}
return (value.doubleValue() - absoluteMinValuePrim)
/ (absoluteMaxValuePrim - absoluteMinValuePrim);
}
/**
* Converts a normalized value into screen space.
*
* #param normalizedCoord
* The normalized value to convert.
* #return The converted value in screen space.
*/
private float normalizedToScreen(double normalizedCoord) {
return (float) (padding + normalizedCoord * (getWidth() - 2 * padding));
}
/**
* Converts screen space x-coordinates into normalized values.
*
* #param screenCoord
* The x-coordinate in screen space to convert.
* #return The normalized value.
*/
private double screenToNormalized(float screenCoord) {
int width = getWidth();
if (width <= 2 * padding) {
// prevent division by zero, simply return 0.
return 0d;
} else {
double result = (screenCoord - padding) / (width - 2 * padding);
return Math.min(1d, Math.max(0d, result));
}
}
/**
* Callback listener interface to notify about changed range values.
*
* #author Stephan Tittel (stephan.tittel#kom.tu-darmstadt.de)
*
* #param <T>
* The Number type the RangeSeekBar has been declared with.
*/
public interface OnRangeSeekBarChangeListener<T> {
public void onRangeSeekBarValuesChanged(RangeSeekBar<?> bar,
T minValue, T maxValue);
}
/**
* Thumb constants (min and max).
*/
private static enum Thumb {
MIN, MAX
};
/**
* Utility enumaration used to convert between Numbers and doubles.
*
* #author Stephan Tittel (stephan.tittel#kom.tu-darmstadt.de)
*
*/
private static enum NumberType {
LONG, DOUBLE, INTEGER, FLOAT, SHORT, BYTE, BIG_DECIMAL;
public static <E extends Number> NumberType fromNumber(E value)
throws IllegalArgumentException {
if (value instanceof Long) {
return LONG;
}
if (value instanceof Double) {
return DOUBLE;
}
if (value instanceof Integer) {
return INTEGER;
}
if (value instanceof Float) {
return FLOAT;
}
if (value instanceof Short) {
return SHORT;
}
if (value instanceof Byte) {
return BYTE;
}
if (value instanceof BigDecimal) {
return BIG_DECIMAL;
}
throw new IllegalArgumentException("Number class '"
+ value.getClass().getName() + "' is not supported");
}
public Number toNumber(double value) {
switch (this) {
case LONG:
return new Long((long) value);
case DOUBLE:
return value;
case INTEGER:
return new Integer((int) value);
case FLOAT:
return new Float(value);
case SHORT:
return new Short((short) value);
case BYTE:
return new Byte((byte) value);
case BIG_DECIMAL:
return new BigDecimal(value);
}
throw new InstantiationError("can't convert " + this
+ " to a Number object");
}
}
}
replace LinearLayout to xml file instead of your seekbar. Like this:
Add these codes for define and add custom seekbar view in your java code class:
RangeSeekBar<Integer> sbPrice;
llPrice = (LinearLayout) v.findViewById(R.id.llPrice);
sbPrice = new RangeSeekBar<Integer>(0, 5000, ctx);
llPrice.addView(sbPrice);
Now you must implement OnRangeSeekBarChangeListener in your java class.(like activity, fragment, etc)
sbPrice.setOnRangeSeekBarChangeListener(this);
use this method to change seeker method:
#Override
public void onRangeSeekBarValuesChanged(RangeSeekBar<?> bar,
Integer minValue, Integer maxValue) {
tvValMin.setText(String.valueOf(minValue));
tvValMax.setText(String.valueOf(maxValue));
}
you can get min and max values by these codes:
sbPrice.getSelectedMinValue();
sbPrice.getSelectedMaxValue();
Related
I'm trying to detect a motion that you would get from bumping two phones with each other.
My question is, is an accelerometer the right sensor for this?
If so, how would I implement it?
Otherwise, what sensor should I use and in what way should I use it?
According to the guide at https://developer.android.com/guide/topics/sensors/sensors_overview, TYPE_LINEAR_ACCELERATION seems like the right one to use, however I can't figure out how to use it.
Here's how you could do it.
1- Initialize sensor object and register for sensor updates events callback like this
private void initSensorObject() {
SensorManager sensorMgr = (SensorManager) getSystemService(SENSOR_SERVICE);
Sensor _Sensor = sensorMgr.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
sensorMgr.registerListener(sensorEventListener, _Sensor,
SensorManager.SENSOR_DELAY_FASTEST);
}
2- Handle the sensor callback in the following way which detects linear acceleration exluding the effect of gravity and then an abrupt stop in motion (I wrote this for two tap motions like a tap then stop, again a tap then again stop. I'll explain the whole process for better understanding, you can easily modify it for only one tap detection.) I have added comments in the code to make it self-explanatory.
/*
* Following are the parameters for Tap detection Algorithm
*/
private static float SPEED_THRESHOLD_RISE1;
private static float SPEED_THRESHOLD_DROP1;
private static float SPEED_THRESHOLD_RISE2;
private static float SPEED_THRESHOLD_DROP2;
private static int DROP_DELTA;
private static int RISE2_DELTA;
private int SENSITIVITY_INDEX = TapParam.SEN_DEFAULT;
private static final int TAP_STATE_RISE1 = 0;
private static final int TAP_STATE_DROP1 = 1;
private static final int TAP_STATE_RISE2 = 2;
private static final int TAP_STATE_DROP2 = 3;
private int tappingState = TAP_STATE_RISE1;
private boolean tapLastStateOnce = false;
private long lastSensorUpdate;
private long tap1DroppedAt = 0;
private int mathMeanIndex = 0;
private float[] lastLinearAcc = new float[3];
private float[] acceleSet = new float[TapParam.AM_SIZE];
private int acceleIndex = 0;
private float[] gravity = new float[3];
private float lastAccele = -99; // an arbitrarily very small value
/**
* onSensorChanged is called when the Motion Sensor value
* is changed and then run the algorithm to detect your desired motion.
*
* #return void
*/
private SensorEventListener sensorEventListener = new SensorEventListener() {
#Override
public void onSensorChanged(SensorEvent event) {
long curSensorTime = System.currentTimeMillis();
if ((curSensorTime - lastSensorUpdate) < TapParam.SENSOR_RE_READ_TIME)
return;
lastSensorUpdate = curSensorTime;
acceleSet[acceleIndex] = getMotionAcceleration(event, curSensorTime);
acceleIndex = (acceleIndex + 1) % TapParam.AM_SIZE;
if (mathMeanIndex < TapParam.AM_SIZE)
mathMeanIndex++;
float accele = Util.getArithmeticMean(acceleSet);
switch (tappingState) {
case TAP_STATE_RISE1:
if (accele > SPEED_THRESHOLD_RISE1) {
tappingState = TAP_STATE_DROP1;
resetTapStateDropping();
mathMeanIndex = 0;
}
break;
case TAP_STATE_DROP1:
if (accele <= SPEED_THRESHOLD_DROP1) {
tappingState = TAP_STATE_RISE2;
resetTapStateRise2();
tap1DroppedAt = curSensorTime;
mathMeanIndex = 0;
}
break;
case TAP_STATE_RISE2:
if (curSensorTime - tap1DroppedAt >= TapParam.DELAY_BETWEEN_TAPS) {
if (accele > SPEED_THRESHOLD_RISE2) {
tappingState = TAP_STATE_DROP2;
resetTapStateDropping();
mathMeanIndex = 0;
}
}
break;
case TAP_STATE_DROP2:
if ((!tapLastStateOnce) && (accele <= SPEED_THRESHOLD_DROP2)) {
tapLastStateOnce = true;
resetTapStateRise2();
mathMeanIndex = 0;
onTapTapDetected();
}
break;
default:
tappingState = TAP_STATE_RISE1;
break;
}
}
/**
* onAccuracyChanged inter shall be called when hardware IMU
* (Inertial Measurement Unit a.k.a Motion Sensor) of the device change
* its accuracy value.
*
* #return void
*/
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
setThresholdValues();
}
};
/**
* It shall return the Linear Acceleration of the device. The force of
* gravity shall be filtered out.
*
* #return float - Linear acceleration
*/
private float getMotionAcceleration(SensorEvent event, long curSensorTime) {
// In this code, alpha is calculated as t / (t + dT),
// where t is the low-pass filter's time-constant and
// dT is the event delivery rate.
final float alpha = 0.8f;
float[] linearAcc = new float[3];
// Isolate the force of gravity with the low-pass filter.
gravity[0] = alpha * gravity[0] + (1 - alpha) * event.values[0];
gravity[1] = alpha * gravity[1] + (1 - alpha) * event.values[1];
gravity[2] = alpha * gravity[2] + (1 - alpha) * event.values[2];
// Remove the gravity contribution with the high-pass filter.
linearAcc[0] = event.values[0] - gravity[0];
linearAcc[1] = event.values[1] - gravity[1];
linearAcc[2] = event.values[2] - gravity[2];
float accele = (Math.abs(lastLinearAcc[0] - linearAcc[0])
+ Math.abs(lastLinearAcc[1] - linearAcc[1]) + Math
.abs(lastLinearAcc[2] - linearAcc[2])) / 3;
lastLinearAcc = linearAcc;
return accele;
}
/**
* resetTapStateRise2 shall reset the tapping state if
* second Tap is not detected within TAP_RISE2_TIME time.
*
* #return void
*/
private void resetTapStateRise2() {
handleResetTapState.removeCallbacks(runResetTapState);
handleResetTapState.postDelayed(runResetTapState, RISE2_DELTA);
}
private Handler handleResetTapState = new Handler();
private Runnable runResetTapState = new Runnable() {
#Override
public void run() {
tappingState = TAP_STATE_RISE1;
tapLastStateOnce = false;
}
};
/**
* resetTapStateDropping shall reset the tapping state if
* Tap Drop is not detected within TAP_DROP_TIME time.
*
* #return void
*/
private void resetTapStateDropping() {
handleResetTapState.removeCallbacks(runResetTapState);
handleResetTapState.postDelayed(runResetTapState, DROP_DELTA);
}
private Handler handleResetTapState = new Handler();
private Runnable runResetTapState = new Runnable() {
#Override
public void run() {
tappingState = TAP_STATE_RISE1;
tapLastStateOnce = false;
}
};
3- Here is a working parameters file to help you get started. thresholds array defines 10 levels of sensitivity of how hard or how soft you want to tap your phone to get detected as a valid motion
TapParam.java
final class TapParam {
static final int SEN_DEFAULT = 4;
static final int SEN_MIN = 0;
static final int SEN_MAX = 9;
static final int DELAY_BETWEEN_TAPS = 75;
static final int SENSOR_RE_READ_TIME = 1;
static final int AM_SIZE = 5;
// Columns: A B Y D T1 T2 T3
private static final double[][] thresholds = new double[][]{
{0.8483763, 0.33935052, 0.5655842, 0.33935052, 175, 300, 175},
{0.95167595, 0.38067037, 0.6344506, 0.38067037, 175, 300, 175},
{1.0836192, 0.4334477, 0.7224128, 0.4334477, 175, 300, 175},
{1.8552876, 0.742115, 1.2368584, 0.742115, 175, 300, 175},
{2.4327612, 0.9731045, 1.6218408, 0.9731045, 175, 300, 175},
{3.5321822, 1.4128729, 2.354788, 1.4128729, 175, 300, 175},
{6.4446864, 2.5778747, 4.296458, 2.5778747, 175, 300, 175},
{8.2, 3.5, 5.4, 2.6, 175, 300, 175},
{9.8, 4.0, 6.0, 2.9, 175, 300, 175},
{12, 6.0, 8.0, 3.1, 175, 300, 175}
};
private static int indexLimiting(int index) {
return (index > SEN_MAX) ? SEN_MAX : (index < SEN_MIN) ? SEN_MIN : index;
}
static float getRISE1(int index) {
index = indexLimiting(index);
return (float) thresholds[index][0];
}
static float getDROP1(int index) {
index = indexLimiting(index);
return (float) thresholds[index][1];
}
static float getRISE2(int index) {
index = indexLimiting(index);
return (float) thresholds[index][2];
}
static float getDROP2(int index) {
index = indexLimiting(index);
return (float) thresholds[index][3];
}
static float getDROP_DELTA1(int index) {
index = indexLimiting(index);
return (float) thresholds[index][4];
}
static float getRISE_DELTA2(int index) {
index = indexLimiting(index);
return (float) thresholds[index][5];
}
}
UPDATE:
/**
* setThresholdValues method shall calculate the Threshold values according
* to the accuracy value of the Motion Sensor.
*/
private void setThresholdValues() {
if (_Sensor == null)
return;
SPEED_THRESHOLD_RISE1 = TapParam.getRISE1(SENSITIVITY_INDEX);
SPEED_THRESHOLD_DROP1 = TapParam.getDROP1(SENSITIVITY_INDEX);
SPEED_THRESHOLD_RISE2 = TapParam.getRISE2(SENSITIVITY_INDEX);
SPEED_THRESHOLD_DROP2 = TapParam.getDROP2(SENSITIVITY_INDEX);
}
/**
* Method shall return the average (Arithmetic Mean) of the set of values
* passed as parameter.
*
* #param float[] set - the set of values
* #return float - arithmetic mean
*/
static float getArithmeticMean(float[] set) {
double sum = 0;
for (float aSet : set) {
sum += aSet;
}
return (float) sum / set.length;
}
UPDATE 2:
Call setTapTapSensitivity() in onCreate() of your activity
private void setTapTapSensitivity() {
setTapTapSensitivity(3); //You can try 0 to 9 for 10 levels of sensitivity defined in TapParam.java. I have tried 3 and it works for a moderate tap
}
private void setTapTapSensitivity(int sensitivityIndex) {
RISE2_DELTA = (int) TapParam.getRISE_DELTA2(sensitivityIndex);
DROP_DELTA = (int) TapParam.getDROP_DELTA1(sensitivityIndex);
SENSITIVITY_INDEX = sensitivityIndex;
}
I am working on an android App which detects faces and show counts on screen. Here is its screen-short.
For face detection I used open-cv-for-android library, but it runs in landscape mode and it is working very well. I want App should run in portrait mode and detect faces properly. I searched this problem on google and followed this post
OpenCV camera orientation issue
but I got this error :
Here is open-cv-java code
package org.opencv.android;
import java.util.List;
import org.opencv.R;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
/**
* This is a basic class, implementing the interaction with Camera and OpenCV library.
* The main responsibility of it - is to control when camera can be enabled, process the frame,
* call external listener to make any adjustments to the frame and then draw the resulting
* frame to the screen.
* The clients shall implement CvCameraViewListener.
*/
public abstract class CameraBridgeViewBase extends SurfaceView implements SurfaceHolder.Callback {
private static final String TAG = "CameraBridge";
private static final int MAX_UNSPECIFIED = -1;
private static final int STOPPED = 0;
private static final int STARTED = 1;
private int mState = STOPPED;
private Bitmap mCacheBitmap;
private CvCameraViewListener2 mListener;
private boolean mSurfaceExist;
private Object mSyncObject = new Object();
protected int mFrameWidth;
protected int mFrameHeight;
protected int mMaxHeight;
protected int mMaxWidth;
protected float mScale = 0;
protected int mPreviewFormat = RGBA;
protected int mCameraIndex = CAMERA_ID_ANY;
protected boolean mEnabled;
protected FpsMeter mFpsMeter = null;
public static final int CAMERA_ID_ANY = -1;
public static final int CAMERA_ID_BACK = 99;
public static final int CAMERA_ID_FRONT = 98;
public static final int RGBA = 1;
public static final int GRAY = 2;
public CameraBridgeViewBase(Context context, int cameraId) {
super(context);
mCameraIndex = cameraId;
getHolder().addCallback(this);
mMaxWidth = MAX_UNSPECIFIED;
mMaxHeight = MAX_UNSPECIFIED;
}
public CameraBridgeViewBase(Context context, AttributeSet attrs) {
super(context, attrs);
int count = attrs.getAttributeCount();
Log.d(TAG, "Attr count: " + Integer.valueOf(count));
TypedArray styledAttrs = getContext().obtainStyledAttributes(attrs, R.styleable.CameraBridgeViewBase);
if (styledAttrs.getBoolean(R.styleable.CameraBridgeViewBase_show_fps, false))
enableFpsMeter();
mCameraIndex = styledAttrs.getInt(R.styleable.CameraBridgeViewBase_camera_id, -1);
getHolder().addCallback(this);
mMaxWidth = MAX_UNSPECIFIED;
mMaxHeight = MAX_UNSPECIFIED;
styledAttrs.recycle();
}
/**
* Sets the camera index
* #param cameraIndex new camera index
*/
public void setCameraIndex(int cameraIndex) {
this.mCameraIndex = cameraIndex;
}
public interface CvCameraViewListener {
/**
* This method is invoked when camera preview has started. After this method is invoked
* the frames will start to be delivered to client via the onCameraFrame() callback.
* #param width - the width of the frames that will be delivered
* #param height - the height of the frames that will be delivered
*/
public void onCameraViewStarted(int width, int height);
/**
* This method is invoked when camera preview has been stopped for some reason.
* No frames will be delivered via onCameraFrame() callback after this method is called.
*/
public void onCameraViewStopped();
/**
* This method is invoked when delivery of the frame needs to be done.
* The returned values - is a modified frame which needs to be displayed on the screen.
* TODO: pass the parameters specifying the format of the frame (BPP, YUV or RGB and etc)
*/
public Mat onCameraFrame(Mat inputFrame);
}
public interface CvCameraViewListener2 {
/**
* This method is invoked when camera preview has started. After this method is invoked
* the frames will start to be delivered to client via the onCameraFrame() callback.
* #param width - the width of the frames that will be delivered
* #param height - the height of the frames that will be delivered
*/
public void onCameraViewStarted(int width, int height);
/**
* This method is invoked when camera preview has been stopped for some reason.
* No frames will be delivered via onCameraFrame() callback after this method is called.
*/
public void onCameraViewStopped();
/**
* This method is invoked when delivery of the frame needs to be done.
* The returned values - is a modified frame which needs to be displayed on the screen.
* TODO: pass the parameters specifying the format of the frame (BPP, YUV or RGB and etc)
*/
public Mat onCameraFrame(CvCameraViewFrame inputFrame);
};
protected class CvCameraViewListenerAdapter implements CvCameraViewListener2 {
public CvCameraViewListenerAdapter(CvCameraViewListener oldStypeListener) {
mOldStyleListener = oldStypeListener;
}
public void onCameraViewStarted(int width, int height) {
mOldStyleListener.onCameraViewStarted(width, height);
}
public void onCameraViewStopped() {
mOldStyleListener.onCameraViewStopped();
}
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
Mat result = null;
switch (mPreviewFormat) {
case RGBA:
result = mOldStyleListener.onCameraFrame(inputFrame.rgba());
break;
case GRAY:
result = mOldStyleListener.onCameraFrame(inputFrame.gray());
break;
default:
Log.e(TAG, "Invalid frame format! Only RGBA and Gray Scale are supported!");
};
return result;
}
public void setFrameFormat(int format) {
mPreviewFormat = format;
}
private int mPreviewFormat = RGBA;
private CvCameraViewListener mOldStyleListener;
};
/**
* This class interface is abstract representation of single frame from camera for onCameraFrame callback
* Attention: Do not use objects, that represents this interface out of onCameraFrame callback!
*/
public interface CvCameraViewFrame {
/**
* This method returns RGBA Mat with frame
*/
public Mat rgba();
/**
* This method returns single channel gray scale Mat with frame
*/
public Mat gray();
};
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
Log.d(TAG, "call surfaceChanged event");
synchronized(mSyncObject) {
if (!mSurfaceExist) {
mSurfaceExist = true;
checkCurrentState();
} else {
/** Surface changed. We need to stop camera and restart with new parameters */
/* Pretend that old surface has been destroyed */
mSurfaceExist = false;
checkCurrentState();
/* Now use new surface. Say we have it now */
mSurfaceExist = true;
checkCurrentState();
}
}
}
public void surfaceCreated(SurfaceHolder holder) {
/* Do nothing. Wait until surfaceChanged delivered */
}
public void surfaceDestroyed(SurfaceHolder holder) {
synchronized(mSyncObject) {
mSurfaceExist = false;
checkCurrentState();
}
}
/**
* This method is provided for clients, so they can enable the camera connection.
* The actual onCameraViewStarted callback will be delivered only after both this method is called and surface is available
*/
public void enableView() {
synchronized(mSyncObject) {
mEnabled = true;
checkCurrentState();
}
}
/**
* This method is provided for clients, so they can disable camera connection and stop
* the delivery of frames even though the surface view itself is not destroyed and still stays on the scren
*/
public void disableView() {
synchronized(mSyncObject) {
mEnabled = false;
checkCurrentState();
}
}
/**
* This method enables label with fps value on the screen
*/
public void enableFpsMeter() {
if (mFpsMeter == null) {
mFpsMeter = new FpsMeter();
mFpsMeter.setResolution(mFrameWidth, mFrameHeight);
}
}
public void disableFpsMeter() {
mFpsMeter = null;
}
/**
*
* #param listener
*/
public void setCvCameraViewListener(CvCameraViewListener2 listener) {
mListener = listener;
}
public void setCvCameraViewListener(CvCameraViewListener listener) {
CvCameraViewListenerAdapter adapter = new CvCameraViewListenerAdapter(listener);
adapter.setFrameFormat(mPreviewFormat);
mListener = adapter;
}
/**
* This method sets the maximum size that camera frame is allowed to be. When selecting
* size - the biggest size which less or equal the size set will be selected.
* As an example - we set setMaxFrameSize(200,200) and we have 176x152 and 320x240 sizes. The
* preview frame will be selected with 176x152 size.
* This method is useful when need to restrict the size of preview frame for some reason (for example for video recording)
* #param maxWidth - the maximum width allowed for camera frame.
* #param maxHeight - the maximum height allowed for camera frame
*/
public void setMaxFrameSize(int maxWidth, int maxHeight) {
mMaxWidth = maxWidth;
mMaxHeight = maxHeight;
}
public void SetCaptureFormat(int format)
{
mPreviewFormat = format;
if (mListener instanceof CvCameraViewListenerAdapter) {
CvCameraViewListenerAdapter adapter = (CvCameraViewListenerAdapter) mListener;
adapter.setFrameFormat(mPreviewFormat);
}
}
/**
* Called when mSyncObject lock is held
*/
private void checkCurrentState() {
Log.d(TAG, "call checkCurrentState");
int targetState;
if (mEnabled && mSurfaceExist && getVisibility() == VISIBLE) {
targetState = STARTED;
} else {
targetState = STOPPED;
}
if (targetState != mState) {
/* The state change detected. Need to exit the current state and enter target state */
processExitState(mState);
mState = targetState;
processEnterState(mState);
}
}
private void processEnterState(int state) {
Log.d(TAG, "call processEnterState: " + state);
switch(state) {
case STARTED:
onEnterStartedState();
if (mListener != null) {
mListener.onCameraViewStarted(mFrameWidth, mFrameHeight);
}
break;
case STOPPED:
onEnterStoppedState();
if (mListener != null) {
mListener.onCameraViewStopped();
}
break;
};
}
private void processExitState(int state) {
Log.d(TAG, "call processExitState: " + state);
switch(state) {
case STARTED:
onExitStartedState();
break;
case STOPPED:
onExitStoppedState();
break;
};
}
private void onEnterStoppedState() {
/* nothing to do */
}
private void onExitStoppedState() {
/* nothing to do */
}
// NOTE: The order of bitmap constructor and camera connection is important for android 4.1.x
// Bitmap must be constructed before surface
private void onEnterStartedState() {
Log.d(TAG, "call onEnterStartedState");
/* Connect camera */
if (!connectCamera(getWidth(), getHeight())) {
AlertDialog ad = new AlertDialog.Builder(getContext()).create();
ad.setCancelable(false); // This blocks the 'BACK' button
ad.setMessage("It seems that you device does not support camera (or it is locked). Application will be closed.");
ad.setButton(DialogInterface.BUTTON_NEUTRAL, "OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
((Activity) getContext()).finish();
}
});
ad.show();
}
}
private void onExitStartedState() {
disconnectCamera();
if (mCacheBitmap != null) {
mCacheBitmap.recycle();
}
}
/**
* This method shall be called by the subclasses when they have valid
* object and want it to be delivered to external client (via callback) and
* then displayed on the screen.
* #param frame - the current frame to be delivered
*/
protected void deliverAndDrawFrame(CvCameraViewFrame frame) {
Mat modified;
if (mListener != null) {
modified = mListener.onCameraFrame(frame);
} else {
modified = frame.rgba();
}
boolean bmpValid = true;
if (modified != null) {
try {
Utils.matToBitmap(modified, mCacheBitmap);
} catch(Exception e) {
Log.e(TAG, "Mat type: " + modified);
Log.e(TAG, "Bitmap type: " + mCacheBitmap.getWidth() + "*" + mCacheBitmap.getHeight());
Log.e(TAG, "Utils.matToBitmap() throws an exception: " + e.getMessage());
bmpValid = false;
}
}
if (bmpValid && mCacheBitmap != null) {
Canvas canvas = getHolder().lockCanvas();
if (canvas != null) {
canvas.drawColor(0, android.graphics.PorterDuff.Mode.CLEAR);
Log.d(TAG, "mStretch value: " + mScale);
if (mScale != 0) {
canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
new Rect((int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2),
(int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2),
(int)((canvas.getWidth() - mScale*mCacheBitmap.getWidth()) / 2 + mScale*mCacheBitmap.getWidth()),
(int)((canvas.getHeight() - mScale*mCacheBitmap.getHeight()) / 2 + mScale*mCacheBitmap.getHeight())), null);
} else {
canvas.drawBitmap(mCacheBitmap, new Rect(0,0,mCacheBitmap.getWidth(), mCacheBitmap.getHeight()),
new Rect((canvas.getWidth() - mCacheBitmap.getWidth()) / 2,
(canvas.getHeight() - mCacheBitmap.getHeight()) / 2,
(canvas.getWidth() - mCacheBitmap.getWidth()) / 2 + mCacheBitmap.getWidth(),
(canvas.getHeight() - mCacheBitmap.getHeight()) / 2 + mCacheBitmap.getHeight()), null);
}
if (mFpsMeter != null) {
mFpsMeter.measure();
mFpsMeter.draw(canvas, 20, 30);
}
getHolder().unlockCanvasAndPost(canvas);
}
}
}
/**
* This method is invoked shall perform concrete operation to initialize the camera.
* CONTRACT: as a result of this method variables mFrameWidth and mFrameHeight MUST be
* initialized with the size of the Camera frames that will be delivered to external processor.
* #param width - the width of this SurfaceView
* #param height - the height of this SurfaceView
*/
protected abstract boolean connectCamera(int width, int height);
/**
* Disconnects and release the particular camera object being connected to this surface view.
* Called when syncObject lock is held
*/
protected abstract void disconnectCamera();
// NOTE: On Android 4.1.x the function must be called before SurfaceTexture constructor!
protected void AllocateCache()
{
mCacheBitmap = Bitmap.createBitmap(mFrameWidth, mFrameHeight, Bitmap.Config.ARGB_8888);
}
public interface ListItemAccessor {
public int getWidth(Object obj);
public int getHeight(Object obj);
};
/**
* This helper method can be called by subclasses to select camera preview size.
* It goes over the list of the supported preview sizes and selects the maximum one which
* fits both values set via setMaxFrameSize() and surface frame allocated for this view
* #param supportedSizes
* #param surfaceWidth
* #param surfaceHeight
* #return optimal frame size
*/
protected Size calculateCameraFrameSize(List<?> supportedSizes, ListItemAccessor accessor, int surfaceWidth, int surfaceHeight) {
int calcWidth = 0;
int calcHeight = 0;
int maxAllowedWidth = (mMaxWidth != MAX_UNSPECIFIED && mMaxWidth < surfaceWidth)? mMaxWidth : surfaceWidth;
int maxAllowedHeight = (mMaxHeight != MAX_UNSPECIFIED && mMaxHeight < surfaceHeight)? mMaxHeight : surfaceHeight;
for (Object size : supportedSizes) {
int width = accessor.getWidth(size);
int height = accessor.getHeight(size);
if (width <= maxAllowedWidth && height <= maxAllowedHeight) {
if (width >= calcWidth && height >= calcHeight) {
calcWidth = (int) width;
calcHeight = (int) height;
}
}
}
return new Size(calcWidth, calcHeight);
}
}
Any help would be appreciated.
All I could see in your code is that:
Canvas canvas = getHolder().lockCanvas();
On this line you may not be getting right canvas object, put it in try-catch block and try to debug it on this point you might find the issue.
Thanks
I am using the range seek bar in my app.It's working fine but my requirement is set the range between the two thumbs.In default both thumbs are overlapping each other in my case thumbs are not overlapping each other.
How to set the range between the both thumbs in range seek bar?
below is my range seek bar class.In my case difference between the two thumbs is 3.if two thumbs difference is 3,thumbs are can't overlap.how to set range between the thumbs?
here is the class what i used
public class RangeSeekBar<T extends Number> extends ImageView {
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Bitmap thumbImage = BitmapFactory.decodeResource(getResources(), R.drawable.seekcircle_blue);
private final Bitmap thumbPressedImage = BitmapFactory.decodeResource(getResources(), R.drawable.seekcircle_red);
private final float thumbWidth = thumbImage.getWidth();
private final float thumbHalfWidth = 0.5f * thumbWidth;
private final float thumbHalfHeight = 0.5f * thumbImage.getHeight();
private final float lineHeight = 0.8f * thumbHalfHeight;
private final float padding = thumbHalfWidth;
private final T absoluteMinValue, absoluteMaxValue;
private final NumberType numberType;
private final double absoluteMinValuePrim, absoluteMaxValuePrim;
private double normalizedMinValue = 0d;
private double normalizedMaxValue = 1d;
private Thumb pressedThumb = null;
private boolean notifyWhileDragging = false;
private OnRangeSeekBarChangeListener<T> listener;
/**
* Default color of a {#link RangeSeekBar}, #FF33B5E5. This is also known as "Ice Cream Sandwich" blue.
*/
public static final int DEFAULT_COLOR = Color.argb(0xFF, 0, 0, 0);
/**
* An invalid pointer id.
*/
public static final int INVALID_POINTER_ID = 255;
// Localized constants from MotionEvent for compatibility
// with API < 8 "Froyo".
public static final int ACTION_POINTER_UP = 0x6, ACTION_POINTER_INDEX_MASK = 0x0000ff00, ACTION_POINTER_INDEX_SHIFT = 8;
private float mDownMotionX;
private int mActivePointerId = INVALID_POINTER_ID;
/**
* On touch, this offset plus the scaled value from the position of the touch will form the progress value. Usually 0.
*/
float mTouchProgressOffset;
private int mScaledTouchSlop;
private boolean mIsDragging;
/**
* Creates a new RangeSeekBar.
*
* #param absoluteMinValue
* The minimum value of the selectable range.
* #param absoluteMaxValue
* The maximum value of the selectable range.
* #param context
* #throws IllegalArgumentException
* Will be thrown if min/max value type is not one of Long, Double, Integer, Float, Short, Byte or BigDecimal.
*/
public RangeSeekBar(T absoluteMinValue, T absoluteMaxValue, Context context) throws IllegalArgumentException {
super(context);
this.absoluteMinValue = absoluteMinValue;
this.absoluteMaxValue = absoluteMaxValue;
absoluteMinValuePrim = absoluteMinValue.doubleValue();
absoluteMaxValuePrim = absoluteMaxValue.doubleValue();
numberType = NumberType.fromNumber(absoluteMinValue);
// make RangeSeekBar focusable. This solves focus handling issues in case EditText widgets are being used along with the RangeSeekBar within ScollViews.
setFocusable(true);
setFocusableInTouchMode(true);
init();
}
private final void init() {
mScaledTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
}
public boolean isNotifyWhileDragging() {
return notifyWhileDragging;
}
/**
* Should the widget notify the listener callback while the user is still dragging a thumb? Default is false.
*
* #param flag
*/
public void setNotifyWhileDragging(boolean flag) {
this.notifyWhileDragging = flag;
}
/**
* Returns the absolute minimum value of the range that has been set at construction time.
*
* #return The absolute minimum value of the range.
*/
public T getAbsoluteMinValue() {
return absoluteMinValue;
}
/**
* Returns the absolute maximum value of the range that has been set at construction time.
*
* #return The absolute maximum value of the range.
*/
public T getAbsoluteMaxValue() {
return absoluteMaxValue;
}
/**
* Returns the currently selected min value.
*
* #return The currently selected min value.
*/
public T getSelectedMinValue() {
return normalizedToValue(normalizedMinValue);
}
/**
* Sets the currently selected minimum value. The widget will be invalidated and redrawn.
*
* #param value
* The Number value to set the minimum value to. Will be clamped to given absolute minimum/maximum range.
*/
public void setSelectedMinValue(T value) {
// in case absoluteMinValue == absoluteMaxValue, avoid division by zero when normalizing.
if (0 == (absoluteMaxValuePrim - absoluteMinValuePrim)) {
setNormalizedMinValue(0d);
}
else {
setNormalizedMinValue(valueToNormalized(value));
}
}
/**
* Returns the currently selected max value.
*
* #return The currently selected max value.
*/
public T getSelectedMaxValue() {
return normalizedToValue(normalizedMaxValue);
}
/**
* Sets the currently selected maximum value. The widget will be invalidated and redrawn.
*
* #param value
* The Number value to set the maximum value to. Will be clamped to given absolute minimum/maximum range.
*/
public void setSelectedMaxValue(T value) {
// in case absoluteMinValue == absoluteMaxValue, avoid division by zero when normalizing.
if (0 == (absoluteMaxValuePrim - absoluteMinValuePrim)) {
setNormalizedMaxValue(1d);
}
else {
setNormalizedMaxValue(valueToNormalized(value));
}
}
/**
* Registers given listener callback to notify about changed selected values.
*
* #param listener
* The listener to notify about changed selected values.
*/
public void setOnRangeSeekBarChangeListener(OnRangeSeekBarChangeListener<T> listener) {
this.listener = listener;
}
/**
* Handles thumb selection and movement. Notifies listener callback on certain events.
*/
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnabled())
return false;
int pointerIndex;
final int action = event.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
// Remember where the motion event started
mActivePointerId = event.getPointerId(event.getPointerCount() - 1);
pointerIndex = event.findPointerIndex(mActivePointerId);
mDownMotionX = event.getX(pointerIndex);
pressedThumb = evalPressedThumb(mDownMotionX);
// Only handle thumb presses.
if (pressedThumb == null)
return super.onTouchEvent(event);
setPressed(true);
invalidate();
onStartTrackingTouch();
trackTouchEvent(event);
attemptClaimDrag();
break;
case MotionEvent.ACTION_MOVE:
if (pressedThumb != null) {
if (mIsDragging) {
trackTouchEvent(event);
}
else {
// Scroll to follow the motion event
pointerIndex = event.findPointerIndex(mActivePointerId);
final float x = event.getX(pointerIndex);
if (Math.abs(x - mDownMotionX) > mScaledTouchSlop) {
setPressed(true);
invalidate();
onStartTrackingTouch();
trackTouchEvent(event);
attemptClaimDrag();
}
}
if (listener != null) {
listener.onRangeSeekBarValuesChanged(this, getSelectedMinValue(), getSelectedMaxValue());
}
}
break;
case MotionEvent.ACTION_UP:
if (mIsDragging) {
trackTouchEvent(event);
onStopTrackingTouch();
setPressed(false);
}
else {
// Touch up when we never crossed the touch slop threshold
// should be interpreted as a tap-seek to that location.
onStartTrackingTouch();
trackTouchEvent(event);
onStopTrackingTouch();
}
pressedThumb = null;
invalidate();
if (listener != null) {
listener.onRangeSeekBarValuesChanged(this, getSelectedMinValue(), getSelectedMaxValue());
}
break;
case MotionEvent.ACTION_POINTER_DOWN: {
final int index = event.getPointerCount() - 1;
// final int index = ev.getActionIndex();
mDownMotionX = event.getX(index);
mActivePointerId = event.getPointerId(index);
invalidate();
break;
}
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(event);
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
if (mIsDragging) {
onStopTrackingTouch();
setPressed(false);
}
invalidate(); // see above explanation
break;
}
return true;
}
private final void onSecondaryPointerUp(MotionEvent ev) {
final int pointerIndex = (ev.getAction() & ACTION_POINTER_INDEX_MASK) >> ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
// This was our active pointer going up. Choose
// a new active pointer and adjust accordingly.
// TODO: Make this decision more intelligent.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mDownMotionX = ev.getX(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
}
}
private final void trackTouchEvent(MotionEvent event) {
final int pointerIndex = event.findPointerIndex(mActivePointerId);
final float x = event.getX(pointerIndex);
if (Thumb.MIN.equals(pressedThumb)) {
setNormalizedMinValue(screenToNormalized(x));
}
else if (Thumb.MAX.equals(pressedThumb)) {
setNormalizedMaxValue(screenToNormalized(x));
}
}
/**
* Tries to claim the user's drag motion, and requests disallowing any ancestors from stealing events in the drag.
*/
private void attemptClaimDrag() {
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
}
/**
* This is called when the user has started touching this widget.
*/
void onStartTrackingTouch() {
mIsDragging = true;
}
/**
* This is called when the user either releases his touch or the touch is canceled.
*/
void onStopTrackingTouch() {
mIsDragging = false;
}
/**
* Ensures correct size of the widget.
*/
#Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = 200;
if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(widthMeasureSpec)) {
width = MeasureSpec.getSize(widthMeasureSpec);
}
int height = thumbImage.getHeight();
if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(heightMeasureSpec)) {
height = Math.min(height, MeasureSpec.getSize(heightMeasureSpec));
}
setMeasuredDimension(width, height);
}
/**
* Draws the widget on the given canvas.
*/
#Override
protected synchronized void onDraw(Canvas canvas) {
super.onDraw(canvas);
// draw seek bar background line
final RectF rect = new RectF(padding, 0.5f * (getHeight() - lineHeight), getWidth() - padding, 0.5f * (getHeight() + lineHeight));
paint.setStyle(Style.FILL);
paint.setColor(Color.GRAY);
paint.setAntiAlias(true);
canvas.drawRect(rect, paint);
// draw seek bar active range line
rect.left = normalizedToScreen(normalizedMinValue);
rect.right = normalizedToScreen(normalizedMaxValue);
// orange color
paint.setColor(DEFAULT_COLOR);
canvas.drawRect(rect, paint);
// draw minimum thumb
drawThumb(normalizedToScreen(normalizedMinValue), Thumb.MIN.equals(pressedThumb), canvas);
// draw maximum thumb
drawThumb_max(normalizedToScreen(normalizedMaxValue), Thumb.MAX.equals(pressedThumb), canvas);
}
/**
* Overridden to save instance state when device orientation changes. This method is called automatically if you assign an id to the RangeSeekBar widget using the {#link #setId(int)} method. Other members of this class than the normalized min and max values don't need to be saved.
*/
#Override
protected Parcelable onSaveInstanceState() {
final Bundle bundle = new Bundle();
bundle.putParcelable("SUPER", super.onSaveInstanceState());
bundle.putDouble("MIN", normalizedMinValue);
bundle.putDouble("MAX", normalizedMaxValue);
return bundle;
}
/**
* Overridden to restore instance state when device orientation changes. This method is called automatically if you assign an id to the RangeSeekBar widget using the {#link #setId(int)} method.
*/
#Override
protected void onRestoreInstanceState(Parcelable parcel) {
final Bundle bundle = (Bundle) parcel;
super.onRestoreInstanceState(bundle.getParcelable("SUPER"));
normalizedMinValue = bundle.getDouble("MIN");
normalizedMaxValue = bundle.getDouble("MAX");
}
/**
* Draws the "normal" resp. "pressed" thumb image on specified x-coordinate.
*
* #param screenCoord
* The x-coordinate in screen space where to draw the image.
* #param pressed
* Is the thumb currently in "pressed" state?
* #param canvas
* The canvas to draw upon.
*/
private void drawThumb(float screenCoord, boolean pressed, Canvas canvas) {
canvas.drawBitmap(pressed ? thumbImage : thumbImage, screenCoord - thumbHalfWidth, (float) ((0.5f * getHeight()) - thumbHalfHeight), paint);
}
private void drawThumb_max(float screenCoord, boolean pressed, Canvas canvas)
{
canvas.drawBitmap(pressed ? thumbPressedImage : thumbPressedImage, screenCoord - thumbHalfWidth, (float) ((0.5f * getHeight()) - thumbHalfHeight), paint);
}
/**
* Decides which (if any) thumb is touched by the given x-coordinate.
*
* #param touchX
* The x-coordinate of a touch event in screen space.
* #return The pressed thumb or null if none has been touched.
*/
private Thumb evalPressedThumb(float touchX) {
Thumb result = null;
boolean minThumbPressed = isInThumbRange(touchX, normalizedMinValue);
boolean maxThumbPressed = isInThumbRange(touchX, normalizedMaxValue);
if (minThumbPressed && maxThumbPressed) {
// if both thumbs are pressed (they lie on top of each other), choose the one with more room to drag. this avoids "stalling" the thumbs in a corner, not being able to drag them apart anymore.
result = (touchX / getWidth() > 0.5f) ? Thumb.MIN : Thumb.MAX;
}
else if (minThumbPressed) {
result = Thumb.MIN;
}
else if (maxThumbPressed) {
result = Thumb.MAX;
}
return result;
}
/**
* Decides if given x-coordinate in screen space needs to be interpreted as "within" the normalized thumb x-coordinate.
*
* #param touchX
* The x-coordinate in screen space to check.
* #param normalizedThumbValue
* The normalized x-coordinate of the thumb to check.
* #return true if x-coordinate is in thumb range, false otherwise.
*/
private boolean isInThumbRange(float touchX, double normalizedThumbValue) {
return Math.abs(touchX - normalizedToScreen(normalizedThumbValue)) <= thumbHalfWidth;
}
/**
* Sets normalized min value to value so that 0 <= value <= normalized max value <= 1. The View will get invalidated when calling this method.
*
* #param value
* The new normalized min value to set.
*/
public void setNormalizedMinValue(double value) {
normalizedMinValue = Math.max(0d, Math.min(1d, Math.min(value, normalizedMaxValue)));
invalidate();
}
/**
* Sets normalized max value to value so that 0 <= normalized min value <= value <= 1. The View will get invalidated when calling this method.
*
* #param value
* The new normalized max value to set.
*/
public void setNormalizedMaxValue(double value) {
normalizedMaxValue = Math.max(0d, Math.min(1d, Math.max(value, normalizedMinValue)));
invalidate();
}
/**
* Converts a normalized value to a Number object in the value space between absolute minimum and maximum.
*
* #param normalized
* #return
*/
#SuppressWarnings("unchecked")
private T normalizedToValue(double normalized) {
return (T) numberType.toNumber(absoluteMinValuePrim + normalized * (absoluteMaxValuePrim - absoluteMinValuePrim));
}
/**
* Converts the given Number value to a normalized double.
*
* #param value
* The Number value to normalize.
* #return The normalized double.
*/
private double valueToNormalized(T value) {
if (0 == absoluteMaxValuePrim - absoluteMinValuePrim) {
// prevent division by zero, simply return 0.
return 0d;
}
return (value.doubleValue() - absoluteMinValuePrim) / (absoluteMaxValuePrim - absoluteMinValuePrim);
}
/**
* Converts a normalized value into screen space.
*
* #param normalizedCoord
* The normalized value to convert.
* #return The converted value in screen space.
*/
private float normalizedToScreen(double normalizedCoord) {
return (float) (padding + normalizedCoord * (getWidth() - 2 * padding));
}
/**
* Converts screen space x-coordinates into normalized values.
*
* #param screenCoord
* The x-coordinate in screen space to convert.
* #return The normalized value.
*/
private double screenToNormalized(float screenCoord) {
int width = getWidth();
if (width <= 2 * padding) {
// prevent division by zero, simply return 0.
return 0d;
}
else {
double result = (screenCoord - padding) / (width - 2 * padding);
return Math.min(1d, Math.max(0d, result));
}
}
/**
* Callback listener interface to notify about changed range values.
*
* #author Stephan Tittel (stephan.tittel#kom.tu-darmstadt.de)
*
* #param <T>
* The Number type the RangeSeekBar has been declared with.
*/
public interface OnRangeSeekBarChangeListener<T> {
public void onRangeSeekBarValuesChanged(RangeSeekBar<?> bar, T minValue, T maxValue);
}
/**
* Thumb constants (min and max).
*/
private static enum Thumb {
MIN, MAX
};
/**
* Utility enumaration used to convert between Numbers and doubles.
*
* #author Stephan Tittel (stephan.tittel#kom.tu-darmstadt.de)
*
*/
private static enum NumberType {
LONG, DOUBLE, INTEGER, FLOAT, SHORT, BYTE, BIG_DECIMAL;
public static <E extends Number> NumberType fromNumber(E value) throws IllegalArgumentException {
if (value instanceof Long) {
return LONG;
}
if (value instanceof Double) {
return DOUBLE;
}
if (value instanceof Integer) {
return INTEGER;
}
if (value instanceof Float) {
return FLOAT;
}
if (value instanceof Short) {
return SHORT;
}
if (value instanceof Byte) {
return BYTE;
}
if (value instanceof BigDecimal) {
return BIG_DECIMAL;
}
throw new IllegalArgumentException("Number class '" + value.getClass().getName() + "' is not supported");
}
public Number toNumber(double value) {
switch (this) {
case LONG:
return new Long((long) value);
case DOUBLE:
return value;
case INTEGER:
return new Integer((int) value);
case FLOAT:
return new Float(value);
case SHORT:
return new Short((short) value);
case BYTE:
return new Byte((byte) value);
case BIG_DECIMAL:
return new BigDecimal(value);
}
throw new InstantiationError("can't convert " + this + " to a Number object");
}
}}
EDIT:Java Class Code
// create RangeSeekBar as Integer range between 20 and 75
final RangeSeekBar<Integer> seekBar = new RangeSeekBar<Integer>(0, 35, this);
seekBar.setSelectedMinValue(5);
seekBar.setSelectedMaxValue(8);
seekBar.setOnRangeSeekBarChangeListener(new RangeSeekBar.OnRangeSeekBarChangeListener<Integer>() {
#Override
public void onRangeSeekBarValuesChanged(RangeSeekBar<?> bar, Integer minValue, Integer maxValue) {
// handle changed range values
Log.i(TAG, "User selected new range values: MIN=" + minValue + ", MAX=" + maxValue);
int diff=maxValue-minValue;
if(diff==4)
{
}
}
});
// add RangeSeekBar to pre-defined layout
ViewGroup layout = (ViewGroup) findViewById(R.id.layout);
layout.addView(seekBar);
For stopping the motion of thumbs when the difference is 4. You can use
if(diff==4) {
bar.setEnabled(false);
}
For getting more clear go to our blog and see How to disable thumbs while dragging in RangedSeekBar section.
I copy-pasted a basic android snake game code in android studio from here
But i get
java.lang.RuntimeException: Unable to start activity ComponentInfo..........Snake}:
android.view.InflateException: Binary XML file line #5: Error inflating class com.example.android.snake.SnakeView
and the line of error shows that setContentView(R.layout.activity_main); has the error.
I checked various similar questions in stackoverflow, but being an absolute beginner i couldnt figure out what the actual problem is.
EDIT:
heres the snake.java:
package com.example.bharatchamakuri.snakefinal;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.Window;
import android.widget.TextView;
public class Snake extends Activity {
private SnakeView mSnakeView;
private static String ICICLE_KEY = "snake-view";
/**
* Called when Activity is first created. Turns off the title bar, sets up
* the content views, and fires up the SnakeView.
*
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// No Title bar
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
mSnakeView = (SnakeView) findViewById(R.id.snake);
mSnakeView.setTextView((TextView) findViewById(R.id.text));
// Register the listener
mSnakeView.setOnTouchListener((View.OnTouchListener) mSnakeView);
if (savedInstanceState == null) {
// We were just launched -- set up a new game
mSnakeView.setMode(SnakeView.READY);
} else {
// We are being restored
Bundle map = savedInstanceState.getBundle(ICICLE_KEY);
if (map != null) {
mSnakeView.restoreState(map);
} else {
mSnakeView.setMode(SnakeView.PAUSE);
}
}
}
#Override
protected void onPause() {
super.onPause();
// Pause the game along with the activity
mSnakeView.setMode(SnakeView.PAUSE);
}
#Override
public void onSaveInstanceState(Bundle outState) {
// Store the game state
outState.putBundle(ICICLE_KEY, mSnakeView.saveState());
}
}
snakeview.java file :
package com.example.bharatchamakuri.snakefinal;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Random;
/**
* Current mode of application: READY to run, RUNNING, or you have already
* lost. static final ints are used instead of an enum for performance
* reasons.
*/
public class SnakeView extends TileView {
private static final String TAG = "SnakeView";
private int mMode = READY;
public static final int PAUSE = 0;
public static final int READY = 1;
public static final int RUNNING = 2;
public static final int LOSE = 3;
/**
* Current direction the snake is headed.
*/
private int mDirection = NORTH;
private int mNextDirection = NORTH;
private static final int NORTH = 1;
private static final int SOUTH = 2;
private static final int EAST = 3;
private static final int WEST = 4;
/**
* Labels for the drawables that will be loaded into the TileView class
*/
private static final int RED_STAR = 1;
private static final int YELLOW_STAR = 2;
private static final int GREEN_STAR = 3;
/**
* mScore: used to track the number of apples captured mMoveDelay: number of
* milliseconds between snake movements. This will decrease as apples are
* captured.
*/
private long mScore = 0;
private long mMoveDelay = 600;
/**
* mLastMove: tracks the absolute time when the snake last moved, and is
* used to determine if a move should be made based on mMoveDelay.
*/
private long mLastMove;
/**
* mStatusText: text shows to the user in some run states
*/
private TextView mStatusText;
/**
* mSnakeTrail: a list of Coordinates that make up the snake's body
* mAppleList: the secret location of the juicy apples the snake craves.
*/
private ArrayList<Coordinate> mSnakeTrail = new ArrayList<Coordinate>();
private ArrayList<Coordinate> mAppleList = new ArrayList<Coordinate>();
/**
* Everyone needs a little randomness in their life
*/
private static final Random RNG = new Random();
/**
* Create a simple handler that we can use to cause animation to happen. We
* set ourselves as a target and we can use the sleep() function to cause an
* update/invalidate to occur at a later date.
*/
private RefreshHandler mRedrawHandler = new RefreshHandler();
class RefreshHandler extends Handler {
#Override
public void handleMessage(Message msg) {
SnakeView.this.update();
SnakeView.this.invalidate();
}
public void sleep(long delayMillis) {
this.removeMessages(0);
sendMessageDelayed(obtainMessage(0), delayMillis);
}
};
/**
* Constructs a SnakeView based on inflation from XML
*
* #param context
* #param attrs
*/
public SnakeView(Context context, AttributeSet attrs) {
super(context, attrs);
initSnakeView();
}
public SnakeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initSnakeView();
}
private void initSnakeView() {
setFocusable(true);
Resources r = this.getContext().getResources();
resetTiles(4);
loadTile(RED_STAR, r.getDrawable(R.drawable.redstar));
loadTile(YELLOW_STAR, r.getDrawable(R.drawable.yellowstar));
loadTile(GREEN_STAR, r.getDrawable(R.drawable.bluestar));
}
private void initNewGame() {
mSnakeTrail.clear();
mAppleList.clear();
// For now we're just going to load up a short default eastbound snake
// that's just turned north
mSnakeTrail.add(new Coordinate(7, 7));
mSnakeTrail.add(new Coordinate(6, 7));
mSnakeTrail.add(new Coordinate(5, 7));
mSnakeTrail.add(new Coordinate(4, 7));
mSnakeTrail.add(new Coordinate(3, 7));
mSnakeTrail.add(new Coordinate(2, 7));
mNextDirection = NORTH;
// Two apples to start with
addRandomApple();
addRandomApple();
mMoveDelay = 600;
mScore = 0;
}
/**
* Given a ArrayList of coordinates, we need to flatten them into an array
* of ints before we can stuff them into a map for flattening and storage.
*
* #param cvec
* : a ArrayList of Coordinate objects
* #return : a simple array containing the x/y values of the coordinates as
* [x1,y1,x2,y2,x3,y3...]
*/
private int[] coordArrayListToArray(ArrayList<Coordinate> cvec) {
int count = cvec.size();
int[] rawArray = new int[count * 2];
for (int index = 0; index < count; index++) {
Coordinate c = cvec.get(index);
rawArray[2 * index] = c.x;
rawArray[2 * index + 1] = c.y;
}
return rawArray;
}
/**
* Save game state so that the user does not lose anything if the game
* process is killed while we are in the background.
*
* #return a Bundle with this view's state
*/
public Bundle saveState() {
Bundle map = new Bundle();
map.putIntArray("mAppleList", coordArrayListToArray(mAppleList));
map.putInt("mDirection", mDirection);
map.putInt("mNextDirection", mNextDirection);
map.putLong("mMoveDelay", mMoveDelay);
map.putLong("mScore", mScore);
map.putIntArray("mSnakeTrail", coordArrayListToArray(mSnakeTrail));
return map;
}
/**
* Given a flattened array of ordinate pairs, we reconstitute them into a
* ArrayList of Coordinate objects
*
* #param rawArray
* : [x1,y1,x2,y2,...]
* #return a ArrayList of Coordinates
*/
private ArrayList<Coordinate> coordArrayToArrayList(int[] rawArray) {
ArrayList<Coordinate> coordArrayList = new ArrayList<Coordinate>();
int coordCount = rawArray.length;
for (int index = 0; index < coordCount; index += 2) {
Coordinate c = new Coordinate(rawArray[index], rawArray[index + 1]);
coordArrayList.add(c);
}
return coordArrayList;
}
/**
* Restore game state if our process is being relaunched
*
* #param icicle
* a Bundle containing the game state
*/
public void restoreState(Bundle icicle) {
setMode(PAUSE);
mAppleList = coordArrayToArrayList(icicle.getIntArray("mAppleList"));
mDirection = icicle.getInt("mDirection");
mNextDirection = icicle.getInt("mNextDirection");
mMoveDelay = icicle.getLong("mMoveDelay");
mScore = icicle.getLong("mScore");
mSnakeTrail = coordArrayToArrayList(icicle.getIntArray("mSnakeTrail"));
}
/*
* handles key events in the game. Update the direction our snake is
* traveling based on the DPAD. Ignore events that would cause the snake to
* immediately turn back on itself.
*
* (non-Javadoc)
*
* #see android.view.View#onKeyDown(int, android.os.KeyEvent)
*/
#Override
public boolean onKeyDown(int keyCode, KeyEvent msg) {
if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
if (mMode == READY | mMode == LOSE) {
/*
* At the beginning of the game, or the end of a previous one,
* we should start a new game.
*/
initNewGame();
setMode(RUNNING);
update();
return (true);
}
if (mMode == PAUSE) {
/*
* If the game is merely paused, we should just continue where
* we left off.
*/
setMode(RUNNING);
update();
return (true);
}
if (mDirection != SOUTH) {
mNextDirection = NORTH;
}
return (true);
}
if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
if (mDirection != NORTH) {
mNextDirection = SOUTH;
}
return (true);
}
if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
if (mDirection != EAST) {
mNextDirection = WEST;
}
return (true);
}
if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
if (mDirection != WEST) {
mNextDirection = EAST;
}
return (true);
}
return super.onKeyDown(keyCode, msg);
}
public boolean onTouch (View v, MotionEvent event)
{
float x = event.getX();
float y = event.getY();
float height = this.getHeight();
float width = this.getWidth();
float slope = height/width;
// Only process DOWN action, so it responds as soon as the
// screen is touched.
if (event.getAction()==MotionEvent.ACTION_DOWN)
{
// Touch event UP
if ((y < slope*x) && (y < -slope*x + height)) {
if (mMode == READY | mMode == LOSE) {
// At the beginning of the game, or the end of a previous one,
// we should start a new game.
initNewGame();
setMode(RUNNING);
update();
return (true);
}
if (mMode == PAUSE) {
// If the game is merely paused, we should just continue where
// we left off.
setMode(RUNNING);
update();
return (true);
}
if (mDirection != SOUTH) {
mNextDirection = NORTH;
}
return (true);
}
// Touch event DOWN
if ((y > slope*x) && (y > -slope*x + height)) {
if (mDirection != NORTH) {
mNextDirection = SOUTH;
}
return (true);
}
// Touch event LEFT
if ((y > slope*x) && (y < (-slope*x + height))) {
if (mDirection != EAST) {
mNextDirection = WEST;
}
return (true);
}
// Touch event RIGHT
if ((y < slope*x) && (y > -slope*x + height)) {
if (mDirection != WEST) {
mNextDirection = EAST;
}
return (true);
}
}
return false;
}
/**
* Sets the TextView that will be used to give information (such as "Game
* Over" to the user.
*
* #param newView
*/
public void setTextView(TextView newView) {
mStatusText = newView;
}
/**
* Updates the current mode of the application (RUNNING or PAUSED or the
* like) as well as sets the visibility of textview for notification
*
* #param newMode
*/
public void setMode(int newMode) {
int oldMode = mMode;
mMode = newMode;
if (newMode == RUNNING & oldMode != RUNNING) {
mStatusText.setVisibility(View.INVISIBLE);
update();
return;
}
Resources res = getContext().getResources();
CharSequence str = "";
if (newMode == PAUSE) {
str = res.getText(R.string.mode_pause);
}
if (newMode == READY) {
str = res.getText(R.string.mode_ready);
}
if (newMode == LOSE) {
str = res.getString(R.string.mode_lose_prefix) + mScore
+ res.getString(R.string.mode_lose_suffix);
}
mStatusText.setText(str);
mStatusText.setVisibility(View.VISIBLE);
}
/**
* Selects a random location within the garden that is not currently covered
* by the snake. Currently _could_ go into an infinite loop if the snake
* currently fills the garden, but we'll leave discovery of this prize to a
* truly excellent snake-player.
*
*/
private void addRandomApple() {
Coordinate newCoord = null;
boolean found = false;
while (!found) {
// Choose a new location for our apple
int newX = 1 + RNG.nextInt(mXTileCount - 2);
int newY = 1 + RNG.nextInt(mYTileCount - 2);
newCoord = new Coordinate(newX, newY);
// Make sure it's not already under the snake
boolean collision = false;
int snakelength = mSnakeTrail.size();
for (int index = 0; index < snakelength; index++) {
if (mSnakeTrail.get(index).equals(newCoord)) {
collision = true;
}
}
// if we're here and there's been no collision, then we have
// a good location for an apple. Otherwise, we'll circle back
// and try again
found = !collision;
}
if (newCoord == null) {
Log.e(TAG, "Somehow ended up with a null newCoord!");
}
mAppleList.add(newCoord);
}
/**
* Handles the basic update loop, checking to see if we are in the running
* state, determining if a move should be made, updating the snake's
* location.
*/
public void update() {
if (mMode == RUNNING) {
long now = System.currentTimeMillis();
if (now - mLastMove > mMoveDelay) {
clearTiles();
updateWalls();
updateSnake();
updateApples();
mLastMove = now;
}
mRedrawHandler.sleep(mMoveDelay);
}
}
/**
* Draws some walls.
*
*/
private void updateWalls() {
for (int x = 0; x < mXTileCount; x++) {
setTile(GREEN_STAR, x, 0);
setTile(GREEN_STAR, x, mYTileCount - 1);
}
for (int y = 1; y < mYTileCount - 1; y++) {
setTile(GREEN_STAR, 0, y);
setTile(GREEN_STAR, mXTileCount - 1, y);
}
}
/**
* Draws some apples.
*
*/
private void updateApples() {
for (Coordinate c : mAppleList) {
setTile(YELLOW_STAR, c.x, c.y);
}
}
/**
* Figure out which way the snake is going, see if he's run into anything
* (the walls, himself, or an apple). If he's not going to die, we then add
* to the front and subtract from the rear in order to simulate motion. If
* we want to grow him, we don't subtract from the rear.
*
*/
private void updateSnake() {
boolean growSnake = false;
// grab the snake by the head
Coordinate head = mSnakeTrail.get(0);
Coordinate newHead = new Coordinate(1, 1);
mDirection = mNextDirection;
switch (mDirection) {
case EAST: {
newHead = new Coordinate(head.x + 1, head.y);
break;
}
case WEST: {
newHead = new Coordinate(head.x - 1, head.y);
break;
}
case NORTH: {
newHead = new Coordinate(head.x, head.y - 1);
break;
}
case SOUTH: {
newHead = new Coordinate(head.x, head.y + 1);
break;
}
}
// Collision detection
// For now we have a 1-square wall around the entire arena
if ((newHead.x < 1) || (newHead.y < 1) || (newHead.x > mXTileCount - 2)
|| (newHead.y > mYTileCount - 2)) {
setMode(LOSE);
return;
}
// Look for collisions with itself
int snakelength = mSnakeTrail.size();
for (int snakeindex = 0; snakeindex < snakelength; snakeindex++) {
Coordinate c = mSnakeTrail.get(snakeindex);
if (c.equals(newHead)) {
setMode(LOSE);
return;
}
}
// Look for apples
int applecount = mAppleList.size();
for (int appleindex = 0; appleindex < applecount; appleindex++) {
Coordinate c = mAppleList.get(appleindex);
if (c.equals(newHead)) {
mAppleList.remove(c);
addRandomApple();
mScore++;
mMoveDelay *= 0.9;
growSnake = true;
}
}
// push a new head onto the ArrayList and pull off the tail
mSnakeTrail.add(0, newHead);
// except if we want the snake to grow
if (!growSnake) {
mSnakeTrail.remove(mSnakeTrail.size() - 1);
}
int index = 0;
for (Coordinate c : mSnakeTrail) {
if (index == 0) {
setTile(YELLOW_STAR, c.x, c.y);
} else {
setTile(RED_STAR, c.x, c.y);
}
index++;
}
}
/**
* Simple class containing two integer values and a comparison function.
* There's probably something I should use instead, but this was quick and
* easy to build.
*
*/
private class Coordinate {
public int x;
public int y;
public Coordinate(int newX, int newY) {
x = newX;
y = newY;
}
public boolean equals(Coordinate other) {
if (x == other.x && y == other.y) {
return true;
}
return false;
}
#Override
public String toString() {
return "Coordinate: [" + x + "," + y + "]";
}
}
}
tileview.java :
package com.example.bharatchamakuri.snakenewww;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
/**
* TileView: a View-variant designed for handling arrays of "icons" or other
* drawables.
*
*/
class TileView extends View {
/**
* Parameters controlling the size of the tiles and their range within view.
* Width/Height are in pixels, and Drawables will be scaled to fit to these
* dimensions. X/Y Tile Counts are the number of tiles that will be drawn.
*/
protected static int mTileSize;
protected static int mXTileCount;
protected static int mYTileCount;
private static int mXOffset;
private static int mYOffset;
/**
* A hash that maps integer handles specified by the subclasser to the
* drawable that will be used for that reference
*/
private Bitmap[] mTileArray;
/**
* A two-dimensional array of integers in which the number represents the
* index of the tile that should be drawn at that locations
*/
private int[][] mTileGrid;
private final Paint mPaint = new Paint();
public TileView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.TileView);
mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);
a.recycle();
}
public TileView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.TileView);
mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);
a.recycle();
}
/**
* Rests the internal array of Bitmaps used for drawing tiles, and sets the
* maximum index of tiles to be inserted
*
* #param tilecount
*/
public void resetTiles(int tilecount) {
mTileArray = new Bitmap[tilecount];
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mXTileCount = (int) Math.floor(w / mTileSize);
mYTileCount = (int) Math.floor(h / mTileSize);
mXOffset = ((w - (mTileSize * mXTileCount)) / 2);
mYOffset = ((h - (mTileSize * mYTileCount)) / 2);
mTileGrid = new int[mXTileCount][mYTileCount];
clearTiles();
}
/**
* Function to set the specified Drawable as the tile for a particular
* integer key.
*
* #param key
* #param tile
*/
public void loadTile(int key, Drawable tile) {
Bitmap bitmap = Bitmap.createBitmap(mTileSize, mTileSize,
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
tile.setBounds(0, 0, mTileSize, mTileSize);
tile.draw(canvas);
mTileArray[key] = bitmap;
}
/**
* Resets all tiles to 0 (empty)
*
*/
public void clearTiles() {
for (int x = 0; x < mXTileCount; x++) {
for (int y = 0; y < mYTileCount; y++) {
setTile(0, x, y);
}
}
}
/**
* Used to indicate that a particular tile (set with loadTile and referenced
* by an integer) should be drawn at the given x/y coordinates during the
* next invalidate/draw cycle.
*
* #param tileindex
* #param x
* #param y
*/
public void setTile(int tileindex, int x, int y) {
mTileGrid[x][y] = tileindex;
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int x = 0; x < mXTileCount; x += 1) {
for (int y = 0; y < mYTileCount; y += 1) {
if (mTileGrid[x][y] > 0) {
canvas.drawBitmap(mTileArray[mTileGrid[x][y]], mXOffset + x
* mTileSize, mYOffset + y * mTileSize, mPaint);
}
}
}
}
}
and the logcat :
> 11-13 00:38:43.336 29231-29231/com.example.bharatchamakuri.snakefinal
> E/AndroidRuntime﹕ FATAL EXCEPTION: main
> Process: com.example.bharatchamakuri.snakefinal, PID: 29231
> java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.bharatchamakuri.snakefinal/com.example.bharatchamakuri.snakefinal.Snake}:
> android.view.InflateException: Binary XML file line #5: Error
> inflating class com.example.android.snake.SnakeView
> at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2338)
> at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2390)
> at android.app.ActivityThread.access$800(ActivityThread.java:151)
> at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1321)
> at android.os.Handler.dispatchMessage(Handler.java:110)
> at android.os.Looper.loop(Looper.java:193)
> at android.app.ActivityThread.main(ActivityThread.java:5292)
> at java.lang.reflect.Method.invokeNative(Native Method)
> at java.lang.reflect.Method.invoke(Method.java:515)
> at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:824)
> at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:640)
> at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)
> at dalvik.system.NativeStart.main(Native Method)
> Caused by: android.view.InflateException: Binary XML file line #5: Error inflating class com.example.android.snake.SnakeView
> at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:707)
> at android.view.LayoutInflater.rInflate(LayoutInflater.java:755)
> at android.view.LayoutInflater.inflate(LayoutInflater.java:492)
> at de.robv.android.xposed.XposedBridge.invokeOriginalMethodNative(Native
> Method)
> at de.robv.android.xposed.XposedBridge.handleHookedMethod(XposedBridge.java:631)
> at android.view.LayoutInflater.inflate(Native Method)
> at android.view.LayoutInflater.inflate(LayoutInflater.java:397)
> at android.view.LayoutInflater.inflate(LayoutInflater.java:353)
> at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:305)
> at android.app.Activity.setContentView(Activity.java:1944)
> at com.example.bharatchamakuri.snakefinal.Snake.onCreate(Snake.java:55)
> at android.app.Activity.performCreate(Activity.java:5264)
> at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1088)
> at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2302)
> at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2390)
> at android.app.ActivityThread.access$800(ActivityThread.java:151)
> at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1321)
> at android.os.Handler.dispatchMessage(Handler.java:110)
> at android.os.Looper.loop(Looper.java:193)
> at android.app.ActivityThread.main(ActivityThread.java:5292)
> at java.lang.reflect.Method.invokeNative(Native Method)
> at java.lang.reflect.Method.invoke(Method.java:515)
> at
The error says "Error inflating class com.example.android.snake.SnakeView" but your SnakeView class is "com.example.bharatchamakuri.snakefinal.SnakeView". Those are different, which is why it can't load the class.
Did you forget to change the name of the SnakeView class in your layout file perhaps?
I have searched for lot of threads but still I am facing the issue.
I have to find out if a lat/lng is inside or outside the polygon. I have used following method:
private boolean LineIntersect(LatLng tap, LatLng vertA, LatLng vertB) {
double aY = vertA.latitude;
double bY = vertB.latitude;
double aX = vertA.longitude;
double bX = vertB.longitude;
double pY = tap.latitude;
double pX = tap.longitude;
if ( (aY>pY && bY>pY) || (aY<pY && bY<pY) || (aX<pX && bX<pX) ) {
return false; }
double m = (aY-bY) / (aX-bX);
double bee = (-aX) * m + aY; // y = mx + b
double x = (pY - bee) / m;
return x > pX;
}
private boolean isPointInPolygon(LatLng tap, ArrayList<LatLng> vertices) {
int intersectCount = 0;
for(int j=0; j<vertices.size()-1; j++) {
if( LineIntersect(tap, vertices.get(j), vertices.get(j+1)) ) {
intersectCount++;
}
}
return ((intersectCount%2)==1); // odd = inside, even = outside;
}
I am calling it as:
if(isPointInPolygon(mLatLng, points))
{
Toast.makeText(getApplicationContext(), "inside",
Toast.LENGTH_LONG).show();
}
else
{
Toast.makeText(getApplicationContext(), "outside",
Toast.LENGTH_LONG).show();
}
The problem I am getting is, when I am inside the geofence I am getting both true and false. WHenever I am inside the geofence I am getting 2 toasts inside and outside both. Please let me know where I am wrong.
I have created geofence app that successfully worked
here is the code i used this https://github.com/sromku/polygon-contains-point to figure out our location inside or outside of polygon.
here is the code
public class MainActivity extends Activity {
private double Longitude, Latitude;
private LocationListener locListener;
private LocationManager lm;
private ProgressDialog gpsProgressDialog;
private Button button_gps;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button_gps = (Button)findViewById(R.id.button_gps);
lm = (LocationManager) MainActivity.this.getSystemService(MainActivity.this.LOCATION_SERVICE);
button_gps.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
getLoction();
// testSimplePolygon();
// testPolygonWithHoles();
}
});
}
class GpsLogation implements LocationListener {
#Override
public void onLocationChanged(Location location) {
if (location != null) {
lm.removeUpdates(locListener);
Longitude = location.getLongitude();
Latitude = location.getLatitude();
/* Toast.makeText(MainActivity.this,
"Longitude :" + Longitude + " Latitude :" + Latitude,
Toast.LENGTH_LONG).show();*/
Polygon polygon = Polygon.Builder()
.addVertex(new Point(6.048857f, 80.211021f)) // change polygon according to your location
.addVertex(new Point(6.048137f, 80.210546f))
.addVertex(new Point(6.048590f, 80.210197f))
.addVertex(new Point(6.049163f, 80.211050f)).build();
// isInside(polygon, new Point((float)Latitude, (float)Longitude));
if( polygon.contains(new Point((float)Latitude, (float)Longitude))==true)
{
Toast.makeText(MainActivity.this, "You are inside", Toast.LENGTH_LONG).show();
}
else
{
Toast.makeText(MainActivity.this, "You are outside", Toast.LENGTH_LONG).show();
}
// /////////////do something//////////////
// insertSalesOrder();
Log.i("TTTAG", "Latitude:" + Latitude);
Log.i("TTTAG", "Longitude:" + Longitude);
if (gpsProgressDialog.isShowing()) {
gpsProgressDialog.dismiss();
}
}
}
#Override
public void onProviderDisabled(String arg0) {
// TODO Auto-generated method stub
}
#Override
public void onProviderEnabled(String arg0) {
// TODO Auto-generated method stub
}
#Override
public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
// TODO Auto-generated method stub
}
}
public void getLoction() {
locListener = new GpsLogation();
boolean enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (!enabled) {
Toast.makeText(MainActivity.this, "Please switch on the GPS",Toast.LENGTH_LONG).show();
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
MainActivity.this.startActivity(intent);
return;
}
Log.i("GPS INFO", " OK Gps Is ON");
gpsProgressDialog = ProgressDialog.show(MainActivity.this, "", "Please wait ... ", true);
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0,locListener);
}
// Here is the polygon java class and i'm not author of it
public class Polygon
{
private final BoundingBox _boundingBox;
private final List<Line> _sides;
private Polygon(List<Line> sides, BoundingBox boundingBox)
{
_sides = sides;
_boundingBox = boundingBox;
}
/**
* Get the builder of the polygon
*
* #return The builder
*/
public static Builder Builder()
{
return new Builder();
}
/**
* Builder of the polygon
*
* #author Roman Kushnarenko (sromku#gmail.com)
*/
public static class Builder
{
private List<Point> _vertexes = new ArrayList<Point>();
private List<Line> _sides = new ArrayList<Line>();
private BoundingBox _boundingBox = null;
private boolean _firstPoint = true;
private boolean _isClosed = false;
/**
* Add vertex points of the polygon.<br>
* It is very important to add the vertexes by order, like you were drawing them one by one.
*
* #param point
* The vertex point
* #return The builder
*/
public Builder addVertex(Point point)
{
if (_isClosed)
{
// each hole we start with the new array of vertex points
_vertexes = new ArrayList<Point>();
_isClosed = false;
}
updateBoundingBox(point);
_vertexes.add(point);
// add line (edge) to the polygon
if (_vertexes.size() > 1)
{
Line Line = new Line(_vertexes.get(_vertexes.size() - 2), point);
_sides.add(Line);
}
return this;
}
/**
* Close the polygon shape. This will create a new side (edge) from the <b>last</b> vertex point to the <b>first</b> vertex point.
*
* #return The builder
*/
public Builder close()
{
validate();
// add last Line
_sides.add(new Line(_vertexes.get(_vertexes.size() - 1), _vertexes.get(0)));
_isClosed = true;
return this;
}
/**
* Build the instance of the polygon shape.
*
* #return The polygon
*/
public Polygon build()
{
validate();
// in case you forgot to close
if (!_isClosed)
{
// add last Line
_sides.add(new Line(_vertexes.get(_vertexes.size() - 1), _vertexes.get(0)));
}
Polygon polygon = new Polygon(_sides, _boundingBox);
return polygon;
}
/**
* Update bounding box with a new point.<br>
*
* #param point
* New point
*/
private void updateBoundingBox(Point point)
{
if (_firstPoint)
{
_boundingBox = new BoundingBox();
_boundingBox.xMax = point.x;
_boundingBox.xMin = point.x;
_boundingBox.yMax = point.y;
_boundingBox.yMin = point.y;
_firstPoint = false;
}
else
{
// set bounding box
if (point.x > _boundingBox.xMax)
{
_boundingBox.xMax = point.x;
}
else if (point.x < _boundingBox.xMin)
{
_boundingBox.xMin = point.x;
}
if (point.y > _boundingBox.yMax)
{
_boundingBox.yMax = point.y;
}
else if (point.y < _boundingBox.yMin)
{
_boundingBox.yMin = point.y;
}
}
}
private void validate()
{
if (_vertexes.size() < 3)
{
throw new RuntimeException("Polygon must have at least 3 points");
}
}
}
/**
* Check if the the given point is inside of the polygon.<br>
*
* #param point
* The point to check
* #return <code>True</code> if the point is inside the polygon, otherwise return <code>False</code>
*/
public boolean contains(Point point)
{
if (inBoundingBox(point))
{
Line ray = createRay(point);
int intersection = 0;
for (Line side : _sides)
{
if (intersect(ray, side))
{
// System.out.println("intersection++");
intersection++;
}
}
/*
* If the number of intersections is odd, then the point is inside the polygon
*/
if (intersection % 2 == 1)
{
return true;
}
}
return false;
}
public List<Line> getSides()
{
return _sides;
}
/**
* By given ray and one side of the polygon, check if both lines intersect.
*
* #param ray
* #param side
* #return <code>True</code> if both lines intersect, otherwise return <code>False</code>
*/
private boolean intersect(Line ray, Line side)
{
Point intersectPoint = null;
// if both vectors aren't from the kind of x=1 lines then go into
if (!ray.isVertical() && !side.isVertical())
{
// check if both vectors are parallel. If they are parallel then no intersection point will exist
if (ray.getA() - side.getA() == 0)
{
return false;
}
float x = ((side.getB() - ray.getB()) / (ray.getA() - side.getA())); // x = (b2-b1)/(a1-a2)
float y = side.getA() * x + side.getB(); // y = a2*x+b2
intersectPoint = new Point(x, y);
}
else if (ray.isVertical() && !side.isVertical())
{
float x = ray.getStart().x;
float y = side.getA() * x + side.getB();
intersectPoint = new Point(x, y);
}
else if (!ray.isVertical() && side.isVertical())
{
float x = side.getStart().x;
float y = ray.getA() * x + ray.getB();
intersectPoint = new Point(x, y);
}
else
{
return false;
}
// System.out.println("Ray: " + ray.toString() + " ,Side: " + side);
// System.out.println("Intersect point: " + intersectPoint.toString());
if (side.isInside(intersectPoint) && ray.isInside(intersectPoint))
{
return true;
}
return false;
}
/**
* Create a ray. The ray will be created by given point and on point outside of the polygon.<br>
* The outside point is calculated automatically.
*
* #param point
* #return
*/
private Line createRay(Point point)
{
// create outside point
float epsilon = (_boundingBox.xMax - _boundingBox.xMin) / 100f;
Point outsidePoint = new Point(_boundingBox.xMin - epsilon, _boundingBox.yMin);
Line vector = new Line(outsidePoint, point);
return vector;
}
/**
* Check if the given point is in bounding box
*
* #param point
* #return <code>True</code> if the point in bounding box, otherwise return <code>False</code>
*/
private boolean inBoundingBox(Point point)
{
if (point.x < _boundingBox.xMin || point.x > _boundingBox.xMax || point.y < _boundingBox.yMin || point.y > _boundingBox.yMax)
{
return false;
}
return true;
}
private static class BoundingBox
{
public float xMax = Float.NEGATIVE_INFINITY;
public float xMin = Float.NEGATIVE_INFINITY;
public float yMax = Float.NEGATIVE_INFINITY;
public float yMin = Float.NEGATIVE_INFINITY;
}
}
//// Here is the Line java class and i'm not author of it
public class Line
{
private final Point _start;
private final Point _end;
private float _a = Float.NaN;
private float _b = Float.NaN;
private boolean _vertical = false;
public Line(Point start, Point end)
{
_start = start;
_end = end;
if (_end.x - _start.x != 0)
{
_a = ((_end.y - _start.y) / (_end.x - _start.x));
_b = _start.y - _a * _start.x;
}
else
{
_vertical = true;
}
}
/**
* Indicate whereas the point lays on the line.
*
* #param point
* - The point to check
* #return <code>True</code> if the point lays on the line, otherwise return <code>False</code>
*/
public boolean isInside(Point point)
{
float maxX = _start.x > _end.x ? _start.x : _end.x;
float minX = _start.x < _end.x ? _start.x : _end.x;
float maxY = _start.y > _end.y ? _start.y : _end.y;
float minY = _start.y < _end.y ? _start.y : _end.y;
if ((point.x >= minX && point.x <= maxX) && (point.y >= minY && point.y <= maxY))
{
return true;
}
return false;
}
/**
* Indicate whereas the line is vertical. <br>
* For example, line like x=1 is vertical, in other words parallel to axis Y. <br>
* In this case the A is (+/-)infinite.
*
* #return <code>True</code> if the line is vertical, otherwise return <code>False</code>
*/
public boolean isVertical()
{
return _vertical;
}
/**
* y = <b>A</b>x + B
*
* #return The <b>A</b>
*/
public float getA()
{
return _a;
}
/**
* y = Ax + <b>B</b>
*
* #return The <b>B</b>
*/
public float getB()
{
return _b;
}
/**
* Get start point
*
* #return The start point
*/
public Point getStart()
{
return _start;
}
/**
* Get end point
*
* #return The end point
*/
public Point getEnd()
{
return _end;
}
#Override
public String toString()
{
return String.format("%s-%s", _start.toString(), _end.toString());
}
}
//// Here is the point java class and i'm not author of it
public class Point
{
public Point(float x, float y)
{
this.x = x;
this.y = y;
}
public float x;
public float y;
#Override
public String toString()
{
return String.format("(%.2f,%.2f)", x, y);
}
}
/*I coded only Mainactivity class other owner of other classes is https://github.com/sromku and all credit goes to him*/