I would like to make Canvas area transparent because I would like to add an Image behind it so that Canvas actions happen above the image.My code for the canvas is here
public class Panel extends SurfaceView implements SurfaceHolder.Callback {
private ViewThread mThread;
private ArrayList<Element> mElements = new ArrayList<Element>();
public Panel(Context context, AttributeSet attrs) {
super(context, attrs);
getHolder().addCallback(this);
mThread = new ViewThread(this);
}
public void doDraw(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
synchronized (mElements) {
for (Element element : mElements) {
element.doDraw(canvas);
}
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
// TODO Auto-generated method stub
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
if (!mThread.isAlive()) {
mThread = new ViewThread(this);
mThread.setRunning(true);
mThread.start();
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (mThread.isAlive()) {
mThread.setRunning(false);
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
synchronized (mElements) {
mElements.add(new Element(getResources(), (int) event.getX(), (int) event.getY()));
}
return super.onTouchEvent(event);
}
}
How to achieve it, any snippets on it will be very much helpful.Thanks
I got the output by this
public Panel(Context context, AttributeSet attrs) {
super(context, attrs);
this.setBackgroundColor(Color.TRANSPARENT);
this.setZOrderOnTop(true); //necessary
getHolder().setFormat(PixelFormat.TRANSPARENT);
getHolder().addCallback(this);
mThread = new ViewThread(this);
}
If you want to have a canvas with a transparent background you have to configure the background image for the
mCanvas = new Canvas(mBackgroundBitmap);
with Bitmap.Config.ARGB_4444
And use colors like 0x00000000 as transparent
Bitmap mBackgroundImage = Bitmap.createBitmap(Size, Size,
Bitmap.Config.ARGB_4444);
mCanvas = new Canvas(mBackgroundImage);
hope this helps! I have a pretty transparent canvas :D yay!
canvas.drawColor(Color.argb(0, 255, 255, 255));
first attribute is alpha and rest are RGB colors.
or
canvas.drawColor(Color.TRANSPARENT);
this make canvas tramsparent and it is work for me :)
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.OVERLAY);
In your onDraw() method add this:
canvas.drawColor(0x00AAAAAA);
This will make your canvas transparent and background View will be visible.
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR)
Works fine for me with
override fun onFinishInflate() {
super.onFinishInflate()
setZOrderOnTop(true)
holder.setFormat(PixelFormat.TRANSLUCENT)
isFocusable = true
holder.addCallback(surfaceCallback)
}
You can create a Bitmap of the same size as the canvas. Erase all the colors of the bitmap using Color.TRANSPARENCY and set it as a canvas bitmap:
Bitmap transparentBitmap = Bitmap.createBitmap(canvas.getWidth(), canvas.getHeight(),
Bitmap.Config.ARGB_8888);
transparentBitmap.eraseColor(Color.TRANSPARENT);
canvas.setBitmap(transparentBitmap);
well, I am not sure about drawing the transparent canvas, but in your case you can do a tweak, that draw the canvas using the background image iteself.
And then you can draw/ paint by finger on it.
Code example:
BitmapDrawable bd = (BitmapDrawable)<YOUR_ACTIVITY>.this.getResources().getDrawable(R.drawable.<DRAWBLE_ID>);
Bitmap b = bd.getBitmap();
mBitmap = Bitmap.createBitmap(b,0,0,100,100); // This line is required only if you wanna some change in the bitmap you created
mCanvas = new Canvas(mBitmap);
Set canvas's view background color to #00000000
Related
I have code that I need to improve.
Here's what's wrong: it's a little slow and choppy, meaning the lines aren't smooth and the drawing is a bit delayed.
public void touchStarted(Point point) {
if (null == drawingModePath) {
drawingModePath = new Path();
}
drawingModePath.moveTo(point.x, point.y);
}
public void touchMoved(Point point) {
drawingModePath.lineTo(point.x, point.y);
Bitmap bitmap = Bitmap.createBitmap((int) getWindowManager()
.getDefaultDisplay().getWidth(), (int) getWindowManager()
.getDefaultDisplay().getHeight(), Bitmap.Config.ARGB_8888);
canvas = new Canvas(bitmap);
mainDrawingView.setImageBitmap(bitmap);
// Path
paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.WHITE);
canvas.drawPath(drawingModePath, paint);
}
public void touchEnded(Point point) {
touchMoved(point);
}
In essence what this code does is drawing a path based on touchStarted, touchMoved, and touchEnded. If someone can help me optimize this, I'd be grateful. Perhaps if I don't recreate the bitmap each time touchMoved occurs? Not sure here... not sure... I use a UIBezierPath to perform this code on iOS and it's a bit faster (and smoother). Anyway, I come to you for help. Input appreciated.
you are recreating everything every move. that will affect the performance of drawing a lot. the event triggers every 8ms (or 16ms im not sure), imagine you are reinstantiating everything every 8ms? thats tough.
so this must be in the instantiation part
Bitmap bitmap = Bitmap.createBitmap((int) getWindowManager()
.getDefaultDisplay().getWidth(), (int) getWindowManager()
.getDefaultDisplay().getHeight(), Bitmap.Config.ARGB_8888);
canvas = new Canvas(bitmap);
mainDrawingView.setImageBitmap(bitmap);
paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setColor(Color.WHITE);
The touchMoved() should only record the new path and call the invalidate() to make the View redraw itself resulting in calling the draw method (onDraw()).
public void touchMoved(Point point) {
drawingModePath.lineTo(point.x, point.y);
invalidate();
}
and then implement onDraw() method to do the drawing
Heres how i do the drawing interface in one of my projects:
public class SignatureView extends View {
public SignatureView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
// instantiating my paint object
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
path = new Path();
}
#Override
protected void onSizeChanged(int xNew, int yNew, int xOld, int yOld)
{
// this is where i initialize my canvas, because in constructor, the view is not completely instantiated yet, so getting the height and width there will result in null exception.
bitmap = Bitmap.createBitmap(xNew, yNew, Bitmap.Config.ARGB_8888);
background_canvas = new Canvas(bitmap);
}
#Override
protected void onDraw(Canvas canvas)
{
// draw the new path to a buffer canvas
background_canvas.drawPath(path, paint);
// put the buffer in the real canvas
canvas.drawBitmap(bitmap, 0, 0, paint);
}
#Override
public boolean onTouchEvent(MotionEvent ev)
{
//this is like your move event, it just records the new path every move.
int action = ev.getActionMasked();
if ( action == MotionEvent.ACTION_DOWN )
{
path.moveTo(ev.getX(), ev.getY());
}
else if ( action == MotionEvent.ACTION_MOVE )
{
path.lineTo(ev.getX(), ev.getY());
// call invalidate() to make the view redraw itself, resulting in calling the onDraw() method.
invalidate();
}
else if ( action == MotionEvent.ACTION_UP )
{
onDone.method();
}
return true;
}
public void clear()
{
background_canvas.drawColor(Color.WHITE);
path.reset();
invalidate();
}
interface OnDone{
void method();
}
public void setOnDone(OnDone new_onDone)
{
onDone = new_onDone;
}
OnDone onDone;
private Paint paint;
private Bitmap bitmap;
private Canvas background_canvas;
private Path path;
public Bitmap getBitmap()
{
return bitmap;
}
}
I have a class called MaskView:
public class MaskView extends View
{
private Context context;
public Bitmap imageMask;
public int maskXPos;
public int maskYPos;
private Paint maskPaint;
public MaskView(Context context)
{
super(context);
this.context = context;
maskXPos = 0;
maskYPos = 0;
maskPaint = new Paint();
maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
// 90% alpha
this.setBackgroundColor(Color.argb(230, 0, 0, 0));
}
#Override public void onDraw(Canvas canvas)
{
super.onDraw(canvas);
canvas.save();
if(imageMask != null)
{
canvas.drawBitmap(imageMask, maskXPos, maskYPos, maskPaint);
}
canvas.restore();
}
}
At the moment, I got an Activity that fades in my mask using alpha fade in animation.
During the animation, I see my black mask overlay with the circular cutout bitmap mask.
However, after the mask view finish animating in, the bitmap mask instead of staying transparent, it gets rendered, and so I see a black circular bitmap instead of circular hole in my Mask view.
Is there a way to get the bitmap mask to remain a bitmap mask, and not get rendered?
I found the solution...after a few hours :D
Needed one line extra:
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
I don't know what the implications are for setting the layer type to software, but on my Galaxy Nexus, the view is rendering fine with animation. Doesn't seem to be any bad performance here.
Here's full code:
public class MaskView extends View
{
private Context context;
public Bitmap imageMask;
public int maskXPos;
public int maskYPos;
private Paint maskPaint;
public MaskView(Context context)
{
super(context);
setClickable(true);
this.context = context;
maskXPos = 0;
maskYPos = 0;
maskPaint = new Paint();
maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
// ------------------------------------------------------
// This line is needed for the mask to work
// ------------------------------------------------------
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
#Override public void onDraw(Canvas canvas)
{
super.onDraw(canvas);
canvas.drawColor(Color.argb(230, 0, 0, 0));
if(imageMask != null)
{
canvas.drawBitmap(imageMask, maskXPos, maskYPos, maskPaint);
}
}
}
I discovered the solution after trying out this tutorial:
https://medium.com/#rgomez/android-how-to-draw-an-overlay-with-a-transparent-hole-471af6cf3953#.7uxgln7n4
Hope it helps others.
In my android application, I want to draw two images - img1 and img2. At first, I will draw img2 on Canvas. After that i will draw img1 on Canvas which will overlap img2. Img1 contains transparent part. The problem is that, transparent part of img1 is shown in Black color, but I want img2 to be visible through the transparent part of img1. I am not able to do that.
Please help me to solve this problem.
Thank you.
Code:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Bitmap b = BitmapFactory.decodeResource(getResources(),
R.drawable.white_bg); //img2
canvas.drawBitmap(b, 0, 0, null);
canvas.save();
canvas.drawBitmap(realImage, 0, 0, null); //img1
}
Try bitmap.setHasAlpha(true) after you load the bitmap.
After some modification in my code, i got my output. Here is a code which i used.
public class FrameView extends View{
Bitmap bitmap = null;
public FrameView(Context context) {
super(context);
this.context = context;
}
public FrameView(Context context, AttributeSet attrs) {
super(context, attrs);
bitmap = Bitmap.createBitmap(this.screenWidth, this.screenHeight,
Bitmap.Config.ARGB_8888);
}
public FrameView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (isTouchGestures) {
invalidate();
mImgDrawables.get(0).draw(canvas);
canvas.drawBitmap(bitmap, 0, 0, null);
}
}
//this function is invoked from my activity which is using this view
public void setFrame(int frame) {
bitmap = BitmapFactory.decodeStream(getResources().openRawResource(
frame));
bitmap = Bitmap.createScaledBitmap(bitmap, this.screenWidth,
this.screenHeight, true);
}
}
Use a Paint object to set the Alpha channel for the transparency.
Paint alphaChannel = new Paint()
alphaChannel.setAlpha(100) // set alpha to 100 for complete transparent
canvas.drawBitmap(b, 0, 0, alphaChannel);
I am trying to use a mask to hide a part of a picture, depending on where the user touch the screen. Todo so, I followed a code sample provided by Cyril Mottier. What I did until now actually work : while clicking a part of my ImageView, all what I got above is hidden. The problem is it is hidden by black color, preventing what there is behind my ImageView from displaying.
Could anybody please provide me tips or tell me what I do wrong?
Here a screen of what I got at the time
Here is the main activity :
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
final MaskedImageView iv = (MaskedImageView) findViewById(R.id.picture_on);
iv.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.d(MainActivity.class.getSimpleName(), "on touch called");
Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
Point point = new Point();
display.getSize(point);
Rect bounds = iv.getDrawable().getBounds();
bounds.top = point.y
- (int) event.getY()
;
iv.setMask(bounds);
iv.invalidate();
return false;
}
});
}
And here the custom ImageView :
public class MaskedImageView extends ImageView {
private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private Bitmap mMask;
public MaskedImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// Prepares the paint that will be used to draw our icon mask. Using
// PorterDuff.Mode.DST_IN means the image that will be drawn will
// mask the already drawn image.
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
}
public MaskedImageView(Context context, AttributeSet attrs) {
super(context, attrs);
// Prepares the paint that will be used to draw our icon mask. Using
// PorterDuff.Mode.DST_IN means the image that will be drawn will
// mask the already drawn image.
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
}
public MaskedImageView(Context context) {
super(context);
// Prepares the paint that will be used to draw our icon mask. Using
// PorterDuff.Mode.DST_IN means the image that will be drawn will
// mask the already drawn image.
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
}
public void setMask(Rect rect) {
mMask = Bitmap.createBitmap(Math.abs(rect.right - rect.left),
Math.abs(rect.bottom - rect.top), Bitmap.Config.ALPHA_8);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.save();
BitmapDrawable bd = (BitmapDrawable) getDrawable();
canvas.drawBitmap(bd.getBitmap(), 0, 0, null);
if (mMask != null) {
canvas.drawBitmap(mMask, 0, 0, mPaint);
}
canvas.restore();
}
}
Thank you for your answers
I want to zoom in the canvas board partially as the letter tray button will be fixed and the board will be zoom,
The partially coding is as mention below and the screen is fully draw in canvas which can be viewed as mention at the bottom.. please help and thanks for you concern.
public class BoardView extends SurfaceView implements SurfaceHolder.Callback
{
class DrawingThread extends Thread implements OnTouchListener {
public DrawingThread(SurfaceHolder holder, Handler handler) {
mSurfaceHolder = holder;
public void setRunning(boolean b) {
mRun = b;
}
#Override
public void run() {
while (mRun) {
Canvas c = null;
try {
c = mSurfaceHolder.lockCanvas(null);
synchronized (mSurfaceHolder) {
// System.gc();
// c.scale(canvasScaleX, canvasScaleY);
// c.save();
// c.translate(canvasTranslateX, canvasTranslateY);
doDraw(c);
}
updateGame();
} finally {
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
private void doDraw(Canvas canvas) {
if (ge == null)
return;
Rect bRect = new Rect(0, 0, dims.getTotalWidth(),
dims.getScoreHeight() + dims.getBoardheight());
Drawable drawable = getResources().getDrawable(R.drawable.board);
drawable.setBounds(bRect);
drawable.draw(canvas);
Rect tRect = new Rect(0, dims.getScoreHeight()
+ dims.getBoardheight(), dims.getTotalWidth(),
dims.getTotalHeight());
canvas.drawRect(tRect, fillTrayPaint);
int topHeight = dims.getScoreHeight() + dims.getBoardheight();
int bottom = (dims.getTotalHeight() + 5)
- (dims.getTotalWidth() / Tray.TRAY_SIZE);
Rect rect = new Rect(0, topHeight, dims.getTotalWidth(), bottom - 7);
Drawable drawableTray = getResources()
.getDrawable(R.drawable.strip);
drawableTray.setBounds(rect);
drawableTray.draw(canvas);
drawTray(canvas);
drawBoard(canvas);
// drawScore(canvas);
drawMovingTile(canvas);
}
public BoardView(Context context, AttributeSet attrs) {
super(context, attrs);
SurfaceHolder holder = getHolder();
holder.addCallback(this);
// endTurn=(ImageButton)findViewById(R.id.endturn_button_horizontal);
thread = new DrawingThread(holder, handler);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
final float scale1 = getResources().getDisplayMetrics().density;
defaultFontSize = (int) (MIN_FONT_DIPS * scale1 + 0.5f);
thread.setDefaultFontSize(defaultFontSize);
dimensions = calculateDimensions(getWidth(), getHeight());
thread.setDimensions(dimensions);
thread.setRunning(true);
// if (thread != null && !thread.isAlive())
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
thread.setRunning(false);
while (retry) {
try {
thread.join();
retry = false;
} catch (InterruptedException e) {
}
}
}
We can simply do it by canvas.clipRect(rectangle), at this point what we are suppose to do is save the canvas canvas.save() and after canvas.clipRect(rectangle) call canvas.restore().
Consider trying something like this:
Create a bitmap from the canvas you wish to zoom into.
When rendering that bitmap to the screen, you can change the scr rect
(which will "zoom" into the bitmap).
I hope this helps.
I think you need to render your game board into offscreen bitmap and then draw this bitmap at canvas using Matrix, so you will be able to change its position and scale.
I believe the easiest way to do this would be to use Canvas#scale(float scale, int pivotx, int pivoty) method. It essentially grows the entire canvas uniformally which really is what zooming is from a 2D perspective.
canvas.scale(2.0f, getWidth()/2, getHeight()/2) will scale the image by 2 from the center. canvas.scale(1.0f, getWidth()/2, getHeight()/2) will shrink it back.
I have never tried this in this context, so if you decide to try it report back the results please! I'd like to know for future reference.