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
Related
I have a custom view that is a circle. Here is the code for my CircleView:
public class CircleView extends View {
private static final int START_ANGLE_POINT = 90;
private final Paint paint;
private final RectF rect;
private float angle;
public CircleView(Context context, AttributeSet attrs) {
super(context, attrs);
final int strokeWidth = 40;
paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(strokeWidth);
//Circle color
paint.setColor(Color.RED);
rect = new RectF(strokeWidth, strokeWidth, 1000 + strokeWidth, 1000 + strokeWidth);
//Initial angle is zero
angle = 0;
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawArc(rect, START_ANGLE_POINT, angle, false, paint);
}
public float getAngle() {
return angle;
}
public void setAngle(float angle) {
this.angle = angle;
} }
and here is how I declare it in the xml layout of an activity:
<com.my_package.ui.recording.CircleView
android:id="#+id/circleView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
All standard stuff. This is how my custom image looks like
Now, I want to place an imageView in the centre on the circleView? Does any one know how can I achieve that?
This is ideally what I would like to end up with:
Thank you in advance.
If you aren't set on using an ImageView and really just want to draw the bitmap in the center then have a look at canvas' drawBitmap method. This will allow you to draw it however/wherever you want.
I have an ImageView with the background set to a drawable.circle. The circle has thick stroke width. I would like to animate that stroke width to shrink from what it is set at to 1dp over a specified duration. If this can't be done with a drawable, I also have a customView that path's a circle with a paint.style set to Stroke. I would like to apply that animation to either the drawable.circle or the customView.
CustomCirleView:
public class CustomCircle extends View {
private Path path;
private Paint paint;
float customStrokeWidth = 80;
public float getCustomStrokeWidth() {
return customStrokeWidth;
}
public void setCustomStrokeWidth(float customStrokeWidth) {
this.customStrokeWidth = customStrokeWidth;
}
public CustomCircle(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
path = new Path();
paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
paint.setColor(Color.parseColor("#FC6C2B"));
paint.setStrokeWidth(customStrokeWidth);
path.addCircle(getMeasuredWidth()/2, getMeasuredHeight()/2, (getMeasuredWidth()/2)-customStrokeWidth, Path.Direction.CW);
canvas.drawPath(path, paint);
}
}
Thanks
I would do something like this and then just draw the circle in your onDraw method. And please never do all the paint initialization inside the onDraw method as it always takes some time and the onDraw method should do as less as possible
private void init() {
animator = ObjectAnimator.ofFloat(this, "animationProgress", startVal, endVal);
animator.setStartDelay(ANIMATION_START_DELAY);
animator.setDuration(ANIMATION_DURATION);
animator.setInterpolator(new FastOutSlowInInterpolator());
path = new Path();
paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setAntiAlias(true);
paint.setColor(Color.parseColor("#FC6C2B"));
paint.setStrokeWidth(customStrokeWidth);
path.addCircle(getMeasuredWidth()/2, getMeasuredHeight()/2, (getMeasuredWidth()/2)-customStrokeWidth, Path.Direction.CW);
}
/**
* Is called by the {#link #animator} after an animation update
*/
protected void setAnimationProgress(float strokeWidth) {
this.strokeWidth = strokeWidth;
postInvalidateOnAnimation();
}
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.
Requirement is to:
Req 1 : Fetch images from url
R2: save them in cache
R3: make ImageView rounded not the image
So for R1 & R2 I found a library:
http://loopj.com/android-smart-image-view/
For R3 I've done a lot of R&D , & everything I found converts the image not the ImageView. This is what I've searched:
Mask ImageView with round corner background
How to make an ImageView with rounded corners?
https://github.com/vinc3m1/RoundedImageView
https://github.com/lopspower/CircularImageView
I know it's possible to use the ImageView bitmap & get the image rounded but with the specific library I want to use that isn't possible(maybe possible with very complex threading).
So please help me to get the ImageView rounded not the image.
so this is the minimalistic version:
class RoundImageView extends ImageView {
private static final int RADIUS = 32;
private Paint mPaint;
private Paint mSrcIn;
private RectF mRect;
public RoundImageView(Context context) {
super(context);
// setBackgroundColor(0xffffffff);
mSrcIn = new Paint();
mSrcIn.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mRect = new RectF();
}
#Override
public void onDraw(Canvas canvas) {
Drawable dr = getDrawable();
if (dr != null) {
mRect.set(dr.getBounds());
getImageMatrix().mapRect(mRect);
mRect.offset(getPaddingLeft(), getPaddingTop());
int rtc = canvas.saveLayer(mRect, null, Canvas.ALL_SAVE_FLAG);
// draw DST
canvas.drawRoundRect(mRect, RADIUS, RADIUS, mPaint);
canvas.saveLayer(mRect, mSrcIn, Canvas.ALL_SAVE_FLAG);
// draw SRC
super.onDraw(canvas);
canvas.restoreToCount(rtc);
}
}
}
or use even shorter one when hardware acceleration is not used and you can use Canvas.clipPath:
class RoundImageViewClipped extends ImageView {
private static final int RADIUS = 32;
private RectF mRect;
private Path mClip;
public RoundImageViewClipped(Context context) {
super(context);
// setBackgroundColor(0xffffffff);
mRect = new RectF();
mClip = new Path();
}
#Override
public void onDraw(Canvas canvas) {
Drawable dr = getDrawable();
if (dr != null) {
mRect.set(dr.getBounds());
getImageMatrix().mapRect(mRect);
mRect.offset(getPaddingLeft(), getPaddingTop());
mClip.reset();
mClip.addRoundRect(mRect, RADIUS, RADIUS, Direction.CCW);
canvas.clipPath(mClip);
super.onDraw(canvas);
}
}
}
I'm pretty sure you can't "make the ImageView round," since all Views are actually rectangular, so what you're going to have to do is fake it.
Use a method like this to cut a circle from the image:
public Bitmap getRoundedBitmap(Bitmap scaleBitmapImage) {
int targetRadius = scaleBitmapImage.getWidth();
if(targetRadius > scaleBitmapImage.getHeight()) targetRadius = scaleBitmapImage.getHeight();
Bitmap targetBitmap = Bitmap.createBitmap(targetRadius, targetRadius, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(targetBitmap);
Path path = new Path();
path.addCircle(((float) scaleBitmapImage.getWidth() - 1) / 2, ((float) scaleBitmapImage.getHeight() - 1) / 2, (Math.min(((float) scaleBitmapImage.getWidth()), ((float) scaleBitmapImage.getHeight())) / 2), Path.Direction.CCW);
canvas.clipPath(path);
Bitmap sourceBitmap = scaleBitmapImage;
canvas.drawBitmap(sourceBitmap, new Rect(0, 0, sourceBitmap.getWidth(), sourceBitmap.getHeight()), new Rect(0, 0, scaleBitmapImage.getWidth(), scaleBitmapImage.getHeight()), null);
return targetBitmap;
}
Since the clipped part is transparent, it will appear as if the actual View is a circle. Also make sure that the bounds of the View are squared (or that adjustViewBounds="true") else you may get visual distortions in terms of width or height.
Pretty sure that's as close to a "rounded View" as you can actually get.
How about the solution give by Romain Guy to use a custom Drawable. You're ImageView will not be round and your source image will be untouched.
class StreamDrawable extends Drawable {
private final float mCornerRadius;
private final RectF mRect = new RectF();
private final BitmapShader mBitmapShader;
private final Paint mPaint;
private final int mMargin;
StreamDrawable(Bitmap bitmap, float cornerRadius, int margin) {
mCornerRadius = cornerRadius;
mBitmapShader = new BitmapShader(bitmap,
Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setShader(mBitmapShader);
mMargin = margin;
}
#Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mRect.set(mMargin, mMargin, bounds.width() - mMargin, bounds.height() - mMargin);
}
#Override
public void draw(Canvas canvas) {
canvas.drawRoundRect(mRect, mCornerRadius, mCornerRadius, mPaint);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
#Override
public void setAlpha(int alpha) {
mPaint.setAlpha(alpha);
}
#Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
}
You can add rounded corners in a android view with the GradientDrawable.
So ,
GradientDrawable gd = new GradientDrawable();
gd.setColor(Color.TRANSPARENT);
gd.setCornerRadius(15f);
gd.setStroke(1f,Color.BLACK);
yourImageView.setBackground(gd);
SmartImageView extends from ImageView .. so you just have to extend from SmartImageView
Here is a working solution (based on pskink code & smartImageView lib )
Create a new Class
public class RoundedCornersSmartImageView extends SmartImageView{
private int RADIUS = 0;
private RectF mRect;
private Path mClip;
public RoundedCornersSmartImageView(Context context) {
super(context);
init();
}
public RoundedCornersSmartImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public RoundedCornersSmartImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
#Override
public void onDraw(Canvas canvas) {
Drawable dr = getDrawable();
if (dr != null) {
mRect.set(dr.getBounds());
getImageMatrix().mapRect(mRect);
mRect.offset(getPaddingLeft(), getPaddingTop());
mClip.reset();
mClip.addRoundRect(mRect, RADIUS, RADIUS, Path.Direction.CCW);
canvas.clipPath(mClip);
super.onDraw(canvas);
}
}
public void setRadius(int radius){
this.RADIUS = radius;
}
private void init(){
mRect = new RectF();
mClip = new Path();
}
}
USAGE
in your layout file your SmartimageView should look like this
<your.package.path.RoundedCornersSmartImageView
android:id="#+id/list_image"
android:layout_width="60dip"
android:layout_height="60dip"
android:src="#drawable/profile_anonyme_thumb"/>
..and init the view in your code this way
RoundedCornersSmartImageView thumb_image=(RoundedCornersSmartImageView) findViewById(R.id.list_image);
thumb_image.setRadius(4);
//SmartImageView methode
thumb_image.setImageUrl(bla.MY_THUMB_URL));
Edit your radius for a round image ..
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