Related
As building upon this drawing library here, the library didn't provide a way to change stroke color or width of a single one but only changing those properties of all of the drawen strokes at the same time. So I decided to seperate each stroke with its own property to be drawen. However now the drawing stroke is not appearing in real-time when moving the finger on the screen. The whole stroke only appears upon the touch_up() function. I assume the problem is from canvas.drawPath(myPath, myPaint); in the onDraw() function but I can not manage to find what causes the problem. Does anyone know what the problem of the strokes not drawing in real-time is? Or how to fix it?
DrawingView.java:
public class DrawingView extends View {
private Paint canvasPaint;
private Canvas drawCanvas;
private Bitmap canvasBitmap;
private ArrayList<Path> paths = new ArrayList<Path>();
private ArrayList<Path> undonePaths = new ArrayList<Path>();
private ArrayList<float[]> PointsCal = new ArrayList<float[]>();
private ArrayList<float[]> Points = new ArrayList<float[]>();
private ArrayList<Integer> id = new ArrayList<Integer>();
private int counter;
public static WebSocket ws;
private OkHttpClient client;
Context myContext;
Stroke drawnStroke;
private List<Stroke> allStrokes;
public DrawingView(Context context, AttributeSet attr) {
super(context, attr);
setupDrawing();
myContext = context;
}
private void setupDrawing() {
allStrokes = new ArrayList<Stroke>();
drawnStroke = new Stroke();
canvasPaint = new Paint(Paint.DITHER_FLAG);
drawCanvas = new Canvas();
drawnStroke.paint.setAntiAlias(true);
drawnStroke.paint.setStyle(Paint.Style.STROKE);
drawnStroke.paint.setStrokeJoin(Paint.Join.ROUND);
drawnStroke.paint.setStrokeCap(Paint.Cap.ROUND);
counter = 0;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
drawCanvas = new Canvas(canvasBitmap);
}
#Override
protected void onDraw(Canvas canvas) {
if (allStrokes != null) {
for (Stroke stroke : allStrokes) {
if (stroke != null) {
Path myPath = stroke.getPath();
Paint myPaint = stroke.getPaint();
myPaint.setAntiAlias(true);
myPaint.setStyle(Paint.Style.STROKE);
//this function below is not rendering in real time
canvas.drawPath(myPath, myPaint);
}
}
}
}
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
private void touch_start(float x, float y) {
drawnStroke.getPaint().setColor(UserSettings.color);
drawnStroke.getPaint().setStrokeWidth(UserSettings.width);
drawnStroke.getPath().moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
drawnStroke.path.quadTo(mX, mY, (x + mX) / 2, (y + mY) / 2);
mX = x;
mY = y;
}
}
private void touch_up() {
drawnStroke.path.lineTo(mX, mY);
drawCanvas.drawPath(drawnStroke.path, drawnStroke.paint);
allStrokes.add(drawnStroke);
drawnStroke = new Stroke();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
float coordinates[] = {0f, 0f};
float coord[] = {0f, 0f};
String eventString;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touch_start(x, y);
coordinates[0] = x;
coordinates[1] = y;
PointsCal.add(coordinates);
Points.add(coordinates);
invalidate();
break;
case MotionEvent.ACTION_MOVE:
touch_move(x, y);
coordinates[0] = x;
coordinates[1] = y;
PointsCal.add(coordinates);
coord[0] = coordinates[0] - PointsCal.get(PointsCal.size() - 2)[0];
coord[1] = coordinates[1] - PointsCal.get(PointsCal.size() - 2)[1];
Points.add(coord);
break;
case MotionEvent.ACTION_UP:
touch_up();
coordinates[0] = x;
coordinates[1] = y;
PointsCal.add(coordinates);
coord[0] = coordinates[0] - PointsCal.get(PointsCal.size() - 2)[0];
coord[1] = coordinates[1] - PointsCal.get(PointsCal.size() - 2)[1];
Points.add(coord);
String sample = "";
if (Points.size() > 2) {
counter++;
id.add(counter);
sample += ("#" + counter);
for (int i = 0; i < Points.size(); i++) {
sample += ("#" + Arrays.toString(Points.get(i)));
}
} else {
paths.remove(paths.size() - 1);
}
Points.clear();
PointsCal.clear();
invalidate();
break;
}
return true;
}
I believe this is because allStrokes.add(drawnStroke); is not called until the private method touch_up is called, so when iterating over allStrokes for drawing purposes, the current stroke is omitted. Try moving that line of code to touch_start.
What i have: Using the below code i am able to draw a pattern(Ex: Line) in the canvas.
What i am planning to do Or How to do this: I am trying to plot dots on that pattern in a uniform distance. (Hope i am clear)
ActDrawPaintImage.java
public class ActDrawPaintImage extends AppCompatActivity implements ColorPickerDialog.OnColorChangedListener {
MyView mv;
AlertDialog dialog;
#Bind(R.id.toolbar)
Toolbar mToolbar;
#Bind(R.id.btnNxtId)
Button btnNxtId;
private Paint mPaint;
private MaskFilter mEmboss;
private MaskFilter mBlur;
LinearLayout canvasLayoutId;
boolean mDotToDraw=false;
private int BRUSHSIZE=20;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_draw_paint_image);
//Bind the views
ButterKnife.bind(this);
initToolbar();
onClickSet();
//ADD THE VIEW WHERE THE IMAGE IS DRAWN
setTheCanvasView();
//SET BRUSH PROPERTIES
setUpBrushProperties();
mEmboss = new EmbossMaskFilter(new float[] { 1, 1, 1 },0.4f, 6, 3.5f);
mBlur = new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL);
}
private void onClickSet() {
btnNxtId.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(mDotToDraw==false){
mDotToDraw=true;
}else{
mDotToDraw=false;
}
}
});
}
private void setTheCanvasView() {
mv= new MyView(this);
mv.setDrawingCacheEnabled(true);
canvasLayoutId=(LinearLayout) findViewById(R.id.canvasLayoutId);
canvasLayoutId.addView(mv);
}
private void setUpBrushProperties() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(ContextCompat.getColor(ActDrawPaintImage.this, R.color.pattern_color));
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(BRUSHSIZE);
}
private void initToolbar() {
setSupportActionBar(mToolbar);
setTitle(getString(R.string.app_name));
mToolbar.setTitleTextColor(ContextCompat.getColor(ActDrawPaintImage.this, R.color.white));
}
public void colorChanged(int color) {
mPaint.setColor(color);
}
public class MyView extends View {
private static final float MINP = 0.25f;
private static final float MAXP = 0.75f;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
Context context;
private int width;
private int height;
public MyView(Context c) {
super(c);
context=c;
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width=w;
height=h;
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
canvas.drawPath(mPath, mPaint);
}
private void touch_start(float x, float y) {
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
mX = x;
mY = y;
}
}
private void touch_up() {
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
mPath.reset();
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
//mPaint.setMaskFilter(null);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
//Create a pointer and log the output
Log.d("POINTS", x + "," + y);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if(mDotToDraw==false){
touch_start(x, y);
}else{
touch_draw_circle(x, y);
}
invalidate();
break;
case MotionEvent.ACTION_MOVE:
if(mDotToDraw==false){
touch_move(x, y);
}else{
}
invalidate();
break;
case MotionEvent.ACTION_UP:
if(mDotToDraw==false){
touch_up();
}else{
}
invalidate();
break;
}
return true;
}
private void touch_draw_circle(float x, float y) {
/*int radius;
radius = 30;
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.WHITE);
mCanvas.drawPaint(paint);
// Use Color.parseColor to define HTML colors
paint.setColor(Color.parseColor("#FF0000"));
mCanvas.drawCircle(x / 2, y / 2, radius, paint);*/
// mPath.quadTo(x, y, x + 0.1f, y);
/*Paint mPaint = new Paint();*/
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(ContextCompat.getColor(ActDrawPaintImage.this, R.color.white));
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(BRUSHSIZE);
mPath.addCircle(x, y, mPaint.getStrokeWidth()/4f, Path.Direction.CW);
}
public void clear()
{
mBitmap = Bitmap.createBitmap(width,height , Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
invalidate();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
new MenuInflater(this).inflate(R.menu.draw_paint_image, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
mPaint.setXfermode(null);
mPaint.setAlpha(0xFF);
switch (item.getItemId()) {
case R.id.COLOR_MENU_ID:
new ColorPickerDialog(this, this, mPaint.getColor()).show();
return true;
case R.id.EMBOSS_MENU_ID:
if (mPaint.getMaskFilter() != mEmboss) {
mPaint.setMaskFilter(mEmboss);
} else {
mPaint.setMaskFilter(null);
}
return true;
case R.id.BLUR_MENU_ID:
if (mPaint.getMaskFilter() != mBlur) {
mPaint.setMaskFilter(mBlur);
} else {
mPaint.setMaskFilter(null);
}
return true;
case R.id.ERASE_MENU_ID:
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mPaint.setAlpha(0x80);
return true;
case R.id.Clear:
mv.clear();
return true;
case R.id.Save:
AlertDialog.Builder editalert = new AlertDialog.Builder(ActDrawPaintImage.this);
editalert.setTitle("Please Enter the name with which you want to Save");
final EditText input = new EditText(ActDrawPaintImage.this);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT,
LinearLayout.LayoutParams.FILL_PARENT);
input.setLayoutParams(lp);
editalert.setView(input);
editalert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
String name= input.getText().toString();
Bitmap bitmap = mv.getDrawingCache();
String path = Environment.getExternalStorageDirectory().getAbsolutePath();
File file = new File("/sdcard/"+name+".png");
try
{
if(!file.exists())
{
file.createNewFile();
}
FileOutputStream ostream = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.PNG, 10, ostream);
ostream.close();
mv.invalidate();
}
catch (Exception e)
{
e.printStackTrace();
}finally
{
mv.setDrawingCacheEnabled(false);
}
}
});
editalert.show();
return true;
}
return super.onOptionsItemSelected(item);
}
}
EDIT
public class ActDrawPaintImage extends AppCompatActivity implements ColorPickerDialog.OnColorChangedListener {
MyView mv;
AlertDialog dialog;
#Bind(R.id.toolbar)
Toolbar mToolbar;
#Bind(R.id.btnNxtId)
Button btnNxtId;
private Paint mPaint;
private MaskFilter mEmboss;
private MaskFilter mBlur;
LinearLayout canvasLayoutId;
boolean mDotToDraw=false;
private int BRUSHSIZE=20;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_draw_paint_image);
//Bind the views
ButterKnife.bind(this);
initToolbar();
onClickSet();
//ADD THE VIEW WHERE THE IMAGE IS DRAWN
setTheCanvasView();
//SET BRUSH PROPERTIES
setUpBrushProperties();
mEmboss = new EmbossMaskFilter(new float[] { 1, 1, 1 },0.4f, 6, 3.5f);
mBlur = new BlurMaskFilter(8, BlurMaskFilter.Blur.NORMAL);
}
private void onClickSet() {
btnNxtId.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(mDotToDraw==false){
mDotToDraw=true;
}else{
mDotToDraw=false;
}
}
});
}
private void setTheCanvasView() {
mv= new MyView(this);
mv.setDrawingCacheEnabled(true);
canvasLayoutId=(LinearLayout) findViewById(R.id.canvasLayoutId);
canvasLayoutId.addView(mv);
}
private void setUpBrushProperties() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(ContextCompat.getColor(ActDrawPaintImage.this, R.color.pattern_color));
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(BRUSHSIZE);
}
private void initToolbar() {
setSupportActionBar(mToolbar);
setTitle(getString(R.string.app_name));
mToolbar.setTitleTextColor(ContextCompat.getColor(ActDrawPaintImage.this, R.color.white));
}
public void colorChanged(int color) {
mPaint.setColor(color);
}
public class MyView extends View {
private static final float MINP = 0.25f;
private static final float MAXP = 0.75f;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mPath;
private Paint mBitmapPaint;
private float mX, mY;
private static final float TOUCH_TOLERANCE = 4;
Context context;
private int width;
private int height;
PathMeasure pathMeasure;
float[] position = new float[2];
float[] slope = new float[2]; // slope will give you the tangent of the position on the path. Not sure if you need this.
public MyView(Context c) {
super(c);
context=c;
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
pathMeasure = new PathMeasure(mPath, false);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width=w;
height=h;
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
drawPlotPoints(canvas); // you should be able to implement this alone. It should draw a dot at a given x/y.
canvas.drawPath(mPath, mPaint);
}
private void drawPlotPoints(Canvas canvas) {
int amountOfPoints = (int)(pathMeasure.getLength() / 120f);
for (float distance = 0; distance <= 1; distance += 1f / amountOfPoints) {
pathMeasure.getPosTan(distance, position, slope);
touch_draw_circle(position[0], position[1]);
}
}
private void touch_start(float x, float y) {
mPath.reset();
mPath.moveTo(x, y);
mX = x;
mY = y;
}
private void touch_move(float x, float y) {
float dx = Math.abs(x - mX);
float dy = Math.abs(y - mY);
if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) {
mPath.quadTo(mX, mY, (x + mX)/2, (y + mY)/2);
mX = x;
mY = y;
}
//drawPlotPoints(x,y);
}
private void touch_up() {
mPath.lineTo(mX, mY);
// commit the path to our offscreen
mCanvas.drawPath(mPath, mPaint);
// kill this so we don't double draw
mPath.reset();
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SCREEN));
//mPaint.setMaskFilter(null);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
//Create a pointer and log the output
//Log.d("POINTS", x + "," + y);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if(mDotToDraw==false){
touch_start(x, y);
}else{
touch_draw_circle(x, y);
}
invalidate();
break;
case MotionEvent.ACTION_MOVE:
if(mDotToDraw==false){
touch_move(x, y);
}else{
}
invalidate();
break;
case MotionEvent.ACTION_UP:
if(mDotToDraw==false){
touch_up();
}else{
}
invalidate();
break;
}
return true;
}
private void touch_draw_circle(float x, float y) {
/*int radius;
radius = 30;
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.WHITE);
mCanvas.drawPaint(paint);
// Use Color.parseColor to define HTML colors
paint.setColor(Color.parseColor("#FF0000"));
mCanvas.drawCircle(x / 2, y / 2, radius, paint);*/
// mPath.quadTo(x, y, x + 0.1f, y);
/*Paint mPaint = new Paint();*/
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(ContextCompat.getColor(ActDrawPaintImage.this, R.color.white));
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(BRUSHSIZE);
mPath.addCircle(x, y, mPaint.getStrokeWidth()/4f, Path.Direction.CW);
}
public void clear()
{
mBitmap = Bitmap.createBitmap(width,height , Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
invalidate();
}
}
}
I am getting output as below:
I am trying to get output as like this:
If I understood you correctly, you're trying to discover certain positions on a given path, then draw dots on them where the distance between each dot should be the same.
You can achieve this using a PathMeasure object. Example:
boolean forceClose = false; //(set to true if you want to close the path or leave it open).
PathMeasure pathMeasure = new PathMeasure(path, forceClose);
float[] position = new float[2];
float[] slope = new float[2]; // slope will give you the tangent of the position on the path. Not sure if you need this.
for (float distance = 0; distance <= 1; distance += 1f / AMOUNT_OF_POINTS) {
pathMeasure.getPosTan(distance, position, slope);
drawDotAtPosition(position[0], position[1]); // you should be able to implement this alone. It should draw a dot at a given x/y.
}
The getPosTan method will give you the position on a given path after a certain ratio of the length has passed. This ratio is defined by AMOUNT_OF_POINTS which you can set to anything for a constant amount of points no matter what the length of the path is, or you could calculate it according to the length of the path:
// this will produce an amount of points equal to 1 point per 10 pixels along the whole path
int amountOfPoints = (int)(pathMeasure.getLength() / 10f);
Hope this helps.
I made this a while back, its not exactly my best work but it worked at the time. Maybe you can use it as a reference.
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
public class line_graph extends View
{
private int padding = 0;
private int width = 0;//this is automatically
private int height = 0;
private Paint paint = new Paint();
private String[] labels;// labels for each column
private int[][] values;// values for each column
private String[] labelsHeadings;// headings for each segment of data
private float xInterval = 0;//space between x grid lines
private float yInterval = 0;//space between y grid lines
private int RowLineCount = 0; // amount of y grid lines
private float scale = 1; // this is automatically set according to the screen density
private int labelSpaceLeft = 0;// space on the left for labels
private int labelSpaceBottom = 0;// space on the bottom for labels
private int keySpace = 0;// space on the bottom for the key
private Path path = new Path();// bothe these paths are used for the shaded effect on the line graph
private Path path2 = new Path();
public line_graph(Context context)
{
this(context, null, 0);
}
public line_graph(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public line_graph(Context context, AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
if(isInEditMode())
return;
scale = context.getResources().getDisplayMetrics().density;
labelSpaceLeft = (int)(40 * scale);
labelSpaceBottom = (int)(85 * scale);
keySpace = (int)(40 * scale);
}
public void setup(String[] labels, int[][] values, String[] labelsHeadings, int padding)
{
this.labels = labels;
this.values = values;
this.padding = padding;
this.labelsHeadings = labelsHeadings;
invalidate();
}
#Override
protected void onSizeChanged(int xNew, int yNew, int xOld, int yOld){
super.onSizeChanged(xNew, yNew, xOld, yOld);
width = xNew;
height = yNew;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (isInEditMode())
return;
try {
paint.reset();
paint.setAntiAlias(true);
paint.setTextSize(16);
paint.setTextAlign(Paint.Align.RIGHT);
int maxValue = 0;
for (int i = 0; i < values.length; i++) {
for (int j = 0; j < values[i].length; j++) {
maxValue = Math.max(maxValue, values[i][j]);
}
}
float innerWidth = width - padding * 2 - labelSpaceLeft;
float innerHeight = height - padding * 2 - labelSpaceBottom - keySpace;
float lx = padding;
paint.setTextAlign(Paint.Align.LEFT);
for (int i = 0; i < values.length; i++) {
paint.setColor(getNextColour(i));
//calculate space for circles
lx += 15 * scale;
//calculate space key label
if (labelsHeadings.length > i) {
lx += padding + paint.measureText(labelsHeadings[i]);
if (i < values.length - 1 && lx + paint.measureText(labelsHeadings[i + 1]) > width) {
lx = padding + labelSpaceLeft;
innerHeight += paint.ascent();
}
}
}
lx = padding + labelSpaceLeft;
float ly = innerHeight + padding*2 + labelSpaceBottom;
paint.setTextAlign(Paint.Align.LEFT);
for (int i = 0; i < values.length; i++) {
paint.setColor(getNextColour(i));
//draw circles
canvas.drawOval(new RectF(lx - 5 * scale, ly - 5 * scale, lx + 5 * scale, ly + 5 * scale), paint);
lx += 15 * scale;
//draw key label
canvas.drawText(labelsHeadings[i], lx, ly - paint.ascent() / 2, paint);
if (labelsHeadings.length > i) {
lx += padding + paint.measureText(labelsHeadings[i]);
if (i < values.length - 1 && lx + paint.measureText(labelsHeadings[i + 1]) > width) {
lx = padding + labelSpaceLeft;
ly -= paint.ascent();
innerHeight += paint.ascent();
}
}
}
xInterval = innerWidth / (labels.length-1);
RowLineCount = (int) Math.min(Math.max(innerHeight / Math.ceil(40 * scale), 2), maxValue/2);
yInterval = innerHeight / RowLineCount;
int currentValue = maxValue;
float y;
paint.setTextAlign(Paint.Align.RIGHT);
for (int i = 0; i < RowLineCount+1; i++) {
//draw left label
y = yInterval * i + padding;
paint.setColor(0xffaaaaaa);
canvas.drawText(String.valueOf(currentValue), labelSpaceLeft, y - paint.ascent()/2, paint);
currentValue = Math.max(currentValue - maxValue/RowLineCount,0);
//draw x grid line
paint.setColor(0xffeeeeee);
canvas.drawLine(padding + labelSpaceLeft, y, innerWidth + padding + labelSpaceLeft, y, paint);
}
float x;
for (int i = 0; i < labels.length; i++) {
//draw bottom label rotated
x = xInterval * i + padding + labelSpaceLeft;
if(i != labels.length) {
canvas.save();
canvas.rotate(300, x, innerHeight + padding*2 - paint.ascent()/2);
paint.setColor(0xffaaaaaa);
canvas.drawText(labels[i], x, innerHeight + padding*2 - paint.ascent()/2, paint);
canvas.restore();
}
//draw y grid line
paint.setColor(0xffeeeeee);
canvas.drawLine(x, padding, x, innerHeight + padding, paint);
}
paint.setStyle(Paint.Style.FILL);
for (int i = 0; i < values.length; i++) {
paint.setColor(getNextColour(i));
path.rewind();
path2.rewind();
for (int j = 0; j < values[i].length; j++)
{
x = xInterval * j + padding + labelSpaceLeft;
y = innerHeight + padding - (innerHeight * values[i][j] / maxValue);
//draw lines
if(j == 0) {
path2.moveTo(padding + labelSpaceLeft, innerHeight + padding);
path.moveTo(x, y);
}
else
path.lineTo(x,y);
path2.lineTo(x, y);
//canvas.drawLine(xOld,yOld,x,y,paint);
//draw circles
canvas.drawOval(new RectF(x - 5 * scale, y - 5 * scale, x + 5 * scale, y + 5 * scale), paint);
}
//draw line
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(2);
canvas.drawPath(path, paint);
//draw shaded area
path2.lineTo(innerWidth + padding + labelSpaceLeft, innerHeight + padding);
paint.setARGB(20, Color.red(paint.getColor()), Color.green(paint.getColor()), Color.blue(paint.getColor()));
paint.setStyle(Paint.Style.FILL);
canvas.drawPath(path2, paint);
}
}
catch (Exception ignored){}
}
//used to get the next color in the series
public static int getNextColour(int index)
{
int[] colours = new int[]{0xFF4CAF50,
0xFF9C27B0,
0xFFF44336,
0xFF00BCD4,
0xFFE91E63,
0xFF03A9F4,
0xFFFF9800,
//0xFFFFEB3B,//removed bright yellow
0xFF9E9E9E,
0xFF795548,
0xFF8BC34A,
0xFF607D8B,
0xFF009688,
0xFFFFC107,
0xFF673AB7,
0xFF2196F3,
0xFFCDDC39,
0xFF3F51B5,
0xFFFF5722};
return colours[index % colours.length];
}
}
its used like this:
line_graph graph = (line_graph)findViewById(R.id.lineGraph);
graph.setup(
//labels
new String[]{"heading1","heading2","heading3","heading4","heading5","heading6","heading7"},
//Values
new int[][]{new int[]{1,4,8,2,5,9,12},new int[]{2,5,2,8,5,2,6},new int[]{8,2,9,23,7,1,11}},
//Series Headings
new String[]{"Series 1", "Series 2", "Series 3"},
//Padding
20);
xml:
<line_graph
android:id="#+id/lineGraph"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
it looks like this:
And yes i do realize this could be made better and that it had poor exception handling. But its just an example.
I'm working on Android project.
I'm using custom View class to showing image and (move, scale, rotate) the image by using matrix.
This is the code I'm using:
public class CustomView extends View implements OnTouchListener {
private Bitmap bitmap;
private float imgWidth;
private float imgHeight;
private float screenWidth;
private float screenHeight;
private Matrix matrix = new Matrix();
private Vector2D position = new Vector2D();
private float scale=1;
private float angle = 0;
private TouchManager touchManager = new TouchManager(2);
private boolean isInitialized = false;
// Debug helpers to draw lines between the two touch points
Vector2D vca;
Vector2D vcb;
Vector2D vpa;
Vector2D vpb;
int PressCount=0;
Paint paint;
public CustomView(Context context, Bitmap bitmap)
{
super(context);
this.bitmap = bitmap;
this.imgWidth = bitmap.getWidth();
this.imgHeight = bitmap.getHeight();
setOnTouchListener(this);
}
private float GetTheta(){
return (float) (angle*180/Math.PI);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!isInitialized)
{
screenWidth = getWidth();
screenHeight = getHeight();
position.set(screenWidth / 2, screenHeight / 2);
scale=1;
paint=new Paint();
isInitialized = true;
}
// Drawing the image
matrix.reset();
matrix.postTranslate(-imgWidth / 2.0f, -imgHeight / 2.0f);
matrix.postRotate(GetTheta());
matrix.postScale(scale, scale);
matrix.postTranslate(position.getX(), position.getY());
canvas.drawBitmap(bitmap, matrix, paint);
}
Vector2D DistancePoint;
#Override
public boolean onTouch(View v, MotionEvent event) {
vca = null;
vcb = null;
vpa = null;
vpb = null;
try {
touchManager.update(event);
PressCount=touchManager.getPressCount();
if (PressCount == 1)
{
vca = touchManager.getPoint(0);
vpa = touchManager.getPreviousPoint(0);
DistancePoint=touchManager.moveDelta(0);
position.add(DistancePoint);
}
else if (PressCount == 2)
{
vca = touchManager.getPoint(0);
vpa = touchManager.getPreviousPoint(0);
vcb = touchManager.getPoint(1);
vpb = touchManager.getPreviousPoint(1);
Vector2D current = touchManager.getVector(0, 1);
Vector2D previous = touchManager.getPreviousVector(0, 1);
float currentDistance = current.getLength();
float previousDistance = previous.getLength();
if (previousDistance != 0)
{
scale *= currentDistance / previousDistance;
}
angle -= Vector2D.getSignedAngleBetween(current, previous);
}
invalidate();
}
catch(Throwable t) {
// So lazy...
}
return true;
}
}
Above code is working perfect!
My question is how to convert screen touch coordinates to image coordinates?
In another word: when touching down on screen the (event.getX, event.getY) are relative to screen coordinates; Okay: I wanna correspond these coordinates to be relative to the image.
Also this image to explain what I want exactly:
Please comment me for any inquiry.
Will be appreciated for any help.
please tell me how can i remove the exception. or give me idea to implement zoom view with curlview.....
my xml file is like this,
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/relat">
<com.example.image.ZoomView
>
<com.example.image.PageCurlView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="#+id/dcgpagecurlPageCurlView1"
>
</com.example.image.PageCurlView>
my java file is
package com.example.image;
public class StandaloneExample extends Activity {
Button bt;LinearLayout lr;
/** Decoded bitmap image */
private Bitmap mBitmap;
private ZoomView zoomview;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.standalone_example);
View v1 = ((LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.standalone_example, null, false);
v1.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
zoomview.addView(v1);
lr = (LinearLayout) findViewById(R.id.relat);
lr.addView(zoomview);
}
#Override
public void onDestroy(){
super.onDestroy();
System.gc();
finish();
}
public void lockOrientationLandscape() {
lockOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
public void lockOrientationPortrait() {
lockOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
public void lockOrientation( int orientation ) {
setRequestedOrientation(orientation);
}
}
i have implemented zoomView.java as described in a link
pagecurlview.java class is
public class PageCurlView extends View {
private final static String TAG = "PageCurlView";
private Paint mTextPaint;
private TextPaint mTextPaintShadow;
private int mCurlSpeed;
private int mUpdateRate;
private int mInitialEdgeOffset;
private int mCurlMode;
public static final int CURLMODE_SIMPLE = 0;
public static final int CURLMODE_DYNAMIC = 1;
private boolean bEnableDebugMode = false;
private WeakReference<Context> mContext;
private FlipAnimationHandler mAnimationHandler;
private float mFlipRadius;
private Vector2D mMovement;
private Vector2D mFinger;
private Vector2D mOldMovement;
private Paint mCurlEdgePaint;
private Vector2D mA, mB, mC, mD, mE, mF, mOldF, mOrigin;
private int mCurrentLeft, mCurrentTop;
private boolean bViewDrawn;
private boolean bFlipRight;
private boolean bFlipping;
private boolean bUserMoves;
private boolean bBlockTouchInput = false;
private boolean bEnableInputAfterDraw = false;
private Bitmap mForeground;
private Bitmap mBackground;
private ArrayList<Bitmap> mPages;
private int mIndex = 0;
private class Vector2D
{
public float x,y;
public Vector2D(float x, float y)
{
this.x = x;
this.y = y;
}
#Override
public String toString() {
// TODO Auto-generated method stub
return "("+this.x+","+this.y+")";
}
public float length() {
return (float) Math.sqrt(x * x + y * y);
}
public float lengthSquared() {
return (x * x) + (y * y);
}
public boolean equals(Object o) {
if (o instanceof Vector2D) {
Vector2D p = (Vector2D) o;
return p.x == x && p.y == y;
}
return false;
}
public Vector2D reverse() {
return new Vector2D(-x,-y);
}
public Vector2D sum(Vector2D b) {
return new Vector2D(x+b.x,y+b.y);
}
public Vector2D sub(Vector2D b) {
return new Vector2D(x-b.x,y-b.y);
}
public float dot(Vector2D vec) {
return (x * vec.x) + (y * vec.y);
}
public float cross(Vector2D a, Vector2D b) {
return a.cross(b);
}
public float cross(Vector2D vec) {
return x * vec.y - y * vec.x;
}
public float distanceSquared(Vector2D other) {
float dx = other.x - x;
float dy = other.y - y;
return (dx * dx) + (dy * dy);
}
public float distance(Vector2D other) {
return (float) Math.sqrt(distanceSquared(other));
}
public float dotProduct(Vector2D other) {
return other.x * x + other.y * y;
}
public Vector2D normalize() {
float magnitude = (float) Math.sqrt(dotProduct(this));
return new Vector2D(x / magnitude, y / magnitude);
}
public Vector2D mult(float scalar) {
return new Vector2D(x*scalar,y*scalar);
}
}
class FlipAnimationHandler extends Handler {
#Override
public void handleMessage(Message msg) {
PageCurlView.this.FlipAnimationStep();
}
public void sleep(long millis) {
this.removeMessages(0);
sendMessageDelayed(obtainMessage(0), millis);
}
}
public PageCurlView(Context context) {
super(context);
init(context);
ResetClipEdge();
}
public PageCurlView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
// Get the data from the XML AttributeSet
{
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PageCurlView);
// Get data
bEnableDebugMode = a.getBoolean(R.styleable.PageCurlView_enableDebugMode, bEnableDebugMode);
mCurlSpeed = a.getInt(R.styleable.PageCurlView_curlSpeed, mCurlSpeed);
mUpdateRate = a.getInt(R.styleable.PageCurlView_updateRate, mUpdateRate);
mInitialEdgeOffset = a.getInt(R.styleable.PageCurlView_initialEdgeOffset, mInitialEdgeOffset);
mCurlMode = a.getInt(R.styleable.PageCurlView_curlMode, mCurlMode);
Log.i(TAG, "mCurlSpeed: " + mCurlSpeed);
Log.i(TAG, "mUpdateRate: " + mUpdateRate);
Log.i(TAG, "mInitialEdgeOffset: " + mInitialEdgeOffset);
Log.i(TAG, "mCurlMode: " + mCurlMode);
// recycle object (so it can be used by others)
a.recycle();
}
ResetClipEdge();
}
private final void init(Context context) {
// Foreground text paint
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
mTextPaint.setTextSize(16);
mTextPaint.setColor(0xFF000000);
// The shadow
mTextPaintShadow = new TextPaint();
mTextPaintShadow.setAntiAlias(true);
mTextPaintShadow.setTextSize(16);
mTextPaintShadow.setColor(0x00000000);
// Cache the context
mContext = new WeakReference<Context>(context);
// Base padding
setPadding(3, 3, 3, 3);
// The focus flags are needed
setFocusable(true);
setFocusableInTouchMode(true);
mMovement = new Vector2D(0,0);
mFinger = new Vector2D(0,0);
mOldMovement = new Vector2D(0,0);
// Create our curl animation handler
mAnimationHandler = new FlipAnimationHandler();
// Create our edge paint
mCurlEdgePaint = new Paint();
mCurlEdgePaint.setColor(Color.WHITE);
mCurlEdgePaint.setAntiAlias(true);
mCurlEdgePaint.setStyle(Paint.Style.FILL);
mCurlEdgePaint.setShadowLayer(10, -5, 5, 0x99000000);
// Set the default props, those come from an XML :D
mCurlSpeed = 30;
mUpdateRate = 33;
mInitialEdgeOffset = 20;
mCurlMode = 1;
// LEGACY PAGE HANDLING!
// Create pages
mPages = new ArrayList<Bitmap>();
mPages.add(BitmapFactory.decodeResource(getResources(), R.drawable.princess));
mPages.add(BitmapFactory.decodeResource(getResources(), R.drawable.temp));
// Create some sample images
mForeground = mPages.get(0);
mBackground = mPages.get(1);
}
public void ResetClipEdge()
{
// Set our base movement
mMovement.x = mInitialEdgeOffset;
mMovement.y = mInitialEdgeOffset;
mOldMovement.x = 0;
mOldMovement.y = 0;
// Now set the points
// TODO: OK, those points MUST come from our measures and
// the actual bounds of the view!
mA = new Vector2D(mInitialEdgeOffset, 0);
mB = new Vector2D(this.getWidth(), this.getHeight());
mC = new Vector2D(this.getWidth(), 0);
mD = new Vector2D(0, 0);
mE = new Vector2D(0, 0);
mF = new Vector2D(0, 0);
mOldF = new Vector2D(0, 0);
// The movement origin point
mOrigin = new Vector2D(this.getWidth(), 0);
}
private Context GetContext() {
return mContext.get();
}
public boolean IsCurlModeDynamic()
{
return mCurlMode == CURLMODE_DYNAMIC;
}
public void SetCurlSpeed(int curlSpeed)
{
if ( curlSpeed < 1 )
throw new IllegalArgumentException("curlSpeed must be greated than 0");
mCurlSpeed = curlSpeed;
}
public int GetCurlSpeed()
{
return mCurlSpeed;
}
public void SetUpdateRate(int updateRate)
{
if ( updateRate < 1 )
throw new IllegalArgumentException("updateRate must be greated than 0");
mUpdateRate = updateRate;
}
public int GetUpdateRate()
{
return mUpdateRate;
}
public void SetInitialEdgeOffset(int initialEdgeOffset)
{
if ( initialEdgeOffset < 0 )
throw new IllegalArgumentException("initialEdgeOffset can not negative");
mInitialEdgeOffset = initialEdgeOffset;
}
public int GetInitialEdgeOffset()
{
return mInitialEdgeOffset;
}
public void SetCurlMode(int curlMode)
{
if ( curlMode != CURLMODE_SIMPLE &&
curlMode != CURLMODE_DYNAMIC )
throw new IllegalArgumentException("Invalid curlMode");
mCurlMode = curlMode;
}
public int GetCurlMode()
{
return mCurlMode;
}
public void SetEnableDebugMode(boolean bFlag)
{
bEnableDebugMode = bFlag;
}
public boolean IsDebugModeEnabled()
{
return bEnableDebugMode;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int finalWidth, finalHeight;
finalWidth = measureWidth(widthMeasureSpec);
finalHeight = measureHeight(heightMeasureSpec);
setMeasuredDimension(finalWidth, finalHeight);
}
private int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else {
// Measure the text
result = specSize;
}
return result;
}
private int measureHeight(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else {
// Measure the text (beware: ascent is a negative number)
result = specSize;
}
return result;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (!bBlockTouchInput) {
// Get our finger position
mFinger.x = event.getX();
mFinger.y = event.getY();
int width = getWidth();
// Depending on the action do what we need to
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mOldMovement.x = mFinger.x;
mOldMovement.y = mFinger.y;
// If we moved over the half of the display flip to next
if (mOldMovement.x > (width >> 1)) {
mMovement.x = mInitialEdgeOffset;
mMovement.y = mInitialEdgeOffset;
// Set the right movement flag
bFlipRight = true;
} else {
// Set the left movement flag
bFlipRight = false;
// go to next previous page
previousView();
// Set new movement
mMovement.x = IsCurlModeDynamic()?width<<1:width;
mMovement.y = mInitialEdgeOffset;
}
break;
case MotionEvent.ACTION_UP:
bUserMoves=false;
bFlipping=true;
FlipAnimationStep();
break;
case MotionEvent.ACTION_MOVE:
bUserMoves=true;
// Get movement
mMovement.x -= mFinger.x - mOldMovement.x;
mMovement.y -= mFinger.y - mOldMovement.y;
mMovement = CapMovement(mMovement, true);
// Make sure the y value get's locked at a nice level
if ( mMovement.y <= 1 )
mMovement.y = 1;
// Get movement direction
if (mFinger.x < mOldMovement.x ) {
bFlipRight = true;
} else {
bFlipRight = false;
}
// Save old movement values
mOldMovement.x = mFinger.x;
mOldMovement.y = mFinger.y;
// Force a new draw call
DoPageCurl();
this.invalidate();
break;
}
}
// TODO: Only consume event if we need to.
return true;
}
private Vector2D CapMovement(Vector2D point, boolean bMaintainMoveDir)
{
// Make sure we never ever move too much
if (point.distance(mOrigin) > mFlipRadius)
{
if ( bMaintainMoveDir )
{
// Maintain the direction
point = mOrigin.sum(point.sub(mOrigin).normalize().mult(mFlipRadius));
}
else
{
// Change direction
if ( point.x > (mOrigin.x+mFlipRadius))
point.x = (mOrigin.x+mFlipRadius);
else if ( point.x < (mOrigin.x-mFlipRadius) )
point.x = (mOrigin.x-mFlipRadius);
point.y = (float) (Math.sin(Math.acos(Math.abs(point.x-mOrigin.x)/mFlipRadius))*mFlipRadius);
}
}
return point;
}
public void FlipAnimationStep() {
if ( !bFlipping )
return;
int width = getWidth();
// No input when flipping
bBlockTouchInput = true;
// Handle speed
float curlSpeed = mCurlSpeed;
if ( !bFlipRight )
curlSpeed *= -1;
// Move us
mMovement.x += curlSpeed;
mMovement = CapMovement(mMovement, false);
// Create values
DoPageCurl();
// Check for endings :D
if (mA.x < 1 || mA.x > width - 1) {
bFlipping = false;
if (bFlipRight) {
//SwapViews();
nextView();
}
ResetClipEdge();
// Create values
DoPageCurl();
// Enable touch input after the next draw event
bEnableInputAfterDraw = true;
}
else
{
mAnimationHandler.sleep(mUpdateRate);
}
// Force a new draw call
this.invalidate();
}
private void DoPageCurl()
{
if(bFlipping){
if ( IsCurlModeDynamic() )
doDynamicCurl();
else
doSimpleCurl();
} else {
if ( IsCurlModeDynamic() )
doDynamicCurl();
else
doSimpleCurl();
}
}
private void doSimpleCurl() {
int width = getWidth();
int height = getHeight();
// Calculate point A
mA.x = width - mMovement.x;
mA.y = height;
// Calculate point D
mD.x = 0;
mD.y = 0;
if (mA.x > width / 2) {
mD.x = width;
mD.y = height - (width - mA.x) * height / mA.x;
} else {
mD.x = 2 * mA.x;
mD.y = 0;
}
double angle = Math.atan((height - mD.y) / (mD.x + mMovement.x - width));
double _cos = Math.cos(2 * angle);
double _sin = Math.sin(2 * angle);
mF.x = (float) (width - mMovement.x + _cos * mMovement.x);
mF.y = (float) (height - _sin * mMovement.x);
// If the x position of A is above half of the page we are still not
// folding the upper-right edge and so E and D are equal.
if (mA.x > width / 2) {
mE.x = mD.x;
mE.y = mD.y;
}
else
{
// So get E
mE.x = (float) (mD.x + _cos * (width - mD.x));
mE.y = (float) -(_sin * (width - mD.x));
}
}
private void doDynamicCurl() {
int width = getWidth();
int height = getHeight();
mF.x = width - mMovement.x+0.1f;
mF.y = height - mMovement.y+0.1f;
if(mA.x==0) {
mF.x= Math.min(mF.x, mOldF.x);
mF.y= Math.max(mF.y, mOldF.y);
}
// Get diffs
float deltaX = width-mF.x;
float deltaY = height-mF.y;
float BH = (float) (Math.sqrt(deltaX * deltaX + deltaY * deltaY) / 2);
double tangAlpha = deltaY / deltaX;
double alpha = Math.atan(deltaY / deltaX);
double _cos = Math.cos(alpha);
double _sin = Math.sin(alpha);
mA.x = (float) (width - (BH / _cos));
mA.y = height;
mD.y = (float) (height - (BH / _sin));
mD.x = width;
mA.x = Math.max(0,mA.x);
if(mA.x==0) {
mOldF.x = mF.x;
mOldF.y = mF.y;
}
// Get W
mE.x = mD.x;
mE.y = mD.y;
// Correct
if (mD.y < 0) {
mD.x = width + (float) (tangAlpha * mD.y);
mE.y = 0;
mE.x = width + (float) (Math.tan(2 * alpha) * mD.y);
}
}
#Deprecated
private void SwapViews() {
Bitmap temp = mForeground;
mForeground = mBackground;
mBackground = temp;
}
private void nextView() {
int foreIndex = mIndex + 1;
if(foreIndex >= mPages.size()) {
foreIndex = 0;
}
int backIndex = foreIndex + 1;
if(backIndex >= mPages.size()) {
backIndex = 0;
}
mIndex = foreIndex;
setViews(foreIndex, backIndex);
}
private void previousView() {
int backIndex = mIndex;
int foreIndex = backIndex - 1;
if(foreIndex < 0) {
foreIndex = mPages.size()-1;
}
mIndex = foreIndex;
setViews(foreIndex, backIndex);
}
private void setViews(int foreground, int background) {
mForeground = mPages.get(foreground);
mBackground = mPages.get(background);
}
#Override
protected void onDraw(Canvas canvas) {
mCurrentLeft = getLeft();
mCurrentTop = getTop();
if ( !bViewDrawn ) {
bViewDrawn = true;
onFirstDrawEvent(canvas);
}
canvas.drawColor(Color.WHITE);
Rect rect = new Rect();
rect.left = 0;
rect.top = 0;
rect.bottom = getHeight();
rect.right = getWidth();
// First Page render
Paint paint = new Paint();
// Draw our elements
drawForeground(canvas, rect, paint);
drawBackground(canvas, rect, paint);
drawCurlEdge(canvas);
// Draw any debug info once we are done
if ( bEnableDebugMode )
drawDebug(canvas);
// Check if we can re-enable input
if ( bEnableInputAfterDraw )
{
bBlockTouchInput = false;
bEnableInputAfterDraw = false;
}
// Restore canvas
//canvas.restore();
}
protected void onFirstDrawEvent(Canvas canvas) {
mFlipRadius = getWidth();
ResetClipEdge();
DoPageCurl();
}
private void drawForeground( Canvas canvas, Rect rect, Paint paint ) {
canvas.drawBitmap(mForeground, null, rect, paint);
// Draw the page number (first page is 1 in real life :D
// there is no page number 0 hehe)
drawPageNum(canvas, mIndex);
}
private Path createBackgroundPath() {
Path path = new Path();
path.moveTo(mA.x, mA.y);
path.lineTo(mB.x, mB.y);
path.lineTo(mC.x, mC.y);
path.lineTo(mD.x, mD.y);
path.lineTo(mA.x, mA.y);
return path;
}
private void drawBackground( Canvas canvas, Rect rect, Paint paint ) {
Path mask = createBackgroundPath();
// Save current canvas so we do not mess it up
canvas.save();
canvas.clipPath(mask);
canvas.drawBitmap(mBackground, null, rect, paint);
// Draw the page number (first page is 1 in real life :D
// there is no page number 0 hehe)
drawPageNum(canvas, mIndex);
canvas.restore();
}
private Path createCurlEdgePath() {
Path path = new Path();
path.moveTo(mA.x, mA.y);
path.lineTo(mD.x, mD.y);
path.lineTo(mE.x, mE.y);
path.lineTo(mF.x, mF.y);
path.lineTo(mA.x, mA.y);
return path;
}
private void drawCurlEdge( Canvas canvas )
{
Path path = createCurlEdgePath();
canvas.drawPath(path, mCurlEdgePaint);
}
private void drawPageNum(Canvas canvas, int pageNum)
{
mTextPaint.setColor(Color.WHITE);
String pageNumText = "- "+pageNum+" -";
drawCentered(canvas, pageNumText,canvas.getHeight()-mTextPaint.getTextSize()-5,mTextPaint,mTextPaintShadow);
}
public static void drawTextShadowed(Canvas canvas, String text, float x, float y, Paint textPain, Paint shadowPaint) {
canvas.drawText(text, x-1, y, shadowPaint);
canvas.drawText(text, x, y+1, shadowPaint);
canvas.drawText(text, x+1, y, shadowPaint);
canvas.drawText(text, x, y-1, shadowPaint);
canvas.drawText(text, x, y, textPain);
}
public static void drawCentered(Canvas canvas, String text, float y, Paint textPain, Paint shadowPaint)
{
float posx = (canvas.getWidth() - textPain.measureText(text))/2;
drawTextShadowed(canvas, text, posx, y, textPain, shadowPaint);
}
private void drawDebug(Canvas canvas)
{
float posX = 10;
float posY = 20;
Paint paint = new Paint();
paint.setStrokeWidth(5);
paint.setStyle(Style.STROKE);
paint.setColor(Color.BLACK);
canvas.drawCircle(mOrigin.x, mOrigin.y, getWidth(), paint);
paint.setStrokeWidth(3);
paint.setColor(Color.RED);
canvas.drawCircle(mOrigin.x, mOrigin.y, getWidth(), paint);
paint.setStrokeWidth(5);
paint.setColor(Color.BLACK);
canvas.drawLine(mOrigin.x, mOrigin.y, mMovement.x, mMovement.y, paint);
paint.setStrokeWidth(3);
paint.setColor(Color.RED);
canvas.drawLine(mOrigin.x, mOrigin.y, mMovement.x, mMovement.y, paint);
posY = debugDrawPoint(canvas,"A",mA,Color.RED,posX,posY);
posY = debugDrawPoint(canvas,"B",mB,Color.GREEN,posX,posY);
posY = debugDrawPoint(canvas,"C",mC,Color.BLUE,posX,posY);
posY = debugDrawPoint(canvas,"D",mD,Color.CYAN,posX,posY);
posY = debugDrawPoint(canvas,"E",mE,Color.YELLOW,posX,posY);
posY = debugDrawPoint(canvas,"F",mF,Color.LTGRAY,posX,posY);
posY = debugDrawPoint(canvas,"Mov",mMovement,Color.DKGRAY,posX,posY);
posY = debugDrawPoint(canvas,"Origin",mOrigin,Color.MAGENTA,posX,posY);
posY = debugDrawPoint(canvas,"Finger",mFinger,Color.GREEN,posX,posY);
}
private float debugDrawPoint(Canvas canvas, String name, Vector2D point, int color, float posX, float posY) {
return debugDrawPoint(canvas,name+" "+point.toString(),point.x, point.y, color, posX, posY);
}
private float debugDrawPoint(Canvas canvas, String name, float X, float Y, int color, float posX, float posY) {
mTextPaint.setColor(color);
drawTextShadowed(canvas,name,posX , posY, mTextPaint,mTextPaintShadow);
Paint paint = new Paint();
paint.setStrokeWidth(5);
paint.setColor(color);
canvas.drawPoint(X, Y, paint);
return posY+15;
}
}
I am having trouble with the canvas function, I have no problem drawing 1 bitmap with the canvas but as soon as I add a 2nd bitmap, it only draws the last bitmap, does not show both.
please help. here is my code.
My intent, is to then animate these 2 different bitmaps independently on the screen.
#Override
protected void onDraw(Canvas canvas) {
football = BitmapFactory.decodeResource(getResources(),
R.drawable.ballicon);
receiver = BitmapFactory.decodeResource(getResources(),
R.drawable.rec);
canvas.drawBitmap(football, translate, null);
canvas.drawBitmap(receiver, translate, null);
Matrix m = canvas.getMatrix();
Log.d(DEBUG_TAG, "Matrix: " + translate.toShortString());
Log.d(DEBUG_TAG, "Canvas: " + m.toShortString());
}
Thanks
Here is a canvasview for drawing with multiple bitmaps
public class AdvanceCanvasView extends View {
private Bitmap bitmap;
private Canvas bitmapCanvas;
private Bitmap mBitmapBrush;
private ArrayList<Bitmap> bitmapArrayList;
private Vector2 mBitmapBrushDimensions;
private Paint paintLine;
private List<Vector2> mPositions = new ArrayList<Vector2>(100);
private HashMap<Integer, Path> pathMap; // current Paths being drawn
private HashMap<Integer, Point> previousPointMap; // current Points
private int i = 0;
private static final class Vector2 {
public Vector2(float x, float y) {
this.x = x;
this.y = y;
}
public final float x;
public final float y;
}
#SuppressLint("UseSparseArrays")
public AdvanceCanvasView(Context context, AttributeSet attrs) {
super(context, attrs); // pass context to View's constructor
}
public AdvanceCanvasView(Context c) {
super(c);
pathMap = new HashMap<>();
previousPointMap = new HashMap<>();
bitmapArrayList = new ArrayList<>();
paintLine = new Paint();
}
#Override
public void onSizeChanged(int w, int h, int oldW, int oldH) {
bitmap = Bitmap.createBitmap(getWidth(), getHeight(),
Bitmap.Config.ARGB_8888);
bitmapCanvas = new Canvas(bitmap);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(bitmap, 0, 0, null);
for (int i = 0; i < mPositions.size(); i++) {
canvas.drawBitmap(bitmapArrayList.get(i), mPositions.get(i).x, mPositions.get(i).y, null);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int action = event.getActionMasked();
int actionIndex = event.getActionIndex();
if (action == MotionEvent.ACTION_DOWN
|| action == MotionEvent.ACTION_POINTER_DOWN) {
touchStarted(event.getX(actionIndex), event.getY(actionIndex),
event.getPointerId(actionIndex));
}
else if (action == MotionEvent.ACTION_UP
|| action == MotionEvent.ACTION_POINTER_UP) {
touchEnded(event.getPointerId(actionIndex));
}
else {
touchMoved(event);
}
invalidate();
return true;
}
private void touchStarted(float x, float y, int lineID) {
Path path;
Point point;
path = new Path(); // create a new Path
pathMap.put(lineID, path); // add the Path to Map
point = new Point(); // create a new Point
previousPointMap.put(lineID, point); // add the Point to the Map
path = new Path(); // create a new Path
point = new Point(); // create a new Point
path.moveTo(x, y);
point.x = (int) x;
point.y = (int) y;
} // end method touchStarted
private void touchMoved(MotionEvent event) {
// for each of the pointers in the given MotionEvent
for (int i = 0; i < event.getPointerCount(); i++) {
final float posX = event.getX();
final float posY = event.getY();
mPositions.add(new Vector2(posX - mBitmapBrushDimensions.x / 2, posY - mBitmapBrushDimensions.y / 2));
bitmapArrayList.add(mBitmapBrush);
}
invalidate();
}
private void touchEnded(int lineID) {
Path path = pathMap.get(lineID);
path.reset();
}
public void init(Bitmap bitmap) {
mBitmapBrush = bitmap;
BitmapShader shader = new BitmapShader(mBitmapBrush, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);
paintLine.setShader(shader);
mBitmapBrushDimensions = new Vector2(mBitmapBrush.getWidth(), mBitmapBrush.getHeight());
}
}