I have onDraw method where I want to cut a small piece of large bitmap. This will be a circle (position X and Y). Then I want to draw it by canvas.
I rewrite method from this answer for that reason, but always got grey circle instead circle piece of large Bitmap.
private Bitmap cutCircleBitmap() {
Bitmap output = Bitmap.createBitmap(2 * RADIUS, 2 * RADIUS, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Paint paint = new Paint();
final Rect rect = new Rect(bitmapX - RADIUS, bitmapY - RADIUS, 2 * RADIUS, 2 * RADIUS);
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawCircle(RADIUS, RADIUS, RADIUS, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}
Use Canvas.drawRoundRect() and BitmapShader to do it :
public class CropView extends View {
public CropView(Context context) {
this(context, null);
}
public CropView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CropView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setFilterBitmap(true);
mPaint.setAntiAlias(true);
mPaint.setDither(true);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(radius * 2, radius * 2);
}
private Paint mPaint;
private int radius = 100;
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.BLACK); // draw background help us see the result
Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.sample);
canvas.drawBitmap(cropCircleBitmap(srcBitmap, 200, 60), 0, 0, mPaint);
}
private Bitmap cropCircleBitmap(Bitmap srcBitmap, float left, float top) {
Bitmap dstBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(dstBitmap);
canvas.drawBitmap(srcBitmap, -left, -top, mPaint);
mPaint.setShader(new BitmapShader(dstBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
dstBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
canvas.setBitmap(dstBitmap);
canvas.drawRoundRect(new RectF(0, 0, getWidth(), getHeight()), radius, radius, mPaint);
return dstBitmap;
}
}
Related
I'm using a custom class (extension of ImageView) to have an XML round image view. The problem is when I call .setColorFilter() it doesn't adhere to the same circular/round bounds.
How can I make the color filter only affect the image and not the entire rectangle of the view?
Here is my custom class for reference:
public class RoundedCornerImageFilterView extends ImageFilterView {
public RoundedCornerImageFilterView(Context context) {
super(context);
}
public RoundedCornerImageFilterView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RoundedCornerImageFilterView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setImageFilter(int color) {
this.setColorFilter(color, PorterDuff.Mode.SRC_IN);
}
#Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (drawable == null) {
return;
}
if (getWidth() == 0 || getHeight() == 0) {
return;
}
Bitmap b = ((BitmapDrawable) drawable).getBitmap();
Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);
int w = getWidth();
int h = getHeight();
Bitmap roundedCornerBitmap = getRoundedCornerBitmap(bitmap, h, w);
canvas.drawBitmap(roundedCornerBitmap, 0, 0, null);
}
public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, int height, int width) {
Bitmap sbmp;
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
bitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0,
(width), (height));
final RectF rectF = new RectF(rect);
final float roundPx = 28;
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}
}
My xml implementation:
<MY_PATH.RoundedCornerImageFilterView
android:id="#+id/MY_IMAGE_VIEW"
android:layout_width="match_parent"
android:layout_height="150dp"
/>
Me trying to set the color filter:
MY_IMAGE_VIEW.setColorFilter(Color.parseColor(color), PorterDuff.Mode.OVERLAY)
Before the filter (looking like it's supposed to):
After setting the filter (you can see the square edges now):
Although the image (bitmap) has been given rounded corners, the canvas that it is written to has not. Since the color filter is being applied to the canvas, the tint spills out into the corners.
I suggest that you apply a rounded rectangle to a path then clip the path to the canvas. Something like this:
public class RoundedImageView extends AppCompatImageView {
private final Path mPath = new Path();
public RoundedImageView(Context context) {
super(context);
init();
}
public RoundedImageView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public RoundedImageView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
setColorFilter(Color.RED, PorterDuff.Mode.OVERLAY);
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mPath.reset();
mPath.addRoundRect(new RectF(0, 0, w, h), 128, 128, Path.Direction.CW);
}
#Override
public void draw(Canvas canvas) {
canvas.save();
canvas.clipPath(mPath);
super.draw(canvas);
canvas.restore();
}
}
I am using ImageView here, but the concept remains the same.
If you do this type of clipping, then rounding the bitmap becomes superfluous since it will also be clipped to the path.
I am in the process of creating an app which requires irregular shaped buttons. I know that i can use image buttons and have the irregular shapes set as the images but no matter what is the shape of the image, it always occupies a rectangular area on the screen.
Is it possible to have the button occupy the exact shape of the image alone?
Do i need to create a custom control or layout for doing this or is there any other valid approach?
If i need to create a custom layout then how do i ensure that the space enclosed by all the buttons that i have placed on the layout is always circular or elliptic?
You have to override onDraw method in any view you need to shape, see this code to round ImageView
public class RoundedImageView extends ImageView {
public RoundedImageView(Context context) {
super(context);
init(context, null, 0);
}
public RoundedImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public RoundedImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs, defStyle);
}
#SuppressLint("DrawAllocation") #Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (drawable == null) {
return;
}
if (getWidth() == 0 || getHeight() == 0) {
return;
}
Bitmap b = null;
int w = getWidth(), h = getHeight();
if (drawable instanceof BitmapDrawable) {
b = ((BitmapDrawable) drawable).getBitmap();
Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);
Bitmap roundBitmap = getCroppedBitmap(bitmap, w);
canvas.drawBitmap(roundBitmap, 0, 0, null);
}
if (drawable instanceof LayerDrawable) {
b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
((LayerDrawable) drawable).setBounds(0, 0, w, h);
((LayerDrawable) drawable).draw( new Canvas(b));
}
}
public static Bitmap getCroppedBitmap(Bitmap bmp, int radius) {
Bitmap sbmp;
if (bmp.getWidth() != radius || bmp.getHeight() != radius)
sbmp = Bitmap.createScaledBitmap(bmp, radius, radius, false);
else
sbmp = bmp;
Bitmap output = Bitmap.createBitmap(sbmp.getWidth(), sbmp.getHeight(),
Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final int color = 0xffa19774;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, sbmp.getWidth(), sbmp.getHeight());
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(Color.parseColor("#BAB399"));
canvas.drawCircle(sbmp.getWidth() / 2 + 0.7f,
sbmp.getHeight() / 2 + 0.7f, sbmp.getWidth() / 2 + 0.1f, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(sbmp, rect, rect, paint);
return output;
}
}
I'm trying to do the following you can see in the picture, but instead of that black square, I'd like to have a white square:
This is my code so far:
public class SmallWhiteThing extends View {
Context context;
Paint paint = new Paint();
// CONSTRUCTOR
public SmallWhiteThing(Context context) {
super(context);
setFocusable(true);
}
public SmallWhiteThing(Context context, AttributeSet attrs)
{
super(context, attrs);
this.context = context;
}
public SmallWhiteThing(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
this.context = context;
}
#Override
protected void onDraw(Canvas canvas) {
Paint paint = new Paint();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap b = Bitmap.createBitmap(120, 120, Bitmap.Config.ALPHA_8);
Canvas c = new Canvas(b);
c.drawColor(Color.WHITE);
paint.setStrokeWidth(0);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
paint.setTextSize(40);
paint.setAntiAlias(true);
c.drawText("Hello", 30, 30, paint);
canvas.drawBitmap(b, 140, 270, paint);
}
}
I tried as you can see this:
c.drawColor(Color.WHITE);
But without any luck.
Tips are really appreciated.
I'm trying somethin else, and I'm getting this:
Code:
Bitmap b = Bitmap.createBitmap(120, 120, Bitmap.Config.ALPHA_8);
Canvas c = new Canvas(b);
c.drawColor(Color.WHITE);
paint.setColor(Color.WHITE);
paint.setStrokeWidth(0f);
c.drawRect(0, 0, 150, 150, paint);
canvas.drawBitmap(b, 100, 100, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
paint.setTextSize(40);
paint.setAntiAlias(true);
paint.setColor(Color.WHITE);
canvas.drawText("Helloo", 100, 200, paint);
For whoever needs it. This is working:
Current usage (fills a imageview in screen):
//Params: Text, textSize
createBlabla("Text to show", 35);
public void createBlabla(String text, int fontSize){
int paddingRight = 10;
int paddingLeft = 5;
int paddingBottom = 5;
//Paint config
Paint paint = new Paint();
paint.setTextSize(fontSize);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
paint.setAntiAlias(true);
Bitmap largeWhiteBitmap = Bitmap.createBitmap((int) paint.measureText(text) + paddingRight, fontSize + paddingRight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(largeWhiteBitmap);
canvas.drawColor(Color.WHITE);
canvas.drawText(text, paddingLeft, fontSize, paint);
ImageView imv = (ImageView)MainActivity.this.findViewById(R.id.imageView1);
imv.setImageBitmap(largeWhiteBitmap);
}
Note that what you see in the screen is a whole relative layout with an imageView in the middle. This imageview gets a Bitmap as Image with the previous code.
The relativeLayout has green background.
I need to have square image view with curved corner. Everything seems okay and Preview of IntelliJ Idea shows it's working fine. However corners are not curve when I run it on real device.
My custom ImgeView:
public class SquareImageView extends ImageView {
private static final String TAG = "SquareImageView";
public SquareImageView(Context context) {
super(context);
}
public SquareImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SquareImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int h = this.getMeasuredHeight();
int w = this.getMeasuredWidth();
setMeasuredDimension(w, w);
}
#Override
protected void onDraw(Canvas canvas) {
Path clipPath = new Path();
float radius = 20.0f;
float padding = radius / 2;
int w = this.getWidth();
int h = this.getHeight();
clipPath.addRoundRect(new RectF(padding, padding, w - padding, h - padding), radius, radius, Path.Direction.CW);
canvas.clipPath(clipPath);
canvas.drawColor(Color.RED);
super.onDraw(canvas);
}
}
XML of layout:
<com.belldigital.widget.SquareImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/ivProfilePicture"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_alignParentTop="true"
android:adjustViewBounds="true"
android:scaleType="fitXY"
android:background="#color/Black"
android:contentDescription="#string/general_content_description"
android:src="#drawable/ic_default_logo"
android:layout_marginTop="#dimen/side_margin"/>
Screenshot of emulator:
Screenshot of real device:
How to make an ImageView with rounded corners?
public class MainActivity extends Activity {
ImageView imageView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.imageView1);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.photo1);
imageView.setImageBitmap(getRoundedCornerBitmap(bitmap, 10));
}
public static Bitmap getRoundedCornerBitmap(Bitmap bitmap, int pixels) {
Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap
.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
final RectF rectF = new RectF(rect);
final float roundPx = pixels;
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawRoundRect(rectF, roundPx, roundPx, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap, rect, rect, paint);
return output;
}
}
I am trying to have a custom EditText with a custom background which is not possible to draw using drawable XMLs
Here is what I have right now
public class EMEditText extends EditText {
private Bitmap framedBitmap;
public EMEditText(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
#Override
protected void onDraw(Canvas canvas) {
if(framedBitmap == null) {
createDrawable(getWidth(), getHeight());
}
Paint paint = new Paint();
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
// canvas.drawBitmap(framedBitmap, 0, 0, paint);
}
private void createDrawable(int width, int height) {
// create a new bitmap of given size
Bitmap start = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(start);
RectF outerRect = new RectF(0, 0, width, height);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.WHITE);
// paint.setStrokeWidth(1);
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
canvas.drawRoundRect(outerRect, height / 2, height / 2, paint);
framedBitmap = start;
}
}
What is probably wrong in your code is that you are creating your own Canvas object in createDrawable() method though onDraw() method gives you the right Canvas which you should use for your drawings.
So what you probably want is to change createDrawable(int width, int height) method to createDrawable(int width, int height, Canvas c). Your code should look like this then:
#Override
protected void onDraw(Canvas canvas) {
if(framedBitmap == null) {
createDrawable(getWidth(), getHeight(), canvas);
}
Paint paint = new Paint();
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
// canvas.drawBitmap(framedBitmap, 0, 0, paint);
}
private void createDrawable(int width, int height, Canvas c) {
// create a new bitmap of given size
Bitmap start = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
c.setBitmap(start);
RectF outerRect = new RectF(0, 0, width, height);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.WHITE);
// paint.setStrokeWidth(1);
// paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
c.drawRoundRect(outerRect, height / 2, height / 2, paint);
framedBitmap = start;
}
As you can see, I changed also the body of your createDrawable() method so that it uses Canvas from onDraw() and sets it's Bitmap to the one created by you.