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
Related
I have a box in my Android application, which needs to be transparent, while filling the rest of the frame outside the box with a specific colour.
How exactly could this be done in Canvas or OpenCV or any other means?
Here is an example showing a circle with transparent hole you can implement the same using Rectangle instead of circle.
public class OverlayWithHoleImageView extends ImageView {
private RectF circleRect;
private int radius;
public OverlayWithHoleImageView(Context context, AttributeSet attrs) {
super(context, attrs);
//In versions > 3.0 need to define layer Type
if (android.os.Build.VERSION.SDK_INT >= 11)
{
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
}
public void setCircle(RectF rect, int radius) {
this.circleRect = rect;
this.radius = radius;
//Redraw after defining circle
postInvalidate();
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(circleRect != null) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(getResources().getColor(android.R.color.black));
paint.setStyle(Paint.Style.FILL);
canvas.drawPaint(paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawRoundRect(circleRect, radius, radius, paint);
}
}
}
reference : Medium
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.
I wanted to show map inside a circle view where circle's outside area filled with a color. I referred a post Draw transparent circle filled outside. But now the problem is touch events. Map can be touched through outside circle view while I need map can be touched (zoom or move) only form inside Circle view (where the map is visible).
What I tried,
setEnabled=false
clickable=false
but still map is touched from outside circle view.
Is that possible to achieve that map can be touched from inside circle view.
public class RadiusOverlayView extends LinearLayout {
private Bitmap windowFrame;
public RadiusOverlayView(Context context) {
super(context);
}
public RadiusOverlayView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RadiusOverlayView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public RadiusOverlayView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (windowFrame == null) {
createWindowFrame(); // Lazy creation of the window frame, this is needed as we don't know the width & height of the screen until draw time
}
canvas.drawBitmap(windowFrame, 0, 0, null);
}
#Override
public boolean isEnabled() {
return false;
}
#Override
public boolean isClickable() {
return false;
}
protected void createWindowFrame() {
windowFrame = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); // Create a new image we will draw over the map
Canvas osCanvas = new Canvas(windowFrame); // Create a canvas to draw onto the new image
RectF outerRectangle = new RectF(0, 0, getWidth(), getHeight());
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); // Anti alias allows for smooth corners
paint.setColor(Color.CYAN); // This is the color of your activity background
osCanvas.drawRect(outerRectangle, paint);
//paint.setColor(Color.TRANSPARENT); // An obvious color to help debugging
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT)); // A out B http://en.wikipedia.org/wiki/File:Alpha_compositing.svg
float centerX = getWidth() / 2;
float centerY = getHeight() / 2;
float radius = Math.min(getWidth(), getHeight()) / 2 - 50;
osCanvas.drawCircle(centerX, centerY, radius, paint);
}
#Override
public boolean isInEditMode() {
return true;
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
windowFrame = null; // If the layout changes null our frame so it can be recreated with the new width and height
}
}
XML layout
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--loading map in container-->
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<mypackage.RadiusOverlayView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
/>
</RelativeLayout>
Result:
Any help will be appreciated.
You could set a View.OnTouchListener to your RadiusOverlayView and calculate whether the RadiusOverlayView needs to manage the touch events or not.
In this example I calculate this by testing if the RadiusOverlayView color touched is != 0 (maybe you want to improve this):
final RadiusOverlayView radiusOverlayView = (RadiusOverlayView) findViewById(R.id.radiusView);
radiusOverlayView.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(final View view, final MotionEvent motionEvent) {
view.setDrawingCacheEnabled(true);
Bitmap bmp = Bitmap.createBitmap(view.getDrawingCache());
view.setDrawingCacheEnabled(false);
return bmp.getPixel((int) motionEvent.getX(), (int) motionEvent.getY()) != 0;
}
});
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 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