Related
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));
}
}
}
I wants to add cropping fature in my ANDROID app. I know there are deafault cropping feature available in gallery but in them only rectangular or circular selection of area is possible. I wants to selected any part of image with free hand and them crop the selected part of image from original image. for example selecting head part of an complete human picture and then crop it. See below what i wants.
BEFORE
AFTER
Please help me and also sugest if any free lib is there.
Thanks
Here is the library I used once:
Android widget for cropping and rotating an image.
To add the Cropper to your application, specify com.edmodo.cropper.CropImageView in your layout XML
<com.edmodo.cropper.CropImageView
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:id="#+id/CropImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Or you can modify attributes programmatically.
See the WIKI here.
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.Point;
import android.os.Build.VERSION;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.SimpleOnScaleGestureListener;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup.LayoutParams;
import java.util.ArrayList;
import java.util.List;
public class HandsCropView extends View implements OnTouchListener {
static Bitmap bitmap;
public static List<Point> points;
int C_H_Point;
int C_W_Point;
int DIST = 2;
int D_height;
int D_width;
boolean NutralButton = false;
boolean bfirstpoint = false;
int canvasHeight;
int canvasWidth;
boolean flgPathDraw = true;
Bitmap img;
int img_height;
int img_width;
LayoutParams layoutParams;
Context mContext;
private ScaleGestureDetector mScaleDetector;
private float mScaleFactor = 1.0f;
Point mfirstpoint = null;
Point mlastpoint = null;
private Paint paint;
Paint tectcolor = new Paint();
private class ScaleListener extends SimpleOnScaleGestureListener {
private ScaleListener() {
}
public boolean onScale(ScaleGestureDetector detector) {
HandsCropView.this.mScaleFactor = HandsCropView.this.mScaleFactor * detector.getScaleFactor();
HandsCropView.this.mScaleFactor = Math.max(0.1f, Math.min(HandsCropView.this.mScaleFactor, 5.0f));
HandsCropView.this.invalidate();
return true;
}
}
public HandsCropView(Context c, Bitmap image) {
super(c);
bitmap = image;
this.img_width = bitmap.getWidth();
this.img_height = bitmap.getHeight();
System.out.println("img_width" + this.img_width + "img_height" + this.img_height);
DisplayMetrics metrics1 = getResources().getDisplayMetrics();
this.D_width = metrics1.widthPixels;
this.D_height = metrics1.heightPixels;
if (this.img_width <= this.D_width) {
this.C_W_Point = this.D_width - this.img_width;
}
if (this.img_height <= this.D_height) {
this.C_H_Point = this.D_height - this.img_height;
}
this.mContext = c;
setFocusable(true);
setFocusableInTouchMode(true);
this.paint = new Paint(1);
this.paint.setStyle(Style.STROKE);
this.paint.setPathEffect(new DashPathEffect(new float[]{10.0f, 20.0f}, 5.0f));
this.paint.setStrokeWidth(5.0f);
this.paint.setColor(-1);
if (VERSION.SDK_INT >= 15) {
setLayerType(1, this.paint);
}
this.paint.setShadowLayer(5.5f, 6.0f, 6.0f, Integer.MIN_VALUE);
this.layoutParams = new LayoutParams(bitmap.getWidth(), bitmap.getHeight());
setOnTouchListener(this);
points = new ArrayList<>();
this.bfirstpoint = false;
this.mScaleDetector = new ScaleGestureDetector(c, new ScaleListener());
}
public HandsCropView(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
setFocusable(true);
setFocusableInTouchMode(true);
this.paint = new Paint(1);
this.paint.setStyle(Style.STROKE);
this.paint.setStrokeWidth(2.0f);
setOnTouchListener(this);
points = new ArrayList<>();
this.bfirstpoint = false;
}
public void onDraw(Canvas canvas) {
canvas.scale(this.mScaleFactor, this.mScaleFactor);
canvas.drawBitmap(bitmap, 0.0f, 0.0f, null);
Path path = new Path();
boolean first = true;
for (int i = 0; i < points.size(); i += 2) {
Point point = (Point) points.get(i);
if (first) {
first = false;
path.moveTo((float) point.x, (float) point.y);
} else if (i < points.size() - 1) {
Point next = (Point) points.get(i + 1);
path.quadTo((float) point.x, (float) point.y, (float) next.x, (float) next.y);
} else {
this.mlastpoint = (Point) points.get(i);
path.lineTo((float) point.x, (float) point.y);
}
}
canvas.drawPath(path, this.paint);
}
public boolean onTouch(View view, MotionEvent event) {
Point point = new Point();
point.x = (int) event.getX();
point.y = (int) event.getY();
if (this.flgPathDraw) {
if (this.bfirstpoint) {
if (comparepoint(this.mfirstpoint, point)) {
points.add(this.mfirstpoint);
this.flgPathDraw = false;
GetValue();
} else if (point.x <= this.img_width && point.y <= this.img_height) {
points.add(point);
}
} else if (point.x <= this.img_width && point.y <= this.img_height) {
points.add(point);
}
if (!this.bfirstpoint) {
this.mfirstpoint = point;
this.bfirstpoint = true;
}
} else {
this.mScaleDetector.onTouchEvent(event);
}
invalidate();
Log.e("Hi ==>", "Size: " + point.x + " " + point.y);
if (event.getAction() == 1) {
this.mlastpoint = point;
if (this.flgPathDraw && points.size() > 12 && !comparepoint(this.mfirstpoint, this.mlastpoint)) {
this.flgPathDraw = false;
points.add(this.mfirstpoint);
GetValue();
}
}
return true;
}
private boolean comparepoint(Point first, Point current) {
int left_range_y = current.y - 3;
int right_range_x = current.x + 3;
int right_range_y = current.y + 3;
if (current.x - 3 >= first.x || first.x >= right_range_x || left_range_y >= first.y || first.y >= right_range_y || points.size() < 10) {
return false;
}
return true;
}
public void fillinPartofPath() {
Point point = new Point();
point.x = ((Point) points.get(0)).x;
point.y = ((Point) points.get(0)).y;
points.add(point);
invalidate();
}
public void resetView() {
points.clear();
this.paint.setColor(-1);
this.paint.setStyle(Style.STROKE);
this.flgPathDraw = true;
invalidate();
}
public static boolean GetValue() {
return true;
}
public boolean getBooleanValue() {
return this.NutralButton;
}
}
As this seems to be a recurring topic on 'the stack', I am going to reinforce my problem as something not covered. What has been covered is 2D tile collision for platform games etc., but with the way I have made my game, there are no tiles. I am also using no extra libraries, everything is written by my own hand.
What I have is bounding Rect's for every object in the game. So far there are only two object classes in use, Platform and Entity. Entity contains all the stuff for player movement etc. while Platform is for a solid non-moving platform.
Platform.java:
package com.toongames.game.objects;
import android.graphics.Color;
import android.graphics.Rect;
import com.toongames.framework.Graphics;
public class Platform {
private int x, y;
private int width, height;
public Platform(int par1, int par2, int par3, int par4) {
x = par1;
y = par2;
width = par3;
height = par4;
}
public Rect getBounds() {
return new Rect(x, y, x + width, y + height);
}
}
Entity.java:
package com.toongames.game.entity;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import com.toongames.framework.Graphics;
import com.toongames.framework.Image;
public class Entity {
public final float GRAVITY = 0.1F;
private String entityID;
private Point pos;
private int dx;
private float vel;
public Point desiredPos;
public boolean onGround;
public Entity(String par0String, int par1, int par2) {
entityID = par0String;
pos = new Point(par1, par2);
desiredPos = pos;
dx = 0;
vel = 0;
}
public void update(float deltaTime) {
vel = vel + (GRAVITY * deltaTime);
pos.y += (vel * deltaTime);
pos.x += dx;
}
public void setDx(int par1) {
dx = par1;
}
public int getDx() {
return dx;
}
public void setVelocity(int par1) {
vel = par1;
}
public float getVelocity() {
return vel;
}
public void setPos() {
pos = desiredPos;
}
public Rect getBounds() {
return new Rect(desiredPos.x, desiredPos.y, desiredPos.x + 80, desiredPos.y + 80);
}
}
I have successfully made the player collide with things both up and down, but I cannot for the life of me manage to make the player collide right and left. Whenever I collide with a platform while moving left or right, I just jump to the top of the platform I collided with.
I know it has something to do with my logic, but I cannot figure out the correct logic to use.
ScreenGame.java:
package com.toongames.game.screen;
// Imports here...
public class ScreenGame extends Screen {
private Entity player;
private Button left, right, jump;
private Platform floor, p, p2, p3;
private ArrayList<Platform> platforms;
public ScreenGame(Game game) {
super(game);
player = new Entity("PLAYER", 300, 100, Assets.charRight);
left = new Button(Assets.move_left, 10, 790 - Assets.move_left.getHeight(), Assets.move_left.getWidth(), Assets.move_left.getHeight());
right = new Button(Assets.move_right, 20 + Assets.move_left.getWidth(), 790 - Assets.move_right.getHeight(), Assets.move_right.getWidth(), Assets.move_right.getHeight());
jump = new Button(Assets.jump, 1270 - Assets.jump.getWidth(), 790 - Assets.jump.getHeight(), Assets.jump.getWidth(), Assets.jump.getHeight());
floor = new Platform(0, 790, 1280, 80);
p = new Platform(1280 - 500, 500, 400, 80);
p2 = new Platform(0, 200, 400, 80);
p3 = new Platform(400, 120, 200, 80);
platforms = new ArrayList<Platform>();
platforms.add(floor);
platforms.add(p);
platforms.add(p2);
platforms.add(p3);
}
// An update method calls these
public void updateMovement(float deltaTime) {
List<TouchEvent> touchEvents = game.getInput().getTouchEvents();
int len = touchEvents.size();
for (int i = 0; i < len; i++) {
TouchEvent event = touchEvents.get(i);
if (event.type == TouchEvent.TOUCH_DOWN) {
if (inBounds(left.getBounds(), event)) {
player.setDx((int) -(deltaTime * 1.5F));
} else if (inBounds(right.getBounds(), event)) {
player.setDx((int) deltaTime * 2);
} else if (inBounds(jump.getBounds(), event)) {
if (player.onGround) {
player.setVelocity(-8);
}
}
} else if (event.type == TouchEvent.TOUCH_DRAGGED) {
if (inBounds(left.getBounds(), event)) {
player.setDx((int) -deltaTime * 2);
} else if (inBounds(right.getBounds(), event)) {
player.setDx((int) deltaTime * 2);
} else if (inBounds(jump.getBounds(), event)) {
if (player.onGround) {
player.setVelocity(-8);
}
} else {
player.setDx(0);
player.jumpCounter = 0;
}
} else if (event.type == TouchEvent.TOUCH_UP) {
player.setDx(0);
player.jumpCounter = 0;
}
}
}
// An update method calls these
public void updateGameObjects(float deltaTime) {
for (Platform p : platforms)
p.update();
player.update(deltaTime);
}
// An update method calls these
public void checkCollisions() {
Rect playerRect = player.getBounds();
for (Platform p : platforms) {
Rect pRect = p.getBounds();
if (Rect.intersects(playerRect, pRect)) {
Rect intersection = playerRect;
intersection.intersect(pRect);
if (player.getVelocity() != player.GRAVITY) {
int resolutionHeight;
if (player.getVelocity() < player.GRAVITY)
resolutionHeight = intersection.height();
else {
resolutionHeight = -intersection.height();
player.onGround = true;
}
player.setVelocity(0);
player.desiredPos = new Point(player.desiredPos.x, player.desiredPos.y + resolutionHeight);
}
}
}
player.setPos();
}
}
As an extra note, I have cut out some of the unnecessary code to do with images for the entity and entity health etc.. Also I have cut out empty methods and stuff like that that have no relevance what so ever.
[EDIT] Cut out most of the drawing code and imports. All the absolutely necessary stuff is there now.
player.desiredPos = new Point(player.desiredPos.x, player.desiredPos.y + resolutionHeight);
isn't this "move above, never right/left?"
I think your Rect.intersects method should return one of { NONE, LEFT, RIGHT, UP, DOWN } indicating in which direction the collision occured. So you can react to the collision in the right direction...
This is my first Android App and I'm running into some difficulty with drawText(). I have been self-learning Android programming though Mario Zechner's Beginning Android Games. I am adapting the code he uses for the Mr. Nom game in his book to create my own game. This is a color learning game in which the user taps on a grid of colors. I am able to successfully draw the color grid and a space showing the color the user is to "find" using graphics.drawRect(), and a couple of other graphical assets using graphics.drawPixmap(). I am trying to put the name of each color over top of each color filled rectangle. I have thrown in an arbitrary line of text toward the bottom of the code as follows:
p.setTypeface(Typeface.SERIF);
p.setTextSize(100);
p.setColor(Color.WHITE);
p.setStyle(Style.FILL);
canvas.drawPaint(p);
canvas.drawText("RED",120, 120, p);
Reading similar quesitons I believe my issue is that I'm not properly telling it to draw to the same screen as the rest of the graphical elements. I'm sorry if I'm not being clear - again this is my first project. Please let me know what other information you might need in helping me.
Thank you for your time and consideration!
Below is the rest of the java code used:
package com.lilyandrosieshow.colors;
import java.util.List;
import java.util.Random;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Canvas;
import android.graphics.Typeface;
import android.graphics.Paint.Style;
import com.badlogic.androidgames.framework.Game;
import com.badlogic.androidgames.framework.Graphics;
import com.badlogic.androidgames.framework.Input.TouchEvent;
import com.badlogic.androidgames.framework.Screen;
import com.badlogic.androidgames.framework.Sound;
import com.lilyandrosieshow.colors.ColorGrid;
public class GameScreen extends Screen {
Random random = new Random();
World world;
ColorGrid colorGrid = new ColorGrid();
Canvas canvas = new Canvas();
Paint p = new Paint();
static Sound[] colorSoundL = { Assets.lred, Assets.lorange, Assets.lyellow, Assets.lgreen,
Assets.lblue, Assets.lpurple, Assets.lbrown, Assets.lgrey,
Assets.lblack, Assets.lpink, Assets.lcyan, Assets.lwhite };
static Sound[] colorSoundR = { Assets.rred, Assets.rorange, Assets.ryellow, Assets.rgreen,
Assets.rblue, Assets.rpurple, Assets.rbrown, Assets.rgrey,
Assets.rblack, Assets.rpink, Assets.rcyan, Assets.rwhite };
static Sound[] correct1 = { Assets.correct1, Assets.correct2, Assets.correct3, Assets.correct4, Assets.correct5 };
static Sound[] correct2 = { Assets.correcta, Assets.correctb, Assets.correctc, Assets.correctd, Assets.correcte };
static Sound[] wrong1 = {Assets.wrong1, Assets.wrong2, Assets.wrong3, Assets.wrong4, Assets.wrong5 };
static Sound[] wrong2 = {Assets.wronga, Assets.wrongb, Assets.wrongc, Assets.wrongd, Assets.wronge };
static Sound[] ask = {Assets.ask1, Assets.ask2, Assets.ask3, Assets.ask4, Assets.ask5 };
static Sound[] repeat = { Assets.again1, Assets.again2, Assets.again3, Assets.again4, Assets.again5 };
int row = -1;
int column = -1;
public GameScreen(Game game) {
super(game);
this.world = new World();
}
#Override
public void update(float deltaTime) {
List<TouchEvent> touchEvents = game.getInput().getTouchEvents();
int len = touchEvents.size();
for (int i = 0; i < len; i++) {
TouchEvent event = touchEvents.get(i);
// Color Touched
if (event.type == TouchEvent.TOUCH_UP &&
event.x >= 0 && event.x <= 320 && event.y >= 0 && event.y <= 240) {
for (int j = 1; j <= 4; j++){
if (event.x < (j*80)) {
column = j;
break;
}
}
for (int j = 1; j <= 3; j++){
if (event.y < (j*80)) {
row = j;
break;
}
}
updateColorTouched(column, row);
if (world.colorTouched.x == world.colorWanted.x && world.colorTouched.y == world.colorWanted.y) {
correct2[new Random().nextInt(5)].play(1);
if (world.whichHead == World.WhichHead.LILY) colorSoundL[world.colorTouched.id].play(1);
if (world.whichHead == World.WhichHead.ROSIE) colorSoundR[world.colorTouched.id].play(1);
//world.colorTouched.s.play(1);
world.colorWanted = colorGrid.squares.get(random.nextInt(12));
//Assets.ask1.play(1);
//colorSet[colorWanted-1].play(1);
} else {
wrong2[new Random().nextInt(5)].play(1);
//colorSet[colorTouched-1].play(1);
}
}
// A Head Was Touched
if (event.type == TouchEvent.TOUCH_UP && event.y > 240 && event.y <= 480){
if (event.x > 0 && event.x < 160) world.changeVoice(World.WhichHead.LILY);
if (event.x > 161 && event.x < 320) world.changeVoice(World.WhichHead.ROSIE);
}
}
}
#Override
public void present(float deltaTime) { // Draw everything to the screen here
Graphics g = game.getGraphics();
g.drawRect(0, 0, g.getWidth(), g.getHeight(), Color.rgb(75, 75, 75));
g.drawPixmap(Assets.findthis, 5, 245);
g.drawRect(165, 245, 150, 70, Color.rgb(world.colorWanted.r,
world.colorWanted.g,
world.colorWanted.b));
g.drawPixmap(Assets.lilyheadopen, 5, 325, 5, 5, 155, 155);
g.drawPixmap(Assets.rosieheadopen, 165, 325, 5, 5, 155, 155);
// Draws the grid
for (int i = 0; i < 12; i++) {
g.drawRect((
(world.colorGrid.squares.get(i).x-1)*80),
(world.colorGrid.squares.get(i).y-1)*80,
80,
80,
Color.rgb(world.colorGrid.squares.get(i).r, world.colorGrid.squares.get(i).g, world.colorGrid.squares.get(i).b));
// ************* DRAW TEXT NAMES HERE ********************
p.setTypeface(Typeface.SERIF);
p.setTextSize(100);
p.setColor(Color.WHITE);
p.setStyle(Style.FILL);
canvas.drawPaint(p);
canvas.drawText("RED",120, 120, p);
}
}
public void updateColorTouched(int x, int y){
for (int i = 1; i <= 12; i++) {
if (x == world.colorGrid.squares.get(i-1).x && y == world.colorGrid.squares.get(i-1).y){
world.colorTouched = world.colorGrid.squares.get(i-1);
break;
}
}
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void dispose() {
}
}
Rather than drawing to a Canvas object that doesn't get used anywhere, you'll probably want to extend the Graphics interface with a drawText(...) method. This would be analogue to all other draw calls that are defined in Graphics and implemented in AndroidGraphics.
To help you on your way:
Add to Graphics.java:
public void drawText(String text, float x, float y, Paint p);
Add to AndroidGraphics.java:
#Override public void drawText(String text, float x, float y, Paint p) {
paint.set(p);
canvas.drawText(text, x, y, paint);
}
If you need any of the other drawText(...) overloads that are available in Android's Canvas class, you can repeat the same steps as outlined above for those.
try this:
p.setTypeface(Typeface.SERIF);
p.setTextSize(100);
p.setColor(Color.WHITE);
p.setStyle(Style.FILL);
Bitmap bitmap = Bitmap.createBitmap(200,200,Bitmap.Config.ARGB_8888);
canvas = new Canvas(bitmap);
//canvas.drawPaint(p);
canvas.drawText("RED",120, 120, p);
g.drawPixmap(new AndroidPixmap(bitmap,PixmapFormat.ARG_8888),839,282);//your actual
//coordinates instead of 839,282 though
Seems Androidplot (androidplot.com) website/forums are down so I'll try asking this question here.
I have multi touch zoom and scrolling. Similar code to http://mlepicki.com/2012/03/androidplot-multitouch-zoom-scroll/ except that I have a bar graph instead. However with 100 data points it has noticeable lag. Even with 10 bars are just showing. Sounds like its drawing/calculating/etc all bars.
Any idea on how I could optimise this?
I can't use hardware rendering as I want to support Android 2.1 and the library doesn't support it(it breaks).
I made a custom renderer to solve my lagging issues. Seems to be much more smooth. This code is based on version 0.5. No idea if it works on v0.51.
import android.graphics.*;
import com.androidplot.exception.PlotRenderException;
import com.androidplot.series.XYSeries;
import com.androidplot.util.ValPixConverter;
import com.androidplot.xy.BarFormatter;
import com.androidplot.xy.XYPlot;
import com.androidplot.xy.XYSeriesRenderer;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
/**
* Renders a point as a Bar
*/
public class OptimisedBarRenderer extends XYSeriesRenderer<BarFormatter> {
private BarWidthStyle style = BarWidthStyle.FIXED_WIDTH;
private float barWidth = 5;
public OptimisedBarRenderer(XYPlot plot) {
super(plot);
}
/**
* Sets the width of the bars draw.
* #param barWidth
*/
public void setBarWidth(float barWidth) {
this.barWidth = barWidth;
}
private final TreeMap<Number, XYSeries> tempSeriesMap = new TreeMap<Number, XYSeries>();
#Override
public void onRender(Canvas canvas, RectF plotArea) throws PlotRenderException {
int longest = getLongestSeries();
if(longest == 0) {
return; // no data, nothing to do.
}
tempSeriesMap.clear();
for(int i = 0; i < longest; i++) {
tempSeriesMap.clear();
List<XYSeries> seriesList = getPlot().getSeriesListForRenderer(this.getClass());
for(XYSeries series : seriesList) {
if(i < series.size()) {
tempSeriesMap.put(series.getY(i), series);
}
}
drawBars(canvas, plotArea, tempSeriesMap, i);
}
}
#Override
public void doDrawLegendIcon(Canvas canvas, RectF rect, BarFormatter formatter) {
canvas.drawRect(rect, formatter.getFillPaint());
canvas.drawRect(rect, formatter.getBorderPaint());
}
private int getLongestSeries() {
int longest = 0;
List<XYSeries> seriesList = getPlot().getSeriesListForRenderer(this.getClass());
if(seriesList == null)
return 0;
for(XYSeries series :seriesList) {
int seriesSize = series.size();
if(seriesSize > longest) {
longest = seriesSize;
}
}
return longest;
}
private void drawBars(Canvas canvas, RectF plotArea, TreeMap<Number, XYSeries> seriesMap, int x) {
// Paint p = new Paint();
// p.setColor(Color.RED);
Object[] oa = seriesMap.entrySet().toArray();
Map.Entry<Number, XYSeries> entry;
Number yVal = null;
Number xVal = null;
float halfWidth = barWidth * 0.5f;
for(int i = oa.length-1; i >= 0; i--) {
entry = (Map.Entry<Number, XYSeries>) oa[i];
XYSeries tempEntry = entry.getValue();
if(tempEntry != null) {
yVal = tempEntry.getY(x);
xVal = tempEntry.getX(x);
if (yVal != null && xVal != null) { // make sure there's a real value to draw
switch (style) {
case FIXED_WIDTH:
float pixX = ValPixConverter.valToPix(xVal.doubleValue(), getPlot().getCalculatedMinX().doubleValue(), getPlot().getCalculatedMaxX().doubleValue(), plotArea.width(), false) + plotArea.left;
float left = pixX - halfWidth;
float right = pixX + halfWidth;
boolean offScreen = left > plotArea.right || right < plotArea.left;
if(!offScreen){
float pixY = ValPixConverter.valToPix(yVal.doubleValue(), getPlot().getCalculatedMinY().doubleValue(), getPlot().getCalculatedMaxY().doubleValue(), plotArea.height(), true) + plotArea.top;
BarFormatter formatter = getFormatter(tempEntry);
if(Math.abs (left - right) > 1f){//Don't draw as it will be hidden anyway.
canvas.drawRect(left, pixY, right, plotArea.bottom, formatter.getFillPaint());
}
canvas.drawRect(left, pixY, right, plotArea.bottom, formatter.getBorderPaint());
}
break;
default:
throw new UnsupportedOperationException("Not yet implemented.");
}
}
}
}
}
}
BarRenderer was optimized quite a bit in Androidplot 1.4.0 so a custom Renderer should no longer be necessary.