I'm having trouble with a library I got from the GitHub.
The Library leads to round an image. The rounding part works very well, but the image resizing doesn't do as great as the rest. It differs from image to image and I'd like to make this work to resize any image to the size of my View on android. For example, if I call this with android:layout_width="100dp" , I'd like to the image to be resized that much.
Thank you very much for your time.
This is the library:
package com.roundimage.support;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.provider.MediaStore;
import android.util.AttributeSet;
import android.widget.ImageView;
public class CircularImageView extends ImageView {
private int borderWidth = 3;
private int viewWidth;
private int viewHeight;
private Bitmap image;
private Paint paint;
private Paint paintBorder;
private BitmapShader shader;
public CircularImageView(Context context) {
super(context);
setup();
}
public CircularImageView(Context context, AttributeSet attrs) {
super(context, attrs);
setup();
}
public CircularImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setup();
}
private void setup()
{
// init paint
paint = new Paint();
paint.setAntiAlias(true);
paintBorder = new Paint();
setBorderColor(Color.WHITE);
paintBorder.setAntiAlias(true);
}
public void setBorderWidth(int borderWidth)
{
this.borderWidth = borderWidth;
this.invalidate();
}
public void setBorderColor(int borderColor)
{
if(paintBorder != null)
paintBorder.setColor(borderColor);
this.invalidate();
}
private void loadBitmap()
{
BitmapDrawable bitmapDrawable = (BitmapDrawable) this.getDrawable();
if(bitmapDrawable != null)
image = bitmapDrawable.getBitmap();
}
#SuppressLint("DrawAllocation")
#Override
public void onDraw(Canvas canvas)
{
//load the bitmap
loadBitmap();
// init shader
if(image !=null)
{
shader = new BitmapShader(Bitmap.createScaledBitmap(image, canvas.getWidth(), canvas.getHeight(), false), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
paint.setShader(shader);
int circleCenter = viewWidth / 2;
// circleCenter is the x or y of the view's center
// radius is the radius in pixels of the cirle to be drawn
// paint contains the shader that will texture the shape
canvas.drawCircle(circleCenter + borderWidth, circleCenter + borderWidth, circleCenter + borderWidth, paintBorder);
canvas.drawCircle(circleCenter + borderWidth, circleCenter + borderWidth, circleCenter, paint);
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec, widthMeasureSpec);
viewWidth = width - (borderWidth *2);
viewHeight = height - (borderWidth*2);
setMeasuredDimension(width, height);
}
private int measureWidth(int measureSpec)
{
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else {
// Measure the text
result = viewWidth;
}
return result;
}
private int measureHeight(int measureSpecHeight, int measureSpecWidth) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpecHeight);
int specSize = MeasureSpec.getSize(measureSpecHeight);
if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else {
// Measure the text (beware: ascent is a negative number)
result = viewHeight;
}
return result;
}
}
This is how I call it on XML:
<com.roundimage.support.CircularImageView
android:id="#+id/item_pic"
android:layout_width="70dp"
android:layout_height="70dp"
android:src="#drawable/example_id" />
How this stays like:
And how the image really is:
And for different images with different sizes, for different devices with diferent resolution, the image appear in a different way..
Ok, your code should work well, But it put image in image view without resize to fit in viewer, because you had these results.
Try adding this in layout:
android:scaleType="fitCenter"
Read mode about this in this tutorial: http://www.peachpit.com/articles/article.aspx?p=1846580&seqNum=2
Try using scaleType in the xml to be fitXY
android:ScaleType = "fitXY"
Or programmatically
imageview.setScaleType (ScaleType.FITXY)
If it doesn't work try another scaletype.
EDIT: See this post here.
#youssefhassan link points it out correctly:
You have to resize your image bitmap to desired size:
`
private void loadBitmap()
{
BitmapDrawable bitmapDrawable = (BitmapDrawable) this.getDrawable();
if(bitmapDrawable != null) {
int size = getPixelsFromDp(70);
Bitmap imageRealSize = bitmapDrawable.getBitmap();
image = Bitmap.createScaledBitmap(imageRealSize, size, size, true);
}
}
`
Where 'getPixelsFromDp' is a function to get dp's you added on xml in pixels (you can find load of pages to get its implementation)
Related
I am trying to get a shadow effect on a text in canvas. The problem I am facing is the shadow that is being created is very dim and showing weird shapes. I am trying to overcome these problems. I am open to suggestions where I can create Images and save to disk without using canvas.
Code:
class MainActivity : AppCompatActivity() {
private lateinit var canvasIV: ImageView
private lateinit var imgBitmap: Bitmap
private lateinit var canvas: Canvas
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
canvasIV = findViewById(R.id.canvasIV)
val displayMetrics = DisplayMetrics()
windowManager.defaultDisplay.getMetrics(displayMetrics)
val height = displayMetrics.heightPixels
val width = displayMetrics.widthPixels
imgBitmap =
Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
canvas = Canvas(imgBitmap)
canvas.drawBitmap(imgBitmap, 0f, 0f, null)
val shadowPaint = Paint()
shadowPaint.isAntiAlias = true
shadowPaint.color = Color.parseColor("#F897B9")
val tSize = 80.0f
shadowPaint.textSize = tSize
shadowPaint.strokeWidth = 20.0f
shadowPaint.style = Paint.Style.FILL
shadowPaint.alpha = 0x100
//shadowPaint.setShadowLayer(20.0f, 5f, 5f, Color.parseColor("#F897B9"))
shadowPaint.setShadowLayer(tSize, 5f, 5f, Color.GREEN)
canvas.drawText("Sayok Dey Majumder", 50f, 200f, shadowPaint)
//canvasIV.setLayerType(View.LAYER_TYPE_SOFTWARE, null)
canvasIV.setImageDrawable(BitmapDrawable(resources, imgBitmap))
}
fun convertToPixels(context: Context, nDP: Int): Float {
val conversionScale: Float = context.getResources().getDisplayMetrics().density
return (nDP * conversionScale + 0.5f)
}
fun getTextWidth(text: String, paint: Paint): Int {
val bounds = Rect()
paint.getTextBounds(text, 0, text.length, bounds)
return bounds.left + bounds.width()
}
fun getTextHeight(text: String, paint: Paint): Int {
val bounds = Rect()
paint.getTextBounds(text, 0, text.length, bounds)
return bounds.bottom + bounds.height()
}
}
What I am getting is:
What I am trying to achieve:
The Bitmap with config Bitmap.Config.ARGB_8888 don't support Alpha channel.
Use this instead:
imgBitmap =Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
also the following line don't do anything:
canvas.drawBitmap(imgBitmap, 0f, 0f, null)
Better Solution:
Also, shadow layer has limited capacity and I recommend to draw the shadow manually.
val shadowPaint = Paint(defaultPaint)
// ... here customize shadow paint
canvas.drawText("your text", x, y, defaultPaint);
canvas.drawText("your text", x, y, shadowPaint);
Also, you could extend TextView and override the onDraw function. Here I create customView that add shadow. you could adjust it.
import android.content.Context;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.text.TextPaint;
import android.util.AttributeSet;
public class ShadowTextView extends androidx.appcompat.widget.AppCompatTextView {
private TextPaint normalPaint;
private int color;
TextPaint shadowPaint;
public ShadowTextView(Context context) {
super(context);
init();
}
public ShadowTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ShadowTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
normalPaint=new TextPaint(getPaint());
shadowPaint=new TextPaint(getPaint());
shadowPaint.setStyle(Paint.Style.FILL_AND_STROKE);
shadowPaint.setStrokeCap(Paint.Cap.ROUND);
shadowPaint.setStrokeJoin(Paint.Join.ROUND);
shadowPaint.setStrokeWidth(30);
shadowPaint.setMaskFilter(new BlurMaskFilter( 10, BlurMaskFilter.Blur.NORMAL));
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
this.savePaint();
getPaint().set(shadowPaint);
setTextColor(Color.argb(100,255,0,0));
super.onDraw(canvas);
this.restorePaint();
}
public void savePaint() {
normalPaint.set(getPaint());
color=getCurrentTextColor();
}
public void restorePaint() {
getPaint().set(normalPaint);
setTextColor(color);
}
}
I want to resize my widgets in the layout by ratio, but I can't.
At time I resizing ViewPager or com.daimajia.slider.SliderLayout by ratio and Measure, but childs of ViewPager or com.daimajia.slider.SliderLayout lose their height.
I create a custom widget for this work.
Code example for ImageView:
package ...;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
public class SquareImageView extends android.support.v7.widget.AppCompatImageView {
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
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
setMeasuredDimension(width, width);
}
#Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (drawable == null) {
return;
}
int w = 0, h = 0;
if (getWidth() == 0 || getHeight() == 0) {
if (getMeasuredWidth() == 0 || getMeasuredHeight() == 0) {
return;
}
else {
w = getMeasuredWidth();
h = getMeasuredHeight();
}
return;
}
else {
w = getWidth();
h = getHeight();
}
Bitmap b = ((BitmapDrawable) drawable).getBitmap();
Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);
final Rect rect = new Rect(0, 0, w, w);
Bitmap roundBitmap = getCroppedBitmap(bitmap, w);
canvas.drawBitmap(roundBitmap, rect, rect, null);
}
public static Bitmap getCroppedBitmap(Bitmap bmp, int radius) {
Bitmap sbmp;
if (bmp.getWidth() != radius || bmp.getHeight() != radius) {
float smallest = Math.min(bmp.getWidth(), bmp.getHeight());
float factor = smallest / radius;
sbmp = Bitmap.createScaledBitmap(bmp,
(int) (bmp.getWidth() / factor),
(int) (bmp.getHeight() / factor), false);
} else {
sbmp = bmp;
}
Bitmap output = Bitmap.createBitmap(radius, radius, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final String color = "#BAB399";
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, radius, radius);
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(Color.parseColor(color));
canvas.drawCircle(radius / 2 + 0.7f, radius / 2 + 0.7f,
radius / 2 + 0.1f, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(sbmp, rect, rect, paint);
return output;
}
}
My Codes not worked for this widgets:
ViewPager, com.daimajia.slider.SliderLayout.
//--------------------------------------------------------
//--------------------------------------------------------
//------------> ***** Answer - Go Down ***** <------------
//--------------------------------------------------------
//------------> ***** Answer - Go Down ***** <------------
//--------------------------------------------------------
//------------> ***** Answer - Go Down ***** <------------
//--------------------------------------------------------
//--------------------------------------------------------
I found the answer my question.
public class ViewPagerByRatioSize extends ViewPager {
Integer layout_ratioX = 0, layout_ratioY = 0;
public ViewPagerByRatioSize(Context context) {
super(context);
}
public ViewPagerByRatioSize(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ViewPagerByRatioSize, 0, 0);
try {
layout_ratioX = typedArray.getInteger(R.styleable.ViewPagerByRatioSize_layout_ratioXViewPager, 0);
layout_ratioY = typedArray.getInteger(R.styleable.ViewPagerByRatioSize_layout_ratioYViewPager, 0);
}
finally {
typedArray.recycle();
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int width = getMeasuredWidth();
super.setMeasuredDimension(width, width / layout_ratioX * layout_ratioY);
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(width / layout_ratioX * layout_ratioY, MeasureSpec.AT_MOST));
}
}
This code worked for me, but I change codes because my application can't get values from xml codes.
This new codes:
public class ViewPagerByRatioSize extends ViewPager {
public ViewPagerByRatioSize(Context context) {
super(context);
}
public ViewPagerByRatioSize(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
int width = getMeasuredWidth();
super.setMeasuredDimension(width, width / 3 * 2);
super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(width / 3 * 2, MeasureSpec.AT_MOST));
}
}
Successful for ever :)
I am using Handler post delay to call onDraw each time I want to draw new rectangle, but for every new rectangle erase the previous one ..
How can I make the canvas keep the content of all rectangles:
My code:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.CountDownTimer;
import android.os.Handler;
import android.util.Log;
import com.adhamenaya.microchart.model.ChartData;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;
/**
* Created by Admin on 18/12/2016.
*/
public class ColumnChart extends Chart implements Runnable {
private int mSpace = 0;
private int mColumnSize = 0;
private int mColumnsCount = 0;
private int mCurrentStart = 0;
private float mHeightUnit = 0;
private int mHeightDiff;
int columnIndex = 0;
private Handler mHandler;
private Canvas mCanvas;
private Iterator mDataIterator;
static final long FRAME_TIME = 100;
private ArrayList<Rect> rectangles = new ArrayList<Rect>();
public ColumnChart(Context context) {
super(context);
mHandler = new Handler();
}
#Override
protected void prepare() {
if (mChartData != null && mChartData.getSingleData().size() > 0) {
mColumnsCount = mChartData.getSingleData().size();
// Column size, 1 is added to reserve a space at the end of the chart
mColumnSize = (int) ((mWidth / (mColumnsCount)) * 0.9);
// Calculate the space between the bars
mSpace = (mWidth / mColumnsCount) - mColumnSize;
// Calculate height unit
// Calculate 80% of the total height
float height08 = mHeight * 0.8f;
mHeightUnit = height08 / mChartData.getMax();
// Draw bars
mDataIterator = mChartData.getSingleData().entrySet().iterator();
mMainPaint = getRectPaint();
// Create rect objects
while (mDataIterator.hasNext()) {
Map.Entry entry = (Map.Entry) mDataIterator.next();
int columnHeight = (int) ((int) entry.getValue() * mHeightUnit);
String key = (String) entry.getKey();
// Shift the
mCurrentStart += mSpace;
// Calculate the difference between the total height and the height of the column
mHeightDiff = mHeight - columnHeight;
mCurrentStart += mColumnSize;
mDataIterator.remove();
Rect rect = new Rect(mCurrentStart, mHeightDiff,
mCurrentStart + mColumnSize, mHeight);
rectangles.add(rect);
}
}
}
#Override
protected void paintChart(Canvas canvas) {
}
#Override
public void setData(ChartData data) {
this.mChartData = data;
prepare();
mHandler.post(this);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// Get measured height and width of the view
setMeasuredDimension(getMeasurement(widthMeasureSpec, mWidth),
getMeasurement(heightMeasureSpec, mHeight));
// mWidth = MeasureSpec.getSize(widthMeasureSpec);
// mHeight = MeasureSpec.getSize(heightMeasureSpec);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(columnIndex > 0 && columnIndex<rectangles.size())
canvas.drawRect(rectangles.get(columnIndex), getRectPaint());
}
#Override
public void run() {
if(columnIndex<rectangles.size()){
invalidate();
columnIndex++;
mHandler.postDelayed(this,FRAME_TIME);
}else{
mHandler.removeCallbacks(this);
}
}
}
Create a FrameLayout on which you want to draw rectangles. Create a view every time you are drawing a new circle. Draw circle on newly created view and the add that view to FrameLayout. Background of the view will be transparent by default. If not then set the background as transparent. This way your previous rectangles will be visible and new rectangle will be drawn on existing rectangles.
Your are near to make it done! Just call you prepare after calling invalidate method.
#Override
public void run() {
if(columnIndex<rectangles.size()){
invalidate();
columnIndex++;
mHandler.postDelayed(this,FRAME_TIME);
}else{
mHandler.removeCallbacks(this);
eraseAll();
}
}
...
public void eraseAll() {
this.canvas.drawColor(0, PorterDuff.Mode.CLEAR);
invalidate();
prepare();
}
For me this logic is working fine
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(columnIndex < rectangles.size()) {
for (int i = 0; i <= columnIndex; ++i) {
canvas.drawRect(rectangles.get(i), paint);
}
}
}
Simplest heck is to keep the canvas object as class level variable and always draw on this canvas object and in super.onDraw() pass on this canvas, it will always keep your previous rectangles drawn
I want to draw circle by canvas. Here is my code:
[MyActivity.java]:
public class MyActivity extends Activity
{
public void onCreate(Bundle savedInstanceState)
{
...
setContentView(new View(this,w,h));
}
}
[View.java]:
public class View extends SurfaceView
{
public View(Context context, int w, int h)
{
super(context);
Canvas grid = new Canvas(Bitmap.createBitmap(h,w, Bitmap.Config.ARGB_8888));
grid. drawColor(Color.WHITE);
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL);
grid.drawCircle(w/2, h/2 , w/2, paint);
}
}
So I have just black screen without circle.
Why it does not work? How to fix it?
You can override the onDraw method of your view and draw the circle.
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(x, y, radius, paint);
}
For a better reference on drawing custom views check out the official Android documentation.
http://developer.android.com/training/custom-views/custom-drawing.html
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends Activity
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(new MyView(this));
}
public class MyView extends View
{
Paint paint = null;
public MyView(Context context)
{
super(context);
paint = new Paint();
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
int x = getWidth();
int y = getHeight();
int radius;
radius = 100;
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.WHITE);
canvas.drawPaint(paint);
// Use Color.parseColor to define HTML colors
paint.setColor(Color.parseColor("#CD5C5C"));
canvas.drawCircle(x / 2, y / 2, radius, paint);
}
}
}
Edit
if you want to draw circle at centre. You could also translate your entire canvas to center then draw circle at center.using
canvas.translate(getWidth()/2f,getHeight()/2f);
canvas.drawCircle(0,0, radius, paint);
These two link also help
http://www.compiletimeerror.com/2013/09/introduction-to-2d-drawing-in-android.html#.VIg_A5SSy9o
http://android-coding.blogspot.com/2012/04/draw-circle-on-canvas-canvasdrawcirclet.html
public class CircleView extends View {
private static final String COLOR_HEX = "#E74300";
private final Paint drawPaint;
private float size;
public CircleView(final Context context, final AttributeSet attrs) {
super(context, attrs);
drawPaint = new Paint();
drawPaint.setColor(Color.parseColor(COLOR_HEX));
drawPaint.setAntiAlias(true);
setOnMeasureCallback();
}
#Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(size, size, size, drawPaint);
}
private void setOnMeasureCallback() {
ViewTreeObserver vto = getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
removeOnGlobalLayoutListener(this);
size = getMeasuredWidth() / 2;
}
});
}
#TargetApi(Build.VERSION_CODES.JELLY_BEAN)
private void removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
if (Build.VERSION.SDK_INT < 16) {
getViewTreeObserver().removeGlobalOnLayoutListener(listener);
} else {
getViewTreeObserver().removeOnGlobalLayoutListener(listener);
}
}
}
Xml example: will produce a circle of 5dp
<com.example.CircleView
android:layout_width="10dp"
android:layout_height="10dp"/>
If you are using your own CustomView extending View class, you need to call canvas.invalidate() method which will internally call onDraw method. You can use default API for canvas to draw a circle. The x, y cordinate define the center of the circle. You can also define color and styling in paint & pass the paint object.
public class CustomView extends View {
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
setupPaint();
}
}
Define default paint settings and canvas (Initialise paint in constructor so that you can reuse the same object everywhere and change only specific settings wherever required)
private Paint drawPaint;
// Setup paint with color and stroke styles
private void setupPaint() {
drawPaint = new Paint();
drawPaint.setColor(Color.BLUE);
drawPaint.setAntiAlias(true);
drawPaint.setStrokeWidth(5);
drawPaint.setStyle(Paint.Style.FILL_AND_STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);
}
And initialise canvas object
private Canvas canvas;
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
this.canvas = canvas;
canvas.drawCircle(xCordinate, yCordinate, RADIUS, drawPaint);
}
And finally, for every view refresh or new draw on the screen, you need to call invalidate method. Remember your entire view is redrawn, hence this is an expensive call. Make sure you do only the necessary operations in onDraw
canvas.invalidate();
For more details on canvas drawing refer https://medium.com/#mayuri.k18/android-canvas-for-drawing-and-custom-views-e1a3e90d468b
#Override
public void onDraw(Canvas canvas){
canvas.drawCircle(xPos, yPos,radius, paint);
}
Above is the code to render a circle. Tweak the parameters to your suiting.
Try this
The entire code for drawing a circle or download project source code and test it on your android studio. Draw circle on canvas programmatically.
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.widget.ImageView;
public class Shape {
private Bitmap bmp;
private ImageView img;
public Shape(Bitmap bmp, ImageView img) {
this.bmp=bmp;
this.img=img;
onDraw();
}
private void onDraw(){
Canvas canvas=new Canvas();
if (bmp.getWidth() == 0 || bmp.getHeight() == 0) {
return;
}
int w = bmp.getWidth(), h = bmp.getHeight();
Bitmap roundBitmap = getRoundedCroppedBitmap(bmp, w);
img.setImageBitmap(roundBitmap);
}
public static Bitmap getRoundedCroppedBitmap(Bitmap bitmap, int radius) {
Bitmap finalBitmap;
if (bitmap.getWidth() != radius || bitmap.getHeight() != radius)
finalBitmap = Bitmap.createScaledBitmap(bitmap, radius, radius,
false);
else
finalBitmap = bitmap;
Bitmap output = Bitmap.createBitmap(finalBitmap.getWidth(),
finalBitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final Paint paint = new Paint();
final Rect rect = new Rect(0, 0, finalBitmap.getWidth(),
finalBitmap.getHeight());
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(Color.parseColor("#BAB399"));
canvas.drawCircle(finalBitmap.getWidth() / 2 + 0.7f, finalBitmap.getHeight() / 2 + 0.7f, finalBitmap.getWidth() / 2 + 0.1f, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(finalBitmap, rect, rect, paint);
return output;
}
Here is example to draw stroke circle canvas
val paint = Paint().apply {
color = Color.RED
style = Paint.Style.STROKE
strokeWidth = 10f
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas?.drawCircle(200f, 100f, 100f, paint)
}
Result
Example to draw solid circle canvas
val paint = Paint().apply {
color = Color.RED
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
canvas?.drawCircle(200f, 100f, 100f, paint)
}
Result
Hope it help
private Paint green = new Paint();
private int greenx , greeny;
green.setColor(Color.GREEN);
green.setAntiAlias(false);
canvas.drawCircle(greenx,greeny,20,green);
I would like to change an image I loaded to have round corners.
Any hints, tutorials, best practices you know of?
For a more controlled method draw a rounded rectangle and mask it onto your image using the porter-duff Xfer mode of the paint.
First setup the Xfer paint and the rounded bitmap:
Bitmap myCoolBitmap = ... ; // <-- Your bitmap you want rounded
int w = myCoolBitmap.getWidth(), h = myCoolBitmap.getHeight();
// We have to make sure our rounded corners have an alpha channel in most cases
Bitmap rounder = Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(rounder);
// We're going to apply this paint eventually using a porter-duff xfer mode.
// This will allow us to only overwrite certain pixels. RED is arbitrary. This
// could be any color that was fully opaque (alpha = 255)
Paint xferPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
xferPaint.setColor(Color.RED);
// We're just reusing xferPaint to paint a normal looking rounded box, the 20.f
// is the amount we're rounding by.
canvas.drawRoundRect(new RectF(0,0,w,h), 20.0f, 20.0f, xferPaint);
// Now we apply the 'magic sauce' to the paint
xferPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
Now apply this bitmap ontop of your image:
Bitmap result = Bitmap.createBitmap(myCoolBitmap.getWidth(), myCoolBitmap.getHeight() ,Bitmap.Config.ARGB_8888);
Canvas resultCanvas = new Canvas(result)
resultCanvas.drawBitmap(myCoolBitmap, 0, 0, null);
resultCanvas.drawBitmap(rounder, 0, 0, xferPaint);
Bitmap with rounded corners now resides in result.
Why not use clipPath?
protected void onDraw(Canvas canvas) {
Path clipPath = new Path();
float radius = 10.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);
super.onDraw(canvas);
}
Romain Guy himself writes about this in his blog:
To generate the rounded images I simply wrote a custom Drawable that
draws a rounded rectangle using Canvas.drawRoundRect(). The trick is
to use a Paint with a BitmapShader to fill the rounded rectangle with
a texture instead of a simple color. Here is what the code looks like:
BitmapShader shader;
shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(shader);
RectF rect = new RectF(0.0f, 0.0f, width, height);
// rect contains the bounds of the shape
// radius is the radius in pixels of the rounded corners
// paint contains the shader that will texture the shape
canvas.drawRoundRect(rect, radius, radius, paint);
The sample application goes a little further and fakes a vignette
effect by combining the BitmapShader with a RadialGradient.
Here's a way I discovered to do it with an ImageView. I tried other methods, including the answers here and on similar questions, but I found that they didn't work well for me, as I needed the corners to be applied to the image view and not directly to the bitmap. Applying directly to the bitmap won't work if you're scaling/cropping/panning that bitmap, since the corners will also be scaled/cropped/panned.
public class RoundedCornersImageView extends ImageView {
private final Paint restorePaint = new Paint();
private final Paint maskXferPaint = new Paint();
private final Paint canvasPaint = new Paint();
private final Rect bounds = new Rect();
private final RectF boundsf = new RectF();
public RoundedCornersImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public RoundedCornersImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public RoundedCornersImageView(Context context) {
super(context);
init();
}
private void init() {
canvasPaint.setAntiAlias(true);
canvasPaint.setColor(Color.argb(255, 255, 255, 255));
restorePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
maskXferPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
}
#Override
protected void onDraw(Canvas canvas) {
canvas.getClipBounds(bounds);
boundsf.set(bounds);
canvas.saveLayer(boundsf, restorePaint, Canvas.ALL_SAVE_FLAG);
super.onDraw(canvas);
canvas.saveLayer(boundsf, maskXferPaint, Canvas.ALL_SAVE_FLAG);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawRoundRect(boundsf, 75, 75, canvasPaint);
canvas.restore();
canvas.restore();
}
}
Here's an alternative that uses hardware layers for the final layer composite:
public class RoundedCornersImageView extends ImageView {
private final Paint restorePaint = new Paint();
private final Paint maskXferPaint = new Paint();
private final Paint canvasPaint = new Paint();
private final Rect bounds = new Rect();
private final RectF boundsf = new RectF();
public RoundedCornersImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public RoundedCornersImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public RoundedCornersImageView(Context context) {
super(context);
init();
}
private void init() {
canvasPaint.setAntiAlias(true);
canvasPaint.setColor(Color.argb(255, 255, 255, 255));
restorePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));
maskXferPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.MULTIPLY));
setLayerType(View.LAYER_TYPE_HARDWARE, restorePaint);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.getClipBounds(bounds);
boundsf.set(bounds);
super.onDraw(canvas);
canvas.saveLayer(boundsf, maskXferPaint, Canvas.ALL_SAVE_FLAG);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawRoundRect(boundsf, 75, 75, canvasPaint);
canvas.restore();
}
}
At first I wasn't able to get it to work with this method because my corners were becoming black; I later realized what the problem was after reading this question: Android how to apply mask on ImageView?. It turns out that modifying the alpha in the canvas is actually "scratching it out" directly on the screen, and punching a hole to the underlying window which is black. That's why two layers are needed: one to apply the mask, and another to apply the composited image to the screen.
How about creating a NinePatchDrawable image that has just rounded corners and has a transparent body. Overlay your image with an appropriately re-sized version of your NinePatchDrawable.
package com.pkg;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Bitmap.Config;
import android.graphics.PorterDuff.Mode;
import android.os.Bundle;
import android.os.Environment;
import android.widget.ImageView;
public class RoundedImage extends Activity {
/** Called when the activity is first created. */
ImageView imag;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
imag=(ImageView)findViewById(R.id.image);
//ImageView img1=(ImageView)findViewById(R.id.imageView1);
BitmapFactory.Options bitopt=new BitmapFactory.Options();
bitopt.inSampleSize=1;
// String img=Environment.getExternalStorageDirectory().toString();
// String filepath =Environment.getExternalStorageDirectory().toString();
String filepath ="/mnt/sdcard/LOST.DIR";
File imagefile = new File(filepath + "/logo.jpg");
FileInputStream fis = null;
try
{
fis = new FileInputStream(imagefile);
}
catch (FileNotFoundException e1)
{
// TODO Auto-generated catch block
e1.printStackTrace();
}
Bitmap bi = BitmapFactory.decodeStream(fis);
if(bi!=null){
imag.setImageBitmap(getRoundedCornerBitmap(bi));
}
}
public static Bitmap getRoundedCornerBitmap(Bitmap bitmap) {
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 = 12;
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;
}
}
Here's another rounded ImageView implementation using Path. The performance is great, but in certain conditions some bugs may appear on emulators because of the hardware drawing.
public class RoundImageView extends ImageView {
private Path mPath;
private RectF mRect;
private Paint mPaint;
private int mCornerRadius;
private float mImageAlpha;
private boolean mIsCircular;
public RoundImageView(Context context) {
this(context, null);
}
public RoundImageView(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.roundImageViewStyle);
}
public RoundImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.RoundImageView, defStyle, 0);
mCornerRadius = a.getDimensionPixelSize(R.styleable.RoundImageView_cornerRadius, 0);
mIsCircular = a.getBoolean(R.styleable.RoundImageView_isCircular, false);
mImageAlpha = a.getFloat(R.styleable.RoundImageView_imageAlpha, 1);
a.recycle();
setAlpha((int) (mImageAlpha * 255));
// Avoid expensive off-screen drawing
setLayerType(LAYER_TYPE_HARDWARE, null);
mPath = new Path();
mRect = new RectF();
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPath.reset();
if (mIsCircular) {
float halfWidth = canvas.getWidth() / 2;
float halfHeight = canvas.getHeight() / 2;
float radius = Math.max(halfWidth, halfHeight);
mPath.addCircle(halfWidth, halfHeight, radius, Path.Direction.CW);
} else {
mRect.right = canvas.getWidth();
mRect.bottom = canvas.getHeight();
mPath.addRoundRect(mRect, mCornerRadius, mCornerRadius, Path.Direction.CW);
}
canvas.drawPath(mPath, mPaint);
}
}
P.S. Learn OpenGL ES provided the best solution. It's very smooth and works on emulators too.
best solution I found ->
1) create rounded corner drawable. And set to imageview as background.
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<corners
android:radius="10dp" /></shape>
2) Then set image view object property of setClipToOutline(true) in java code.
imageview.setClipToOutline(true);
It works like charm