Ok this one is just bizarre - I needed two Rects for a Healthbar (border and color filling)
When I calculated the first rectangle (filling) I assigned that value to my bar rectangle - but at runtime when the filling rectangle is modified the bar is also - only by manually setting the parameters of the BarRectangle was I able to keep its shape-- how is this even possible?
Can variables become linked forever through simple assignment-
Here is the entirety of the code:
public class HealthBar {
public int MaxValue;
int CurrentValue;
public int TargetValue;
int margin = 10;
Rect Rectangle;
private Rect BarRectangle;
int screenWidth;
boolean IsPlayer1;
Paint paint = new Paint();
int Green;
int Yellow;
int Red;
int opacity = 196;
public HealthBar(boolean isPlayer1, DeviceProperties device, int maxvalue) {
paint.setARGB(196, 0, 255, 0);
paint.setStyle(Paint.Style.FILL);
Green = Color.argb(opacity, 0, 255, 128);
Yellow = Color.argb(opacity, 255, 255, 128);
Red = Color.argb(opacity, 255, 0, 128);
MaxValue = maxvalue;
CurrentValue = MaxValue;
TargetValue = MaxValue;
IsPlayer1 = isPlayer1;
screenWidth = device.Screen.width();
if(IsPlayer1) {
Rectangle = new Rect(
margin, //x
device.Screen.height() - 14, //y
margin + MaxValue, //width
device.Screen.height() - 2 //height
);
} else {
Rectangle = new Rect(
device.Screen.width() - margin - MaxValue, //x
device.Screen.height() - 14, //y
device.Screen.width() - margin, //width
device.Screen.height() - 2 //height
);
}
//Assign Bar Rectangle to Rectangle
BarRectangle = Rectangle;
}
public void Damage(int amount)
{
TargetValue = CurrentValue - amount;
}
public void Update()
{
if (CurrentValue > TargetValue)
{
CurrentValue -= 1;
if (IsPlayer1)
Rectangle.right = margin + CurrentValue;
else
Rectangle.left = screenWidth - margin - CurrentValue;
}
if (TargetValue <= MaxValue * 0.33) {
paint.setColor(Red);
} else if (TargetValue <= MaxValue * 0.66) {
paint.setColor(Yellow);
} else {
paint.setColor(Green);
}
}
public void Draw(ResourceManager rm, Canvas c, Paint p)
{
c.drawRect(Rectangle, paint);
c.drawBitmap(rm.getBitmap("healthbar"), null, BarRectangle, p);
}
}//End HealthBar
The way I "fixed" it was by clunkily assigning the BarRectangle seperate:
Rectangle = new Rect(
margin, //x
device.Screen.height() - 14, //y
margin + MaxValue, //width
device.Screen.height() - 2 //height
);
BarRectangle = new Rect(
margin, //x
device.Screen.height() - 14, //y
margin + MaxValue, //width
device.Screen.height() - 2 //height
);
Can anyone explain to me how assigning a variable in the COnstructor somehow makes it update it's value whenever the other variable is updated?
Now that I've got a reproducable issue - I think this has happened before when it came to assigning character positions, I was using a custom class to hold the values (Vector2) and whenever one got updated the other would too.
The variables Rectangle and BarRectangle are reference to an object only. This means that by "simple assignment" like in your code
//Assign Bar Rectangle to Rectangle
BarRectangle = Rectangle;
you do not copy the contents (i.e. fields of your Rect) but only its reference. Changing one of the fields reflects itself in both since they now refer to the same instance.
In order to create a new instance with the same content (and therefore not share a reference) you can use the following constructor:
BarRectangle = new Rect(Rectangle);
Related
I am making one app in which i need to display visible spectrum (400nm to 780nm). I am using MPAndroidchart. I converted wavelength to color and rendered the spectrum. Below is the screenshot of the app. I can display the rendered spectrum on the background grid but how can i display in the lineDataset. LineDataSet only have one function SetFillColor(int). I want to fill the lineDataset with this Paint. This is my code.
Paint paint = new Paint();// = chart.setPaint();
int[] colors =new int[7];
float[] pos = {0.0f, 0.15f, 0.275f, 0.325f, 0.5f,0.6625f,1};
final float[] bands = { 380, 440, 490, 510, 580, 645, 780};
for(int i =0;i<bands.length;i++) {
colors[i]=Wavelength.wvColor(bands[i], gamma);
//Wavelength.wvColor is the function which returns the `int`.
}
paint.setShader(new LinearGradient(0, 0, chart.getWidht(), 0, colors, pos, Shader.TileMode.CLAMP));
In chart, i can easily display it with the below code
chart.setPaint(paint, Chart.PAINT_GRID_BACKGROUND);
Question: How can i fill my LineDataset with the linearGradient or fill with array of colors?
You can find the solution here: https://github.com/PhilJay/MPAndroidChart/issues/1076
public class LineChartRenderer extends com.github.mikephil.charting.renderer.LineChartRenderer {
public LineChartRenderer(LineDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler) {
super(chart, animator, viewPortHandler);
}
#Override
protected void drawLinearFill(Canvas c, LineDataSet dataSet, List<Entry> entries, int minx, int maxx, Transformer trans) {
mRenderPaint.setStyle(Paint.Style.FILL);
mRenderPaint.setColor(dataSet.getFillColor());
// filled is drawn with less alpha
mRenderPaint.setAlpha(dataSet.getFillAlpha());
Path filled = generateFilledPath(entries, dataSet.getFillFormatter().getFillLinePosition(dataSet, mChart), minx, maxx);
trans.pathValueToPixel(filled);
// GRADIENT BG - SET SHADER
ALog.d(this, "drawLinearFill #LineChartRenderer - c.getHeight()=" + c.getHeight());
mRenderPaint.setShader(new LinearGradient(0, 0, 0, c.getHeight(), AConstant.COLOR_CHART_LINE, AConstant.COLOR_CHART_BG,
Shader.TileMode.CLAMP));
c.drawPath(filled, mRenderPaint);
// restore alpha
mRenderPaint.setAlpha(255);
// GRADIENT BG - REMOVE SHADER
mRenderPaint.setShader(null);
}
/**
* Generates the path that is used for filled drawing.
*
* #param entries
* #return
*/
private Path generateFilledPath(List<Entry> entries, float fillMin, int from, int to) {
ALog.d(this, "generateFilledPath #LineChartRenderer");
float phaseX = mAnimator.getPhaseX();
float phaseY = mAnimator.getPhaseY();
Path filled = new Path();
filled.moveTo(entries.get(from).getXIndex(), fillMin);
filled.lineTo(entries.get(from).getXIndex(), entries.get(from).getVal() * phaseY);
// create a new path
for (int x = from + 1, count = (int) Math.ceil((to - from) * phaseX + from); x < count; x++) {
Entry e = entries.get(x);
filled.lineTo(e.getXIndex(), e.getVal() * phaseY);
}
// close up
filled.lineTo(entries.get(Math.max(Math.min((int) Math.ceil((to - from) * phaseX + from) - 1, entries.size() - 1), 0))
.getXIndex(), fillMin);
filled.close();
return filled;
}
}
Currently in my android app, I made a custom view with 8 rect blocks in two different colors. What I want to do is to make the 8 rectangle update the colour according to a array change. Now my code blew could make a static view, how can make it dynamic change, which another class could manipulate this view class object.
public class SampleCanvasActivity extends View {
Paint paint = new Paint();
int array[] = new int[8];
public SampleCanvasActivity(Context context,AttributeSet attributeSet) {
super(context,attributeSet);
}
#Override
public void onDraw(Canvas canvas) {
/*
paint.setColor(Color.BLACK);
paint.setStrokeWidth(3);
canvas.drawRect(130, 130, 180, 180, paint);
paint.setStrokeWidth(0);
paint.setColor(Color.CYAN);
canvas.drawRect(133, 160, 177, 177, paint );
paint.setColor(Color.YELLOW);
canvas.drawRect(133, 133, 177, 160, paint );
*/
for(int i=0;i<array.length;i++){
array[i] = 0;
}
array[7] = 1;
int left = 50; // initial start position of rectangles (50 pixels from left)
int top = 50; // 50 pixels from the top
int width = 50;
int height = 50;
for (int row = 0; row < 2; row++) { // draw 2 rows
left = 50;
for(int col = 0; col < 4; col++) { // draw 4 columns
int id = row*4 + col;
if(array[id]==0)
paint.setColor(Color.parseColor("#CD5C5C"));
else
paint.setColor(Color.CYAN);
canvas.drawRect(left, top, left+width, top+height, paint);
left = (left + width + 10); // set new left co-ordinate + 10 pixel gap
// Do other things here
// i.e. change colour
}
top = top + height + 10; // move to new row by changing the top co-ordinate
}
}
}
You can use a parameter in custom view, and define a attr in xml.
<resources>
<declare-styleable name="YourStyle">
<attr name="YourColor" format="color"></attr>
</declare-styleable>
</resources>
In SampleCanvasActivity,
get the parameter in your constructor
TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.YourStyle);
Resources mResources = context.getResources();
yourcolor = mTypedArray.getColor(R.styleable.YourStyle_YourColor, mResources.getColor(R.color.YourColor));
And add a setter for yourcolor
public void setYourColor(int innerCircleColor) {
this. yourcolor = yourcolor;
invalidate();
}
At last, in onDraw
paint.setColor(yourcolor);
When array is changed, call setYourColor.
I have canvas which draws pie chart for me. After I change radio button to disagree position, my pie chart has to show different values. I am doing this with Listener (setted up on radio group). It is working but for updating the canvas (not adding and overdrawing, I have to clear it first and redraw with new values) I use this:
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
Under the piechart I want to write number of percent. It is working without that line what is on top. But after I execute the line, only piechart is redrawn (with new values) but text with percentage is not shown (even if I set text to that again):
if (checkedId == R.id.agreeRadio) {
values[0] += 1;
values[1] -= 1;
setupPieChart();
setupPercentageValueToGraph();
}
Method calculating percentage and writing it:
public void setupPercentageValueToGraph() {
float[] degrees = calculateData(values);
// get percentage number from values
float percentage = (degrees[0] / (degrees[0] + degrees[1])) * 100;
// setup color of the number shown red/green
if (percentage >= 50) {
percentageTV.setTextColor(Color.parseColor("#47B243"));
} else {
percentageTV.setTextColor(Color.parseColor("#DB262A"));
}
// set the text
percentageTV.setText((int) percentage + "%");
}
Method to set piechart to linearLayout:
public void setupPieChart() {
float[] degrees = calculateData(values);
graphLayout.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
graphLayout.addView(new MyGraphView(this, degrees, size));
}
Class which is drawing the piechart where that clear is used:
public class MyGraphView extends View {
public static final int PADDING = 4;
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
private float[] value_degree;
int strokeWidth;
private int[] COLORS = { Color.parseColor("#47B243"),
Color.parseColor("#DB262A") };
// size of bigger half circle
RectF rectf = new RectF(2, 2, 62, 62);
// size of smaller half circle
RectF rectf2 = new RectF(9, 9, 55, 55);
// size of the smallest half circle
RectF rectf3 = new RectF(16, 16, 48, 48);
int temp = 0;
public MyGraphView(Context context, float[] values, int size) {
super(context);
// setting up size of pie chart dynamically
int difference = size / 9;
rectf.set(PADDING, PADDING, size + PADDING, size + PADDING);
rectf2.set(difference + PADDING, difference + PADDING, size
- difference + PADDING, size - difference + PADDING);
rectf3.set(difference * 2 + PADDING, difference * 2 + PADDING, size
- difference * 2 + PADDING, size - difference * 2 +
PADDING);
// setting up brush size
strokeWidth = size / 15;
// assign degrees of agree and disagree to array
value_degree = new float[values.length];
for (int i = 0; i < values.length; i++) {
value_degree[i] = values[i];
}
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
//canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
for (int i = 0; i < value_degree.length; i++) {
// set type of "brush"
paint.setStrokeWidth(strokeWidth);
paint.setStyle(Paint.Style.STROKE);
// set shadow
paint.setShadowLayer(2, 1, 1, Color.BLACK);
// setLayerType(LAYER_TYPE_SOFTWARE, paint);
// agree
if (i == 0) {
final Path path = new Path();
paint.setColor(COLORS[i]);
// draw 3 paths to show 3 curves
path.addArc(rectf, 180, value_degree[i] - 4);
path.addArc(rectf2, 180, value_degree[i] - 5);
path.addArc(rectf3, 180, value_degree[i] - 6);
// draw the path
canvas.drawPath(path, paint);
// disagree
} else {
temp += (int) value_degree[i - 1];
paint.setColor(COLORS[i]);
final Path path = new Path();
path.addArc(rectf, temp + 180 + 4, value_degree[i] - 4);
path.addArc(rectf2, temp + 180 + 5, value_degree[i] - 5);
path.addArc(rectf3, temp + 180 + 6, value_degree[i] - 6);
// draw the path
canvas.drawPath(path, paint);
}
}
}
}
Any idea why text is not written? Or any other way how to update piechart after radiobutton is changed?
Finally I found a way how to do it. To draw the text on the same canvas as graph was drawn:
// draw the number of percent under piechart
// setup brush
Paint paint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
// set shadow
paint2.setShadowLayer(3, 2, 2, Color.parseColor("#404040"));
// fill it not stroke
paint2.setStyle(Style.FILL);
// set text family + make it bold
paint2.setTypeface(Typeface.create(Typeface.DEFAULT, Typeface.BOLD));
// convert dp to pixels and setup the size of text
int MY_DIP_VALUE = 23; // 5dp
int pixel = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, MY_DIP_VALUE,
getResources()
.getDisplayMetrics());
// setup color of the number shown red/green
if (percent >= 50) {
paint2.setColor(COLORS[0]);
} else {
paint2.setColor(COLORS[1]);
}
// draw the text
paint2.setTextSize(pixel);
float textWidth = paint2.measureText(percent + "%");
canvas.drawText(percent + "%", size / 2 - textWidth / 2 + PADDING,
size, paint2);
I have removed TextView and did few little changes, but the key was to draw it. Still dont understand why I was not able to write text on it but had to draw but it is working!
I have a small program that show a circle, and when you click on that circle it re-appears somewhere else on the screen.
This works good in 90% of the cases, but sometimes the circle is buggy. It can be that it appears outside the view, appears as an oval instead of circle, or is placed halfway outside the view.
Can anyone point me in the right direction, what am I doing wrong?
Screens:
Code example:
public class Activity1 : Activity
{
int margin = 20;
Button ball;
TextView debug;
RelativeLayout mRel;
RelativeLayout.LayoutParams ballParams;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Create a debug label
debug = new TextView(this);
// Create a new ball
ball = new Button(this);
ball.SetBackgroundDrawable(Resources.GetDrawable(Resource.Drawable.round_button));
ball.Click += (o, e) => {
RandomizePosition();
};
// Set ball parameters
ballParams = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WrapContent,
RelativeLayout.LayoutParams.WrapContent);
// Create relative layout
mRel = new RelativeLayout(this);
mRel.SetBackgroundColor(Color.AntiqueWhite);
mRel.AddView(ball);
mRel.AddView(debug);
SetContentView(mRel);
// Randmize the ball position
RandomizePosition ();
}
void RandomizePosition ()
{
// Get height and width
Display display = WindowManager.DefaultDisplay;
int width = display.Width;
int height = display.Height;
int relativeBallSize = ((((width * 2) + (height * 2)) / 100) * 3);
// Set random parameters
Random r = new Random();
int maxWidth = (width - relativeBallSize);
int maxHeight = (height - relativeBallSize);
int x = r.Next(margin, (maxWidth < margin) ? margin : maxWidth);
int y = r.Next(margin, (maxHeight < margin) ? margin : maxHeight);
// Place the ball randomly
ballParams.SetMargins(x, y, x, y);
ball.LayoutParameters = ballParams;
ball.SetHeight(relativeBallSize);
ball.SetWidth(relativeBallSize);
debug.SetText(string.Format("X = {0}, Y = {1}, Width = {2}, Height = {3}, Ball Width = {4}, Ball Height = {5}, Ball size = {6}", x, y, width, height, ball.Width, ball.Height, relativeBallSize), TextView.BufferType.Normal);
}
}
Assuming that your r.Next method is working correctly I think the problem is here:
ballParams.SetMargins(x, y, x, y);
You're setting the margins for the left,top,right,bottom respectively and I don't think you mean to be setting the right and bottom margins. You might want to try using the setX and setY methods instead.
I have worked on pie chart in android. I found an excellent solution from http://tutorials-android.blogspot.in/2011/05/how-create-pie-chart-in-android.html and worked on that. I am able to display the pie chart with colors but in my application in addition to colors I need to display the text also dynamically on that pie chart. How can I display text dynamically on those pie chart slices?
Please help me regarding this...Will be thankful...
To draw piechart you had use very long process.....Hope this help you..
public class Demo extends Activity {
/** Called when the activity is first created. */
float values[]={500,400,300,200,100};
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
LinearLayout linear=(LinearLayout) findViewById(R.id.linear);
values=calculateData(values);
linear.addView(new MyGraphview(this,values));
}
private float[] calculateData(float[] data) {
// TODO Auto-generated method stub
float total=0;
for(int i=0;i<data.length;i++)
{
total+=data[i];
}
for(int i=0;i<data.length;i++)
{
data[i]=360*(data[i]/total);
}
return data;
}
public class MyGraphview extends View
{
private Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG);
private float[] value_degree;
private int[] COLORS={Color.BLUE,Color.GREEN,Color.GRAY,Color.CYAN,Color.RED};
RectF rectf = new RectF (10, 10, 200, 200);
int temp=0;
public MyGraphview(Context context, float[] values) {
super(context);
value_degree=new float[values.length];
for(int i=0;i<values.length;i++)
{
value_degree[i]=values[i];
}
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
for (int i = 0; i < value_degree.length; i++) {//values2.length; i++) {
if (i == 0) {
paint.setColor(COLORS[i]);
canvas.drawArc(rectf, 0, value_degree[i], true, paint);
}
else
{
temp += (int) value_degree[i - 1];
paint.setColor(COLORS[i]);
canvas.drawArc(rectf, temp, value_degree[i], true, paint);
}
}
}
}
}
Which set the color according to values in decending order...
And for text,you can set dynamic text separately and give color square in front of text:)
To draw text at the centre of each pie chart segment you need to calculate the centre of each segment. The centre of each text item to paint on that segment should align with that centre point - which is achieved by subtracting half the text bounds width from the central x coord (or using paint.setTextAlign(Align.CENTER); ) and half the text bound height from the central y coord.
As for finding the centre of a segment, it requires just a little bit more consideration than using simple geometry.
The central coords of a segment can be found by:
x = (/* radius of pie chart */ /2)*cos(/*angle in RADIANS */) [angle in radians = Math.toRadians(/*half the sweep angle in degrees*/)
y = (/* radius of pie chart */ /2)*sin(/*angle in RADIANS */)
Almost there... dont forget to add the x and y coords of the centre of your pie chart to the above x and y values, otherwise you're trying to paint on a circle centring on (0,0) in your custom view!
Say your pie chart is centred at the actual centre of your view, you want to be adding:
x += getWidth()/2;
y += getHeight()/2;
Last but not least, accounting for the length of the text to be painted - get the bounds of your text using, for example:
Paint paint = new Paint();
paint.setColor(Color.WHITE);
paint.setTextAlign(Align.CENTER); // This centres the text horizontally
String labelText = "TEST";
Rect textBounds = new Rect();
paint.getTextBounds(labelText, 0, labelText.length(), textBounds);
y -= textBounds.height()/2;
Then your text should appear correctly.
You can use the library called MPAndroidChart, which is very simple and easy to use. Simply import this
compile 'com.github.PhilJay:MPAndroidChart:v3.0.1'
and add this line in your gradle file
allprojects {
repositories {
maven { url "https://jitpack.io" }
}
}
Hope this will help you.
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mState != IS_READY_TO_DRAW) {
return;
}
canvas.drawColor(mBgcolor);
mBagpaints.setAntiAlias(true);
mBagpaints.setStyle(Paint.Style.FILL);
mBagpaints.setColor(0x88FF0000);
mBagpaints.setStrokeWidth(0.0f);
mLinePaints.setAntiAlias(true);
mLinePaints.setColor(0xff000000);
mLinePaints.setStrokeWidth(3.0f);
mLinePaints.setStyle(Paint.Style.STROKE);
RectF mOvals = new RectF(mGapleft, mGapTop, mWidth - mGapright, mHeight
- mGapBottm);
mStart = START_INC;
PieDetailsItem item;
for (int i = 0; i < mdataArray.size(); i++) {
item = (PieDetailsItem) mdataArray.get(i);
mBagpaints.setColor(item.color);
mSweep = (float) 360* ((float) item.count / (float) mMaxConnection);
canvas.drawArc(mOvals, mStart, mSweep, true, mBagpaints);
canvas.drawArc(mOvals, mStart, mSweep, true, mLinePaints);
mStart = mStart + mSweep;
// set your text here
canvas.drawText("here is some text", mStart, someYvalue, mLinePaints);
}
mState = IS_DRAW;
}
Just some drawText() calls ought to work fine.
You'll have to do a little math to decide where your y coordinate should be