Android: Don't understand why canvas not draw my stored paths - android

I used the fingerpaint demo and modify it for multi touch. This part is all fine and the path get drawn as expected. Now, I need to stored these path into an ArrayList so when I swipe each View in viewpager I can keep the state and redrawn those path stored in memory.
This is my code to draw canvas.
protected override void OnDraw (Canvas canvas)
{
base.OnDraw (canvas);
canvas.DrawBitmap (mBitmap, 0, 0, mBitmapPaint);
if (Strokes != null && Strokes.Count > 0)
{
for (int i = 0; i < Strokes.Count; i++) {
var path = (Path) Strokes[i].Path;
canvas.DrawPath (path, Strokes[i].Pen);
}
}
}
The part I passed the Stroke List :
viewPagerAdapter.InitializedView += (object sender, PreviewPagerViewEventArgs e) => {
if (mPageData.Count <= e.Position)
mPageData.Add (e.Data);
else {
var view = (DrawableView) GetVisibleView (e.Position);
view.Background = new ColorDrawable(Android.Graphics.Color.Blue);
view.Strokes = (List<StrokeData>) mPageData [e.Position];
view.Invalidate();
}
};
I placed a bullet point and the canvas.DrawPath did get through but the canvas still not get drawn

Related

Drawing background color between limit lines

Im trying to draw colors between limit lines in a LineChart, by extending the LineChart class and overriding the onDraw method. Im doing it as seen in https://github.com/PhilJay/MPAndroidChart/issues/485 where colored rectangles are simply drawn on the canvas:
public class CustomLineChart extends LineChart {
protected Paint mYAxisZonePaint;
protected String[] arrayColors = {"#FFFFFF","#F4D03F","#F5B041","#EB984E","#DC7633"};
#Override
protected void init() {
super.init();
mYAxisZonePaint = new Paint();
mYAxisZonePaint.setStyle(Paint.Style.FILL);
}
#Override
protected void onDraw(Canvas canvas) {
List<LimitLine> limitLines = mAxisLeft.getLimitLines();
if (limitLines == null || limitLines.size() < 4){
super.onDraw(canvas);
return;
}
float[] pts = new float[2];
float startPts = 0;
for (int i = 0; i < limitLines.size(); i++) {
pts[0] = limitLines.get(i).getLimit();
pts[1] = startPts;
mLeftAxisTransformer.pointValuesToPixel(pts);
mYAxisZonePaint.setColor(Color.parseColor(arrayColors[i]));
canvas.drawRect(mViewPortHandler.contentLeft(), pts[1], mViewPortHandler.contentRight(), pts[0], mYAxisZonePaint);
startPts = limitLines.get(i).getLimit();
}
super.onDraw(canvas);
}
}
With the code above im able to draw the colored rectangles as a background behind the data on the line chart as seen here
However when I scroll on the Y-axis, the rectangles will "overflow" into the area where X-axis labels are drawn as demonstrated in this image
How should i draw the colored background so it doesn't color the area where X-axis labels are drawn, similar to how the line data and limit lines are cut off just above the X-axis?
It seems like i have found a solution to my issue. Inside the onDraw method i should restrict the area that the following draw operations can write to. I added the following to my onDraw method:
#Override
protected void onDraw(Canvas canvas) {
List<LimitLine> limitLines = mAxisLeft.getLimitLines();
// Saves current canvas
int clipRestoreCount = canvas.save();
// Makes sure that the colored background cannot be drawn outside the content-rect. Otherwise the colored background would be drawn above and beneath the graph "borders".
canvas.clipRect(mViewPortHandler.getContentRect());
if (limitLines == null || limitLines.size() < 4){
super.onDraw(canvas);
return;
}
float[] pts = new float[2];
float startPts = 0;
for (int i = 0; i < limitLines.size(); i++) {
pts[0] = limitLines.get(i).getLimit();
pts[1] = startPts;
mLeftAxisTransformer.pointValuesToPixel(pts);
mYAxisZonePaint.setColor(Color.parseColor(arrayColors[i]));
canvas.drawRect(mViewPortHandler.contentLeft(), pts[1], mViewPortHandler.contentRight(), pts[0], mYAxisZonePaint);
startPts = limitLines.get(i).getLimit();
}
// Removes the clipping rectangles so rest of graph elements can be drawn (like x-axis/y-axis labels)
canvas.restoreToCount(clipRestoreCount);
super.onDraw(canvas);
}
Result:

Draw Every pixel of path with different different color

I want to draw a path of drawing. What i want that when i draw path , every pixel of my drawing path is drawn with different different color like chosen form array of colors . Please give me idea .
my code look like this :
for (int i = 0; i < cappedPointerCount; i++) {
if (mFingerPaths[i] != null) {
int index = event.findPointerIndex(i);
if (isSetRanbow) {
cor.setColor(mColors[random.nextInt(mColors.length)]);
}
mFingerPaths[i].lineTo(event.getX(index), event.getY(index));
///mFingerPaths[i].computeBounds(mPathBounds, true);
if (isSetRanbow) {
drawCanvas.drawPoint(event.getX(index),
event.getY(index), cor);
cor);
} else {
drawCanvas.drawPath(mFingerPaths[i], cor);
}
invalidate();
}
}

How to avoid ghost drawing after clearing SurfaceView canvas

Hi I am working on plotting a real time graph of incoming signals using SurfaceView.
The sampling rate is 128Hz and the target graph refresh rate is 50Zh.
Things run pretty smoothly, the points are drawn real-time properly.
I plot the data in segments of a few points using Path()
for each segment I call path.computeBounds() to get a rect that I will use to call holder.lockCanvas(rect) and draw the path. Using a rect prevents flickering and reduces cpu usage
when the graph reaches the end I lock the entire canvas and clear the background, draw the graph frame and then continue on plotting.
the problem is that at the beginning of each new "page" I get a ghost image from the last page:
I believe this is caused by double buffering / use of a dirty area when plotting.
I have looked for solutions to this problem but none seem adequate for this type of application. Any help is most welcome.
Thanks
Jean-Pierre
Code follows:
private void draw() {
Point point = null;
Canvas canvas = null;
Path path = new Path();
ArrayList<Point> pointArray;
float oldX = -1;
boolean setToClear = false;
boolean isNewSegment = false;
if (samplesInQueue == 0) {
return;
}
pointArray = new ArrayList<Point>((int) samplesInQueue);
for (int i = 0; i < samplesInQueue; i++) {
// take a peek at the point without retrieving it from the point
// queue
point = Points.peek();
// check if first point of segment is the start of a page
if (i == 0) {
if (lastSegmentEndPoint != null) {
if (point.x < lastSegmentEndPoint.x) {
// yes then we will need to clear the screen now
isNewSegment = true;
}
} else {
// yes then we will need to clear the screen now
isNewSegment = true;
}
}
if (point != null) {
if (point.x > oldX) {
// put consecutive points in the path point array
point = Points.poll();
samplesInQueue--;
pointArray.add(point);
oldX = point.x;
} else {
// we have a wrap around, stop and indicate we need to clear
// the screen on the next pass
if (!isNewSegment) {
setToClear = true;
}
break;
}
}
}
// no points, return
if (pointArray.size() == 0) {
return;
}
// fill the path
for (int i = 0; i < pointArray.size(); i++) {
Point p = pointArray.get(i);
if (i == 0) {
if (lastSegmentEndPoint != null) {
if (p.x >= lastSegmentEndPoint.x) {
// if we have the end of the last segment, move to it
// and line to the new point
path.moveTo(lastSegmentEndPoint.x, lastSegmentEndPoint.y);
path.lineTo(p.x, p.y);
} else {
// otherwise just line to the new point
path.moveTo(p.x, p.y);
}
} else {
path.moveTo(p.x, p.y);
}
} else {
path.lineTo(p.x, p.y);
}
}
if (clear || isNewSegment) {
if (clear) {
clear = false;
}
// we need to clear, lock the whole canvas
canvas = holder.lockCanvas();
// draw the graph frame / scales
drawGraphFrame = true;
drawGraphFrame(canvas);
} else {
// just draw the path
RectF bounds = new RectF();
Rect dirty = new Rect();
// calculate path bounds
path.computeBounds(bounds, true);
int extra = 0;
dirty.left = (int) java.lang.Math.floor(bounds.left - extra);
dirty.top = (int) java.lang.Math.floor(bounds.top - extra);
dirty.right = (int) java.lang.Math.round(bounds.right + 0.5);
dirty.bottom = (int) java.lang.Math.round(bounds.bottom + 0.5);
// just lock what is needed to plot the path
canvas = holder.lockCanvas(dirty);
}
// draw the path
canvas.drawPath(path, linePaint);
// unlock the canvas
holder.unlockCanvasAndPost(canvas);
// remember last segment end point
lastSegmentEndPoint = pointArray.get(pointArray.size() - 1);
// set clear flag for next pass
if (setToClear) {
clear = true;
}
}
Draw frame / clear graph code
private void drawGraphFrame(Canvas canvas) {
if (!drawGraphFrame) {
return;
}
if (canvas == null) {
Log.e(TAG, "trying to draw on a null canvas");
return;
}
drawGraphFrame = false;
// clear the graph
canvas.drawColor(Color.BLACK, Mode.CLEAR);
// draw the graph frame
canvas.drawLine(leftMargin, topMargin, leftMargin, mCanvasHeight - bottomMargin, framePaint);
canvas.drawLine(leftMargin, mCanvasHeight - bottomMargin, mCanvasWidth - rightMargin, mCanvasHeight
- bottomMargin, framePaint);
// more drawing
}
Your problem is quite straight forward.. your only locking the new portion of the canvas that the new path covers. So the best thing to do is to make your path and dirty rect's private members of your class. Then at the start of your draw method get the path's current bounds (the old bounds) in your dirty rect. Now call path.rewind(); and start modifying your path. After do a union on the dirty rect with the new bounds. Now your dirty rect covers the old and new rect's. So your clear will remove the old path. This also reduces overhead because you don't want to be allocating 100+ objects per second for rect's and path's. Now since your drawing an oscilloscope then you probably want to adjust the old bounds to only be a portion of the width of the view. The same amount your new portion covers.
Hope that's cleared things up.
My simple answer is just using this function clear_holder() wherever you want to clear the canvas. I copy and paste 3 line for 3 times because it need 3 times clear to leave holder blank.
After clearing holder, you should draw any new thing you want!
This link give me this source code!
private void clear_holder(SurfaceHolder holder){
Canvas c = holder.lockCanvas();
c.drawColor( 0, PorterDuff.Mode.CLEAR );
holder.unlockCanvasAndPost(c);
c = holder.lockCanvas();
c.drawColor( 0, PorterDuff.Mode.CLEAR );
holder.unlockCanvasAndPost(c);
c = holder.lockCanvas();
c.drawColor( 0, PorterDuff.Mode.CLEAR );
holder.unlockCanvasAndPost(c);
}
It looks like you are clearing the canvas so, it's not double buffering problem. I think it's related to your path been reused.
Try adding adding the next line when starting new page.
path.reset();

Edit Custom View's Canvas Without Calling OnDraw?

I'm making a graphing library in Android and am aware that if I update the canvas by calling invalidate it can cause a heap memory problem in some devices especially when the user is using a Relative Layout and has some views loading or has multiple graphs stacked on each other. So what I want to do is change the graph of the view without android having to redraw all other views on the Relative Layout.
The only solution I have come across is adding a boolean system to the custom view's on draw to check if the same view requested the on draw, otherwise it would keep the same canvas. However, this will only prevent the same view from being redrawn again and will also draw the other views when it is drawn.
Here is the solution mentioned above.
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(cropx != 0){
croppedBmp = Bitmap.createBitmap(drawGraph2, 0, 0, (drawGraph2.getWidth()*cropx/100), drawGraph2.getHeight());
canvas.drawBitmap(croppedBmp, 0, 0, new Paint());
croppedBmp.recycle();
System.gc();
Runtime.getRuntime().gc();
}
status = false;
}
public void plotPoints(ArrayList<Double> yvalue, ArrayList<Double> xpoint){
drawpath2 = new Path();
drawCanvas = new Canvas(drawGraph2);
drawCanvas.scale(1, -1);
boolean first = false;
for(int i = 0; i< xpoint.size(); i++){
if(!Double.isNaN(yvalue.get(i))){
if(first == false){
drawpath2.moveTo(xpoint.get(i).floatValue(),yvalue.get(i).floatValue());
first = true;
}else if(i==xpoint.size()-1){
drawCanvas.drawPath(drawpath2, paint);
drawpath2.reset();
}else{
drawpath2.lineTo(xpoint.get(i).floatValue(), yvalue.get(i).floatValue());
}
}
}
Canvas singleUseCanvas = new Canvas(drawGraph2);
singleUseCanvas.drawPath(drawpath, paint);
status = true;
invalidate();
}

Drawing to the canvas on Android - screen flickering/tearing

I have an isometric map which I draw to the canvas. When I try and move the map around, these black lines flicker in between some of the tiles making the whole thing look rather shoddy.
Here is the relevant code from my updating thread
public void run() {
Canvas c;
while (isRunning){
c = null;
try {
c = cellMap.getHolder().lockCanvas(null);
synchronized (cellMap.getHolder()) {
cellMap.onDraw(c);
}
} finally {
if( c != null){
cellMap.getHolder().unlockCanvasAndPost(c);
}
}
}
}
and from my CellMap class (which extends SurfaceView):
public void onDraw(Canvas canvas){
canvas.drawColor(Color.BLACK);
int x = 0;
int y = 0;
for(int i = 0; i < mapSize; i++){
for(int j = 0; j < mapSize; j++){
x = (i-j) * 30 + xOffset;
y = (i+j) * 15 + yOffset;
mapCells[i][j].draw(canvas, paint, x, y);
}
}
mapCells[][] is an array of objects which contain the Bitmap image that needs to be drawn. The draw() function is only 1 line canvas.drawBitmap(bitmapImage, x, y, null)
I have discovered that removing the line Canvas.draw(Color.black) gets rid of the flicker. However, when I move the map the canvas is not cleared so I still see the image from the previous frames. I'd imagine it is because it is taking too long to draw the bitmaps? but the map is only very small at the moment.
I found the problem. When moving the map, the offset values were being changed. This lead to sections of the map being drawn temporarily with different offset values - creating the tearing. duh!
I solved this by copying the xOffset and yOffset values at the start of the onDraw() method, and using those values for the update.

Categories

Resources