I am using MPAndroidChart library and my requirement is to show stack value in stack color as given in picture and also not above the bar but left and right for the alternate stack.
I have tried like this way but it didn't work
set.setValueTextColors(colorList);
It's giving one color for one complete bar.
I used a custom renderer to fix the issue
Here is the code
Chart creation
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mp_android_stacked_bar_chart);
barChart = (BarChart) this.findViewById(R.id.bar_chart);
float[] val1 = {10, 20, 30, 40, 50, 60, 70};
float[] val2 = {70, 60, 50, 40, 30, 20, 10};
ArrayList<BarEntry> yVals1 = new ArrayList<BarEntry>();
for (int i = 0; i < val1.length; i++) {
yVals1.add(new BarEntry(i, new float[]{val1[i], val2[i]}));
}
barChart.getDescription().setEnabled(false);
barChart.setPinchZoom(false); // scaling can now only be done on x- and y-axis separately
barChart.setDrawGridBackground(false);
barChart.setDrawBarShadow(false);
barChart.setDrawValueAboveBar(true);
barChart.setHighlightFullBarEnabled(false);
barChart.getAxisRight().setEnabled(false);
YAxis yAxis = barChart.getAxisLeft(); // change the position of the y-labels
yAxis.setAxisMinimum(0f);
yAxis.setDrawGridLines(false);
XAxis xAxis = barChart.getXAxis();
xAxis.setPosition(XAxis.XAxisPosition.BOTTOM);
xAxis.setGranularity(1f);
xAxis.setDrawGridLines(false);
ArrayList<Integer> colorList = new ArrayList<>();
colorList.add(ContextCompat.getColor(this, R.color.colorPrimaryDark));
colorList.add(ContextCompat.getColor(this, R.color.colorAccent));
barChart.getLegend().setEnabled(false);
barChart.setRenderer(new StackedBarChartRenderer(barChart, barChart.getAnimator(), barChart.getViewPortHandler(), colorList));
BarDataSet set1 = new BarDataSet(yVals1, "");
set1.setColors(colorList);
ArrayList<IBarDataSet> dataSets = new ArrayList<>();
dataSets.add(set1);
BarData data = new BarData(dataSets);
barChart.setData(data);
barChart.setVisibleXRangeMaximum(4f);
barChart.invalidate();
}
These are important lines in the above code. Here we are setting the our own renderer class for the graph where we pass the required colorList
ArrayList<Integer> colorList = new ArrayList<>();
colorList.add(ContextCompat.getColor(this, R.color.colorPrimaryDark));
colorList.add(ContextCompat.getColor(this, R.color.colorAccent));
barChart.setRenderer(new StackedBarChartRenderer(barChart, barChart.getAnimator(), barChart.getViewPortHandler(), colorList));
RENDERER
public class StackedBarChartRenderer extends BarChartRenderer {
private ArrayList<Integer> colorList;
private int index = 0;
private int numOfColors;
public StackedBarChartRenderer(BarDataProvider chart, ChartAnimator animator, ViewPortHandler viewPortHandler, ArrayList<Integer> colorList) {
super(chart, animator, viewPortHandler);
this.colorList = colorList;
this.numOfColors = colorList.size();
}
public void drawValue(Canvas c, IValueFormatter formatter, float value, Entry entry, int dataSetIndex, float x, float y, int color) {
mValuePaint.setColor(colorList.get(index));
if(value != 0){
c.drawText(formatter.getFormattedValue(value, entry, dataSetIndex, mViewPortHandler), x, y, mValuePaint);
}
index = ((index + 1) % (numOfColors));
}
}
In the above code we are overriding the necessary methods and iterating through the colorList in a cicular manner. You may have to change the logic
of the drawValue() function to suit your requirements.
To know more about how custom renderer works, check this link
Result
EDIT
#Override
public void drawValues(Canvas c) {
// if values are drawn
if (isDrawingValuesAllowed(mChart)) {
List<IBarDataSet> dataSets = mChart.getBarData().getDataSets();
final float valueOffsetPlus = Utils.convertDpToPixel(4.5f);
float posOffset = 0f;
float negOffset = 0f;
boolean drawValueAboveBar = mChart.isDrawValueAboveBarEnabled();
for (int i = 0; i < mChart.getBarData().getDataSetCount(); i++) {
IBarDataSet dataSet = dataSets.get(i);
if (!shouldDrawValues(dataSet))
continue;
// apply the text-styling defined by the DataSet
applyValueTextStyle(dataSet);
boolean isInverted = mChart.isInverted(dataSet.getAxisDependency());
// calculate the correct offset depending on the draw position of
// the value
float valueTextHeight = Utils.calcTextHeight(mValuePaint, "8");
posOffset = (drawValueAboveBar ? -valueOffsetPlus : valueTextHeight + valueOffsetPlus);
negOffset = (drawValueAboveBar ? valueTextHeight + valueOffsetPlus : -valueOffsetPlus);
if (isInverted) {
posOffset = -posOffset - valueTextHeight;
negOffset = -negOffset - valueTextHeight;
}
// get the buffer
BarBuffer buffer = mBarBuffers[i];
final float phaseY = mAnimator.getPhaseY();
MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset());
iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x);
iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y);
// if only single values are drawn (sum)
if (!dataSet.isStacked()) {
for (int j = 0; j < buffer.buffer.length * mAnimator.getPhaseX(); j += 4) {
float x = (buffer.buffer[j] + buffer.buffer[j + 2]) / 2f;
if (!mViewPortHandler.isInBoundsRight(x))
break;
if (!mViewPortHandler.isInBoundsY(buffer.buffer[j + 1])
|| !mViewPortHandler.isInBoundsLeft(x))
continue;
BarEntry entry = dataSet.getEntryForIndex(j / 4);
float val = entry.getY();
if (dataSet.isDrawValuesEnabled()) {
drawValue(c, dataSet.getValueFormatter(), val, entry, i, x,
val >= 0 ?
(buffer.buffer[j + 1] + posOffset) :
(buffer.buffer[j + 3] + negOffset),
dataSet.getValueTextColor(j / 4));
}
if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) {
Drawable icon = entry.getIcon();
float px = x;
float py = val >= 0 ?
(buffer.buffer[j + 1] + posOffset) :
(buffer.buffer[j + 3] + negOffset);
px += iconsOffset.x;
py += iconsOffset.y;
Utils.drawImage(
c,
icon,
(int)px,
(int)py,
icon.getIntrinsicWidth(),
icon.getIntrinsicHeight());
}
}
// if we have stacks
} else {
Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
int bufferIndex = 0;
int index = 0;
while (index < dataSet.getEntryCount() * mAnimator.getPhaseX()) {
BarEntry entry = dataSet.getEntryForIndex(index);
float[] vals = entry.getYVals();
float x = (buffer.buffer[bufferIndex] + buffer.buffer[bufferIndex + 2]) / 2f;
int color = dataSet.getValueTextColor(index);
// we still draw stacked bars, but there is one
// non-stacked
// in between
if (vals == null) {
if (!mViewPortHandler.isInBoundsRight(x))
break;
if (!mViewPortHandler.isInBoundsY(buffer.buffer[bufferIndex + 1])
|| !mViewPortHandler.isInBoundsLeft(x))
continue;
if (dataSet.isDrawValuesEnabled()) {
drawValue(c, dataSet.getValueFormatter(), entry.getY(), entry, i, x,
buffer.buffer[bufferIndex + 1] +
(entry.getY() >= 0 ? posOffset : negOffset),
color);
}
if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) {
Drawable icon = entry.getIcon();
float px = x;
float py = buffer.buffer[bufferIndex + 1] +
(entry.getY() >= 0 ? posOffset : negOffset);
px += iconsOffset.x;
py += iconsOffset.y;
Utils.drawImage(
c,
icon,
(int)px,
(int)py,
icon.getIntrinsicWidth(),
icon.getIntrinsicHeight());
}
// draw stack values
} else {
float[] transformed = new float[vals.length * 2];
float posY = 0f;
float negY = -entry.getNegativeSum();
for (int k = 0, idx = 0; k < transformed.length; k += 2, idx++) {
float value = vals[idx];
float y;
if (value == 0.0f && (posY == 0.0f || negY == 0.0f)) {
// Take care of the situation of a 0.0 value, which overlaps a non-zero bar
y = value;
} else if (value >= 0.0f) {
posY += value;
y = posY;
} else {
y = negY;
negY -= value;
}
transformed[k + 1] = y * phaseY;
}
trans.pointValuesToPixel(transformed);
for (int k = 0; k < transformed.length; k += 2) {
final float val = vals[k / 2];
final boolean drawBelow =
(val == 0.0f && negY == 0.0f && posY > 0.0f) ||
val < 0.0f;
float y = transformed[k + 1]
+ (drawBelow ? negOffset : posOffset);
if (!mViewPortHandler.isInBoundsRight(x))
break;
if(val == 0){
drawValue(c,
dataSet.getValueFormatter(),
vals[k / 2],
entry,
i,
x,
y,
color);
}
if (!mViewPortHandler.isInBoundsY(y)
|| !mViewPortHandler.isInBoundsLeft(x))
continue;
if (dataSet.isDrawValuesEnabled()) {
drawValue(c,
dataSet.getValueFormatter(),
vals[k / 2],
entry,
i,
x,
y,
color);
}
if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) {
Drawable icon = entry.getIcon();
Utils.drawImage(
c,
icon,
(int)(x + iconsOffset.x),
(int)(y + iconsOffset.y),
icon.getIntrinsicWidth(),
icon.getIntrinsicHeight());
}
}
}
bufferIndex = vals == null ? bufferIndex + 4 : bufferIndex + 4 * vals.length;
index++;
}
}
MPPointF.recycleInstance(iconsOffset);
}
}
}
Add this function to your custom render pick up the proper color even if some of your values are 0 in the stacked graph. This above function is exactly same as the default implementation expect for the following part :
if(val == 0){
drawValue(c,
dataSet.getValueFormatter(),
vals[k / 2],
entry,
i,
x,
y,
color);
}
Related
I am not able to set labels on inverted chart View. I am using MPchart library for my graph. I have to show inverted graph with labels and ranks on the top according to the value. I am not able to set the labels at X-axis.
I want my graph to look like this. But I am not able to show labels and numbering on each graph.
What I am getting:
Here is my code:
ArrayList<BarEntry> bargroup1 = new ArrayList<>();
bargroup1.add(new BarEntry(12f, 4));
bargroup1.add(new BarEntry(16f, 2));
bargroup1.add(new BarEntry(20f, 1));
bargroup1.add(new BarEntry(24f, 3));
bargroup1.add(new BarEntry(28f, 5));
ArrayList<BarEntry> bargroup2 = new ArrayList<>();
bargroup2.add(new BarEntry(13f, 4));
bargroup2.add(new BarEntry(17f, 2));
bargroup2.add(new BarEntry(21f, 1));
bargroup2.add(new BarEntry(25f, 3));
bargroup2.add(new BarEntry(29f, 5));
BarDataSet barDataSet1 = new BarDataSet(bargroup1, "");
barDataSet1.setColors(getResources().getColor(R.color.app_logo_color));
BarDataSet barDataSet2 = new BarDataSet(bargroup2, "");
barDataSet2.setColors(getResources().getColor(R.color.app_grey_color));
final ArrayList<String> labels = new ArrayList<String>();
labels.add("Buy");
labels.add("Over");
labels.add("Hold");
labels.add("Under");
labels.add("Sell");
dataSets = new ArrayList<>();
datasetdemo = new ArrayList<>();
dataSets.add(barDataSet1);
dataSets.add(barDataSet2);
YAxis yAxisRight = barChart.getAxisRight();
yAxisRight.setEnabled(false);
YAxis yAxisLeft = barChart.getAxisLeft();
yAxisLeft.setEnabled(false);
// or use some other logic to save your data in list. For ex.
XAxis xAxis = barChart.getXAxis();
xAxis.setEnabled(false);
xAxis.setEnabled(false);
xAxis.setDrawGridLines(false);
barChart.invalidate();
barChart.setScaleEnabled(false);
barChart.getXAxis().setDrawGridLines(false);
barChart.getAxisLeft().setDrawGridLines(false);
barChart.getAxisRight().setDrawGridLines(false);
xAxis.setValueFormatter(new IndexAxisValueFormatter(labels));
BarData data1 = new BarData(dataSets);
barChart.setData(data1);
I have to show labels and numeric data on the top of each bar.
Here is the solution for the graph you want to achieve: (Below code allows you to group bars, invert graph as well show label below verticle bars when graph inverted)
I have created my own "BarChartRendererMine" class which we will require to display label below inverted verticle bars. So create "BarChartRendererMine" class and copy paste below code:
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import com.github.mikephil.charting.animation.ChartAnimator;
import com.github.mikephil.charting.buffer.BarBuffer;
import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.formatter.ValueFormatter;
import com.github.mikephil.charting.highlight.Highlight;
import com.github.mikephil.charting.highlight.Range;
import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider;
import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
import com.github.mikephil.charting.model.GradientColor;
import com.github.mikephil.charting.renderer.BarLineScatterCandleBubbleRenderer;
import com.github.mikephil.charting.utils.MPPointF;
import com.github.mikephil.charting.utils.Transformer;
import com.github.mikephil.charting.utils.Utils;
import com.github.mikephil.charting.utils.ViewPortHandler;
import java.util.List;
public class BarChartRendererMine extends BarLineScatterCandleBubbleRenderer {
protected BarDataProvider mChart;
Float commonY1 = 0f;
/**
* the rect object that is used for drawing the bars
*/
protected RectF mBarRect = new RectF();
protected BarBuffer[] mBarBuffers;
protected Paint mShadowPaint;
protected Paint mBarBorderPaint;
public BarChartRendererMine(BarDataProvider chart, ChartAnimator animator,
ViewPortHandler viewPortHandler) {
super(animator, viewPortHandler);
this.mChart = chart;
mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mHighlightPaint.setStyle(Paint.Style.FILL);
mHighlightPaint.setColor(Color.rgb(0, 0, 0));
// set alpha after color
mHighlightPaint.setAlpha(120);
mShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mShadowPaint.setStyle(Paint.Style.FILL);
mBarBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBarBorderPaint.setStyle(Paint.Style.STROKE);
}
#Override
public void initBuffers() {
BarData barData = mChart.getBarData();
mBarBuffers = new BarBuffer[barData.getDataSetCount()];
for (int i = 0; i < mBarBuffers.length; i++) {
IBarDataSet set = barData.getDataSetByIndex(i);
mBarBuffers[i] = new BarBuffer(set.getEntryCount() * 4 * (set.isStacked() ? set.getStackSize() : 1),
barData.getDataSetCount(), set.isStacked());
}
}
#Override
public void drawData(Canvas c) {
BarData barData = mChart.getBarData();
for (int i = 0; i < barData.getDataSetCount(); i++) {
IBarDataSet set = barData.getDataSetByIndex(i);
if (set.isVisible()) {
drawDataSet(c, set, i);
}
}
}
private RectF mBarShadowRectBuffer = new RectF();
protected void drawDataSet(Canvas c, IBarDataSet dataSet, int index) {
Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
mBarBorderPaint.setColor(dataSet.getBarBorderColor());
mBarBorderPaint.setStrokeWidth(Utils.convertDpToPixel(dataSet.getBarBorderWidth()));
final boolean drawBorder = dataSet.getBarBorderWidth() > 0.f;
float phaseX = mAnimator.getPhaseX();
float phaseY = mAnimator.getPhaseY();
// draw the bar shadow before the values
if (mChart.isDrawBarShadowEnabled()) {
mShadowPaint.setColor(dataSet.getBarShadowColor());
BarData barData = mChart.getBarData();
final float barWidth = barData.getBarWidth();
final float barWidthHalf = barWidth / 2.0f;
float x;
for (int i = 0, count = Math.min((int) (Math.ceil((float) (dataSet.getEntryCount()) * phaseX)), dataSet.getEntryCount());
i < count;
i++) {
BarEntry e = dataSet.getEntryForIndex(i);
x = e.getX();
mBarShadowRectBuffer.left = x - barWidthHalf;
mBarShadowRectBuffer.right = x + barWidthHalf;
trans.rectValueToPixel(mBarShadowRectBuffer);
if (!mViewPortHandler.isInBoundsLeft(mBarShadowRectBuffer.right))
continue;
if (!mViewPortHandler.isInBoundsRight(mBarShadowRectBuffer.left))
break;
mBarShadowRectBuffer.top = mViewPortHandler.contentTop();
mBarShadowRectBuffer.bottom = mViewPortHandler.contentBottom();
c.drawRect(mBarShadowRectBuffer, mShadowPaint);
}
}
// initialize the buffer
BarBuffer buffer = mBarBuffers[index];
buffer.setPhases(phaseX, phaseY);
buffer.setDataSet(index);
buffer.setInverted(mChart.isInverted(dataSet.getAxisDependency()));
buffer.setBarWidth(mChart.getBarData().getBarWidth());
buffer.feed(dataSet);
trans.pointValuesToPixel(buffer.buffer);
final boolean isSingleColor = dataSet.getColors().size() == 1;
if (isSingleColor) {
mRenderPaint.setColor(dataSet.getColor());
}
for (int j = 0; j < buffer.size(); j += 4) {
if (!mViewPortHandler.isInBoundsLeft(buffer.buffer[j + 2]))
continue;
if (!mViewPortHandler.isInBoundsRight(buffer.buffer[j]))
break;
if (!isSingleColor) {
// Set the color for the currently drawn value. If the index
// is out of bounds, reuse colors.
mRenderPaint.setColor(dataSet.getColor(j / 4));
}
if (dataSet.getGradientColor() != null) {
GradientColor gradientColor = dataSet.getGradientColor();
mRenderPaint.setShader(
new LinearGradient(
buffer.buffer[j],
buffer.buffer[j + 3],
buffer.buffer[j],
buffer.buffer[j + 1],
gradientColor.getStartColor(),
gradientColor.getEndColor(),
android.graphics.Shader.TileMode.MIRROR));
}
if (dataSet.getGradientColors() != null) {
mRenderPaint.setShader(
new LinearGradient(
buffer.buffer[j],
buffer.buffer[j + 3],
buffer.buffer[j],
buffer.buffer[j + 1],
dataSet.getGradientColor(j / 4).getStartColor(),
dataSet.getGradientColor(j / 4).getEndColor(),
android.graphics.Shader.TileMode.MIRROR));
}
c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
buffer.buffer[j + 3], mRenderPaint);
if (drawBorder) {
c.drawRect(buffer.buffer[j], buffer.buffer[j + 1], buffer.buffer[j + 2],
buffer.buffer[j + 3], mBarBorderPaint);
}
}
}
protected void prepareBarHighlight(float x, float y1, float y2, float barWidthHalf, Transformer trans) {
float left = x - barWidthHalf;
float right = x + barWidthHalf;
float top = y1;
float bottom = y2;
mBarRect.set(left, top, right, bottom);
trans.rectToPixelPhase(mBarRect, mAnimator.getPhaseY());
}
#Override
public void drawValues(Canvas c) {
// if values are drawn
if (isDrawingValuesAllowed(mChart)) {
List<IBarDataSet> dataSets = mChart.getBarData().getDataSets();
final float valueOffsetPlus = Utils.convertDpToPixel(4.5f);
float posOffset = 0f;
float negOffset = 0f;
boolean drawValueAboveBar = mChart.isDrawValueAboveBarEnabled();
for (int i = 0; i < mChart.getBarData().getDataSetCount(); i++) {
IBarDataSet dataSet = dataSets.get(i);
if (!shouldDrawValues(dataSet))
continue;
// apply the text-styling defined by the DataSet
applyValueTextStyle(dataSet);
// calculate the correct offset depending on the draw position of
// the value
float valueTextHeight = Utils.calcTextHeight(mValuePaint, "8");
posOffset = (drawValueAboveBar ? -valueOffsetPlus : valueTextHeight + valueOffsetPlus);
negOffset = (drawValueAboveBar ? valueTextHeight + valueOffsetPlus : -valueOffsetPlus);
// get the buffer
BarBuffer buffer = mBarBuffers[i];
final float phaseY = mAnimator.getPhaseY();
ValueFormatter formatter = dataSet.getValueFormatter();
MPPointF iconsOffset = MPPointF.getInstance(dataSet.getIconsOffset());
iconsOffset.x = Utils.convertDpToPixel(iconsOffset.x);
iconsOffset.y = Utils.convertDpToPixel(iconsOffset.y);
// if only single values are drawn (sum)
if (!dataSet.isStacked()) {
commonY1 = 0f;
for (int j = 0; j < buffer.buffer.length * mAnimator.getPhaseX(); j += 4) {
float x = (buffer.buffer[j] + buffer.buffer[j + 2]) / 2f;
if (!mViewPortHandler.isInBoundsRight(x))
break;
if (!mViewPortHandler.isInBoundsY(buffer.buffer[j + 1])
|| !mViewPortHandler.isInBoundsLeft(x))
continue;
BarEntry entry = dataSet.getEntryForIndex(j / 4);
float val = entry.getY();
if (dataSet.isDrawValuesEnabled()) {
commonY1 = (buffer.buffer[j + 3] + negOffset);
drawValue(c, formatter.getBarLabel(entry), x, val >= 0 ?
(buffer.buffer[j + 1] + posOffset) :
(buffer.buffer[j + 3] + negOffset),
dataSet.getValueTextColor(j / 4));
}
if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) {
Drawable icon = entry.getIcon();
float px = x;
float py = val >= 0 ?
(buffer.buffer[j + 1] + posOffset) :
(buffer.buffer[j + 3] + negOffset);
px += iconsOffset.x;
py += iconsOffset.y;
Utils.drawImage(
c,
icon,
(int) px,
(int) py,
icon.getIntrinsicWidth(),
icon.getIntrinsicHeight());
}
}
// if we have stacks
} else {
Transformer trans = mChart.getTransformer(dataSet.getAxisDependency());
int bufferIndex = 0;
int index = 0;
while (index < dataSet.getEntryCount() * mAnimator.getPhaseX()) {
BarEntry entry = dataSet.getEntryForIndex(index);
float[] vals = entry.getYVals();
float x = (buffer.buffer[bufferIndex] + buffer.buffer[bufferIndex + 2]) / 2f;
int color = dataSet.getValueTextColor(index);
// we still draw stacked bars, but there is one
// non-stacked
// in between
if (vals == null) {
if (!mViewPortHandler.isInBoundsRight(x))
break;
if (!mViewPortHandler.isInBoundsY(buffer.buffer[bufferIndex + 1])
|| !mViewPortHandler.isInBoundsLeft(x))
continue;
if (dataSet.isDrawValuesEnabled()) {
drawValue(c, formatter.getBarLabel(entry), x, buffer.buffer[bufferIndex + 1] +
(entry.getY() >= 0 ? posOffset : negOffset),
color);
}
if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) {
Drawable icon = entry.getIcon();
float px = x;
float py = buffer.buffer[bufferIndex + 1] +
(entry.getY() >= 0 ? posOffset : negOffset);
px += iconsOffset.x;
py += iconsOffset.y;
Utils.drawImage(
c,
icon,
(int) px,
(int) py,
icon.getIntrinsicWidth(),
icon.getIntrinsicHeight());
}
// draw stack values
} else {
float[] transformed = new float[vals.length * 2];
float posY = 0f;
float negY = -entry.getNegativeSum();
for (int k = 0, idx = 0; k < transformed.length; k += 2, idx++) {
float value = vals[idx];
float y;
if (value == 0.0f && (posY == 0.0f || negY == 0.0f)) {
// Take care of the situation of a 0.0 value, which overlaps a non-zero bar
y = value;
} else if (value >= 0.0f) {
posY += value;
y = posY;
} else {
y = negY;
negY -= value;
}
transformed[k + 1] = y * phaseY;
}
trans.pointValuesToPixel(transformed);
for (int k = 0; k < transformed.length; k += 2) {
final float val = vals[k / 2];
final boolean drawBelow =
(val == 0.0f && negY == 0.0f && posY > 0.0f) ||
val < 0.0f;
float y = transformed[k + 1]
+ (drawBelow ? negOffset : posOffset);
if (!mViewPortHandler.isInBoundsRight(x))
break;
if (!mViewPortHandler.isInBoundsY(y)
|| !mViewPortHandler.isInBoundsLeft(x))
continue;
if (dataSet.isDrawValuesEnabled()) {
drawValue(c, formatter.getBarStackedLabel(val, entry), x, y, color);
}
if (entry.getIcon() != null && dataSet.isDrawIconsEnabled()) {
Drawable icon = entry.getIcon();
Utils.drawImage(
c,
icon,
(int) (x + iconsOffset.x),
(int) (y + iconsOffset.y),
icon.getIntrinsicWidth(),
icon.getIntrinsicHeight());
}
}
}
bufferIndex = vals == null ? bufferIndex + 4 : bufferIndex + 4 * vals.length;
index++;
}
}
MPPointF.recycleInstance(iconsOffset);
}
}
}
#Override
public void drawValue(Canvas c, String valueText, float x, float y, int color) {
mValuePaint.setColor(color);
try{
c.drawText(valueText, x, commonY1 + 30f, mValuePaint);
}catch (Exception e){
c.drawText(valueText, x, y, mValuePaint);
}
}
#Override
public void drawHighlighted(Canvas c, Highlight[] indices) {
BarData barData = mChart.getBarData();
for (Highlight high : indices) {
IBarDataSet set = barData.getDataSetByIndex(high.getDataSetIndex());
if (set == null || !set.isHighlightEnabled())
continue;
BarEntry e = set.getEntryForXValue(high.getX(), high.getY());
if (!isInBoundsX(e, set))
continue;
Transformer trans = mChart.getTransformer(set.getAxisDependency());
mHighlightPaint.setColor(set.getHighLightColor());
mHighlightPaint.setAlpha(set.getHighLightAlpha());
boolean isStack = (high.getStackIndex() >= 0 && e.isStacked()) ? true : false;
final float y1;
final float y2;
if (isStack) {
if (mChart.isHighlightFullBarEnabled()) {
y1 = e.getPositiveSum();
y2 = -e.getNegativeSum();
} else {
Range range = e.getRanges()[high.getStackIndex()];
y1 = range.from;
y2 = range.to;
}
} else {
y1 = e.getY();
y2 = 0.f;
}
prepareBarHighlight(e.getX(), y1, y2, barData.getBarWidth() / 2f, trans);
setHighlightDrawPos(high, mBarRect);
c.drawRect(mBarRect, mHighlightPaint);
}
}
/**
* Sets the drawing position of the highlight object based on the riven bar-rect.
*
* #param high
*/
protected void setHighlightDrawPos(Highlight high, RectF bar) {
high.setDraw(bar.centerX(), bar.top);
}
#Override
public void drawExtras(Canvas c) {
}
}
Then in our activity do as below:
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Bundle;
import android.view.WindowManager;
import com.a.mpandroidchartbarwidth.custom.BarChartRendererMine;
import com.github.mikephil.charting.animation.ChartAnimator;
import com.github.mikephil.charting.charts.BarChart;
import com.github.mikephil.charting.components.AxisBase;
import com.github.mikephil.charting.components.Legend;
import com.github.mikephil.charting.components.XAxis;
import com.github.mikephil.charting.components.YAxis;
import com.github.mikephil.charting.data.BarData;
import com.github.mikephil.charting.data.BarDataSet;
import com.github.mikephil.charting.data.BarEntry;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.formatter.IAxisValueFormatter;
import com.github.mikephil.charting.formatter.IValueFormatter;
import com.github.mikephil.charting.formatter.IndexAxisValueFormatter;
import com.github.mikephil.charting.formatter.LargeValueFormatter;
import com.github.mikephil.charting.formatter.ValueFormatter;
import com.github.mikephil.charting.interfaces.dataprovider.BarDataProvider;
import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
import com.github.mikephil.charting.renderer.BarChartRenderer;
import com.github.mikephil.charting.utils.ViewPortHandler;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import androidx.appcompat.app.AppCompatActivity;
public class BarChartInvertedActivity extends AppCompatActivity {
private BarChart barChart;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
setTitle("BarChartActivity");
barChart = findViewById(R.id.barchart);
float barWidth = 0.2f;
float barSpace = 0.1f;
float groupSpace = 0.25f;
int groupCount = 2;
ArrayList xVals = new ArrayList();
xVals.add("Hold");
xVals.add("Over");
xVals.add("Buy");
ArrayList<String> yValsTop = new ArrayList();
yValsTop.add("0");
yValsTop.add("1");
yValsTop.add("2");
yValsTop.add("3");
yValsTop.add("4");
yValsTop.add("5");
yValsTop.add("6");
yValsTop.add("7");
yValsTop.add("8");
ArrayList yVals1 = new ArrayList();
ArrayList yVals2 = new ArrayList();
ArrayList yVals3 = new ArrayList();
yVals1.add(new BarEntry(1, (float) 1));
yVals2.add(new BarEntry(1, (float) 2));
yVals1.add(new BarEntry(2, (float) 3));
yVals2.add(new BarEntry(2, (float) 4));
yVals1.add(new BarEntry(3, (float) 5));
yVals2.add(new BarEntry(3, (float) 6));
BarDataSet set1, set2, set3;
set1 = new BarDataSet(yVals1, "Buy");
set1.setColor(Color.parseColor("#3EBB9B"));
set2 = new BarDataSet(yVals2, "Sell");
set2.setColor(Color.parseColor("#3698DB"));
set1.setDrawValues(true);
set2.setDrawValues(true);
BarData data1 = new BarData(set1, set2);
barChart.setData(data1);
barChart.getBarData().setBarWidth(barWidth);
barChart.getXAxis().setAxisMinimum(0);
barChart.getXAxis().setAxisMaximum(0 + barChart.getBarData().getGroupWidth(groupSpace, barSpace) * groupCount);
barChart.getData().setHighlightEnabled(false);
barChart.getLegend().setEnabled(false);
barChart.invalidate();
barChart.setDescription(null);
barChart.setPinchZoom(false);
barChart.setScaleEnabled(false);
barChart.setDrawBarShadow(false);
barChart.setDrawGridBackground(false);
//X-axis
XAxis xAxis = barChart.getXAxis();
xAxis.setGranularity(1f);
xAxis.setGranularityEnabled(true);
xAxis.setCenterAxisLabels(true);
xAxis.setDrawGridLines(false);
xAxis.setAxisMaximum(3);
xAxis.setPosition(XAxis.XAxisPosition.TOP);
xAxis.setValueFormatter(new IndexAxisValueFormatter(xVals));
//Y-axis
barChart.getAxisRight().setEnabled(false);
YAxis leftAxis = barChart.getAxisLeft();
leftAxis.setDrawGridLines(true);
leftAxis.setSpaceTop(35f);
leftAxis.setAxisMinimum(0f);
YAxis yAxisRight = barChart.getAxisRight();
yAxisRight.setEnabled(false);
YAxis yAxisLeft = barChart.getAxisLeft();
yAxisLeft.setEnabled(false);
barChart.groupBars(groupSpace, groupSpace, barSpace);
barChart.getAxisLeft().setInverted(true);
barChart.setDrawValueAboveBar(false);
BarChartRendererMine barChartCustomRenderer = new BarChartRendererMine(barChart, barChart.getAnimator(), barChart.getViewPortHandler());
barChart.setRenderer(barChartCustomRenderer);
xAxis.setAxisLineColor(getResources().getColor(R.color.colorTransparent));
}
}
This will change line color and looks like hidden
"xAxis.setAxisLineColor(getResources().getColor(R.color.colorTransparent));"
Below lines required to custom draw for bar chart:
BarChartRendererMine barChartCustomRenderer = new BarChartRendererMine(barChart, barChart.getAnimator(), barChart.getViewPortHandler());
barChart.setRenderer(barChartCustomRenderer);
Hope it helps :)
I'm developing a game using SurfaceView which listens to touch events. The onTouchEvent method in SurfaceView works fine for many of the devices, but in some devices, sometimes it doesn't get called (Moto X Style is the one) and my app also stops responding.
I guess that this might be due to the overloading of main thread due to which onTouchEvent is starving.
Could some Android experts over here give me some tips to reduce the load on main thread if it's getting overloaded, or there might be some other reason which may cause this
The code is quite complex but still I'm posting some if you want to go through it
GameLoopThread
public class GameLoopThread extends Thread{
private GameView view;
// desired fps
private final static int MAX_FPS = 120;
// maximum number of frames to be skipped
private final static int MAX_FRAME_SKIPS = 5;
// the frame period
private final static int FRAME_PERIOD = 1000 / MAX_FPS;
private boolean running = false;
public GameLoopThread(GameView view){
this.view = view;
}
public void setRunning(boolean running){
this.running = running;
}
public boolean isRunning() {
return running;
}
#Override
public void run() {
Canvas canvas;
long beginTime; // the time when the cycle begun
long timeDiff; // the time it took for the cycle to execute
int sleepTime; // ms to sleep (<0 if we're behind)
int framesSkipped; // number of frames being skipped
while (running) {
canvas = null;
// try locking the canvas for exclusive pixel editing
// in the surface
try {
canvas = view.getHolder().lockCanvas();
synchronized (view.getHolder()) {
beginTime = System.nanoTime();
framesSkipped = 0; // resetting the frames skipped
// update game state
// render state to the screen
// draws the canvas on the panel
this.view.draw(canvas);
// calculate how long did the cycle take
timeDiff = System.nanoTime() - beginTime;
// calculate sleep time
sleepTime = (int)(FRAME_PERIOD - timeDiff/1000000);
if (sleepTime > 0) {
// if sleepTime > 0 we're OK
try {
// send the thread to sleep for a short period
// very useful for battery saving
Thread.sleep(sleepTime);
} catch (InterruptedException e) {}
}
while (sleepTime < 0 && framesSkipped < MAX_FRAME_SKIPS) {
// update without rendering
// add frame period to check if in next frame
sleepTime += FRAME_PERIOD;
framesSkipped++;
}
}
}
finally {
// in case of an exception the surface is not left in
// an inconsistent state
view.getHolder().unlockCanvasAndPost(canvas);
} // end finally
}
}
}
GameView
public class GameView extends SurfaceView {
ArrayList<Bitmap> circles = new ArrayList<>();
int color;
public static boolean isGameOver;
public GameLoopThread gameLoopThread;
Circle circle; // Code for Circle class is provided below
public static int score = 0;
public static int stars = 0;
final Handler handler = new Handler();
int remainingTime;
boolean oneTimeFlag;
Bitmap replay;
Bitmap home;
Bitmap star;
int highScore;
boolean isLeaving;
public GameView(Context context, ArrayList<Bitmap> circles, int color) {
super(context);
this.circles = circles;
this.color = color;
oneTimeFlag = true;
gameLoopThread = new GameLoopThread(GameView.this);
getHolder().addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
if (!gameLoopThread.isRunning()) {
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
gameLoopThread.setRunning(false);
gameLoopThread = new GameLoopThread(GameView.this);
}
});
initializeCircles();
if(!gameLoopThread.isRunning()) {
gameLoopThread.setRunning(true);
gameLoopThread.start();
}
}
public void initializeCircles() {
ArrayList<String> numbers = new ArrayList<>();
for(int i=0;i<10;i++)
numbers.add(i+"");
Random random = new Random();
int position = random.nextInt(4);
numbers.remove(color + "");
int p1 = position;
int r1 = Integer.valueOf(numbers.get(random.nextInt(9)));
numbers.remove(r1+"");
int r2 = Integer.valueOf(numbers.get(random.nextInt(8)));
numbers.remove(r2 + "");
int r3 = Integer.valueOf(numbers.get(random.nextInt(7)));
ArrayList<Bitmap> bitmaps = new ArrayList<>();
if(position == 0) {
bitmaps.add(circles.get(color));
bitmaps.add(circles.get(r1));
bitmaps.add(circles.get(r2));
bitmaps.add(circles.get(r3));
}
else if(position == 1) {
bitmaps.add(circles.get(r1));
bitmaps.add(circles.get(color));
bitmaps.add(circles.get(r2));
bitmaps.add(circles.get(r3));
}
else if(position == 2) {
bitmaps.add(circles.get(r1));
bitmaps.add(circles.get(r2));
bitmaps.add(circles.get(color));
bitmaps.add(circles.get(r3));
}
else {
bitmaps.add(circles.get(r1));
bitmaps.add(circles.get(r2));
bitmaps.add(circles.get(r3));
bitmaps.add(circles.get(color));
}
numbers = new ArrayList<>();
for(int i=0;i<10;i++)
numbers.add(i+"");
position = random.nextInt(4);
numbers.remove(color + "");
r1 = Integer.valueOf(numbers.get(random.nextInt(9)));
numbers.remove(r1 + "");
r2 = Integer.valueOf(numbers.get(random.nextInt(8)));
numbers.remove(r2 + "");
r3 = Integer.valueOf(numbers.get(random.nextInt(7)));
if(position == 0) {
bitmaps.add(circles.get(color));
bitmaps.add(circles.get(r1));
bitmaps.add(circles.get(r2));
bitmaps.add(circles.get(r3));
}
else if(position == 1) {
bitmaps.add(circles.get(r1));
bitmaps.add(circles.get(color));
bitmaps.add(circles.get(r2));
bitmaps.add(circles.get(r3));
}
else if(position == 2) {
bitmaps.add(circles.get(r1));
bitmaps.add(circles.get(r2));
bitmaps.add(circles.get(color));
bitmaps.add(circles.get(r3));
}
else {
bitmaps.add(circles.get(r1));
bitmaps.add(circles.get(r2));
bitmaps.add(circles.get(r3));
bitmaps.add(circles.get(color));
}
circle = new Circle(this, bitmaps, circles, p1, position, color, getContext());
}
#Override
public void draw(Canvas canvas) {
if(canvas != null) {
super.draw(canvas);
canvas.drawColor(Color.WHITE);
if(!isGameOver && timer != null)
stopTimerTask();
try {
circle.draw(canvas);
} catch (GameOverException e) {
isGameOver = true;
if(isLeaving)
gameOver(canvas);
else if(GameActivity.counter > 0) {
gameOver(canvas);
GameActivity.counter++;
} else {
if (oneTimeFlag) {
int size1 = 200 * GameActivity.SCREEN_HEIGHT / 1280;
int size2 = 125 * GameActivity.SCREEN_HEIGHT / 1280;
float ratio = (float) GameActivity.SCREEN_HEIGHT / 1280;
replay = GameActivity.decodeSampledBitmapFromResource(getResources(), R.drawable.replay, size1, size1);
home = GameActivity.decodeSampledBitmapFromResource(getResources(), R.drawable.home, size2, size2);
continueButton = GameActivity.decodeSampledBitmapFromResource(getContext().getResources(), R.drawable.button, (int) (540 * ratio), (int) (100 * ratio));
star = GameActivity.decodeSampledBitmapFromResource(getContext().getResources(), R.drawable.star1, (int) (220 * ratio), (int) (220 * ratio));
int w = (int) ((float) GameActivity.SCREEN_WIDTH * 0.9);
oneTimeFlag = false;
}
if (askPurchaseScreen == 2) {
gameOver(canvas);
} else {
canvas.drawColor(Circle.endColor);
}
}
}
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
circle.onTouch(x, y);
return true;
}
}
Circle
public class Circle {
int x;
int y1;
int y2;
public static float speedY1 = 12.5f*(float)GameActivity.SCREEN_HEIGHT/1280;
public static float speedY2 = 12.5f*(float)GameActivity.SCREEN_HEIGHT/1280;
ArrayList<Bitmap> bitmaps;
GameView gameView;
int p1; // Position of required circle in slot 1
int p2; // Position of required circle in slot 2
int color;
int tempColor;
int width;
Context context;
// Centers of required circle
float centerX1;
float centerX2;
float centerY1;
float centerY2;
ArrayList<Bitmap> circles = new ArrayList<>();
boolean touchedFirst;
boolean touchedSecond;
int count1 = 1; // Slot 1 circle radius animation
int count2 = 1; // Slot 2 circle radius animation
float tempSpeedY1;
float tempSpeedY2;
boolean stopY1;
boolean stopY2;
int barCounter = 1;
int loopCount = 0;
int endGameCount = 0; // Count to move circle upwards
double limit;
float endRadiusSpeed;
int endSlot; // Where you died
int endRadiusCount = 0; // Count to increase circle radius
int barEndCounter = 1;
final Handler handler = new Handler();
boolean exception;
public static int endColor;
public Circle(GameView gameView, ArrayList<Bitmap> bitmaps, ArrayList<Bitmap> circles, int p1, int p2, int color, Context context) {
this.gameView = gameView;
this.bitmaps = bitmaps;
this.circles = circles;
this.p1 = p1;
this.p2 = p2;
this.color = color;
this.context = context;
width = GameActivity.SCREEN_WIDTH / 4 - 10;
x = 10;
y1 = 0;
y2 = -(GameActivity.SCREEN_HEIGHT + width) / 2;
centerX1 = x + p1 * (10 + width) + width / 2;
centerY1 = y1 + width / 2;
centerX2 = x + p2 * (10 + width) + width / 2;
centerY2 = y2 + width / 2;
}
public void update() throws GameOverException {
y1+= speedY1;
y2+= speedY2;
centerY1+= speedY1;
centerY2+= speedY2;
float ratio = (float)GameActivity.SCREEN_HEIGHT/1280;
limit = width/(20*ratio);
if(y1 >= gameView.getHeight()) {
loopCount++;
if(touchedFirst)
touchedFirst = false;
else {
speedY1 = speedY2 = -(12.5f * ratio);
endColor = bitmaps.get(p1).getPixel(width/2, width/2);
endGameCount += 1;
endSlot = 1;
}
if(endGameCount == 0) {
if (stopY1) {
tempSpeedY1 = speedY1;
speedY1 = 0;
ArrayList<Integer> numbers = new ArrayList<>();
for (int i = 0; i < 10; i++) {
if (i != color)
numbers.add(i);
}
tempColor = numbers.get(new Random().nextInt(9));
}
y1 = -(gameView.getWidth() / 4 - 10);
count1 = 1;
setBitmaps(1);
}
}
else if(y2 >= gameView.getHeight()) {
loopCount++;
if(touchedSecond)
touchedSecond = false;
else {
speedY1 = speedY2 = -(12.5f * ratio);
endColor = bitmaps.get(p2 + 4
).getPixel(width/2, width/2);
endGameCount += 1;
endSlot = 2;
}
if(endGameCount == 0) {
if (stopY2) {
tempSpeedY2 = speedY2;
speedY2 = 0;
}
y2 = -(gameView.getWidth() / 4 - 10);
count2 = 1;
setBitmaps(2);
}
}
}
public void setBitmaps(int slot) {
ArrayList<String> numbers = new ArrayList<>();
for(int i=0;i<10;i++)
numbers.add(i+"");
Random random = new Random();
int position = random.nextInt(4);
numbers.remove(color + "");
int r1 = Integer.valueOf(numbers.get(random.nextInt(9)));
numbers.remove(r1+"");
int r2 = Integer.valueOf(numbers.get(random.nextInt(8)));
numbers.remove(r2 + "");
int r3 = Integer.valueOf(numbers.get(random.nextInt(7)));
if(position == 0) {
bitmaps.set((slot - 1)*4, circles.get(color));
bitmaps.set((slot - 1)*4 + 1, circles.get(r1));
bitmaps.set((slot - 1)*4 + 2, circles.get(r2));
bitmaps.set((slot - 1)*4 + 3, circles.get(r3));
}
else if(position == 1) {
bitmaps.set((slot - 1)*4, circles.get(r1));
bitmaps.set((slot - 1)*4 + 1, circles.get(color));
bitmaps.set((slot - 1)*4 + 2, circles.get(r2));
bitmaps.set((slot - 1)*4 + 3, circles.get(r3));
}
else if(position == 2) {
bitmaps.set((slot - 1)*4, circles.get(r1));
bitmaps.set((slot - 1)*4 + 1, circles.get(r2));
bitmaps.set((slot - 1)*4 + 2, circles.get(color));
bitmaps.set((slot - 1)*4 + 3, circles.get(r3));
} else {
bitmaps.set((slot - 1)*4,circles.get(r1));
bitmaps.set((slot - 1)*4 + 1,circles.get(r2));
bitmaps.set((slot - 1)*4 + 2,circles.get(r3));
bitmaps.set((slot - 1)*4 + 3,circles.get(color));
}
if(slot == 1) {
p1 = position;
centerX1 = x+position*(10 + width) + width/2;
centerY1 = y1 + width/2;
}
else if(slot == 2) {
p2 = position;
centerX2 = x+position*(10 + width) + width/2;
centerY2 = y2 + width/2;
}
}
public void onTouch(float X, float Y) {
int radius = (gameView.getWidth() / 4 - 10) / 2;
if(endGameCount == 0) {
if ((X >= centerX1 - radius) && (X <= centerX1 + radius) && (Y >= centerY1 - radius) && (Y <= centerY1 + radius)) {
GameView.score++;
touchedFirst = true;
centerX1 = centerY1 = -1;
if(p1 == (timerCount - 1) && timer != null && starSlot == 1) {
GameView.stars++;
starCollected = true;
timerCount = 0;
stopTimerTask(0);
}
} else if ((X >= centerX2 - radius) && (X <= centerX2 + radius) && (Y >= centerY2 - radius) && (Y <= centerY2 + radius)) {
GameView.score++;
touchedSecond = true;
centerX2 = centerY2 = -1;
if(p2 == (timerCount - 1) && timer != null && starSlot == 2) {
GameView.stars++;
starCollected = true;
timerCount = 0;
stopTimerTask(0);
}
} else {
endSlot = 0;
if ((Y >= centerY1 - radius) && (Y <= centerY1 + radius)) {
endSlot = 1;
if (X >= 10 && X <= 10 + 2 * radius) {
p1 = 0;
centerX1 = 10 + radius;
} else if (X >= 20 + 2 * radius && X <= 20 + 4 * radius) {
p1 = 1;
centerX1 = 20 + 3 * radius;
} else if (X >= 30 + 4 * radius && X <= 30 + 6 * radius) {
p1 = 2;
centerX1 = 30 + 5 * radius;
} else if (X >= 40 + 6 * radius && X <= 40 + 8 * radius) {
p1 = 3;
centerX1 = 40 + 2 * radius;
} else
endSlot = 0;
} else if ((Y >= centerY2 - radius) && (Y <= centerY2 + radius)) {
endSlot = 2;
if (X >= 10 && X <= 10 + 2 * radius) {
p2 = 0;
centerX2 = 10 + radius;
} else if (X >= 20 + 2 * radius && X <= 20 + 4 * radius) {
p2 = 1;
centerX2 = 20 + 3 * radius;
} else if (X >= 30 + 4 * radius && X <= 30 + 6 * radius) {
p2 = 2;
centerX2 = 30 + 5 * radius;
} else if (X >= 40 + 6 * radius && X <= 40 + 8 * radius) {
p2 = 3;
centerX2 = 40 + 2 * radius;
} else
endSlot = 0;
}
if (endSlot != 0) {
speedY1 = speedY2 = 0;
limit = endGameCount = 6;
if (endSlot == 1) {
endColor= bitmaps.get(p1).getPixel(width/2, width/2);
} else {
endColor = bitmaps.get(p2 + 4).getPixel(width/2, width/2);
}
}
}
if (GameView.score % 5 == 0 && GameView.score <= 110 && barCounter == 1) {
float ratio = (float)GameActivity.SCREEN_HEIGHT/1280;
speedY1 += ratio*0.5;
speedY2 += ratio*0.5;
}
if (GameView.score > 0 && GameView.score % 15 == 14) {
if(isOddScore)
stopY1 = true;
else
stopY2 = true;
}
if (GameView.score > 0 && GameView.score % 15 == 0 && barCounter == 1) {
if(isOddScore)
stopY2 = true;
else
stopY1 = true;
}
if (GameView.score % 15 == 1)
barCounter = 1;
}
}
public void draw(Canvas canvas) throws GameOverException {
GameView.isGameOver = false;
if(exception)
throw new GameOverException(color);
update();
for(int i=0;i<bitmaps.size();i++) {
if(i<4) {
Rect rect = new Rect(x+i*(10 + width),y1,(x+width)*(i+1),y1+width);
if(endGameCount == Math.ceil(limit) && endSlot == 1) {
if(i == p1) {
endRadiusCount += 1;
if (endRadiusCount > 23) {
star.recycle();
loopCount = loopCount%starInterval;
Cryptography.saveFile((loopCount + "").getBytes(), context, "interval");
endGameCount = 0;
exception = true;
throw new GameOverException(color);
}
rect = new Rect(x + i * (10 + width) - endRadiusCount*(int)Math.ceil(endRadiusSpeed), y1 - endRadiusCount*(int)Math.ceil(endRadiusSpeed), (x + width) * (i + 1) + endRadiusCount*(int)Math.ceil(endRadiusSpeed), y1 + width + endRadiusCount*(int)Math.ceil(endRadiusSpeed));
canvas.drawBitmap(bitmaps.get(i), null, rect, null);
}
}
// TOUCH ANIMATION : DIMINISH CIRCLE
else if(i==p1 && touchedFirst) {
rect = new Rect(x + i * (10 + width) + 3*count1 + ((int)speedY1-15), y1 + 3*count1 + ((int)speedY1-15), (x + width) * (i + 1) - 3*count1 - ((int)speedY1-15), y1 + width - 3*count1 - ((int)speedY1-15));
canvas.drawBitmap(bitmaps.get(i), null, rect, null);
count1++;
}
else if(endSlot != 2) {
canvas.drawBitmap(bitmaps.get(i), null, rect, null);
if(timerCount > 0 && starSlot == 1) {
int size = width * 30 / 50;
int difference = (width - size) / 2;
Rect starRect = new Rect(x + (timerCount - 1) * (10 + width) + difference, y1 + difference, (x + width) * (timerCount) - difference, y1 + width - difference);
canvas.drawBitmap(star, null, starRect, null);
}
}
}
if(i >= 4) {
Rect rect = new Rect(x + (i % 4) * (10 + width), y2, (x + width) * ((i % 4) + 1), y2 + width);
if(endGameCount == Math.ceil(limit) && endSlot == 2) {
if((i%4)==p2) {
endRadiusCount += 1;
if (endRadiusCount > 23) {
star.recycle();
loopCount = loopCount%starInterval;
Cryptography.saveFile((loopCount + "").getBytes(), context, "interval");
endGameCount = 0;
exception = true;
throw new GameOverException(color);
}
rect = new Rect(x + (i % 4) * (10 + width) - endRadiusCount*(int)Math.ceil(endRadiusSpeed), y2 - endRadiusCount*(int)Math.ceil(endRadiusSpeed), (x + width) * ((i % 4) + 1) + endRadiusCount*(int)Math.ceil(endRadiusSpeed), y2 + width + endRadiusCount*(int)Math.ceil(endRadiusSpeed));
canvas.drawBitmap(bitmaps.get(i), null, rect, null);
}
}
else if((i%4)==p2 && touchedSecond) {
rect = new Rect(x + (i % 4) * (10 + width) + 3*count2 + ((int)speedY1-15), y2 + 3*count2 + ((int)speedY1-15), (x + width) * ((i % 4) + 1) - 3*count2 - ((int)speedY1-15), y2 + width - 3*count2 - ((int)speedY1-15));
canvas.drawBitmap(bitmaps.get(i), null, rect, null);
count2++;
}
else if(endSlot != 1) {
canvas.drawBitmap(bitmaps.get(i), null, rect, null);
if(timerCount > 0 && starSlot == 2) {
int size = width * 30 / 50;
int difference = (width - size) / 2;
Rect starRect = new Rect(x + (timerCount - 1) * (10 + width) + difference, y2 + difference, (x + width) * (timerCount) - difference, y2 + width - difference);
canvas.drawBitmap(star, null, starRect, null);
}
}
}
}
Rect src = new Rect(circles.get(color).getWidth()/2 - 10,circles.get(color).getHeight()/2 - 10,circles.get(color).getWidth()/2 + 10,circles.get(color).getHeight()/2 + 10);
Rect dst;
Paint paint = new Paint();
paint.setColor(Color.WHITE);
paint.setTextAlign(Paint.Align.RIGHT);
paint.setTypeface(Typeface.SANS_SERIF);
paint.setTextSize(72 * ratio);
canvas.drawText(GameView.score + " ", GameActivity.SCREEN_WIDTH, width / 2, paint);
dst = new Rect(5,5, (int) (120 * ratio - 5), (int) (120 * ratio - 5));
canvas.drawBitmap(star,null,dst,null);
paint.setTextAlign(Paint.Align.LEFT);
canvas.drawText("" + GameView.stars, 120 * ratio, width/2, paint);
}
}
Don't override draw(). That's used to render the View, not the Surface, and you generally shouldn't override that method even if you're creating a custom View:
When implementing a view, implement onDraw(android.graphics.Canvas) instead of overriding this method.
SurfaceViews have two parts, the Surface and the View. The View part is handled like any other View, but is generally just a transparent "hole" in the layout. The Surface is a separate layer that, by default, sits behind the View layer. Whatever you draw on the Surface "shows through" the transparent hole.
By overriding draw() you're drawing on the View whenever the View UI is invalidated. You're also calling draw() from the render thread, so you're drawing on the Surface, but with default Z-ordering you can't see that because the View contents are fully opaque. You will reduce your impact on the UI thread by not drawing everything in two different layers.
Unless you're deliberately drawing on the View, it's best to avoid subclassing SurfaceView entirely, and just use it as a member.
Because your draw code is synchronized, the two draw passes will not execute concurrently. That means your View layer draw call will block waiting for the Surface layer rendering to complete. Canvas rendering on a Surface is not hardware-accelerated, so if you're touching a lot of pixels it can get slow, and the UI thread will have to wait for it to run. That wouldn't be so bad, but you're holding on to the mutex while you're sleeping, which means the only opportunity for the main UI thread to run comes during the brief instant when the loop wraps around. The thread scheduler does not guarantee fairness, so it's entirely possible to starve the main UI thread this way.
If you change #override draw() to myDraw() things should get better. You should probably move your sleep call out of the synchronized block just on general principles, or work to eliminate it entirely. You might also want to consider using a custom View instead of SurfaceView.
On an unrelated note, you should probably avoid doing this every update:
Random random = new Random();
for the reasons noted here.
Successfully solved the issue. Can't imagine that the solution would be this much simple as compared to the problem that I was considering that complex. Just reduced the frame rate from 120 to 90 and guess what, it worked like charm!
Due to a high frame rate, the SurfaceView was busy doing all the drawing and onTouchEvent() method had to starve
I'm trying to find 4 corners from a rect object(paper sheet).
Mat source = new Mat();
Org.Opencv.Core.Point center;
public GetCorners(Bitmap _sourceImg)
{
Utils.BitmapToMat(_sourceImg, source);
}
//find corners
public void FindCorners()
{
center = new Org.Opencv.Core.Point(0, 0);
//Mat source = new Mat();
if (source == null)
{
Console.WriteLine("No IMG");
return;
}
Mat BlackWhite = new Mat();
Imgproc.CvtColor(source, BlackWhite, Imgproc. ColorBgr2gray); //ColorBgra2gray, 4);
Imgproc.Blur(BlackWhite, BlackWhite, new Size(3, 3));
Imgproc.Canny(BlackWhite, BlackWhite, 100, 100, 3, true);
Mat Lines = new Mat();
int treshold = 70;
int minLinsize = 30;
int lineGap = 10;
Imgproc.HoughLinesP(BlackWhite, Lines, 1, Math.PI / 180, treshold, minLinsize, lineGap);
for (int i = 0; i < Lines.Cols(); i++)
{
double[] Vector = Lines.Get(0, i);
double[] Value = new double[4];
Value[0] = 0;
Value[1] = ((float) Vector[1] - Vector[3]) / (Vector[0] - Vector[2]) * -Vector[0] + Vector[1];
Value[2] = source.Cols();
Value[3] = ((float)Vector[1] - Vector[3]) / (Vector[0] - Vector[2]) * (source.Cols() - Vector[2]) + Vector[3];
Lines.Put(0, i, Value);
}
Console.WriteLine("##Quantity {0} Founded##",Lines.Cols());
List<Org.Opencv.Core.Point> Corners = new List<Org.Opencv.Core.Point>();
for (int i = 0; i < Lines.Cols(); i++)
{
for (int j = 0 ; i < Lines.Cols(); j++)
{
Mat m1 = new Mat(),
m2 = new Mat();
double[] d1 = Lines.Get(0, i);
double[] d2 = Lines.Get(0, j);
m1.Put(0, j, d1);
m2.Put(0, j, d2);
try
{
//i'm getting exception here
Org.Opencv.Core.Point pt = ComputeInteresect(Lines.Get(0, i), Lines.Get(0, j)); //(m1, m2);
if (pt.X >= 0 && pt.Y >= 0)
{
Corners.Add(pt);
Console.WriteLine ("dobavleno {0} koordinat",Corners.Count);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
and also i got a method that calculate intersects :
static Org.Opencv.Core.Point ComputeInteresect(double[] a, double[] b) //(Mat es,Mat es2)//
{
double x1 = a[0], y1 = a[1], x2 = a[2], y2 = a[3], x3 = b[0], y3 = b[1], x4 = b[2], y4 = b[3];
double denom = ((x1 - x2) * (y3 - y4)) - ((y1 - y2) * (x3 - x4));
Org.Opencv.Core.Point pt = new Org.Opencv.Core.Point();
if (denom != 0)
{
pt.X = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2)
* (x3 * y4 - y3 * x4))
/ denom;
pt.Y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2)
* (x3 * y4 - y3 * x4))
/ denom;
return pt;
}
else
return new Org.Opencv.Core.Point(-1, -1);
}
And I don't understand why, but I'm getting this exception:
java.lang.UnsupportedOperationException:
Provided data element number (0)
should be multiple of the Mat channels
count (1)
I found,that this problem occurs, when an image is in RGB Format (and has 4 channels), but at first, I'm converting to gray (1 channel), via this method:
Imgproc.CvtColor(source, BlackWhite, Imgproc. ColorBgr2gray); //ColorBgra2gray, 4);
Any help will be appreciated, thanks!
Finally, i changed some part of code and its works:
Console.WriteLine("##Quantity {0} Founded##",Lines.Cols());
List<Org.Opencv.Core.Point> Corners = new List<Org.Opencv.Core.Point>();
for (int i = 0; i < Lines.Cols(); i++)
{
for (int j = i+1 ; i < Lines.Cols(); j++)
{
try
{
Org.Opencv.Core.Point pt = ComputeInteresect(Lines.Get(0, i), Lines.Get(0, j));
if (pt.X >= 0 && pt.Y >= 0)
{
Corners.Add(pt);
Console.WriteLine ("dobavleno {0} koordinat",Corners.Count);
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
I've got a question regarding Bitmaps in Android: I 've got a Bitmap with white margins [size unknown] around. Is it possible to create a new Bitmap with all the white margins removed (rectangular shape)?
Bitmap bmp = Bitmap.createBitmap(width, bmpheigth, Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);
canvas.setBitmap(bmp);
canvas.drawColor(Color.WHITE);
// draw here things!
It is asumed to be unknown where are things painted.
What is a good way to do that?
thanks!
Thanks #Maxim Efimov & #StackOverflowException
Just in Case Someone will need a snippet for this kind of problems:
this method returns a cut out smaller Bitmap with Margins removed. passing the pixels to a int-array first and then working with the array is a bit faster than the Bitmap.getPixel method
just call the method indicating Source Bitmap and Background color.
Bitmap bmp2 = removeMargins(bmp, Color.WHITE);
private static Bitmap removeMargins2(Bitmap bmp, int color) {
// TODO Auto-generated method stub
long dtMili = System.currentTimeMillis();
int MTop = 0, MBot = 0, MLeft = 0, MRight = 0;
boolean found1 = false, found2 = false;
int[] bmpIn = new int[bmp.getWidth() * bmp.getHeight()];
int[][] bmpInt = new int[bmp.getWidth()][bmp.getHeight()];
bmp.getPixels(bmpIn, 0, bmp.getWidth(), 0, 0, bmp.getWidth(),
bmp.getHeight());
for (int ii = 0, contX = 0, contY = 0; ii < bmpIn.length; ii++) {
bmpInt[contX][contY] = bmpIn[ii];
contX++;
if (contX >= bmp.getWidth()) {
contX = 0;
contY++;
if (contY >= bmp.getHeight()) {
break;
}
}
}
for (int hP = 0; hP < bmpInt[0].length && !found2; hP++) {
// looking for MTop
for (int wP = 0; wP < bmpInt.length && !found2; wP++) {
if (bmpInt[wP][hP] != color) {
Log.e("MTop 2", "Pixel found #" + hP);
MTop = hP;
found2 = true;
break;
}
}
}
found2 = false;
for (int hP = bmpInt[0].length - 1; hP >= 0 && !found2; hP--) {
// looking for MBot
for (int wP = 0; wP < bmpInt.length && !found2; wP++) {
if (bmpInt[wP][hP] != color) {
Log.e("MBot 2", "Pixel found #" + hP);
MBot = bmp.getHeight() - hP;
found2 = true;
break;
}
}
}
found2 = false;
for (int wP = 0; wP < bmpInt.length && !found2; wP++) {
// looking for MLeft
for (int hP = 0; hP < bmpInt[0].length && !found2; hP++) {
if (bmpInt[wP][hP] != color) {
Log.e("MLeft 2", "Pixel found #" + wP);
MLeft = wP;
found2 = true;
break;
}
}
}
found2 = false;
for (int wP = bmpInt.length - 1; wP >= 0 && !found2; wP--) {
// looking for MRight
for (int hP = 0; hP < bmpInt[0].length && !found2; hP++) {
if (bmpInt[wP][hP] != color) {
Log.e("MRight 2", "Pixel found #" + wP);
MRight = bmp.getWidth() - wP;
found2 = true;
break;
}
}
}
found2 = false;
int sizeY = bmp.getHeight() - MBot - MTop, sizeX = bmp.getWidth()
- MRight - MLeft;
Bitmap bmp2 = Bitmap.createBitmap(bmp, MLeft, MTop, sizeX, sizeY);
dtMili = (System.currentTimeMillis() - dtMili);
Log.e("Margin 2",
"Time needed " + dtMili + "mSec\nh:" + bmp.getWidth() + "w:"
+ bmp.getHeight() + "\narray x:" + bmpInt.length + "y:"
+ bmpInt[0].length);
return bmp2;
}
Use Bitmap.createBitmap(source, x, y, width, height) so knowing the white margin size you can do what you want.
My solution:
private Bitmap trim(Bitmap bitmap, int trimColor){
int minX = Integer.MAX_VALUE;
int maxX = 0;
int minY = Integer.MAX_VALUE;
int maxY = 0;
for(int x = 0; x < bitmap.getWidth(); x++){
for(int y = 0; y < bitmap.getHeight(); y++){
if(bitmap.getPixel(x, y) != trimColor){
if(x < minX){
minX = x;
}
if(x > maxX){
maxX = x;
}
if(y < minY){
minY = y;
}
if(y > maxY){
maxY = y;
}
}
}
}
return Bitmap.createBitmap(bitmap, minX, minY, maxX - minX + 1, maxY - minY + 1);
}
It isn't very fast, for 1280 x 576 px bitmap execution took 2965ms on Xiaomi Redmi 3S.
If it possible scale down image before triming:
private Bitmap scaleDown(Bitmap bitmap, float maxImageSize, boolean filter) {
float ratio = Math.min(maxImageSize / bitmap.getWidth(), maxImageSize / bitmap.getHeight());
int width = Math.round(ratio * bitmap.getWidth());
int height = Math.round(ratio * bitmap.getHeight());
return Bitmap.createScaledBitmap(bitmap, width, height, filter);
}
Late to the party, but this variation is a bit faster and perhaps easier to read:
public static Bitmap imageWithMargin(Bitmap bitmap, int color, int maxMargin) {
int maxTop = 0, maxBottom = 0, maxLeft = 0, maxRight = 0;
int width = bitmap.getWidth();
int height = bitmap.getHeight();
int[] bitmapArray = new int[width * height];
bitmap.getPixels(bitmapArray, 0, width, 0, 0, width, height);
// Find first non-color pixel from top of bitmap
searchTopMargin:
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if (bitmapArray[width * y + x] != color) {
maxTop = y > maxMargin ? y - maxMargin : 0;
break searchTopMargin;
}
}
}
// Find first non-color pixel from bottom of bitmap
searchBottomMargin:
for (int y = height - 1; y >= 0; y--) {
for (int x = width - 1; x >= 0; x--) {
if (bitmapArray[width * y + x] != color) {
maxBottom = y < height - maxMargin ? y + maxMargin : height;
break searchBottomMargin;
}
}
}
// Find first non-color pixel from left of bitmap
searchLeftMargin:
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
if (bitmapArray[width * y + x] != color) {
maxLeft = x > maxMargin ? x - maxMargin : 0;
break searchLeftMargin;
}
}
}
// Find first non-color pixel from right of bitmap
searchRightMargin:
for (int x = width - 1; x >= 0; x--) {
for (int y = height - 1; y >= 0; y--) {
if (bitmapArray[width * y + x] != color) {
maxRight = x < width - maxMargin ? x + maxMargin : width;
break searchRightMargin;
}
}
}
return Bitmap.createBitmap(bitmap, maxLeft, maxTop, maxRight - maxLeft, maxBottom - maxTop);
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I understand how to draw lines using a canvas, but how can I use the same lines using a canvas to draw a graph?
The problem is with the coordinates. (0,0) starts right at the top left corner of the device. How can I set (0,0) as the margin and draw the particular line with respect to the margin?
1)Create an activity.
public class GraphView1 extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
float[] values = new float[] { "your values"};
String[] verlabels = new String[] { "your values" };
String[] horlabels = new String[] { "your values"
GraphView graphView = new GraphView(this, values,"GraphView",horlabels, verlabels, GraphView.BAR);
setContentView(graphView);
}
}
2)Then create another class extends View:
public class GraphView2 extends View{
public static boolean LINE = true;
private Paint paint;
private float[] values;
private String[] str;
private String[] verlabels;
private String title;
private boolean type;
Context context;
public GraphView(Context context, float[] values, String title, String[] str,String[] verlabels, boolean type) {
super(context);
if (values == null)
values = new float[0];
else
this.values = values;
if (title == null)
title = "";
else
this.title = title;
if (str == null)
this.str = new String[0];
else
this.str = str;
if (verlabels == null)
this.verlabels = new String[0];
else
this.verlabels = verlabels;
this.type = type;
paint = new Paint();
}
#Override
protected void onDraw(final Canvas canvas) {
context=getContext();
float border = 15;
float horstart = border * 2;
float height = getHeight();
float width = getWidth();
float max = getMax();
Log.w("max", ""+max);
float min = getMin();
Log.w("min", ""+min);
float diff = max - min;
float graphheight = height - (2 * border);
float graphwidth = width - (2 * border);
paint.setTextAlign(Align.LEFT);
int vers = verlabels.length;
for (int i = 0; i < verlabels.length; i++) {
paint.setColor(Color.DKGRAY);
float y = ((graphheight / vers) * i) + border;
canvas.drawLine(horstart, y, width, y, paint);
paint.setColor(Color.WHITE);
paint.setTextSize(10);
canvas.drawText(verlabels[i], 0, y, paint);
}
int hors = values.length;
for (int i = 0; i < str.length; i++) {
paint.setColor(Color.DKGRAY);
float x = ((graphwidth / hors) * i) + horstart;
canvas.drawLine(x, height - border, x, border, paint);
paint.setTextAlign(Align.LEFT);
if (i==str.length)
paint.setTextAlign(Align.RIGHT);
if (i==0)
paint.setTextAlign(Align.LEFT);
paint.setColor(Color.WHITE);
paint.setTextSize(9);
canvas.drawText( str[i], x, height - 4, paint);
}
paint.setTextAlign(Align.CENTER);
canvas.drawText(title, (graphwidth / 2) + horstart, border - 4, paint);
if (max != min) {
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.FILL);
if (type == BAR) {
float datalength = values.length;
float colwidth = (width - (2 * border)) / datalength;
for (int i = 0; i < values.length; i++) {
// float val = values[i] - min;
// float rat = val / diff;
// float h = graphheight * rat;
// canvas.drawRect((i * colwidth) + horstart, (border - h) + graphheight, ((i * colwidth) + horstart) + (colwidth - 1), height - (border - 1), paint);
float graph_h = getHeight()-(border*2);
// Log.e("", "graph_h > "+graph_h);
float ind_h = graph_h/7;
//Log.e("", "ind_h > "+ind_h);
float t = values[i]/5;
float top = (graph_h - ind_h*(t));
// Log.e("", " > "+i+1);
// Log.e("", "top > "+top);
//for values between 0 and 5 ,vice versa
//Log.e("", " values[i] > "+values[i]);
float acc = ind_h/5;
acc = acc * (values[i]%5);
// Log.e("", " acc > "+acc);
canvas.drawRect((i * colwidth) + horstart, top+border-acc , ((i * colwidth) + horstart) + (colwidth - 1), graph_h+border, paint);
}
} else {
float datalength = values.length;
float colwidth = (width - (2 * border)) / datalength;
float halfcol = colwidth / 2;
float lasth = 0;
for (int i = 0; i < values.length; i++) {
float val = values[i] - min;
float rat = val / diff;
float h = graphheight * rat;
if (i > 0)
canvas.drawLine(((i - 1) * colwidth) + (horstart + 1) + halfcol, (border - lasth) + graphheight, (i * colwidth) + (horstart + 1) + halfcol, (border - h) + graphheight, paint);
lasth = h;
}
}
}
}
private float getMax() {
float largest = Integer.MIN_VALUE;
for (int i = 0; i < values.length; i++)
if (values[i] > largest)
largest = values[i];
return largest;
}
private float getMin() {
float smallest = Integer.MAX_VALUE;
for (int i = 0; i < values.length; i++)
if (values[i] < smallest)
smallest = values[i];
return smallest;
}
}