My project is about to censor the picture. It works like this: user choose a photo from gallery and when select an area of photo , this area change to the other color.
It works fine and my problem is that I couldn't save it my gallery.Saving picture to gallery is easy and I do it many time without error. But now I got no error and may method doesn't work.
here is my code:
public boolean save() {
if (mTouchRects.isEmpty() || bmMosaicLayer == null) {
return false;
}
Bitmap bitmap = Bitmap.createBitmap(mImageWidth, mImageHeight,
Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(bmBaseLayer, 0, 0, null);
canvas.drawBitmap(bmMosaicLayer, 0, 0, null);
canvas.save();
try {
FileOutputStream fos = new FileOutputStream(outPath);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "failed to write image content");
return false;
}
return true;
}
and in my activity :
else if (view.equals(btSave)) {
boolean succced = mvImage.save();
String text = "save image "
+ (succced ? " succeed" : " failed");
Toast.makeText(view.getContext(), text, Toast.LENGTH_SHORT)
.show();
}
here is my full code:
public class MosaicView extends ViewGroup {
public static final String TAG = "MosaicView";
public static enum Effect {
GRID, COLOR, BLUR,
};
public static enum Mode {
GRID, PATH,
}
// default image inner padding, in dip pixels
private static final int INNER_PADDING = 6;
// default grid width, in dip pixels
private static final int GRID_WIDTH = 5;
// default grid width, in dip pixels
private static final int PATH_WIDTH = 20;
// default stroke rectangle color
private static final int STROKE_COLOR = 0xff2a5caa;
// default stroke width, in pixels
private static final int STROKE_WIDTH = 6;
private int mImageWidth;
private int mImageHeight;
private Bitmap bmBaseLayer;
private Bitmap bmCoverLayer;
private Bitmap bmMosaicLayer;
private Point startPoint;
private int mGridWidth;
private int mPathWidth;
private int mStrokeWidth;
private int mStrokeColor;
private String inPath;
private String outPath;
private Effect mEffect;
private Mode mMode;
private Rect mImageRect;
private Paint mPaint;
private Rect mTouchRect;
private List<Rect> mTouchRects;
private Path mTouchPath;
private List<Rect> mEraseRects;
private int mMosaicColor;
private int mPadding;
private List<Path> mTouchPaths;
private List<Path> mErasePaths;
private boolean mMosaic;
public MosaicView(Context context) {
super(context);
initImage();
}
public MosaicView(Context context, AttributeSet attrs) {
super(context, attrs);
initImage();
}
private void initImage() {
mMosaic = true;
mTouchRects = new ArrayList<Rect>();
mEraseRects = new ArrayList<Rect>();
mTouchPaths = new ArrayList<Path>();
mErasePaths = new ArrayList<Path>();
mStrokeWidth = STROKE_WIDTH;
mStrokeColor = STROKE_COLOR;
mPadding = dp2px(INNER_PADDING);
mPathWidth = dp2px(PATH_WIDTH);
mGridWidth = dp2px(GRID_WIDTH);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setColor(mStrokeColor);
mImageRect = new Rect();
setWillNotDraw(false);
mMode = Mode.PATH;
mEffect = Effect.GRID;
}
public void setSrcPath(String absPath) {
File file = new File(absPath);
if (file == null || !file.exists()) {
Log.w(TAG, "invalid file path " + absPath);
return;
}
reset();
inPath = absPath;
String fileName = file.getName();
String parent = file.getParent();
int index = fileName.lastIndexOf(".");
String stem = fileName.substring(0, index);
String newStem = stem + "_mosaic";
fileName = fileName.replace(stem, newStem);
outPath = parent + "/" + fileName;
Size size = BitmapUtil.getImageSize(inPath);
mImageWidth = size.width;
mImageHeight = size.height;
bmBaseLayer = BitmapUtil.getImage(absPath);
bmCoverLayer = getCoverLayer();
bmMosaicLayer = null;
requestLayout();
invalidate();
}
public void setEffect(Effect effect) {
if (mEffect == effect) {
Log.d(TAG, "duplicated effect " + effect);
return;
}
this.mEffect = effect;
if (bmCoverLayer != null) {
bmCoverLayer.recycle();
}
bmCoverLayer = getCoverLayer();
if (mMode == Mode.GRID) {
updateGridMosaic();
} else if (mMode == Mode.PATH) {
updatePathMosaic();
}
invalidate();
}
public void setMode(Mode mode) {
if (mMode == mode) {
Log.d(TAG, "duplicated mode " + mode);
return;
}
if (bmMosaicLayer != null) {
bmMosaicLayer.recycle();
bmMosaicLayer = null;
}
this.mMode = mode;
invalidate();
}
private Bitmap getCoverLayer() {
Bitmap bitmap = null;
if (mEffect == Effect.GRID) {
bitmap = getGridMosaic();
} else if (mEffect == Effect.COLOR) {
bitmap = getColorMosaic();
} else if (mEffect == Effect.BLUR) {
bitmap = getBlurMosaic();
}
return bitmap;
}
private Bitmap getColorMosaic() {
if (mImageWidth <= 0 || mImageHeight <= 0) {
return null;
}
Bitmap bitmap = Bitmap.createBitmap(mImageWidth, mImageHeight,
Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Rect rect = new Rect(0, 0, mImageWidth, mImageHeight);
Paint paint = new Paint();
paint.setColor(mMosaicColor);
canvas.drawRect(rect, paint);
canvas.save();
return bitmap;
}
private Bitmap getBlurMosaic() {
if (mImageWidth <= 0 || mImageHeight <= 0) {
return null;
}
if (bmBaseLayer == null) {
return null;
}
Bitmap bitmap = BitmapUtil.blur(bmBaseLayer);
return bitmap;
}
private Bitmap getGridMosaic() {
if (mImageWidth <= 0 || mImageHeight <= 0) {
return null;
}
Bitmap bitmap = Bitmap.createBitmap(mImageWidth, mImageHeight,
Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
int horCount = (int) Math.ceil(mImageWidth / (float) mGridWidth);
int verCount = (int) Math.ceil(mImageHeight / (float) mGridWidth);
Paint paint = new Paint();
paint.setAntiAlias(true);
for (int horIndex = 0; horIndex < horCount; ++horIndex) {
for (int verIndex = 0; verIndex < verCount; ++verIndex) {
int l = mGridWidth * horIndex;
int t = mGridWidth * verIndex;
int r = l + mGridWidth;
if (r > mImageWidth) {
r = mImageWidth;
}
int b = t + mGridWidth;
if (b > mImageHeight) {
b = mImageHeight;
}
int color = bmBaseLayer.getPixel(l, t);
Rect rect = new Rect(l, t, r, b);
paint.setColor(color);
canvas.drawRect(rect, paint);
}
}
canvas.save();
return bitmap;
}
public boolean isSaved() {
return (bmCoverLayer == null);
}
public void setOutPath(String absPath) {
this.outPath = absPath;
}
public void setGridWidth(int width) {
this.mGridWidth = dp2px(width);
}
public void setPathWidth(int width) {
this.mPathWidth = dp2px(width);
}
public int getGridWidth() {
return this.mGridWidth;
}
public void setStrokeColor(int color) {
this.mStrokeColor = color;
mPaint.setColor(mStrokeColor);
}
public void setMosaicColor(int color) {
this.mMosaicColor = color;
}
public int getStrokeColor() {
return this.mStrokeColor;
}
public void setStrokeWidth(int width) {
this.mStrokeWidth = width;
mPaint.setStrokeWidth(mStrokeWidth);
}
public int getStrokeWidth() {
return this.mStrokeWidth;
}
public void setErase(boolean erase) {
this.mMosaic = !erase;
}
public void clear() {
mTouchRects.clear();
mEraseRects.clear();
mTouchPaths.clear();
mErasePaths.clear();
if (bmMosaicLayer != null) {
bmMosaicLayer.recycle();
bmMosaicLayer = null;
}
invalidate();
}
public boolean reset() {
if (bmCoverLayer != null) {
bmCoverLayer.recycle();
bmCoverLayer = null;
}
if (bmBaseLayer != null) {
bmBaseLayer.recycle();
bmBaseLayer = null;
}
if (bmMosaicLayer != null) {
bmMosaicLayer.recycle();
bmMosaicLayer = null;
}
mTouchRects.clear();
mEraseRects.clear();
mTouchPaths.clear();
mErasePaths.clear();
return true;
}
public boolean save() {
if (mTouchRects.isEmpty() || bmMosaicLayer == null) {
return false;
}
Bitmap bitmap = Bitmap.createBitmap(mImageWidth, mImageHeight,
Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(bmBaseLayer, 0, 0, null);
canvas.drawBitmap(bmMosaicLayer, 0, 0, null);
canvas.save();
try {
FileOutputStream fos = new FileOutputStream(outPath);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
} catch (IOException e) {
e.printStackTrace();
Log.e(TAG, "failed to write image content");
return false;
}
return true;
}
public boolean dispatchTouchEvent(MotionEvent event) {
super.dispatchTouchEvent(event);
int action = event.getAction();
int x = (int) event.getX();
int y = (int) event.getY();
Log.d(TAG, "action " + action + " x " + x + " y " + y);
if (mMode == Mode.GRID) {
onGridEvent(action, x, y);
} else if (mMode == Mode.PATH) {
onPathEvent(action, x, y);
}
return true;
}
private void onGridEvent(int action, int x, int y) {
if (x >= mImageRect.left && x <= mImageRect.right
&& y >= mImageRect.top && y <= mImageRect.bottom) {
int left = x;
int right = x;
int top = y;
int bottom = y;
if (startPoint == null) {
startPoint = new Point();
startPoint.set(x, y);
mTouchRect = new Rect();
} else {
left = startPoint.x < x ? startPoint.x : x;
top = startPoint.y < y ? startPoint.y : y;
right = x > startPoint.x ? x : startPoint.x;
bottom = y > startPoint.y ? y : startPoint.y;
}
mTouchRect.set(left, top, right, bottom);
}
if (action == MotionEvent.ACTION_UP) {
if (mMosaic) {
mTouchRects.add(mTouchRect);
} else {
mEraseRects.add(mTouchRect);
}
mTouchRect = null;
startPoint = null;
updateGridMosaic();
}
invalidate();
}
private void onPathEvent(int action, int x, int y) {
if (mImageWidth <= 0 || mImageHeight <= 0) {
return;
}
if (x < mImageRect.left || x > mImageRect.right || y < mImageRect.top
|| y > mImageRect.bottom) {
return;
}
float ratio = (mImageRect.right - mImageRect.left)
/ (float) mImageWidth;
x = (int) ((x - mImageRect.left) / ratio);
y = (int) ((y - mImageRect.top) / ratio);
if (action == MotionEvent.ACTION_DOWN) {
mTouchPath = new Path();
mTouchPath.moveTo(x, y);
if (mMosaic) {
mTouchPaths.add(mTouchPath);
} else {
mErasePaths.add(mTouchPath);
}
} else if (action == MotionEvent.ACTION_MOVE) {
mTouchPath.lineTo(x, y);
updatePathMosaic();
invalidate();
}
}
private void updatePathMosaic() {
if (mImageWidth <= 0 || mImageHeight <= 0) {
return;
}
long time = System.currentTimeMillis();
if (bmMosaicLayer != null) {
bmMosaicLayer.recycle();
}
bmMosaicLayer = Bitmap.createBitmap(mImageWidth, mImageHeight,
Config.ARGB_8888);
Bitmap bmTouchLayer = Bitmap.createBitmap(mImageWidth, mImageHeight,
Config.ARGB_8888);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setPathEffect(new CornerPathEffect(10));
paint.setStrokeWidth(mPathWidth);
paint.setColor(Color.BLUE);
Canvas canvas = new Canvas(bmTouchLayer);
for (Path path : mTouchPaths) {
canvas.drawPath(path, paint);
}
paint.setColor(Color.TRANSPARENT);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
for (Path path : mErasePaths) {
canvas.drawPath(path, paint);
}
canvas.setBitmap(bmMosaicLayer);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawBitmap(bmCoverLayer, 0, 0, null);
paint.reset();
paint.setAntiAlias(true);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
canvas.drawBitmap(bmTouchLayer, 0, 0, paint);
paint.setXfermode(null);
canvas.save();
bmTouchLayer.recycle();
Log.d(TAG, "updatePathMosaic " + (System.currentTimeMillis() - time));
}
private void updateGridMosaic() {
if (mImageWidth <= 0 || mImageHeight <= 0) {
return;
}
long time = System.currentTimeMillis();
if (bmMosaicLayer != null) {
bmMosaicLayer.recycle();
}
bmMosaicLayer = Bitmap.createBitmap(mImageWidth, mImageHeight,
Config.ARGB_8888);
float ratio = (mImageRect.right - mImageRect.left)
/ (float) mImageWidth;
Bitmap bmTouchLayer = Bitmap.createBitmap(mImageWidth, mImageHeight,
Config.ARGB_8888);
Canvas canvas = null;
canvas = new Canvas(bmTouchLayer);
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
paint.setColor(mStrokeColor);
for (Rect rect : mTouchRects) {
int left = (int) ((rect.left - mImageRect.left) / ratio);
int right = (int) ((rect.right - mImageRect.left) / ratio);
int top = (int) ((rect.top - mImageRect.top) / ratio);
int bottom = (int) ((rect.bottom - mImageRect.top) / ratio);
canvas.drawRect(left, top, right, bottom, paint);
}
paint.setColor(Color.TRANSPARENT);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
for (Rect rect : mEraseRects) {
int left = (int) ((rect.left - mImageRect.left) / ratio);
int right = (int) ((rect.right - mImageRect.left) / ratio);
int top = (int) ((rect.top - mImageRect.top) / ratio);
int bottom = (int) ((rect.bottom - mImageRect.top) / ratio);
canvas.drawRect(left, top, right, bottom, paint);
}
canvas.setBitmap(bmMosaicLayer);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawBitmap(bmCoverLayer, 0, 0, null);
paint.reset();
paint.setAntiAlias(true);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
canvas.drawBitmap(bmTouchLayer, 0, 0, paint);
paint.setXfermode(null);
canvas.save();
bmTouchLayer.recycle();
Log.d(TAG, "updateGridMosaic " + (System.currentTimeMillis() - time));
}
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Log.d(TAG, "onDraw canvas " + canvas + " mTouchRect " + mTouchRect);
if (bmBaseLayer != null) {
canvas.drawBitmap(bmBaseLayer, null, mImageRect, null);
}
if (bmMosaicLayer != null) {
canvas.drawBitmap(bmMosaicLayer, null, mImageRect, null);
}
if (mTouchRect != null) {
canvas.drawRect(mTouchRect, mPaint);
}
}
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
if (mImageWidth <= 0 || mImageHeight <= 0) {
return;
}
int contentWidth = right - left;
int contentHeight = bottom - top;
int viewWidth = contentWidth - mPadding * 2;
int viewHeight = contentHeight - mPadding * 2;
float widthRatio = viewWidth / ((float) mImageWidth);
float heightRatio = viewHeight / ((float) mImageHeight);
float ratio = widthRatio < heightRatio ? widthRatio : heightRatio;
int realWidth = (int) (mImageWidth * ratio);
int realHeight = (int) (mImageHeight * ratio);
int imageLeft = (contentWidth - realWidth) / 2;
int imageTop = (contentHeight - realHeight) / 2;
int imageRight = imageLeft + realWidth;
int imageBottom = imageTop + realHeight;
mImageRect.set(imageLeft, imageTop, imageRight, imageBottom);
}
private int dp2px(int dip) {
Context context = this.getContext();
Resources resources = context.getResources();
int px = Math
.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dip, resources.getDisplayMetrics()));
return px;
}
}
and here:
inPath = absPath;
String fileName = file.getName();
String parent = file.getParent();
int index = fileName.lastIndexOf(".");
String stem = fileName.substring(0, index);
String newStem = stem + "_mosaic";
fileName = fileName.replace(stem, newStem);
outPath = parent + "/" + fileName;
here is related to what you find.
Related
I try to make a game in android studio (for an univeristy project) and I have some problems.
The game in question is a maze game type. I create the maze and the player, it work but I would to insert an image for the player, but I don't know how.
This is the code of the entire GameView.
public class GameView extends View {
private enum Direction {
UP, DOWN, LEFT, RIGHT
}
private Cell[][] cells;
private Cell player, exit;
private static final int COLS = 7 , ROWS = 10;
private static final float WALL_THICKNESS = 7;
private float cellSize, hMargin, vMargin;
private Paint wallPaint, exitPaint,playerPaint;
private Random random;
public GameView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
wallPaint = new Paint();
wallPaint.setColor(Color.BLACK);
wallPaint.setStrokeWidth(WALL_THICKNESS);
playerPaint = new Paint();
playerPaint.setColor(Color.RED);
exitPaint = new Paint();
exitPaint.setColor(Color.BLUE);
random = new Random();
createMaze();
}
private Cell getNeighbour(Cell cell){
ArrayList<Cell> neighbours = new ArrayList<>();
if(cell.col> 0){
if(!cells[cell.col-1][cell.row].visited){
neighbours.add(cells[cell.col-1][cell.row]);
}
}
if(cell.col < COLS-1){
if(!cells[cell.col+1][cell.row].visited){
neighbours.add(cells[cell.col+1][cell.row]);
}
}
if(cell.row > 0){
if(!cells[cell.col][cell.row-1].visited){
neighbours.add(cells[cell.col][cell.row-1]);
}
}
if(cell.row < ROWS-1){
if(!cells[cell.col][cell.row+1].visited){
neighbours.add(cells[cell.col][cell.row+1]);
}
}
if(neighbours.size() > 0){
int index = random.nextInt(neighbours.size());
return neighbours.get(index);
}
return null;
}
private void removeWall(Cell current, Cell next){
if(current.col == next.col && current.row == next.row+1){
current.topWall = false;
next.bottomWall = false;
}
if(current.col == next.col && current.row == next.row-1){
current.bottomWall = false;
next.topWall = false;
}
if(current.col == next.col+1 && current.row == next.row){
current.leftWall = false;
next.rightWall = false;
}
if(current.col == next.col-1 && current.row == next.row){
current.rightWall = false;
next.leftWall = false;
}
}
private void createMaze(){
Stack<Cell> stack = new Stack<>();
Cell current, next;
cells= new Cell[COLS][ROWS];
for(int x=0;x<COLS;x++){
for(int y=0;y<ROWS;y++){
cells[x][y] = new Cell(x, y);
}
}
player = cells[0][0];
exit = cells[COLS-1][ROWS-1];
current = cells[0][0];
current.visited = true;
do {
next = getNeighbour(current);
if (next != null) {
removeWall(current, next);
stack.push(current);
current = next;
current.visited = true;
} else {
current = stack.pop();
}
}while(!stack.empty());
}
#Override
protected void onDraw(Canvas canvas) {
//canvas.drawColor(Color.GREEN);
int width = getWidth();
int height = getHeight();
if (width / COLS > height / ROWS){
cellSize = height / (ROWS + 1);
}
else {
cellSize = width / (COLS + 1);
}
hMargin = (width - COLS*cellSize)/2;
vMargin = (height - ROWS*cellSize)/2;
for(int x=0;x<COLS;x++){
for(int y=0;y<ROWS;y++){
if(cells[x][y].topWall){
canvas.drawLine(
x*cellSize,
y*cellSize,
(x+1)*cellSize,
y*cellSize,
wallPaint);
}
if(cells[x][y].leftWall){
canvas.drawLine(
x*cellSize,
y*cellSize,
x*cellSize,
(y+1)*cellSize,
wallPaint);
}
if(cells[x][y].bottomWall){
canvas.drawLine(
x*cellSize,
(y+1)*cellSize,
(x+1)*cellSize,
(y+1)*cellSize,
wallPaint);
}
if(cells[x][y].rightWall){
canvas.drawLine(
(x+1)*cellSize,
y*cellSize,
(x+1)*cellSize,
(y+1)*cellSize,
wallPaint);
}
}
}
float margin = cellSize/20;
canvas.drawRect(
player.col*cellSize+margin,
player.row*cellSize+margin,
(player.col+1)*cellSize-margin,
(player.row+1)*cellSize-margin,
playerPaint
);
canvas.drawRect(
exit.col*cellSize+margin,
exit.row*cellSize+margin,
(exit.col+1)*cellSize-margin,
(exit.row+1)*cellSize-margin,
exitPaint
);
}
private void movePlayer(Direction direction){
switch (direction){
case UP :
if(!player.topWall)
player = cells[player.col][player.row-1];
break;
case DOWN :
if(!player.bottomWall)
player = cells[player.col][player.row+1];
break;
case LEFT :
if(!player.leftWall)
player = cells[player.col-1][player.row];
break;
case RIGHT :
if(!player.rightWall)
player = cells[player.col+1][player.row];
break;
}
invalidate();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN)
return true;
if(event.getAction() == MotionEvent.ACTION_MOVE){
float x = event.getX();
float y = event.getY();
float playerCenterX = hMargin + (player.col+0.5f)*cellSize;
float playerCenterY = vMargin + (player.col+0.5f)*cellSize;
float dx = x - playerCenterX;
float dy = y - playerCenterY;
float absDx = Math.abs(dx);
float absDy = Math.abs(dy);
if(absDx > cellSize || absDy > cellSize){
if(absDx > absDy){
if(dx>0){
movePlayer(Direction.RIGHT);
}else{
movePlayer(Direction.LEFT);
}
}
else{
if(dy>0){
movePlayer(Direction.DOWN);
}else{
movePlayer(Direction.UP);
}
}
}
return true;
}
return super.onTouchEvent(event);
}
private static class Cell{
boolean
topWall = true,
leftWall = true,
bottomWall = true,
rightWall = true,
visited = false;
int col, row;
public Cell(int col, int row) {
this.col = col;
this.row = row;
}
}
}
I try to do it with bitmap but when I start the app, it crash.
public class MovingBagView extends View {
private Bitmap bag[] = new Bitmap[2];
private int bagX;
private int bagY = 1000;
private int bagSpeed;
private Boolean touch = false;
private int canvasWidth, canvasHeight;
private int yellowX = 500, yellowY, yellowSpeed = -16;
private Paint yellowPaint = new Paint();
private int score;
private Bitmap backgroundImage;
private Paint scorePaint = new Paint();
private Bitmap life[] = new Bitmap[2];
public MovingBagView(Context context) {
super(context);
bag[0] = BitmapFactory.decodeResource(getResources(), R.drawable.bag1);
bag[1] = BitmapFactory.decodeResource(getResources(), R.drawable.bag2);
backgroundImage = BitmapFactory.decodeResource(getResources(), R.drawable.background);
yellowPaint.setColor(Color.YELLOW);
yellowPaint.setAntiAlias(false);
scorePaint.setColor(Color.BLACK);
scorePaint.setTextSize(40);
scorePaint.setTypeface(Typeface.DEFAULT_BOLD);
scorePaint.setAntiAlias(true);
life[0] = BitmapFactory.decodeResource(getResources(), R.drawable.heart);
life[1] = BitmapFactory.decodeResource(getResources(), R.drawable.heart_grey);
bagX = 10;
score = 0;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvasWidth = canvas.getWidth();
canvasHeight = canvas.getHeight();
canvas.drawBitmap(backgroundImage, 0, 0, null);
int minBagX = bag[0].getWidth();
int maxBagX = canvasWidth - bag[0].getWidth() * 2;
bagX = bagX + bagSpeed;
if (bagX < minBagX) {
bagX = minBagX;
}
if (bagX >= maxBagX) {
bagX = maxBagX;
}
bagSpeed = bagSpeed + 2;
if (touch) {
canvas.drawBitmap(bag[1], bagX, bagY, null);
}
else {
canvas.drawBitmap(bag[0], bagX, bagY, null);
}
yellowY = yellowY - yellowSpeed;
if (hitBallChecker(yellowX, yellowY)) {
score = score + 10;
yellowY = -100;
}
if (yellowY < 0) {
yellowY = canvasHeight + 21;
yellowX = (int)Math.floor(Math.random() * (maxBagX - minBagX)) + maxBagX;
}
canvas.drawCircle(yellowX, yellowY, 15, yellowPaint);
canvas.drawText("Score : " + score, 20, 60, scorePaint);
canvas.drawBitmap(life[0], 500, 10, null);
canvas.drawBitmap(life[0], 570, 10, null);
canvas.drawBitmap(life[0], 640, 10, null);
}
public boolean hitBallChecker(int x, int y) {
if (bagY < y && y < (bagY + bag[0].getHeight()) && bagX < x && x < (bagX + bag[0].getWidth())) {
return true;
}
return false;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
touch = true;
bagSpeed = -22;
}
return true;
}
}
I've figured out how to make the balls drop from the top of the screen. The code is supposed to make multiple yellow balls drop from the top of the screen, but only one yellow ball drops. Random yellow balls are supposed to drop from the top, and they drop from different positions. You can see the preview of it below:
To implement this, you should consider creating a custom object Ball
public class Ball{
public int x;
public int y;
public int speed;
public Ball(int x, int y, int speed){
this.x = x;
this.y = y;
this.speed = speed;
}
}
Then you can add multiple Balls in an ArrayList and in the draw() you iterate through every Ball in the array and do what you've been doing to one Ball with each.
for(int i = 0; i < balls.length(); i++){
Ball ball = balls.get(i);
ball.y -= ball.speed;
// check for collisions
// draw ball
}
How can i implement freehand cropping on Imageview.
Using below code i'm able to draw freehand path and can able to crop image but i'm facing some other problems
Now what i have tried so far
Here is my code
code for cropping image using canvas
public class SomeView extends View implements View.OnTouchListener {
private Paint paint;
int DIST = 2;
boolean flgPathDraw = true;
Point mfirstpoint = null;
boolean bfirstpoint = false;
Point mlastpoint = null;
Bitmap bitmap;
Context mContext;
public SomeView(Context c, Bitmap bitmap) {
super(c);
mContext = c;
this.bitmap = bitmap;
setFocusable(true);
setFocusableInTouchMode(true);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setPathEffect(new DashPathEffect(new float[]{10, 20}, 0));
paint.setStrokeWidth(5);
paint.setColor(Color.RED);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
this.setOnTouchListener(this);
points = new ArrayList<Point>();
bfirstpoint = false;
}
public SomeView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
setFocusable(true);
setFocusableInTouchMode(true);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
paint.setColor(Color.RED);
points = new ArrayList<Point>();
bfirstpoint = false;
this.setOnTouchListener(this);
}
public void onDraw(Canvas canvas) {
/*Rect dest = new Rect(0, 0, getWidth(), getHeight());
paint.setFilterBitmap(true);
canvas.drawBitmap(bitmap, null, dest, paint);*/
canvas.drawBitmap(bitmap, 0, 0, null);
Path path = new Path();
boolean first = true;
for (int i = 0; i < points.size(); i += 2) {
Point point = points.get(i);
if (first) {
first = false;
path.moveTo(point.x, point.y);
} else if (i < points.size() - 1) {
Point next = points.get(i + 1);
path.quadTo(point.x, point.y, next.x, next.y);
} else {
mlastpoint = points.get(i);
path.lineTo(point.x, point.y);
}
}
canvas.drawPath(path, paint);
}
public boolean onTouch(View view, MotionEvent event) {
// if(event.getAction() != MotionEvent.ACTION_DOWN)
// return super.onTouchEvent(event);
Point point = new Point();
point.x = (int) event.getX();
point.y = (int) event.getY();
if (flgPathDraw) {
if (bfirstpoint) {
if (comparepoint(mfirstpoint, point)) {
// points.add(point);
points.add(mfirstpoint);
flgPathDraw = false;
showcropdialog();
} else {
points.add(point);
}
} else {
points.add(point);
}
if (!(bfirstpoint)) {
mfirstpoint = point;
bfirstpoint = true;
}
}
invalidate();
Log.e("Hi ==>", "Size: " + point.x + " " + point.y);
if (event.getAction() == MotionEvent.ACTION_UP) {
Log.d("Action up*****~~>>>>", "called");
mlastpoint = point;
if (flgPathDraw) {
if (points.size() > 12) {
if (!comparepoint(mfirstpoint, mlastpoint)) {
flgPathDraw = false;
points.add(mfirstpoint);
showcropdialog();
}
}
}
}
return true;
}
private boolean comparepoint(Point first, Point current) {
int left_range_x = (int) (current.x - 3);
int left_range_y = (int) (current.y - 3);
int right_range_x = (int) (current.x + 3);
int right_range_y = (int) (current.y + 3);
if ((left_range_x < first.x && first.x < right_range_x)
&& (left_range_y < first.y && first.y < right_range_y)) {
if (points.size() < 10) {
return false;
} else {
return true;
}
} else {
return false;
}
}
public void fillinPartofPath() {
Point point = new Point();
point.x = points.get(0).x;
point.y = points.get(0).y;
points.add(point);
invalidate();
}
public void resetView() {
points.clear();
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.STROKE);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
paint.setColor(Color.RED);
points = new ArrayList<Point>();
bfirstpoint = false;
flgPathDraw = true;
invalidate();
}
private void showcropdialog() {
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Intent intent;
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
cropImage();
break;
case DialogInterface.BUTTON_NEGATIVE:
/*// No button clicked
intent = new Intent(mContext, DisplayCropActivity.class);
intent.putExtra("crop", false);
mContext.startActivity(intent);
bfirstpoint = false;*/
resetView();
break;
}
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setMessage("Do you Want to save Crop or Non-crop image?")
.setPositiveButton("Crop", dialogClickListener)
.setNegativeButton("Non-crop", dialogClickListener).show()
.setCancelable(false);
}
}
Code for cropping bitmap
public void cropImage() {
setContentView(R.layout.activity_picture_preview);
imageView = findViewById(R.id.image);
int widthOfscreen = 0;
int heightOfScreen = 0;
DisplayMetrics dm = new DisplayMetrics();
try {
getWindowManager().getDefaultDisplay().getMetrics(dm);
} catch (Exception ex) {
}
widthOfscreen = dm.widthPixels;
heightOfScreen = dm.heightPixels;
Bitmap bitmap2 = mBitmap;
Bitmap resultingImage = Bitmap.createBitmap(widthOfscreen,
heightOfScreen, bitmap2.getConfig());
Canvas canvas = new Canvas(resultingImage);
Paint paint = new Paint();
Path path = new Path();
for (int i = 0; i < points.size(); i++) {
path.lineTo(points.get(i).x, points.get(i).y);
}
canvas.drawPath(path, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap2, 0, 0, paint);
imageView.setImageBitmap(resultingImage);
}
Here what i get result using above code
Cropping image using Finger touch
This image showing result after cropping image
This is my expected output
Please check below screenshot for the same
This Image showing cropping image using Finger touch
This image showing result after cropping image
The Below problems i'am facing in above code
Unable to set bitmap in full screen using canvas
If i set bitmap in full screen in canvas than image is stretching
How to set transparent background to cropped bitmap
Unable to add border to cropped image
The result of image Cropping is not as expected
Here are some other post that i have tried so far
crop free hand selected part of image
Freehand cropping on the image in android
Android Freehand bitmap cropping
Android: Free Cropping of Image
implemanting freehand crop in android
Get the free hand cropping Image From the original in android
Darken parts of a free-hand cropped image in android
Android-freehand-image-crop
Android Top Image Cropper Libraries
Crop a shape from an Android bitmap
Freehand Image Crop draw inside bitmap region
none of the above post help to achieve my excepted output
If need more information please do let me know. Thanks in advance. Your efforts will be appreciated.
In general, your code looks OK, but I have a few comments:
Unable to set bitmap in full screen using canvas
If i set bitmap in full screen in canvas than image is stretching
The portion of the image that you are selecting needs to be placed in a smaller bitmap so the layout XML can position it as you like. You are creating a bitmap that is full screen. See the following demo for details.
How to set transparent background to cropped bitmap
I am unclear about what the issue is.
Unable to add border to cropped image
The result of image Cropping is not as expected
See below.
Here is a small demo app using your code. You didn't provide an MCVE, so I threw the following together for demonstration purposes. Other than getting the app to function I think that the only change is to draw the border in MainActivity.java. The border width starts at the cut-out path that the user draws and reaches inward to the cut-out. If you want to actually frame the cutout without losing any pixels, then you will need to expand the path to accommodate the frame which I arbitrarily set to 20 pixels.
I also had to create the layouts used, so you may want to look at those. They are posted below.
Here is the demo video with the code to follow:
MainActivity.java
public class MainActivity extends AppCompatActivity {
private Bitmap mBitmap;
private SomeView mSomeView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dog);
mSomeView = new SomeView(this, mBitmap);
LinearLayout layout = findViewById(R.id.layout);
LinearLayout.LayoutParams lp =
new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
layout.addView(mSomeView, lp);
}
public void cropImage() {
setContentView(R.layout.activity_picture_preview);
ImageView imageView = findViewById(R.id.image);
Bitmap fullScreenBitmap =
Bitmap.createBitmap(mSomeView.getWidth(), mSomeView.getHeight(), mBitmap.getConfig());
Canvas canvas = new Canvas(fullScreenBitmap);
Path path = new Path();
List<Point> points = mSomeView.getPoints();
for (int i = 0; i < points.size(); i++) {
path.lineTo(points.get(i).x, points.get(i).y);
}
// Cut out the selected portion of the image...
Paint paint = new Paint();
canvas.drawPath(path, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(mBitmap, 0, 0, paint);
// Frame the cut out portion...
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(10f);
canvas.drawPath(path, paint);
// Create a bitmap with just the cropped area.
Region region = new Region();
Region clip = new Region(0, 0, fullScreenBitmap.getWidth(), fullScreenBitmap.getHeight());
region.setPath(path, clip);
Rect bounds = region.getBounds();
Bitmap croppedBitmap =
Bitmap.createBitmap(fullScreenBitmap, bounds.left, bounds.top,
bounds.width(), bounds.height());
imageView.setImageBitmap(croppedBitmap);
}
}
SomeView.java
I don't think there were any substantive changes to this class.
public class SomeView extends View implements View.OnTouchListener {
private Paint paint;
private List<Point> points;
int DIST = 2;
boolean flgPathDraw = true;
Point mfirstpoint = null;
boolean bfirstpoint = false;
Point mlastpoint = null;
Bitmap bitmap;
Context mContext;
public SomeView(Context c, Bitmap bitmap) {
super(c);
mContext = c;
this.bitmap = bitmap;
setFocusable(true);
setFocusableInTouchMode(true);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setPathEffect(new DashPathEffect(new float[]{10, 20}, 0));
paint.setStrokeWidth(5);
paint.setColor(Color.RED);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
this.setOnTouchListener(this);
points = new ArrayList<Point>();
bfirstpoint = false;
}
public SomeView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
setFocusable(true);
setFocusableInTouchMode(true);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
paint.setColor(Color.RED);
points = new ArrayList<Point>();
bfirstpoint = false;
this.setOnTouchListener(this);
}
public void onDraw(Canvas canvas) {
/*Rect dest = new Rect(0, 0, getWidth(), getHeight());
paint.setFilterBitmap(true); canvas.drawBitmap(bitmap, null, dest, paint);*/
canvas.drawBitmap(bitmap, 0, 0, null);
Path path = new Path();
boolean first = true;
for (int i = 0; i < points.size(); i += 2) {
Point point = points.get(i);
if (first) {
first = false;
path.moveTo(point.x, point.y);
} else if (i < points.size() - 1) {
Point next = points.get(i + 1);
path.quadTo(point.x, point.y, next.x, next.y);
} else {
mlastpoint = points.get(i);
path.lineTo(point.x, point.y);
}
}
canvas.drawPath(path, paint);
}
public boolean onTouch(View view, MotionEvent event) {
// if(event.getAction() != MotionEvent.ACTION_DOWN)
// return super.onTouchEvent(event);
Point point = new Point();
point.x = (int) event.getX();
point.y = (int) event.getY();
if (flgPathDraw) {
if (bfirstpoint) {
if (comparepoint(mfirstpoint, point)) {
// points.add(point);
points.add(mfirstpoint);
flgPathDraw = false;
showcropdialog();
} else {
points.add(point);
}
} else {
points.add(point);
}
if (!(bfirstpoint)) {
mfirstpoint = point;
bfirstpoint = true;
}
}
invalidate();
Log.e("Hi ==>", "Size: " + point.x + " " + point.y);
if (event.getAction() == MotionEvent.ACTION_UP) {
Log.d("Action up*****~~>>>>", "called");
mlastpoint = point;
if (flgPathDraw) {
if (points.size() > 12) {
if (!comparepoint(mfirstpoint, mlastpoint)) {
flgPathDraw = false;
points.add(mfirstpoint);
showcropdialog();
}
}
}
}
return true;
}
private boolean comparepoint(Point first, Point current) {
int left_range_x = (int) (current.x - 3);
int left_range_y = (int) (current.y - 3);
int right_range_x = (int) (current.x + 3);
int right_range_y = (int) (current.y + 3);
if ((left_range_x < first.x && first.x < right_range_x)
&& (left_range_y < first.y && first.y < right_range_y)) {
if (points.size() < 10) {
return false;
} else {
return true;
}
} else {
return false;
}
}
public void fillinPartofPath() {
Point point = new Point();
point.x = points.get(0).x;
point.y = points.get(0).y;
points.add(point);
invalidate();
}
public void resetView() {
points.clear();
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.STROKE);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
paint.setColor(Color.RED);
points = new ArrayList<Point>();
bfirstpoint = false;
flgPathDraw = true;
invalidate();
}
private void showcropdialog() {
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
Intent intent;
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
((MainActivity) mContext).cropImage();
break;
case DialogInterface.BUTTON_NEGATIVE:
/*// No button clicked
intent = new Intent(mContext, DisplayCropActivity.class); intent.putExtra("crop", false); mContext.startActivity(intent);
bfirstpoint = false;*/ resetView();
break;
}
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setMessage("Do you Want to save Crop or Non-crop image?")
.setPositiveButton("Crop", dialogClickListener)
.setNegativeButton("Non-crop", dialogClickListener).show()
.setCancelable(false);
}
public List<Point> getPoints() {
return points;
}
}
activity_main.xml
<LinearLayout
android:id="#+id/layout"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity"/>
activity_picture_preview.xml
<android.support.constraint.ConstraintLayout x
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorPrimary">
<ImageView
android:id="#+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:srcCompat="#drawable/dog" />
</android.support.constraint.ConstraintLayout>
If you want to create a cropped bitmap with a 100px border, use the following code in cropImage():
// Create a bitmap with just the cropped area.
Region region = new Region();
Region clip = new Region(0, 0, fullScreenBitmap.getWidth(), fullScreenBitmap.getHeight());
region.setPath(path, clip);
Rect sourceBounds = region.getBounds();
Rect destBounds =
new Rect(CROPPED_MARGIN, CROPPED_MARGIN, sourceBounds.width() + CROPPED_MARGIN,
sourceBounds.height() + CROPPED_MARGIN);
Bitmap croppedBitmap =
Bitmap.createBitmap(sourceBounds.width() + CROPPED_MARGIN * 2,
sourceBounds.height() + CROPPED_MARGIN * 2, mBitmap.getConfig());
canvas.setBitmap(croppedBitmap);
canvas.drawBitmap(fullScreenBitmap, sourceBounds, destBounds, null);
imageView.setImageBitmap(croppedBitmap);
// Add as member variable.
private static final int CROPPED_MARGIN = 100;
I have tried lots of solution for this problem in my project, but this code is working.
Here, i put my own project's Code Here. You can use it, if you found your solution in it.
public class CropView extends View implements View.OnTouchListener {
public static final String INTENT_KEY_CROP = "crop";
public static final String CACHE_KEY = "bitmap";
public static List<Point> points;
boolean flgPathDraw = true;
boolean bFirstPoint = false;
private Point firstPoint = null;
private Point lastPoint = null;
private final Bitmap originalImageBitmap;
private int canvasWidth;
private int canvasHeight;
private Paint paint;
private Context context;
private static LruCache<String, Bitmap> mMemoryCache;
private final ImageCropListener imageCropListener;
public interface ImageCropListener {
void onClickDialogPositiveButton();
void onClickDialogNegativeButton();
}
public static Bitmap getBitmapFromMemCache() {
return mMemoryCache.get(CACHE_KEY);
}
public CropView(Context c, Bitmap bm, ImageCropListener listener) {
super(c);
context = c;
setFocusable(true);
setFocusableInTouchMode(true);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setPathEffect(new DashPathEffect(new float[] { 10, 20 }, 0));
paint.setStrokeWidth(5);
paint.setColor(Color.WHITE);
this.setOnTouchListener(this);
points = new ArrayList<>();
bFirstPoint = false;
this.originalImageBitmap = bm;
this.imageCropListener = listener;
final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
final int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
#Override
protected int sizeOf(String key, Bitmap bitmap) {
// The cache size will be measured in kilobytes rather than
// number of items.
return bitmap.getByteCount() / 1024;
}
};
}
public CropView(Context context, AttributeSet attrs, Bitmap bm,
ImageCropListener listener) {
super(context, attrs);
this.context = context;
setFocusable(true);
setFocusableInTouchMode(true);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(2);
paint.setColor(Color.WHITE);
this.setOnTouchListener(this);
points = new ArrayList<>();
bFirstPoint = false;
this.originalImageBitmap = bm;
this.imageCropListener = listener;
}
public void addBitmapToMemoryCache(Bitmap bitmap) {
if (getBitmapFromMemCache() == null) {
mMemoryCache.put(CACHE_KEY, bitmap);
}
}
private float calcBitmapScale(int canvasWidth, int canvasHeight, int bmpWidth, int bmpHeight) {
float scale = (float)canvasWidth / (float)bmpWidth;
float tmp = bmpHeight * scale;
if (tmp < canvasHeight) {
scale = (float)canvasHeight / (float)bmpHeight;
return scale;
}
return scale;
}
public void onDraw(Canvas canvas) {
canvasWidth = canvas.getWidth();
canvasHeight = canvas.getHeight();
/* int bmpWidth = this.originalImageBitmap.getWidth();
int bmpHeight = this.originalImageBitmap.getHeight();
float toCanvasScale = this.calcBitmapScale(canvasWidth, canvasHeight, bmpWidth, bmpHeight);
float diffX = (bmpWidth * toCanvasScale - canvasWidth);
float diffY = (bmpHeight * toCanvasScale - canvasHeight);
float addX = (diffX / toCanvasScale) / 2;
float addY = (diffY / toCanvasScale) / 2;
Rect rSrc = new Rect((int)addX, (int)addY,
(int)((canvasWidth / toCanvasScale) + addX), (int)((canvasHeight /
toCanvasScale) + addY));
RectF rDest = new RectF(0, 0, canvasWidth, canvasHeight);
*/
canvas.drawBitmap(originalImageBitmap, 0, 0, null);
Path cropAreaPath = new Path();
boolean isFirstPoint = true;
for (int i = 0; i < points.size(); i += 2) {
Point point = points.get(i);
if (isFirstPoint) {
isFirstPoint = false;
// 最初の処理でPathのx,y座標をpointの座標に移動する
cropAreaPath.moveTo(point.x, point.y);
} else if (i < points.size() - 1) {
Point next = points.get(i + 1);
cropAreaPath.quadTo(point.x, point.y, next.x, next.y);
} else {
lastPoint = points.get(i);
cropAreaPath.lineTo(point.x, point.y);
}
}
canvas.drawPath(cropAreaPath, paint);
}
public boolean onTouch(View view, MotionEvent event) {
Point point = new Point();
point.x = (int) event.getX();
point.y = (int) event.getY();
if (flgPathDraw) {
if (bFirstPoint) {
if (comparePoint(firstPoint, point)) {
// points.add(point);
points.add(firstPoint);
flgPathDraw = false;
showCropDialog();
} else {
points.add(point);
}
} else {
points.add(point);
}
if (!(bFirstPoint)) {
firstPoint = point;
bFirstPoint = true;
}
}
invalidate();
//Log.e("Hi ==>", "Size: " + point.x + " " + point.y);
if (event.getAction() == MotionEvent.ACTION_UP) {
Log.d("Action up***>", "called");
lastPoint = point;
if (flgPathDraw) {
if (points.size() > 12) {
if (!comparePoint(firstPoint, lastPoint)) {
flgPathDraw = false;
points.add(firstPoint);
showCropDialog();
}
}
}
}
return true;
}
private boolean comparePoint(Point first, Point current) {
int left_range_x = (int) (current.x - 3);
int left_range_y = (int) (current.y - 3);
int right_range_x = (int) (current.x + 3);
int right_range_y = (int) (current.y + 3);
if ((left_range_x < first.x && first.x < right_range_x)
&& (left_range_y < first.y && first.y < right_range_y)) {
if (points.size() < 10) {
return false;
} else {
return true;
}
} else {
return false;
}
}
public void fillinPartofPath() {
Point point = new Point();
point.x = points.get(0).x;
point.y = points.get(0).y;
points.add(point);
invalidate();
}
public void resetView() {
points.clear();
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.STROKE);
flgPathDraw = true;
invalidate();
}
private void showCropDialog() {
final Bitmap croppedImage = cropImage(this.originalImageBitmap);
DialogInterface.OnClickListener dialogClickListener = new
DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
addBitmapToMemoryCache(croppedImage);
imageCropListener.onClickDialogPositiveButton();
break;
case DialogInterface.BUTTON_NEGATIVE:
bFirstPoint = false;
resetView();
imageCropListener.onClickDialogNegativeButton();
break;
}
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage("Do you Want to save Crop or Non-crop image?")
.setPositiveButton("Crop", dialogClickListener)
.setNegativeButton("Cancel", dialogClickListener).show()
.setCancelable(false);
}
private Bitmap cropImage(Bitmap image) {
Bitmap cropImage = Bitmap.createBitmap(canvasWidth, canvasHeight,
image.getConfig());
Canvas canvas = new Canvas(cropImage);
Paint paint = new Paint();
paint.setAntiAlias(true);
Path path = new Path();
for (int i = 0; i < CropView.points.size(); i++) {
path.lineTo(CropView.points.get(i).x, CropView.points.get(i).y);
}
canvas.drawPath(path, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(originalImageBitmap, 0, 0, paint);
return cropImage;
}
class Point {
public float dy;
public float dx;
float x, y;
#Override
public String toString(){
return x + ", " + y;
}
}
}
Then You can get Crop Result..as like,
Bitmap cropBitmap = CropView.getBitmapFromMemCache();
cropBitmap = getBitmapWithTransparentBG(cropBitmap,Color.WHITE);
Drawable d = new BitmapDrawable(getResources(),cropBitmap);
public Bitmap getBitmapWithTransparentBG(Bitmap srcBitmap, int bgColor) {
Bitmap result = srcBitmap.copy(Bitmap.Config.ARGB_8888, true);
int nWidth = result.getWidth();
int nHeight = result.getHeight();
for (int y = 0; y < nHeight; ++y)
for (int x = 0; x < nWidth; ++x) {
int nPixelColor = result.getPixel(x, y);
if (nPixelColor == bgColor)
result.setPixel(x, y, Color.TRANSPARENT);
}
return result;
}
I Think, This code will be perfect for your problem solution.. Thanks
The arrow around the arc is what I want to draw
Here is the code for the animated arc.
The code I have added draws an animated arc to the point of touch on another arc. The arc fills up with the provided gradient color to the point of touch on the arc. The filling up of the arc is an animation. The code for the animation works. I want to draw an arrow around the arc which follows the arc to the point where it is filled. The end result has to be an arrow which follows the arc.
Please help me with this.
//Rectangle for the arc
private RectF mArcRect = new RectF();
//Paints required for drawing
private Paint mArcPaint;
private Paint mArcProgressPaint;
private Paint mTickPaint;
private Paint mTickProgressPaint;
private Paint mTickTextPaint;
private Paint mTickTextColoredPaint;
private Paint linePaint;
//Arc related dimens
private int mArcRadius = 0;
private int mArcWidth = 2;
private int mArcProgressWidth = 18;
private boolean mRoundedEdges = true;
//Thumb Drawable
private Drawable mThumb;
//Thumb position related coordinates
private int mTranslateX;
private int mTranslateY;
private int mThumbXPos;
private int mThumbYPos;
private int mAngleTextSize = 12;
private int LOWER_LIMIT = -16;
private int mTickOffset = 12;
private int mTickLength = 10;
private int mTickWidth = 2;
private int mTickProgressWidth = 2;
private int mAngle = LOWER_LIMIT;
private boolean mTouchInside = true;
private boolean mEnabled = true;
private TicksBetweenLabel mTicksBetweenLabel = TicksBetweenLabel.TWO;
private int mTickIntervals = 15;
private double mTouchAngle = 0;
private float mTouchIgnoreRadius;
private int sweepAngle = 0;
private int oldAngle = 0;
private int bottomLeft;
private int top;
private long startClickTime = 0;
//Event listener
private OnProtractorViewChangeListener mOnProtractorViewChangeListener = null;
//Interface for event listener
public interface OnProtractorViewChangeListener {
void onProgressChanged(ProtractorView protractorView, double progress, boolean fromUser);
void onStartTrackingTouch(ProtractorView protractorView);
void onStopTrackingTouch(ProtractorView protractorView);
}
public enum TicksBetweenLabel {
ZERO,
ONE,
TWO,
THREE
}
public ProtractorView(Context context) {
super(context);
init(context, null, 0);
}
public ProtractorView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, R.attr.protractorViewStyle);
}
public ProtractorView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
private void init(Context context, AttributeSet attrs, int defStyle) {
final Resources res = getResources();
int textColor = res.getColor(R.color.progress_gray);
int textProgressColor = res.getColor(R.color.default_blue_light);
int tickColor = res.getColor(R.color.progress_gray);
int tickProgressColor = res.getColor(R.color.default_blue_light);
int thumbHalfHeight;
int thumbHalfWidth;
mThumb = res.getDrawable(R.drawable.thumb_selector);
mArcWidth = (int)(mArcWidth * DENSITY);
mArcProgressWidth = (int)(mArcProgressWidth * DENSITY);
mAngleTextSize = (int)(mAngleTextSize * DENSITY);
mTickOffset = (int)(mTickOffset * DENSITY);
mTickLength = (int)(mTickLength * DENSITY);
mTickWidth = (int)(mTickWidth * DENSITY);
mTickProgressWidth = (int)(mTickProgressWidth * DENSITY);
if (attrs != null) {
final TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ProtractorView, defStyle, 0);
Drawable thumb = array.getDrawable(R.styleable.ProtractorView_thumb);
if (thumb != null) {
mThumb = thumb;
}
thumbHalfHeight = mThumb.getIntrinsicHeight() / 2;
thumbHalfWidth = mThumb.getIntrinsicWidth() / 2;
mThumb.setBounds(-thumbHalfWidth, -thumbHalfHeight, thumbHalfWidth, thumbHalfHeight);
//Dimensions
mAngleTextSize = (int) array.getDimension(R.styleable.ProtractorView_angleTextSize, mAngleTextSize);
mArcProgressWidth = (int) array.getDimension(R.styleable.ProtractorView_progressWidth, mArcProgressWidth);
mTickOffset = (int) array.getDimension(R.styleable.ProtractorView_tickOffset, mTickOffset);
mTickLength = (int) array.getDimension(R.styleable.ProtractorView_tickLength, mTickLength);
mArcWidth = (int) array.getDimension(R.styleable.ProtractorView_arcWidth, mArcWidth);
//Integers
mAngle = array.getInteger(R.styleable.ProtractorView_angle, mAngle);
mTickIntervals = array.getInt(R.styleable.ProtractorView_tickIntervals, mTickIntervals);
//Colors
textColor = array.getColor(R.styleable.ProtractorView_textColor, textColor);
textProgressColor = array.getColor(R.styleable.ProtractorView_textProgressColor, textProgressColor);
tickColor = array.getColor(R.styleable.ProtractorView_tickColor, tickColor);
tickProgressColor = array.getColor(R.styleable.ProtractorView_tickProgressColor, tickProgressColor);
//Boolean
mRoundedEdges = array.getBoolean(R.styleable.ProtractorView_roundEdges, mRoundedEdges);
mEnabled = array.getBoolean(R.styleable.ProtractorView_enabled, mEnabled);
mTouchInside = array.getBoolean(R.styleable.ProtractorView_touchInside, mTouchInside);
int ordinal = array.getInt(R.styleable.ProtractorView_ticksBetweenLabel, mTicksBetweenLabel.ordinal());
mTicksBetweenLabel = TicksBetweenLabel.values()[ordinal];
}
/**
* Creating and configuring the paints as required.
*/
mAngle = (mAngle > MAX) ? MAX : ((mAngle < 0) ? LOWER_LIMIT : mAngle);
int[] colors = buildColorArray("60");
mArcPaint = new Paint();
mArcPaint.setShader(new LinearGradient(0, 0, 550, 0, colors, null, Shader.TileMode.MIRROR));
mArcPaint.setAntiAlias(true);
mArcPaint.setStyle(Paint.Style.STROKE);
mArcPaint.setStrokeWidth(mArcWidth);
int[] foregroundColors = buildColorArray("");
mArcProgressPaint = new Paint();
mArcProgressPaint.setShader(new LinearGradient(0, 0, 550, 0, foregroundColors, null, Shader.TileMode.MIRROR));
mArcProgressPaint.setAntiAlias(true);
mArcProgressPaint.setStyle(Paint.Style.STROKE);
mArcProgressPaint.setStrokeWidth(mArcProgressWidth);
if (mRoundedEdges) {
mArcPaint.setStrokeCap(Paint.Cap.ROUND);
mArcProgressPaint.setStrokeCap(Paint.Cap.ROUND);
}
mTickPaint = new Paint();
mTickPaint.setColor(tickColor);
mTickPaint.setAntiAlias(true);
mTickPaint.setStyle(Paint.Style.STROKE);
mTickPaint.setStrokeWidth(mTickWidth);
mTickProgressPaint = new Paint();
mTickProgressPaint.setColor(tickProgressColor);
mTickProgressPaint.setAntiAlias(true);
mTickProgressPaint.setStyle(Paint.Style.STROKE);
mTickProgressPaint.setStrokeWidth(mTickProgressWidth);
mTickTextPaint = new Paint();
mTickTextPaint.setColor(textColor);
mTickTextPaint.setAntiAlias(true);
mTickTextPaint.setStyle(Paint.Style.FILL);
mTickTextPaint.setTextSize(mAngleTextSize);
mTickTextPaint.setTextAlign(Paint.Align.CENTER);
mTickTextColoredPaint = new Paint();
mTickTextColoredPaint.setColor(textProgressColor);
mTickTextColoredPaint.setAntiAlias(true);
mTickTextColoredPaint.setStyle(Paint.Style.FILL);
mTickTextColoredPaint.setTextSize(mAngleTextSize);
mTickTextColoredPaint.setTextAlign(Paint.Align.CENTER);
linePaint = new Paint();
linePaint.setColor(Color.BLACK);
linePaint.setAntiAlias(true);
linePaint.setStyle(Paint.Style.STROKE);
}
private int[] buildColorArray(String alpha) {
//#ffc0c0
//#fd708a
//#54fcff
// String [] colors = {"#" + alpha + "60f0f6", "#" + alpha + "fd708a", "#" + alpha + "fd708a", "#" + alpha + "60f0f6", "#" + alpha + "d683a0", "#" + alpha + "60f0f6"};
String[] colors = {
"#" + alpha + "60f0f6",
"#" + alpha + "69dee9",
"#" + alpha + "99bbcd",
"#" + alpha + "fd708a",
"#" + alpha + "f8849b"
};
int[] colorValues = new int[colors.length]; //#59dae1
int j = 0;
for (int i = 0; i < colors.length; i++) {
colorValues[j++] = Color.parseColor(colors[i]); //60f0f6, 54fcff, fd708a
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "Color value: " + colors[i]);
}
}
return colorValues;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = getDefaultSize(getSuggestedMinimumHeight(),
heightMeasureSpec);
int width = getDefaultSize(getSuggestedMinimumWidth(),
widthMeasureSpec);
int min = Math.min(width, height);
//width = min;
height = min / 2;
int arcDiameter = 0;
int tickEndToArc = (mTickOffset + mTickLength);
int radiusConstant = 30;
arcDiameter = min - 2 * tickEndToArc + radiusConstant;
arcDiameter = (int)(arcDiameter - 2 * 20 * DENSITY);
mArcRadius = arcDiameter / 2;
top = height - (mArcRadius);
bottomLeft = width / 2 - mArcRadius;
mArcRect.set(bottomLeft, top, bottomLeft + arcDiameter, top + arcDiameter);
mTranslateX = (int) mArcRect.centerX();
mTranslateY = (int) mArcRect.centerY();
int thumbAngle = mAngle;
mThumbXPos = (int)(mArcRadius * Math.cos(Math.toRadians(thumbAngle)));
mThumbYPos = (int)(mArcRadius * Math.sin(Math.toRadians(thumbAngle)));
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "co ordinate = x = " + mThumbXPos + " y = " + mThumbYPos + " mAngle = " + mAngle);
}
setTouchInside(mTouchInside);
setMeasuredDimension(width, height + tickEndToArc + 33);
}
#SuppressLint("DrawAllocation")
#Override
protected void onDraw(Canvas canvas) {
if (mAngle > 0) {
mAngle = mAngle - 360;
}
if (mAngle <= 0 && mAngle > LOWER_LIMIT) {
mAngle = LOWER_LIMIT;
}
if (mTouchAngle > START_ANGLE && mTouchAngle < THRESHOLD_VALUE) {
mAngle = LOWER_LIMIT;
}
sweepAngle = mAngle;
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "The Sweep Angle is : " + sweepAngle);
}
if (sweepAngle < -280) {
mAngle = LOWER_LIMIT;
}
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "Max angle " + mAngle);
}
if ((double)(mAngle / 13) > 19) {
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "Inside If condition to set Complete angle");
}
mAngle = COMPLETE_ANGLE;
}
canvas.save();
canvas.scale(1, -1, mArcRect.centerX(), mArcRect.centerY());
canvas.drawArc(mArcRect, 0, MAX, false, mArcPaint);
canvas.drawArc(mArcRect, (int) START_ANGLE, mAngle, false, mArcProgressPaint);
canvas.restore();
double slope, startTickX, startTickY, endTickX, endTickY, midTickX, midTickY, thetaInRadians;
double radiusOffset = mArcRadius + mTickOffset;
}
#Override
protected void drawableStateChanged() {
super.drawableStateChanged();
if (mThumb != null && mThumb.isStateful()) {
int[] state = getDrawableState();
mThumb.setState(state);
}
invalidate();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if (mEnabled) {
this.getParent().requestDisallowInterceptTouchEvent(true);
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
if (ignoreTouch(event.getX(), event.getY())) {
return false;
}
if (ignoreAngleTouch(event.getX(), event.getY())) {
return false;
}
onStartTrackingTouch();
oldAngle = getAngle();
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "onTouchEvent: ACTION_DOWN old angle" + getAngle());
}
startClickTime = Calendar.getInstance().getTimeInMillis();
updateOnTouch(event);
break;
case MotionEvent.ACTION_MOVE:
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "ACTION_MOVE TRIGGERED");
}
if (ignoreAngleTouch(event.getX(), event.getY())) {
return false;
}
updateOnTouchForMove(event);
break;
case MotionEvent.ACTION_UP:
onStopTrackingTouch();
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "onTouchEvent: ACTION_UP old angle" + getAngle());
}
long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime;
if (clickDuration > 50) {
updateOnTouchForMove(event);
} else {
updateOnTouch(event);
}
setPressed(false);
this.getParent().requestDisallowInterceptTouchEvent(false);
break;
case MotionEvent.ACTION_CANCEL:
onStopTrackingTouch();
setPressed(false);
this.getParent().requestDisallowInterceptTouchEvent(false);
break;
}
return true;
}
return false;
}
private boolean ignoreAngleTouch(float x, float y) {
boolean ignore = false;
double touchAngle = getTouchDegrees(x, y);
if (touchAngle > START_ANGLE && touchAngle < LOWER_THRESHOLD_VALUE || touchAngle > START_ANGLE && touchAngle < THRESHOLD_VALUE) {
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "ignoreAngleTouch");
}
ignore = true;
}
return ignore;
}
private void updateOnTouchForMove(MotionEvent event) {
setPressed(true);
mTouchAngle = getTouchDegrees(event.getX(), event.getY());
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "Touch Angle " + mTouchAngle);
}
int angle = (int) mTouchAngle - (int) START_ANGLE;
updateAngleForMove(angle);
}
private void updateAngleForMove(int angle) {
mAngle = (angle > MAX) ? angle : (angle < 0) ? angle : angle;
if (angle == 0) {
mAngle = (int) START_ANGLE;
}
int cloneAngle = mAngle;
if (mTouchAngle > START_ANGLE && mTouchAngle < LOWER_THRESHOLD_VALUE) {
mAngle = LOWER_LIMIT;
}
if ((mAngle / 13) > 19) {
mAngle = COMPLETE_ANGLE;
}
if (mOnProtractorViewChangeListener != null) {
mOnProtractorViewChangeListener.onProgressChanged(this, cloneAngle, true);
}
boolean check = mTouchAngle > START_ANGLE && mTouchAngle < THRESHOLD_VALUE;
if (!check) {
updateThumbPosition();
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "updateAngle: Re Drawing");
}
invalidate();
} else {
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "Inside else condition");
}
mAngle = LOWER_LIMIT;
updateThumbPosition();
invalidate();
}
}
private void onStartTrackingTouch() {
if (mOnProtractorViewChangeListener != null) {
mOnProtractorViewChangeListener.onStartTrackingTouch(this);
}
}
private void onStopTrackingTouch() {
if (mOnProtractorViewChangeListener != null) {
mOnProtractorViewChangeListener.onStopTrackingTouch(this);
}
}
private boolean ignoreTouch(float xPos, float yPos) {
boolean ignore = false;
float x = xPos - mTranslateX;
float y = yPos - mTranslateY;
float touchRadius = (float) Math.sqrt(((x * x) + (y * y)));
if (touchRadius < mTouchIgnoreRadius + 30 || touchRadius > (mArcRadius + mTickLength + mTickOffset)) {
ignore = true;
}
return ignore;
}
private void updateOnTouch(MotionEvent event) {
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "updateOnTouch: Old Angle" + getAngle());
}
boolean ignoreTouch = ignoreTouch(event.getX(), event.getY());
if (ignoreTouch) {
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "in ignore touch");
}
return;
}
setPressed(true);
mTouchAngle = getTouchDegrees(event.getX(), event.getY());
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "Touch Angle " + mTouchAngle);
}
int angle = (int) mTouchAngle - (int) START_ANGLE;
onProgressRefresh(angle, true);
}
private double getTouchDegrees(float xPos, float yPos) {
float x = xPos - mTranslateX;
float y = yPos - mTranslateY;
x = -x;
// convert to arc Angle
double angle = Math.toDegrees(Math.atan2(y, x) + (Math.PI));
/*if (angle > 270)
angle = 0;
else if (angle > 180)
angle = 360;*/
return angle;
}
private void onProgressRefresh(int angle, boolean fromUser) {
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "Change Angle: " + angle);
}
updateAngle(angle, fromUser);
}
private void updateAngle(int angle, boolean fromUser) {
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "updateAngle: " + angle);
}
mAngle = (angle > MAX) ? angle : (angle < 0) ? angle : angle;
if (angle == 0) {
mAngle = (int) START_ANGLE;
}
if (mAngle > LOWER_LIMIT && mAngle < 0) {
mAngle = LOWER_LIMIT;
}
if (mTouchAngle > START_ANGLE && mTouchAngle < THRESHOLD_VALUE) {
mAngle = LOWER_LIMIT;
}
int cloneAngle = mAngle;
if (mAngle > 0) {
cloneAngle = mAngle - 360;
}
if ((mAngle / 13) > 19) {
mAngle = COMPLETE_ANGLE;
}
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "updateAngle: Angle being sent to onProgressChanged " + cloneAngle);
}
if (mOnProtractorViewChangeListener != null) {
mOnProtractorViewChangeListener.onProgressChanged(this, cloneAngle, true);
}
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "Angle being sent to the function = " + mAngle);
}
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "Value of Sweep Angle " + sweepAngle);
}
boolean check = mTouchAngle > START_ANGLE && mTouchAngle < THRESHOLD_VALUE;
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "Check Condition Value: " + check);
}
if (!check) {
updateThumbPosition();
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "updateAngle: Re Drawing");
}
startAnimatingArc();
} else {
mAngle = LOWER_LIMIT;
updateThumbPosition();
startAnimatingArc();
}
}
public void startAnimatingArc() {
final ProtractorView protractorView = findViewById(R.id.protractorview);
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "Inside start Animating Arc\nOld Angle: " + getOldAngle() + "\nNew Angle: " + getAngle());
}
int newAngle = getAngle();
if (newAngle > 0) {
newAngle -= 360;
}
ValueAnimator animator = ValueAnimator.ofInt(getOldAngle(), newAngle);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
int animatedValue = (int) animation.getAnimatedValue();
if (Util.IS_DEBUG_LOGGABLE) {
Log.d(TAG, "onAnimationUpdate: Animated Value " + animatedValue);
}
mAngle = animatedValue;
protractorView.invalidate();
}
});
animator.setDuration(450);
animator.start();
// invalidate();
}
private void updateThumbPosition() {
int thumbAngle = mAngle; //(int) (mStartAngle + mProgressSweep + mRotation + 90);
mThumbXPos = (int)(mArcRadius * Math.cos(Math.toRadians(thumbAngle)));
mThumbYPos = (int)(mArcRadius * Math.sin(Math.toRadians(thumbAngle)));
}
//*****************************************************
// Setters and Getters
//*****************************************************
public boolean getTouchInside() {
return mTouchInside;
}
public void setTouchInside(boolean isEnabled) {
int thumbHalfheight = mThumb.getIntrinsicHeight() / 2;
int thumbHalfWidth = mThumb.getIntrinsicWidth() / 2;
mTouchInside = isEnabled;
if (mTouchInside) {
mTouchIgnoreRadius = (float)(mArcRadius / 1.5) + 5;
} else {
mTouchIgnoreRadius = mArcRadius + 5 - Math.min(thumbHalfWidth, thumbHalfheight);
}
}
public void setOnProtractorViewChangeListener(OnProtractorViewChangeListener l) {
mOnProtractorViewChangeListener = l;
}
public OnProtractorViewChangeListener getOnProtractorViewChangeListener() {
return mOnProtractorViewChangeListener;
}
public int getAngle() {
return mAngle;
}
public void setAngle(int angle) {
this.mAngle = angle;
onProgressRefresh(mAngle, false);
}
public int getOldAngle() {
return oldAngle;
}
public boolean isEnabled() {
return mEnabled;
}
public void setEnabled(boolean enabled) {
this.mEnabled = enabled;
invalidate();
}
public int getProgressColor() {
return mArcProgressPaint.getColor();
}
public void setProgressColor(#ColorInt int color) {
mArcProgressPaint.setColor(color);
invalidate();
}
public int getArcColor() {
return mArcPaint.getColor();
}
public void setArcColor(#ColorInt int color) {
mArcPaint.setColor(color);
invalidate();
}
public int getArcProgressWidth() {
return mArcProgressWidth;
}
public void setArcProgressWidth(int arcProgressWidth) {
this.mArcProgressWidth = arcProgressWidth;
mArcProgressPaint.setStrokeWidth(arcProgressWidth);
invalidate();
}
public int getArcWidth() {
return mArcWidth;
}
public void setArcWidth(int arcWidth) {
this.mArcWidth = arcWidth;
mArcPaint.setStrokeWidth(arcWidth);
invalidate();
}
public boolean isRoundedEdges() {
return mRoundedEdges;
}
public void setRoundedEdges(boolean roundedEdges) {
this.mRoundedEdges = roundedEdges;
if (roundedEdges) {
mArcPaint.setStrokeCap(Paint.Cap.ROUND);
mArcProgressPaint.setStrokeCap(Paint.Cap.ROUND);
} else {
mArcPaint.setStrokeCap(Paint.Cap.SQUARE);
mArcPaint.setStrokeCap(Paint.Cap.SQUARE);
}
invalidate();
}
public Drawable getThumb() {
return mThumb;
}
public void setThumb(Drawable thumb) {
this.mThumb = thumb;
invalidate();
}
public int getAngleTextSize() {
return mAngleTextSize;
}
public void setAngleTextSize(int angleTextSize) {
this.mAngleTextSize = angleTextSize;
invalidate();
}
public int getTickOffset() {
return mTickOffset;
}
public void setTickOffset(int tickOffset) {
this.mTickOffset = tickOffset;
}
public int getTickLength() {
return mTickLength;
}
public void setTickLength(int tickLength) {
this.mTickLength = tickLength;
}
public TicksBetweenLabel getTicksBetweenLabel() {
return mTicksBetweenLabel;
}
public void setTicksBetweenLabel(TicksBetweenLabel ticksBetweenLabel) {
this.mTicksBetweenLabel = mTicksBetweenLabel;
invalidate();
}
public int getTickIntervals() {
return mTickIntervals;
}
public void setTickIntervals(int tickIntervals) {
this.mTickIntervals = tickIntervals;
invalidate();
}
}
I am making an app where balls fall from the top of the screen to the bottom of the screen, and the user has to touch a certain area to make the balls disappear. However, when the balls fall, they appear to stutter as the fall down the screen. Here is the entire code for the project:
public class GameActivity extends Activity implements View.OnTouchListener {
MyBringBack ourSurfaceView;
Paint p;
float x, y, secondX, secondY;
int firstRun;
Bitmap green, red, blue, purple, white;
int currentColor;
ArrayList<Ball> balls = new ArrayList<>();
Paint buttonPaint;
Boolean thingFirstRun;
int score;
Model model;
int cHeight, cWidth;
int nextBallSpeed;
int totalBalls;
int countBalls;
int buttonState = 0;
public final static int BUTTON_NONE = 0;
public final static int BUTTON_LEFT = 1;
public final static int BUTTON_RIGHT = 2;
public final static int BUTTON_BOTH = 3;
class Ball {
public Ball(int x, int y, int s, int color, Bitmap b) {
xCord = x;
yCord = y;
speed = s;
this.color = color;
passedLineYet = false;
bitmap = b;
}
public int xCord;
public int yCord;
public int speed;
public int color;
public boolean passedLineYet;
public Bitmap bitmap;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
red = BitmapFactory.decodeResource(getResources(), R.drawable.redcircle);
blue = BitmapFactory.decodeResource(getResources(), R.drawable.bluecircle);
green = BitmapFactory.decodeResource(getResources(), R.drawable.greencircle);
purple = BitmapFactory.decodeResource(getResources(), R.drawable.purplecircle);
white = BitmapFactory.decodeResource(getResources(), R.drawable.white);
secondX = secondY = -1;
cHeight = 0;
cWidth = 0;
countBalls = 0;
nextBallSpeed = 60;
p = new Paint();
p.setColor(Color.WHITE);
p.setTextSize(200);
ourSurfaceView = new MyBringBack(this);
ourSurfaceView.setOnTouchListener(this);
setContentView(ourSurfaceView);
model = new Model();
score = 0;
(new Thread(model)).start();
thingFirstRun = false;
currentColor = Color.GREEN;
buttonPaint = new Paint();
buttonPaint.setColor(currentColor);
buttonPaint.setStrokeWidth(10);
buttonPaint.setStyle(Paint.Style.FILL);
firstRun = 0;
}
public boolean correctColor(Ball b) {
if (currentColor == b.color) {
return true;
}
return false;
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
return true;
}
#Override
protected void onPause() {
super.onPause();
ourSurfaceView.pause();
}
#Override
protected void onResume() {
super.onResume();
ourSurfaceView.resume();
}
#Override
public boolean onTouch(View v, MotionEvent event) {
try {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
float xEvent = event.getX(event.getPointerId(event.getActionIndex()));
float yEvent = event.getY(event.getPointerId(event.getActionIndex()));
if (yEvent > cHeight / 4 * 3) {
if (xEvent < cWidth / 2) {
buttonState |= BUTTON_LEFT;
}
if (xEvent > cWidth / 2) {
buttonState |= BUTTON_RIGHT;
}
}
break;
case MotionEvent.ACTION_POINTER_UP:
case MotionEvent.ACTION_UP:
try {
xEvent = event.getX(event.getPointerId(event.getActionIndex()));
yEvent = event.getY(event.getPointerId(event.getActionIndex()));
} catch (Exception e) {
xEvent = event.getX();
yEvent = event.getY();
}
if (yEvent > cHeight / 4 * 3) {
if (xEvent < cWidth / 2) {
buttonState &= ~BUTTON_LEFT;
}
if (xEvent > cWidth / 2) {
buttonState &= ~BUTTON_RIGHT;
}
}
break;
}
switch (buttonState) {
case BUTTON_LEFT:
currentColor = Color.RED;
break;
case BUTTON_RIGHT:
currentColor = Color.BLUE;
break;
case BUTTON_BOTH:
currentColor = Color.MAGENTA;
break;
case BUTTON_NONE:
currentColor = Color.GREEN;
break;
}
buttonPaint.setColor(currentColor);
Log.v("BUTTON STATE", buttonState + "");
Log.v("BUTTON COLOR", currentColor + "");
return true;
} catch (Exception e) {
}
return true;
}
public class Model extends Thread implements Runnable {
#Override
public void run() {
while (true) {
try {
Thread.sleep(1000 / 240);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (countBalls % nextBallSpeed == 0) {
int temp = (int) (Math.random() * 4);
if (temp == 0) {
balls.add(new Ball((int) (Math.random() * (cWidth - red.getWidth())), 0 - red.getHeight(), cHeight / 800, Color.RED, red));
} else if (temp == 1) {
balls.add(new Ball((int) (Math.random() * (cWidth - red.getWidth())), 0 - green.getHeight(), cHeight / 800, Color.GREEN, green));
} else if (temp == 2) {
balls.add(new Ball((int) (Math.random() * (cWidth - blue.getWidth())), 0 - blue.getHeight(), cHeight / 800, Color.BLUE, blue));
} else if (temp == 3) {
balls.add(new Ball((int) (Math.random() * (cWidth - purple.getWidth())), 0 - purple.getHeight(), cHeight / 800, Color.MAGENTA, purple));
}
totalBalls++;
}
if (totalBalls < 10) {
nextBallSpeed = 30;
} else if (totalBalls < 30) {
nextBallSpeed = 25;
} else if (totalBalls < 50) {
nextBallSpeed = 20;
} else if (totalBalls < 80) {
nextBallSpeed = 15;
} else if (totalBalls < 150) {
nextBallSpeed = 10;
} else {
nextBallSpeed = 5;
}
for (int i = 0; i < balls.size(); i++) {
int h = cHeight;
balls.get(i).yCord += balls.get(i).speed;
}
for (int i = 0; i < balls.size(); i++) {
try {
if (balls.get(i).yCord + green.getHeight() > ((double) cHeight * .75) && !balls.get(i).passedLineYet) {
balls.get(i).passedLineYet = true;
boolean b = correctColor(balls.get(i));
if (b) {
balls.remove(i);
score++;
}
if (!b) {
}
} else if (balls.get(i).yCord > cHeight) {
balls.remove(i);
score -= 5;
}
} catch (Exception ignored) {
}
}
countBalls++;
}
}
public boolean correctColor(Ball b) {
if (currentColor == b.color) {
return true;
}
return false;
}
}
public class MyBringBack extends SurfaceView implements Runnable {
SurfaceHolder ourHolder;
Thread ourThread = null;
boolean isRunning = false;
public MyBringBack(Context context) {
super(context);
ourHolder = getHolder();
}
public void pause() {
isRunning = false;
while (true) {
try {
ourThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
ourThread = null;
}
public void resume() {
isRunning = true;
ourThread = new Thread(this);
ourThread.start();
}
#Override
public void run() {
while (isRunning) {
try {
Thread.sleep(1000 / 240);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (!ourHolder.getSurface().isValid()) {
continue;
}
//NE
Bitmap tempCanvasBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas tempCanvas = new Canvas();
tempCanvas.setBitmap(tempCanvasBitmap);
//NEND
//Canvas canvas = ourHolder.lockCanvas();
tempCanvas.drawColor(Color.BLACK);
if (firstRun == 0) {
cHeight = tempCanvas.getHeight();
cWidth = tempCanvas.getWidth();
red = getResizedBitmap(red, tempCanvas.getWidth() / 12, tempCanvas.getWidth() / 12);
green = getResizedBitmap(green, tempCanvas.getWidth() / 12, tempCanvas.getWidth() / 12);
blue = getResizedBitmap(blue, tempCanvas.getWidth() / 12, tempCanvas.getWidth() / 12);
purple = getResizedBitmap(purple, tempCanvas.getWidth() / 12, tempCanvas.getWidth() / 12);
white = getResizedBitmap(white, cHeight / 4 * 3, cWidth / 20);
firstRun = 1;
totalBalls = 0;
}
//canvas.drawBitmap(white, );
tempCanvas.drawRect(0, tempCanvas.getHeight() * 3 / 4, tempCanvas.getWidth(), tempCanvas.getHeight(), buttonPaint);
for (int i = 0; i < balls.size(); i++) {
try {
tempCanvas.drawBitmap(balls.get(i).bitmap, balls.get(i).xCord, balls.get(i).yCord, null);
} catch (Exception ignored) {
}
}
Rect r = new Rect();
p.getTextBounds(score + "", 0, (score + "").length(), r);
tempCanvas.drawText(score + "", cWidth - r.width() - 20, r.height() + 20, p);
Canvas canvas = ourHolder.lockCanvas();
canvas.drawBitmap(tempCanvasBitmap, 0, 0, p);
ourHolder.unlockCanvasAndPost(canvas);
}
}
}
public static Bitmap getResizedBitmap(Bitmap bm, int newHeight, int newWidth) {
int width = bm.getWidth();
int height = bm.getHeight();
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// recreate the new Bitmap
Bitmap resizedBitmap = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, false);
return resizedBitmap;
}
}
I have created animations before, but without the use of a Runnable (all code in the onDraw() method). Perhaps the issue is with that.
Does anyone know why this stuttering might be occurring?
Use This:
Add the class member fields, replace your run(), and add primeCanvas() method.
boolean isPrimed = false;
Canvas canvas;
#Override
public void run() {
while(isRunning) {
if (!ourHolder.getSurface().isValid())
continue;
canvas = ourHolder.lockCanvas();
if(!isPrimed)
primeCanvas();
canvas.drawColor(Color.BLACK);
canvas.drawRect(0, canvas.getHeight() * 3 / 4, canvas.getWidth(), canvas.getHeight(), buttonPaint);
for(int i = 0; i < balls.size(); i ++)
canvas.drawBitmap(balls.get(i).bitmap, balls.get(i).xCord, balls.get(i).yCord, null);
Rect r = new Rect();
p.getTextBounds(score + "", 0, (score + "").length(), r);
canvas.drawText(score + "", cWidth - r.width() - 20, r.height() + 20, p);
ourHolder.unlockCanvasAndPost(canvas);
}
}
private void primeCanvas() {
cHeight = canvas.getHeight();
cWidth = canvas.getWidth();
red = getResizedBitmap(red, canvas.getWidth()/12, canvas.getWidth()/12);
green = getResizedBitmap(green, canvas.getWidth()/12, canvas.getWidth()/12);
blue = getResizedBitmap(blue, canvas.getWidth()/12, canvas.getWidth()/12);
purple = getResizedBitmap(purple, canvas.getWidth()/12, canvas.getWidth()/12);
white = getResizedBitmap(white, cHeight / 4 * 3, cWidth / 20);
firstRun = 1;
totalBalls = 0;
}