Detect touch outside of View - android

I have a custom View and I wonder, is there any chace that I could detect if something outside of my View is clicked. It MUST be in the View class, or else it can not work as I intended it to!
Full file: https://github.com/Nicba1010/AndroidLibrary/blob/master/src/com/nicba1010/utils/views/PieChartView.java
OnTouchEvent(so you know I did something)
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
selected = null;
invalidate();
return true;
}
double deltaX = event.getX() - rect.width() / 2 - rect.left;
double deltaY = -(event.getY() - rect.height() / 2 - rect.top);
double fromMid = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
boolean inCircle = fromMid < (rectSelect.bottom / 2);
if (inCircle) {
double angleInDegrees = getPositionOnCircumference(deltaX, deltaY);
float percentage = (float) (angleInDegrees / 360f);
float totalPerc = 0;
int index = -1, i = 0;
for (PieChartSlice e : slices) {
if (percentage > totalPerc) {
index = i;
} else if (percentage < totalPerc) {
break;
}
totalPerc += e.getPercentage();
i++;
}
if (index == -1) {
Toast.makeText(getContext(), "ERROR", Toast.LENGTH_LONG).show();
} else {
final PieChartSlice tmp = slices.get(index);
addScaleTask(rect, 500, 0.9f, new Runnable() {
#Override
public void run() {
selected = tmp;
}
});
selected = slices.get(index);
invalidate();
Toast.makeText(getContext(), selected.getName(),
Toast.LENGTH_SHORT).show();
onSliceSelectedListener.onSliceSelected(this, selected);
}
} else {
if (selected != null) {
addScaleTask(rect, 500, 1f, new Runnable() {
#Override
public void run() {
selected = null;
}
});
invalidate();
}
}
return super.onTouchEvent(event);
}

View has the getHitRect(Rect) method. You can use to retrieve it hit rect of this view and you can use this rect to check it the MotionEvent it is inside it. For instance
Rect rect = new Rect();
getHitRect(rect);
if (rect.contains((int) event.getX(), (int) event.getY()) {
}

Related

I can not use MotionEvent with Rajawali

Rajawali Version or Branch
rajawali:1.0.186
Device and Android Version
ADV Nexus 5X API 19 Android 4.4
Summary
I'm trying to make the camera moves when a MotionEvent (touch) occurs. I tried the code from https://github.com/Rajawali/Rajawali/issues/428 but it did not work for me.
My code
public class Renderer extends RajawaliRenderer {
public Context context;
private DirectionalLight directionalLight;
public Renderer(Context context) {
super(context);
this.context = context;
setFrameRate(60);
}
private Object3D object, star;
public void initScene(){
getCurrentScene().setBackgroundColor(Color.rgb(5,104,255));
directionalLight = new DirectionalLight(1f, .2f, -1.0f);
directionalLight.setColor(1.0f, 1.0f, 1.0f);
directionalLight.setPower(2);
getCurrentScene().addLight(directionalLight);
//LoaderOBJ objParser = new LoaderOBJ(this,"Load/1c_obj");
LoaderOBJ objParser = new LoaderOBJ(mContext.getResources(),mTextureManager, R.raw.primo_obj);
LoaderOBJ starParser = new LoaderOBJ(mContext.getResources(),mTextureManager,R.raw.star_obj);
try {
objParser.parse();
starParser.parse();
object = objParser.getParsedObject();
star = starParser.getParsedObject();
getCurrentScene().addChild(star);
getCurrentScene().addChild(object);
} catch (ParsingException e) {
e.printStackTrace();
}
getCurrentCamera().setLookAt(object.getWorldPosition());
Log.d("->",getCurrentCamera().getX()+","+getCurrentCamera().getY()+","+getCurrentCamera().getZ());
getCurrentCamera().setZ(40);
}
#Override
public void onRender(final long elapsedTime, final double deltaTime) {
super.onRender(elapsedTime, deltaTime);
Camera cam = getCurrentCamera();
Vector3 s = cam.getPosition();
if (touchTurn != 0) {
if (flagMulti) {
Double r = Math.sqrt(s.x * s.x + s.z * s.z);
angle += touchTurn;
angle %= 360;
cam.setPosition((float) (r * Math.cos(angle)), s.y, (float) (r * Math.sin(angle)));
cam.setLookAt(object.getLookAt());
}
else{
s.x+=touchTurn*5;
cam.setPosition(s);
}
touchTurn = 0;
}
if (touchTurnUp != 0) {
if (!flagMulti) {
s.z += touchTurnUp * 5;
cam.setPosition(s);
}
touchTurnUp = 0;
}
}
#Override
public void onTouchEvent(MotionEvent me){
Log.d("touch ","Log touch");
int pointerCount = me.getPointerCount();
if(pointerCount == 2)
flagMulti = true;
if (me.getAction() == MotionEvent.ACTION_DOWN) {
xpos = me.getX();
ypos = me.getY();
}
if (me.getAction() == MotionEvent.ACTION_UP) {
xpos = -1;
ypos = -1;
touchTurn = 0;
touchTurnUp = 0;
}
if (me.getAction() == MotionEvent.ACTION_MOVE) {
float xd = me.getX() - xpos;
float yd = me.getY() - ypos;
xpos = me.getX();
ypos = me.getY();
touchTurn = xd / -100f;
touchTurnUp = yd / -100f;
Log.d("touchTurn ", touchTurn+" - "+touchTurnUp);
}
try {
Thread.sleep(15);
} catch (Exception e) {
}
}
public void onOffsetsChanged(float x, float y, float z, float w, int i, int j){
}}
Would you consider arcballCamera and target to your 3D object. You can then rotate the 3D object and zoom in/out with arcballCamera.

How to add multiple pin marker on image dyanamically

I want to put marker on image with below functionality
-Dynamically adding multiple markers on image with zoom and scroll functionality on image.
-Blink animation on marker after added on image.
-Marker can be click-able and drag & drop on image.
-Marker's x & y position on image to sync same with other devices
I have used https://github.com/davemorrissey/subsampling-scale-image-view library
Java Code :
public class PinMarkerView extends SubsamplingScaleImageView implements View.OnTouchListener {
private PointF sPin;
private Bitmap pin;
private int resId;
public float vX;
public float vY;
private PointF vPrevious;
private PointF vStart;
private boolean drawing = false;
private boolean markerTouch = false;
private int strokeWidth;
private ArrayList<MapPins> allPins = new ArrayList<MapPins>();
private ArrayList<DrawPins> drawnPins = new ArrayList<DrawPins>();
public PinView(Context context) {
this(context, null);
}
public PinView(Context context, AttributeSet attr) {
super(context, attr);
// initialise();
}
public void setPin(PointF sPin, #DrawableRes int resid, String extras) {
MapPins mapPins = new MapPins();
mapPins.setPointF(sPin);
mapPins.setResId(resid);
mapPins.setExtrasData(extras);
mapPins.setX(sPin.x);
mapPins.setY(sPin.y);
allPins.add(mapPins);
//this.sPin = sPin;
// this.resId = resid;
initialise();
invalidate();
}
public PointF getPin() {
return sPin;
}
public Bitmap getMarker() {
return pin;
}
private void initialise() {
setOnTouchListener(this);
float density = getResources().getDisplayMetrics().densityDpi;
strokeWidth = (int) (density / 60f);
for (int i = 0; i < allPins.size(); i++) {
MapPins mapPins = allPins.get(i);
Bitmap localpin = BitmapFactory.decodeResource(this.getResources(), mapPins.getResId());
float w = (density / 100f) * localpin.getWidth();
float h = (density / 100f) * localpin.getHeight();
//pin = Bitmap.createScaledBitmap(pin, (int) w, (int) h, true);
pin = Bitmap.createScaledBitmap(localpin, localpin.getWidth(), localpin.getHeight(), true);
mapPins.setCreatedBitmap(pin);
allPins.set(i, mapPins);
//-------
}
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Don't draw pin before image is ready so it doesn't move around during setup.
if (!isReady()) {
return;
}
Paint paint = new Paint();
paint.setAntiAlias(true);
for (int i = 0; i < allPins.size(); i++) {
MapPins mapPins = allPins.get(i);
if (mapPins.getPointF() != null && mapPins.getCreatedBitmap() != null) {
PointF vPin = sourceToViewCoord(mapPins.getPointF());
vX = vPin.x - (mapPins.getCreatedBitmap().getWidth() / 2);
vY = vPin.y - mapPins.getCreatedBitmap().getHeight();
canvas.drawBitmap(mapPins.getCreatedBitmap(), vX, vY, paint);
}
}
}
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
setMaxScale(5f);
return false;
}
#Override
public boolean onTouchEvent(#NonNull MotionEvent event) {
/* if (isZoomEnabled()) {
return super.onTouchEvent(event);
}*/
boolean consumed = false;
int touchCount = event.getPointerCount();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_1_DOWN:
vStart = new PointF(event.getX(), event.getY());
vPrevious = new PointF(event.getX(), event.getY());
getPinIdByPoint(vStart, viewToSourceCoord(event.getX(), event.getY()));
handleActionDown((int) event.getX(), (int) event.getY());
break;
case MotionEvent.ACTION_POINTER_2_DOWN:
// Abort any current drawing, user is zooming
vStart = null;
vPrevious = null;
break;
case MotionEvent.ACTION_MOVE:
if (markerTouch) {
setPanEnabled(false);
PointF sCurrentF = viewToSourceCoord(event.getX(), event.getY());
PointF sCurrent = new PointF(sCurrentF.x, sCurrentF.y);
PointF sStart = vStart == null ? null : new PointF(viewToSourceCoord(vStart).x, viewToSourceCoord(vStart).y);
if (touchCount == 1 && vStart != null) {
float vDX = Math.abs(event.getX() - vPrevious.x);
float vDY = Math.abs(event.getY() - vPrevious.y);
if (vDX >= strokeWidth * 5 || vDY >= strokeWidth * 5) {
if (sPin == null) {
sPin = sStart;
}
sPin = sCurrent;
vPrevious.x = event.getX();
vPrevious.y = event.getY();
drawing = true;
}
consumed = true;
invalidate();
} else if (touchCount == 1) {
// Consume all one touch drags to prevent odd panning effects handled by the superclass.
consumed = true;
}
} else {
return super.onTouchEvent(event);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
invalidate();
setPanEnabled(true);
drawing = false;
markerTouch = false;
vPrevious = null;
vStart = null;
}
// Use parent to handle pinch and two-finger pan.
return consumed || super.onTouchEvent(event);
}
public void handleActionDown(int eventX, int eventY) {
if (eventX >= (sPin.x - pin.getWidth()) && (eventX <= (sPin.x + pin.getWidth()))) {
if (eventY >= (sPin.y - pin.getHeight()) && (sPin.y <= (sPin.y + pin.getHeight()))) {
markerTouch = true;
} else {
markerTouch = false;
}
} else {
markerTouch = false;
}
}
public int getPinIdByPoint(PointF tappedCoordinate, PointF deeplinkCoordinate) {
for (int i = allPins.size() - 1; i >= 0; i--) {
MapPins dPin = allPins.get(i);
int blockWidth = dPin.getCreatedBitmap().getWidth();
int blockHeight = dPin.getCreatedBitmap().getHeight();
int deeplinkX = (int) (deeplinkCoordinate.x - (dPin.getCreatedBitmap().getWidth() / 2));
int deeplinkY = (int) (deeplinkCoordinate.y - dPin.getCreatedBitmap().getHeight());
// center coordinate -/+ blockWidth actually sets touchable area to 2x icon size
if (tappedCoordinate.x >= deeplinkX - blockWidth && tappedCoordinate.x <= deeplinkX + blockWidth &&
tappedCoordinate.y >= deeplinkY - blockHeight && tappedCoordinate.y <= deeplinkY + blockHeight) {
sPin = dPin.getPointF();
pin = dPin.getCreatedBitmap();
return dPin.getId();
}
}
return -1; //negative no means no pin selected
}
}
Currently, I am able to add multiple marker, but I am facing issue of marker click and dragging on image.

Issue with CustomView

I am creating an application. If the custom View goes off the screen, a method is called. Here is my code for the custom View.
public class CustomView extends View {
private boolean bubbleOver;
private static final int BITMAP_SIZE = 64;
private static final int REFRESH_RATE = 40;
private final Paint mPainter = new Paint();
private ScheduledFuture<?> mMoverFuture;
private int mScaledBitmapWidth;
private Bitmap mScaledBitmap;
// location, speed and direction of the bubble
private float mXPos, mYPos, mDx, mDy, mRadius, mRadiusSquared;
private long mRotate, mDRotate;
CustomView (Context context, float x, float y) {
super (context);
// Create a new random number generator to randomize size, rotation, speed and direction
Random r = new Random ();
// Creates the bubble bitmap for this BubbleView
createScaledBitmap (r);
// Radius of the Bitmap
mRadius = mScaledBitmapWidth / 2;
mRadiusSquared = mRadius * mRadius;
// Adjust position to center the bubble under user's finger
mXPos = x - mRadius;
mYPos = y - mRadius;
// Set the BubbleView's speed and direction
setSpeedAndDirection(r);
// Set the BubbleView's rotation
setRotation(r);
mPainter.setAntiAlias(true);
}
private void setRotation(Random r) {
if (speedMode == RANDOM) {
// TODO - set rotation in range [1..3]
mDRotate = r.nextInt (3) + 1;
} else {
mDRotate = 0;
}
}
private void setSpeedAndDirection(Random r) {
// Used by test cases
switch (speedMode) {
case SINGLE:
mDx = 20;
mDy = 20;
break;
case STILL:
// No speed
mDx = 0;
mDy = 0;
break;
default:
// Limit movement speed in the x and y direction to [-3..3] pixels per movement.
mDx = r.nextFloat() * 6.0f - 3.0f;
mDy = r.nextFloat() * 6.0f - 3.0f;
}
}
private void createScaledBitmap (Random r) {
if (speedMode != RANDOM) {
mScaledBitmapWidth = BITMAP_SIZE * 3;
} else {
mScaledBitmapWidth = (r.nextInt(3) + 1) * BITMAP_SIZE;
}
mScaledBitmap = Bitmap.createScaledBitmap(mBitmap, mScaledBitmapWidth, mScaledBitmapWidth, true);
}
// Start moving the BubbleView & updating the display
private void start () {
// Creates a WorkerThread
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
// Execute the run() in Worker Thread every REFRESH_RATE milliseconds. Save reference to this job in mMoverFuture
mMoverFuture = executor.scheduleWithFixedDelay(new Runnable() {
#Override
public void run() {
if (moveWhileOnScreen()) {
stop (false);
} else {
BubbleView.this.postInvalidate();
}
}
}, 0, REFRESH_RATE, TimeUnit.MILLISECONDS);
}
// Returns true if the BubbleView intersects position (x,y)
private synchronized boolean intersects(float x, float y) {
// TODO - Return true if the BubbleView intersects position (x,y)
if ((mXPos <= x) && (x <= mXPos + mScaledBitmapWidth) && (mYPos <= y) && (y <= mYPos + mScaledBitmapWidth)) {
return true;
}
return false;
}
// Cancel the Bubble's movement. Remove Bubble from mFrame.
// Play pop sound if the BubbleView was popped
private void stop (final boolean wasPopped) {
if (null != mMoverFuture && !mMoverFuture.isDone()) {
mMoverFuture.cancel(true);
}
// This work will be performed on the UI Thread
mFrame.post(new Runnable() {
#Override
public void run() {
mFrame.removeView(BubbleView.this);
if (wasPopped) {
mSoundPool.play(mSoundID, mStreamVolume, mStreamVolume, 1, 0, 1f);
}
}
});
}
// Change the Bubble's speed and direction
private synchronized void deflect(float velocityX, float velocityY) {
mDx = velocityX / REFRESH_RATE;
mDy = velocityY / REFRESH_RATE;
}
// Draw the Bubble at its current location
#Override
protected synchronized void onDraw(Canvas canvas) {
canvas.save();
mRotate += mDRotate;
canvas.rotate(mRotate, mXPos + (mScaledBitmapWidth / 2), mYPos + (mScaledBitmapWidth / 2));
canvas.drawBitmap(mScaledBitmap, mXPos, mYPos, mPainter);
canvas.restore();
}
// Returns true if the BubbleView is still on the screen after the move operation
private synchronized boolean moveWhileOnScreen() {
mXPos += mDx;
mYPos += mDy;
return isOutOfView();
}
// Return true if the BubbleView is off the screen after the move operation
private boolean isOutOfView() {
if ((mXPos + mDisplayWidth < 0) || (mYPos + mDisplayHeight < 0) || (mXPos> mDisplayWidth) || (mYPos > mDisplayHeight)) {
loseGame();
return true;
}
return false;
}
}
When the View goes off the screen, loseGame() is not called. Only if another CustomView is created, loseGame() is called.
You have wrong sum to check, try with mScaledBitmapWidth not the mDisplayWidth.
if ((mXPos + mScaledBitmapWidth < 0)
|| (mYPos + mScaledBitmapWidth < 0)
|| (mXPos> mDisplayWidth)
|| (mYPos > mDisplayHeight)
{
loseGame();
return true;
}

Android Music Seekbar match_parent

Basically, I cloned a material design widget library and wanted to use the Seekbar view. Even when using match_parent for the width, there was still some padding on the left and right. To combat this, I cloned the project from GitHub and went to the Slider.java class to try and figure out how to truly make this a full length media SeekBar.
I've included my current code, which basically shows a full length seekbar, but starts it a little to the right from the beginning (and consequently stops a little to the left from the end). Here it is mid-media-playback:
CODE
public class Slider extends CustomView {
private int backgroundColor = Color.parseColor("#614E8D");
private Ball ball;
private Bitmap bitmap;
private int max = 100;
private int min = 0;
private NumberIndicator numberIndicator;
private OnValueChangedListener onValueChangedListener;
private boolean placedBall = false;
private boolean press = false;
private boolean showNumberIndicator = false;
private int value = 0;
public Slider(Context context, AttributeSet attrs) {
super(context, attrs);
setAttributes(attrs);
}
public int getMax() {
return max;
}
public void setMax(int max) {
this.max = max;
}
public int getMin() {
return min;
}
public void setMin(int min) {
this.min = min;
}
public OnValueChangedListener getOnValueChangedListener() {
return onValueChangedListener;
}
public void setOnValueChangedListener(
OnValueChangedListener onValueChangedListener) {
this.onValueChangedListener = onValueChangedListener;
}
// GETERS & SETTERS
public int getValue() {
return value;
}
public void setValue(final int value) {
if (placedBall == false)
post(new Runnable() {
#Override
public void run() {
setValue(value);
}
});
else {
this.value = value;
float division = (ball.xFin - ball.xIni) / max;
ViewHelper.setX(ball,
value * division + getHeight() / 2 - ball.getWidth() / 2);
ball.changeBackground();
}
}
#Override
public void invalidate() {
ball.invalidate();
super.invalidate();
}
public boolean isShowNumberIndicator() {
return showNumberIndicator;
}
public void setShowNumberIndicator(boolean showNumberIndicator) {
this.showNumberIndicator = showNumberIndicator;
numberIndicator = (showNumberIndicator) ? new NumberIndicator(
getContext()) : null;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
isLastTouch = true;
if (isEnabled()) {
if (event.getAction() == MotionEvent.ACTION_DOWN
|| event.getAction() == MotionEvent.ACTION_MOVE) {
if (numberIndicator != null
&& numberIndicator.isShowing() == false)
numberIndicator.show();
if ((event.getX() <= getWidth() && event.getX() >= 0)) {
press = true;
// calculate value
int newValue = 0;
float division = (ball.xFin - ball.xIni) / (max - min);
if (event.getX() > ball.xFin) {
newValue = max;
} else if (event.getX() < ball.xIni) {
newValue = min;
} else {
newValue = min + (int) ((event.getX() - ball.xIni) / division);
}
if (value != newValue) {
value = newValue;
if (onValueChangedListener != null)
onValueChangedListener.onValueChanged(newValue);
}
// move ball indicator
float x = event.getX();
x = (x < ball.xIni) ? ball.xIni : x;
x = (x > ball.xFin) ? ball.xFin : x;
ViewHelper.setX(ball, x);
ball.changeBackground();
// If slider has number indicator
if (numberIndicator != null) {
// move number indicator
numberIndicator.indicator.x = x;
numberIndicator.indicator.finalY = Utils
.getRelativeTop(this) - getHeight() / 2;
numberIndicator.indicator.finalSize = getHeight() / 2;
numberIndicator.numberIndicator.setText("");
}
} else {
press = false;
isLastTouch = false;
if (numberIndicator != null)
numberIndicator.dismiss();
}
} else if (event.getAction() == MotionEvent.ACTION_UP ||
event.getAction() == MotionEvent.ACTION_CANCEL) {
if (numberIndicator != null)
numberIndicator.dismiss();
isLastTouch = false;
press = false;
}
}
return true;
}
#Override
public void setBackgroundColor(int color) {
backgroundColor = color;
if (isEnabled())
beforeBackground = backgroundColor;
}
/**
* Make a dark color to press effect
*
* #return
*/
protected int makePressColor() {
int r = (this.backgroundColor >> 16) & 0xFF;
int g = (this.backgroundColor >> 8) & 0xFF;
int b = (this.backgroundColor >> 0) & 0xFF;
r = (r - 30 < 0) ? 0 : r - 30;
g = (g - 30 < 0) ? 0 : g - 30;
b = (b - 30 < 0) ? 0 : b - 30;
return Color.argb(70, r, g, b);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!placedBall) {
placeBall();
}
Paint paint = new Paint();
if (value == min) {
// Crop line to transparent effect
// before song loaded, basically. Need to make this similar to value != min
if (bitmap == null) {
bitmap = Bitmap.createBitmap(canvas.getWidth(),
canvas.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas temp = new Canvas(bitmap);
paint.setColor(Color.parseColor("#54457A")); //purple
paint.setStrokeWidth(Utils.dpToPx(2, getResources()));
// temp.drawLine(getHeight() / 2, getHeight() / 2, getWidth()
// - getHeight() / 2, getHeight() / 2, paint);
temp.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2, paint);
Paint transparentPaint = new Paint();
transparentPaint.setColor(getResources().getColor(
android.R.color.transparent));
transparentPaint.setXfermode(new PorterDuffXfermode(
PorterDuff.Mode.CLEAR));
temp.drawCircle(ViewHelper.getX(ball) + ball.getWidth() / 2,
ViewHelper.getY(ball) + ball.getHeight() / 2,
ball.getWidth() / 2, transparentPaint);
canvas.drawBitmap(bitmap, 0, 0, new Paint());
} else {
/*TRACK*/
paint.setColor(Color.parseColor("#5A5A5C")); //track
paint.setStrokeWidth(Utils.dpToPx(10, getResources()));
canvas.drawLine(0, getHeight()/2, getWidth(), getHeight()/2, paint); //track length
paint.setColor(backgroundColor);
/*END TRACK*/
float division = (ball.xFin - ball.xIni) / (max - min);
int value = this.value - min;
//DO NOT TOUCH -- Progress coloring
canvas.drawLine(getHeight() / 2, getHeight() / 2, value * division
+ getHeight() / 2, getHeight() / 2, paint);
}
if (press && !showNumberIndicator) {
paint.setColor(backgroundColor);
paint.setAntiAlias(true);
canvas.drawCircle(ViewHelper.getX(ball) + ball.getWidth() / 2,
getHeight() / 2, getHeight() / 3, paint);
}
invalidate();
}
// Set atributtes of XML to View
protected void setAttributes(AttributeSet attrs) {
setBackgroundResource(R.drawable.background_transparent);
// Set size of view
setMinimumHeight(Utils.dpToPx(48, getResources()));
setMinimumWidth(Utils.dpToPx(80, getResources()));
// Set background Color
// Color by resource
int bacgroundColor = attrs.getAttributeResourceValue(ANDROIDXML,
"background", -1);
if (bacgroundColor != -1) {
setBackgroundColor(getResources().getColor(bacgroundColor));
} else {
// Color by hexadecimal
int background = attrs.getAttributeIntValue(ANDROIDXML, "background", -1);
if (background != -1)
setBackgroundColor(background);
}
showNumberIndicator = attrs.getAttributeBooleanValue(MATERIALDESIGNXML,
"showNumberIndicator", false);
min = attrs.getAttributeIntValue(MATERIALDESIGNXML, "min", 0);
max = attrs.getAttributeIntValue(MATERIALDESIGNXML, "max", 0);
value = attrs.getAttributeIntValue(MATERIALDESIGNXML, "value", min);
ball = new Ball(getContext());
RelativeLayout.LayoutParams params = new LayoutParams(Utils.dpToPx(20,
getResources()), Utils.dpToPx(20, getResources()));
params.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);
ball.setLayoutParams(params);
addView(ball);
// Set if slider content number indicator
// TODO
if (showNumberIndicator) {
numberIndicator = new NumberIndicator(getContext());
}
}
private void placeBall() {
ViewHelper.setX(ball, getHeight() / 2 - ball.getWidth() / 2);
ViewHelper.setX(ball, ball.getWidth());
ball.xIni = ViewHelper.getX(ball);
ball.xFin = getWidth() - ball.getWidth();// - getHeight() / 2 - ball.getWidth() / 2;
ball.xCen = getWidth() / 2 - ball.getWidth() / 2;
placedBall = true;
}
// Event when slider change value
public interface OnValueChangedListener {
public void onValueChanged(int value);
}
class Ball extends View {
float xIni, xFin, xCen;
public Ball(Context context) {
super(context);
setBackgroundResource(R.drawable.background_switch_ball_uncheck);
}
public void changeBackground() {
if (value != min) {
setBackgroundResource(R.drawable.background_checkbox);
LayerDrawable layer = (LayerDrawable) getBackground();
GradientDrawable shape = (GradientDrawable) layer
.findDrawableByLayerId(R.id.shape_bacground);
shape.setColor(Color.parseColor("#D5C46A")); //yellow ball
} else {
setBackgroundResource(R.drawable.background_switch_ball_uncheck);
}
}
}
// Slider Number Indicator
class Indicator extends RelativeLayout {
boolean animate = true;
// Final size after animation
float finalSize = 0;
// Final y position after animation
float finalY = 0;
boolean numberIndicatorResize = false;
// Size of number indicator
float size = 0;
// Position of number indicator
float x = 0;
float y = 0;
public Indicator(Context context) {
super(context);
setBackgroundColor(getResources().getColor(
android.R.color.transparent));
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (numberIndicatorResize == false) {
LayoutParams params = (LayoutParams) numberIndicator.numberIndicator
.getLayoutParams();
params.height = (int) finalSize * 2;
params.width = (int) finalSize * 2;
numberIndicator.numberIndicator.setLayoutParams(params);
}
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(backgroundColor);
if (animate) {
if (y == 0)
y = finalY + finalSize * 2;
y -= Utils.dpToPx(6, getResources());
size += Utils.dpToPx(2, getResources());
}
canvas.drawCircle(
ViewHelper.getX(ball)
+ Utils.getRelativeLeft((View) ball.getParent())
+ ball.getWidth() / 2, y, size, paint);
if (animate && size >= finalSize)
animate = false;
if (animate == false) {
ViewHelper
.setX(numberIndicator.numberIndicator,
(ViewHelper.getX(ball)
+ Utils.getRelativeLeft((View) ball
.getParent()) + ball.getWidth() / 2)
- size);
ViewHelper.setY(numberIndicator.numberIndicator, y - size);
numberIndicator.numberIndicator.setText(value + "");
}
invalidate();
}
}
class NumberIndicator extends Dialog {
Indicator indicator;
TextView numberIndicator;
public NumberIndicator(Context context) {
super(context, android.R.style.Theme_Translucent);
}
#Override
public void dismiss() {
super.dismiss();
indicator.y = 0;
indicator.size = 0;
indicator.animate = true;
}
#Override
public void onBackPressed() {
}
#Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.number_indicator_spinner);
setCanceledOnTouchOutside(false);
RelativeLayout content = (RelativeLayout) this
.findViewById(R.id.number_indicator_spinner_content);
indicator = new Indicator(this.getContext());
content.addView(indicator);
numberIndicator = new TextView(getContext());
numberIndicator.setTextColor(Color.WHITE);
numberIndicator.setGravity(Gravity.CENTER);
content.addView(numberIndicator);
indicator.setLayoutParams(new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.FILL_PARENT,
RelativeLayout.LayoutParams.FILL_PARENT));
}
}
}
Any help on how to get the circle to start at the very left and end at the very right would be greatly appreciated.
Playing around some more, I found that the original developer of this Slider.java class used a lot of getHeight() / 2 which caused some padding that I didn't want. I've included the edited class. Credit for original class to navasmdc, the creator of Material Design Library.. Feel free to use this class if you need a full sized SeekBar
Slider.java
package com.gc.materialdesign.views;
import android.app.Dialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.LayerDrawable;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.gc.materialdesign.R;
import com.gc.materialdesign.utils.Utils;
import com.nineoldandroids.view.ViewHelper;
public class Slider extends CustomView {
private int backgroundColor = Color.parseColor("#614E8D");
private Ball ball;
private Bitmap bitmap;
private int max = 100;
private int min = 0;
private NumberIndicator numberIndicator;
private OnValueChangedListener onValueChangedListener;
private boolean placedBall = false;
private boolean press = false;
private boolean showNumberIndicator = false;
private int value = 0;
public Slider(Context context, AttributeSet attrs) {
super(context, attrs);
setAttributes(attrs);
}
public int getMax() {
return max;
}
public void setMax(int max) {
this.max = max;
}
public int getMin() {
return min;
}
public void setMin(int min) {
this.min = min;
}
public OnValueChangedListener getOnValueChangedListener() {
return onValueChangedListener;
}
public void setOnValueChangedListener(
OnValueChangedListener onValueChangedListener) {
this.onValueChangedListener = onValueChangedListener;
}
// GETERS & SETTERS
public int getValue() {
return value;
}
public void setValue(final int value) {
if (placedBall == false)
post(new Runnable() {
#Override
public void run() {
setValue(value);
}
});
else {
this.value = value;
float division = (ball.xFin - ball.xIni) / max;
ViewHelper.setX(ball,
value* division);//value * division - ball.getWidth() / 2);
ball.changeBackground();
}
}
#Override
public void invalidate() {
ball.invalidate();
super.invalidate();
}
public boolean isShowNumberIndicator() {
return showNumberIndicator;
}
public void setShowNumberIndicator(boolean showNumberIndicator) {
this.showNumberIndicator = showNumberIndicator;
numberIndicator = (showNumberIndicator) ? new NumberIndicator(
getContext()) : null;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
isLastTouch = true;
if (isEnabled()) {
if (event.getAction() == MotionEvent.ACTION_DOWN
|| event.getAction() == MotionEvent.ACTION_MOVE) {
if (numberIndicator != null
&& numberIndicator.isShowing() == false)
numberIndicator.show();
if ((event.getX() <= getWidth() && event.getX() >= 0)) {
press = true;
// calculate value
int newValue = 0;
float division = (ball.xFin - ball.xIni) / (max - min);
if (event.getX() > ball.xFin) {
newValue = max;
} else if (event.getX() < ball.xIni) {
newValue = min;
} else {
newValue = min + (int) ((event.getX() - ball.xIni) / division);
}
if (value != newValue) {
value = newValue;
if (onValueChangedListener != null)
onValueChangedListener.onValueChanged(newValue);
}
// move ball indicator
float x = event.getX();
x = (x < ball.xIni) ? ball.xIni : x;
x = (x > ball.xFin) ? ball.xFin : x;
ViewHelper.setX(ball, x);
ball.changeBackground();
// If slider has number indicator
if (numberIndicator != null) {
// move number indicator
numberIndicator.indicator.x = x;
numberIndicator.indicator.finalY = Utils
.getRelativeTop(this) - getHeight() / 2;
numberIndicator.indicator.finalSize = getHeight() / 2;
numberIndicator.numberIndicator.setText("");
}
} else {
press = false;
isLastTouch = false;
if (numberIndicator != null)
numberIndicator.dismiss();
}
} else if (event.getAction() == MotionEvent.ACTION_UP ||
event.getAction() == MotionEvent.ACTION_CANCEL) {
if (numberIndicator != null)
numberIndicator.dismiss();
isLastTouch = false;
press = false;
}
}
return true;
}
#Override
public void setBackgroundColor(int color) {
backgroundColor = color;
if (isEnabled())
beforeBackground = backgroundColor;
}
/**
* Make a dark color to press effect
*
* #return
*/
protected int makePressColor() {
int r = (this.backgroundColor >> 16) & 0xFF;
int g = (this.backgroundColor >> 8) & 0xFF;
int b = (this.backgroundColor >> 0) & 0xFF;
r = (r - 30 < 0) ? 0 : r - 30;
g = (g - 30 < 0) ? 0 : g - 30;
b = (b - 30 < 0) ? 0 : b - 30;
return Color.argb(70, r, g, b);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!placedBall) {
placeBall();
}
Paint paint = new Paint();
if (value == min) {
// Crop line to transparent effect
// before song loaded, basically. Need to make this similar to value != min
if (bitmap == null) {
bitmap = Bitmap.createBitmap(canvas.getWidth(),
canvas.getHeight(), Bitmap.Config.ARGB_8888);
}
Canvas temp = new Canvas(bitmap);
paint.setColor(Color.parseColor("#54457A")); //purple
paint.setStrokeWidth(Utils.dpToPx(2, getResources()));
// temp.drawLine(getHeight() / 2, getHeight() / 2, getWidth()
// - getHeight() / 2, getHeight() / 2, paint);
temp.drawLine(0, getHeight() / 2, getWidth(), getHeight() / 2, paint);
Paint transparentPaint = new Paint();
transparentPaint.setColor(getResources().getColor(
android.R.color.transparent));
transparentPaint.setXfermode(new PorterDuffXfermode(
PorterDuff.Mode.CLEAR));
temp.drawCircle(ViewHelper.getX(ball) + ball.getWidth() / 2,
ViewHelper.getY(ball) + ball.getHeight() / 2,
ball.getWidth() / 2, transparentPaint);
canvas.drawBitmap(bitmap, 0, 0, new Paint());
} else {
/*TRACK*/
paint.setColor(Color.parseColor("#5A5A5C")); //track
paint.setStrokeWidth(Utils.dpToPx(10, getResources()));
canvas.drawLine(0, getHeight()/2, getWidth(), getHeight()/2, paint); //track length
paint.setColor(backgroundColor);
/*END TRACK*/
float division = (ball.xFin - ball.xIni) / (max - min);
int value = this.value - min;
//DO NOT TOUCH -- Progress coloring
canvas.drawLine(0, getHeight() / 2, value * division
+ball.getWidth(), getHeight() / 2, paint);
}
if (press && !showNumberIndicator) {
paint.setColor(backgroundColor);
paint.setAntiAlias(true);
canvas.drawCircle(ViewHelper.getX(ball) + ball.getWidth() / 2,
getHeight() / 2, getHeight() / 3, paint);
}
invalidate();
}
// Set atributtes of XML to View
protected void setAttributes(AttributeSet attrs) {
setBackgroundResource(R.drawable.background_transparent);
// Set size of view
setMinimumHeight(Utils.dpToPx(48, getResources()));
setMinimumWidth(Utils.dpToPx(80, getResources()));
// Set background Color
// Color by resource
int bacgroundColor = attrs.getAttributeResourceValue(ANDROIDXML,
"background", -1);
if (bacgroundColor != -1) {
setBackgroundColor(getResources().getColor(bacgroundColor));
} else {
// Color by hexadecimal
int background = attrs.getAttributeIntValue(ANDROIDXML, "background", -1);
if (background != -1)
setBackgroundColor(background);
}
showNumberIndicator = attrs.getAttributeBooleanValue(MATERIALDESIGNXML,
"showNumberIndicator", false);
min = attrs.getAttributeIntValue(MATERIALDESIGNXML, "min", 0);
max = attrs.getAttributeIntValue(MATERIALDESIGNXML, "max", 0);
value = attrs.getAttributeIntValue(MATERIALDESIGNXML, "value", min);
ball = new Ball(getContext());
RelativeLayout.LayoutParams params = new LayoutParams(Utils.dpToPx(20,
getResources()), Utils.dpToPx(20, getResources()));
params.addRule(RelativeLayout.CENTER_VERTICAL, RelativeLayout.TRUE);
ball.setLayoutParams(params);
addView(ball);
// Set if slider content number indicator
// TODO
if (showNumberIndicator) {
numberIndicator = new NumberIndicator(getContext());
}
}
private void placeBall() {
// ViewHelper.setX(ball, getHeight() / 2 - ball.getWidth() / 2);
// ViewHelper.setX(ball, 0);
ViewHelper.setX(ball, ball.getWidth());
ball.xIni = 0;//ViewHelper.getX(ball);
ball.xFin = getWidth() - ball.getWidth();// - getHeight() / 2 - ball.getWidth() / 2;
ball.xCen = getWidth() / 2 - ball.getWidth() / 2;
placedBall = true;
}
// Event when slider change value
public interface OnValueChangedListener {
public void onValueChanged(int value);
}
class Ball extends View {
float xIni, xFin, xCen;
public Ball(Context context) {
super(context);
setBackgroundResource(R.drawable.background_switch_ball_uncheck);
}
public void changeBackground() {
if (value != min) {
setBackgroundResource(R.drawable.background_checkbox);
LayerDrawable layer = (LayerDrawable) getBackground();
GradientDrawable shape = (GradientDrawable) layer
.findDrawableByLayerId(R.id.shape_bacground);
shape.setColor(Color.parseColor("#D5C46A")); //yellow ball
} else {
setBackgroundResource(R.drawable.background_switch_ball_uncheck);
}
}
}
// Slider Number Indicator
class Indicator extends RelativeLayout {
boolean animate = true;
// Final size after animation
float finalSize = 0;
// Final y position after animation
float finalY = 0;
boolean numberIndicatorResize = false;
// Size of number indicator
float size = 0;
// Position of number indicator
float x = 0;
float y = 0;
public Indicator(Context context) {
super(context);
setBackgroundColor(getResources().getColor(
android.R.color.transparent));
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (numberIndicatorResize == false) {
LayoutParams params = (LayoutParams) numberIndicator.numberIndicator
.getLayoutParams();
params.height = (int) finalSize * 2;
params.width = (int) finalSize * 2;
numberIndicator.numberIndicator.setLayoutParams(params);
}
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(backgroundColor);
if (animate) {
if (y == 0)
y = finalY + finalSize * 2;
y -= Utils.dpToPx(6, getResources());
size += Utils.dpToPx(2, getResources());
}
canvas.drawCircle(
ViewHelper.getX(ball)
+ Utils.getRelativeLeft((View) ball.getParent())
+ ball.getWidth() / 2, y, size, paint);
if (animate && size >= finalSize)
animate = false;
if (animate == false) {
ViewHelper
.setX(numberIndicator.numberIndicator,
(ViewHelper.getX(ball)
+ Utils.getRelativeLeft((View) ball
.getParent()) + ball.getWidth() / 2)
- size);
ViewHelper.setY(numberIndicator.numberIndicator, y - size);
numberIndicator.numberIndicator.setText(value + "");
}
invalidate();
}
}
class NumberIndicator extends Dialog {
Indicator indicator;
TextView numberIndicator;
public NumberIndicator(Context context) {
super(context, android.R.style.Theme_Translucent);
}
#Override
public void dismiss() {
super.dismiss();
indicator.y = 0;
indicator.size = 0;
indicator.animate = true;
}
#Override
public void onBackPressed() {
}
#Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
setContentView(R.layout.number_indicator_spinner);
setCanceledOnTouchOutside(false);
RelativeLayout content = (RelativeLayout) this
.findViewById(R.id.number_indicator_spinner_content);
indicator = new Indicator(this.getContext());
content.addView(indicator);
numberIndicator = new TextView(getContext());
numberIndicator.setTextColor(Color.WHITE);
numberIndicator.setGravity(Gravity.CENTER);
content.addView(numberIndicator);
indicator.setLayoutParams(new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.FILL_PARENT,
RelativeLayout.LayoutParams.FILL_PARENT));
}
}
}

Android custom Seekbar with two thumbs view issues

I've been working with a custom Seekbar (with two thumbs) taken from this site
I've did a couple of modifications with the code to adapt it to my needs and be a little more modular (thumb positioning, drawing, etc.). I've noticed that it was a bit glitchy when activities were created. Here are the symptoms of my problem.
When used in an activity (Android 4.0.3 - Asus transformer)
No issues at all, works 100%
When used in a fragment (Android 4.0.3 - Asus transformer)
Gray and blue (progress) lines are centered, thumbs are at top of the seekbar frame. (cut in half). They still work (sliding left to right with the proper progress line in the center)
Same application and activity as #1 (Android 4.2.2 - Galaxy Tab 3 10.1)
Both thumbs are in the center of the screen, you can't move them. Oddly, when you select one and move it from side to side, the seekbar value changed interface is called and the proper value is returned. When you release the selection of the thumb, the value sent to the listener returns to the center value.
Blank activity in a fresh application (Android 4.2.2 - Galaxy Tab 3 10.1)
The seekbar works 100%
Same behavior as #1 and #2 with a Google Nexus 7 LTE (2013) on Android 4.3.1
With this info, I can notice that the behavior varies from on activity to another depending on the amount of "stuff" included in it. I pretty sure it has to do with something about the measurements of the view. Probably the whole Init() method can be canned if the initialization is done properly overrinding the correct methods called during the creation of all the views in an activity or fragment.
Any ideas?
Thanks for your help!
Here is the complete Class:
public class SeekBarWithTwoThumb extends View {
// ***************
// Members
// ***************
// Drawing resources
private Bitmap thumb = BitmapFactory.decodeResource(getResources(),R.drawable.seek_thumb_normal);
private Bitmap thumb_pressed = BitmapFactory.decodeResource(getResources(),R.drawable.seek_thumb_pressed);
private Bitmap thumb_dissabled = BitmapFactory.decodeResource(getResources(),R.drawable.seek_thumb_dissabled);
// Thumb's X coordinates and Y common coordinate
private int thumb1X, thumb2X;
private int thumbY;
// Thumb's return values
private int thumb1Value, thumb2Value;
// Thumb's dimensions
private int thumbHalfWidth;
private int thumbWidth;
// Selected thumb
private int selectedThumb;
private int lastSelectedThumb = 1;
// Paint object and listener
private Paint paint = new Paint();
private SeekBarChangeListener scl;
// Max value returned to listener
private int maxValue = 100;
// Thumb's enabled status
private boolean thumb1Enabled = true;
private boolean thumb2Enabled = false;
// Before init happens (not drawn in UI)
private boolean initDone = false;
private int thumb1Xpreset = 0, thumb2Xpreset = 50;
// ****************
// Constructors
// ****************
public SeekBarWithTwoThumb(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public SeekBarWithTwoThumb(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SeekBarWithTwoThumb(Context context) {
super(context);
}
// ***************
// Overrides
// ***************
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if ((widthMeasureSpec != 0) && (heightMeasureSpec != 0)) {
init();
}
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Print the gray line in the back
paint.setStrokeWidth(1);
paint.setColor(Color.GRAY);
canvas.drawLine(thumbHalfWidth, getHeight() / 2, getWidth() - thumbHalfWidth, getHeight() / 2, paint);
// Print the progress line according seekbar settings
paint.setStrokeWidth(3);
paint.setColor(0xFF33B5E5); // Hollow blue
int drawThumbFirst = 0; // Variable used declare which thumb will be drawn first
if(thumb1Enabled && !thumb2Enabled) {
canvas.drawLine(thumbHalfWidth, getHeight() / 2, thumb1X, getHeight() / 2, paint);
drawThumbFirst = 2;
} else if (!thumb1Enabled && thumb2Enabled){
canvas.drawLine(thumbHalfWidth, getHeight() / 2, thumb2X, getHeight() / 2, paint);
drawThumbFirst = 1;
} else if (!thumb1Enabled && !thumb2Enabled) {
// Do not draw, both are disabled
} else if (selectedThumb == 1){
canvas.drawLine(thumbHalfWidth, getHeight() / 2, thumb1X, getHeight() / 2, paint);
} else if (selectedThumb == 2){
canvas.drawLine(thumbHalfWidth, getHeight() / 2, thumb2X, getHeight() / 2, paint);
} else if (lastSelectedThumb == 1){
canvas.drawLine(thumbHalfWidth, getHeight() / 2, thumb1X, getHeight() / 2, paint);
} else if (lastSelectedThumb == 2){
canvas.drawLine(thumbHalfWidth, getHeight() / 2, thumb2X, getHeight() / 2, paint);
}
if (drawThumbFirst == 2){
if (!thumb2Enabled) {
canvas.drawBitmap(thumb_dissabled, thumb2X - thumbHalfWidth, thumbY,paint);
} else if(selectedThumb == 2){
canvas.drawBitmap(thumb_pressed, thumb2X - thumbHalfWidth, thumbY,paint);
} else {
canvas.drawBitmap(thumb, thumb2X - thumbHalfWidth, thumbY,paint);
}
if (!thumb1Enabled) {
canvas.drawBitmap(thumb_dissabled, thumb1X - thumbHalfWidth, thumbY,paint);
} else if(selectedThumb == 1){
canvas.drawBitmap(thumb_pressed, thumb1X - thumbHalfWidth, thumbY,paint);
} else {
canvas.drawBitmap(thumb, thumb1X - thumbHalfWidth, thumbY,paint);
}
} else { // drawThumbFirst == 1 or 0 (0 has no importance since both thumbs either enabled or disabled)
if (!thumb1Enabled) {
canvas.drawBitmap(thumb_dissabled, thumb1X - thumbHalfWidth, thumbY,paint);
} else if(selectedThumb == 1){
canvas.drawBitmap(thumb_pressed, thumb1X - thumbHalfWidth, thumbY,paint);
} else {
canvas.drawBitmap(thumb, thumb1X - thumbHalfWidth, thumbY,paint);
}
if (!thumb2Enabled) {
canvas.drawBitmap(thumb_dissabled, thumb2X - thumbHalfWidth, thumbY,paint);
} else if(selectedThumb == 2){
canvas.drawBitmap(thumb_pressed, thumb2X - thumbHalfWidth, thumbY,paint);
} else {
canvas.drawBitmap(thumb, thumb2X - thumbHalfWidth, thumbY,paint);
}
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
// Get X coordinate of event
int mx = (int) event.getX();
switch (event.getAction()) {
// Check if one of the thumbs are selected
case MotionEvent.ACTION_DOWN:
if (mx >= thumb1X - thumbHalfWidth &&
mx <= thumb1X + thumbHalfWidth &&
thumb1Enabled) {
selectedThumb = 1;
} else if (mx >= thumb2X - thumbHalfWidth &&
mx <= thumb2X + thumbHalfWidth &&
thumb2Enabled) {
selectedThumb = 2;
}
break;
// If one of the thumbs are selected, move it
case MotionEvent.ACTION_MOVE:
// Prevent thumb to get out of frame
if(mx < thumbHalfWidth){
mx = thumbHalfWidth;
} else if(mx > (getWidth() - thumbHalfWidth)) {
mx = getWidth() - thumbHalfWidth;
}
// Move the selected thumb to location
if (selectedThumb == 1) {
thumb1X = mx;
} else if (selectedThumb == 2) {
thumb2X = mx;
}
break;
// Remove selection, if any
case MotionEvent.ACTION_UP:
lastSelectedThumb = selectedThumb;
selectedThumb = 0;
break;
}
// Refresh
invalidate();
// Call listener if set
if(scl != null){
calculateThumbValue();
scl.SeekBarValueChanged(thumb1Value,thumb2Value);
}
return true;
}
// ***************
// Methods
// ***************
private void init() {
// Fix layout if thumb image is to big
Log.d("Custom Seekbars", "Height: " + thumb.getHeight() + ", " + getHeight());
if (thumb.getHeight() > getHeight()){
getLayoutParams().height = thumb.getHeight();
}
// Set the thumb's dimensions
thumbHalfWidth = thumb.getWidth()/2;
thumbWidth = thumb.getWidth();
// Set the thumb's coordinates
float workingWidth = getWidth() - thumbWidth;
float scale1 = ((float)(thumb1Xpreset))/((float)(maxValue));
float scale2 = ((float)(thumb2Xpreset))/((float)(maxValue));
thumb1X = Math.round((scale1*workingWidth) + thumbHalfWidth);
thumb2X = Math.round((scale2*workingWidth) + thumbHalfWidth);
thumbY = (getHeight() / 2) - (thumb.getHeight() / 2);
initDone = true;
// Refresh
invalidate();
}
public void setSeekBarChangeListener(SeekBarChangeListener scl){
this.scl = scl;
}
private void calculateThumbValue(){
thumb1Value = getThumb1Value();
thumb2Value = getThumb2Value();
}
public interface SeekBarChangeListener{
void SeekBarValueChanged(int Thumb1Value,int Thumb2Value);
}
public void setMax(int maxValue){
if(initDone){
int thumb1val = getThumb1Value();
int thumb2val = getThumb1Value();
this.maxValue = maxValue;
setThumbValues(thumb1val, thumb2val);
} else {
this.maxValue = maxValue;
}
}
// Disable & enable methods
public void setEnable(boolean value){
thumb1Enabled = value;
thumb2Enabled = value;
}
public void setDisable(boolean value){
thumb1Enabled = !value;
thumb2Enabled = !value;
}
public void setEnable(boolean thumb1, boolean thumb2){
thumb1Enabled = thumb1;
thumb2Enabled = thumb2;
}
public void setDisable(boolean thumb1, boolean thumb2){
thumb1Enabled = !thumb1;
thumb2Enabled = !thumb2;
}
// Thumb value setters and getters
public void setThumbValues(int thumb1, int thumb2){
if(initDone){
if(thumb1 >= maxValue){
thumb1X = getWidth() - thumbHalfWidth;
} else if(thumb1 <= 0) {
thumb1X = thumbHalfWidth;
} else {
float workingWidth = getWidth() - thumbWidth;
float scale = ((float)(thumb1))/((float)(maxValue));
thumb1X = Math.round((scale*workingWidth) + thumbHalfWidth);
}
if(thumb2 >= maxValue){
thumb2X = getWidth() - thumbHalfWidth;
} else if(thumb2 <= 0) {
thumb2X = thumbHalfWidth;
} else {
float workingWidth = getWidth() - thumbWidth;
float scale = ((float)(thumb2))/((float)(maxValue));
thumb2X = Math.round((scale*workingWidth) + thumbHalfWidth);
}
// Refresh
invalidate();
// Call listener if set
if(scl != null){
calculateThumbValue();
scl.SeekBarValueChanged(thumb1Value,thumb2Value);
}
} else {
if(thumb1 >= maxValue){
thumb1Xpreset = maxValue;
} else if(thumb1 <= 0) {
thumb1Xpreset = 0;
} else {
thumb1Xpreset = thumb1;
}
if(thumb2 >= maxValue){
thumb2Xpreset = maxValue;
} else if(thumb2 <= 0) {
thumb2Xpreset = 0;
} else {
thumb2Xpreset = thumb2;
}
}
}
public void setThumb1Value(int value){
if(initDone){
if(value >= maxValue){
thumb1X = getWidth() - thumbHalfWidth;
} else if(value <= 0) {
thumb1X = thumbHalfWidth;
} else {
float workingWidth = getWidth() - thumbWidth;
float scale = ((float)(value))/((float)(maxValue));
thumb1X = Math.round((scale*workingWidth) + thumbHalfWidth);
}
// Refresh
invalidate();
// Call listener if set
if(scl != null){
calculateThumbValue();
scl.SeekBarValueChanged(thumb1Value,thumb2Value);
}
} else {
if(value >= maxValue){
thumb1Xpreset = maxValue;
} else if(value <= 0) {
thumb1Xpreset = 0;
} else {
thumb1Xpreset = value;
}
}
}
public void setThumb2Value(int value){
if(initDone){
if(value >= maxValue){
thumb2X = getWidth() - thumbHalfWidth;
} else if(value <= 0) {
thumb2X = thumbHalfWidth;
} else {
float workingWidth = getWidth() - thumbWidth;
float scale = ((float)(value))/((float)(maxValue));
thumb2X = Math.round((scale*workingWidth) + thumbHalfWidth);
}
// Refresh
invalidate();
// Call listener if set
if(scl != null){
calculateThumbValue();
scl.SeekBarValueChanged(thumb1Value,thumb2Value);
}
} else {
if(value >= maxValue){
thumb2Xpreset = maxValue;
} else if(value <= 0) {
thumb2Xpreset = 0;
} else {
thumb2Xpreset = value;
}
}
}
public int getThumb1Value(){
if(initDone){
float workingWidth = getWidth() - thumbWidth;
return Math.round((float)maxValue * ((float)(thumb1X - thumbHalfWidth)/workingWidth));
} else {
return thumb1Xpreset;
}
}
public int getThumb2Value(){
if(initDone){
float workingWidth = getWidth() - thumbWidth;
return Math.round((float)maxValue * ((float)(thumb2X - thumbHalfWidth)/workingWidth));
} else {
return thumb2Xpreset;
}
}
}

Categories

Resources