I am using RoundedImageView by vinc3m1 and I am trying to create an image that has a bitmap in it.
I want the ImageView to be rounded but not the bitmap I place into it, like in this pic:
However, this is the result I am getting:
I use padding on the RoundedImageView to achieve this, the View is in a View which contains it and a label; I use the padding in code like this:
int dpPadding = Utils.dpToPx(25);
view.getImage().setPadding(dpPadding,dpPadding,dpPadding,dpPadding);
and I see the padding, but I don't want the image inside to have the rounded corners, only the background.
Though it might not be the best way, can any one help?
Scale down your image and add a border to it (to fill the space left by the scaling).
You can do it using Bitmap.createScaledBitmap.
To add the border to it, you can create a "white" image with the original size and then combine the two images by overlaying them.
Then save it and use it in your RoundedImageView.
You can use this...
public class RoundImage extends Drawable {
private final Bitmap mBitmap;
private final Paint mPaint;
private final RectF mRectF;
private final int mBitmapWidth;
private final int mBitmapHeight;
public RoundImage(Bitmap bitmap) {
mBitmap = bitmap;
mRectF = new RectF();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
final BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);
mPaint.setShader(shader);
mBitmapWidth = (int) 240;
mBitmapHeight = (int) 240;
}
#Override
public void draw(Canvas canvas) {
canvas.drawOval(mRectF, mPaint);
}
#Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mRectF.set(bounds);
}
#Override
public void setAlpha(int alpha) {
if (mPaint.getAlpha() != alpha) {
mPaint.setAlpha(alpha);
invalidateSelf();
}
}
#Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
#Override
public int getIntrinsicWidth() {
return mBitmapWidth;
}
#Override
public int getIntrinsicHeight() {
return mBitmapHeight;
}
public void setAntiAlias(boolean aa) {
mPaint.setAntiAlias(aa);
invalidateSelf();
}
#Override
public void setFilterBitmap(boolean filter) {
mPaint.setFilterBitmap(filter);
invalidateSelf();
}
#Override
public void setDither(boolean dither) {
mPaint.setDither(dither);
invalidateSelf();
}
public Bitmap getBitmap() {
return mBitmap;
}
enter code here
}
// Use like this
imageview.setBackgroundDrawable(new RoundImage(bitmapimage));
i use this method to add some padding to my image :
.RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.makeramen.roundedimageview.RoundedImageView
android:layout_width="90dp"
android:layout_height="90dp"
android:scaleType="fitCenter"
android:src="#color/transparent"
app:riv_border_color="#color/colorWhite"
app:riv_border_width="2dip"
app:riv_corner_radius="25dp"
app:riv_oval="true" />
<ImageView
android:id="#+id/listimage"
android:layout_width="90dp"
android:layout_height="90dp"
android:padding="16dp"
android:src="#drawable/search" />
</RelativeLayout>`
Related
I am looking at using the Picasso library to download an image from URL and pass this into circle image view, but since picasso requires that you pass in an actual imageView I have come to a standstill on how to do it
I am using the picasso library from here http://square.github.io/picasso/
and the circle image view class from here https://github.com/hdodenhof/CircleImageView
Here is the start of my code to get the image
private void getData() {
userName.setText(prefs.getString("userName",""));
jobTitle.setText(prefs.getString("profile",""));
userLocation.setText(prefs.getString("location",""));
// ??????
// Picasso.with(context).load(image link here).into(imageview here);
//CircleImageView img = new CircleImageView(this);
//img.setImageResource();
//img.setImageBitmap();
//img.setImageDrawable();
//img.setImageURI();
}
Edit:
here is the xml for the circleImageView
<michael.CircleImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:src="#drawable/shadow"
android:layout_gravity="center"
android:layout_marginTop="16dp"
app:border_width="2dp"
app:border_color="#274978"
android:id="#+id/circleImageView"
I don't think you require CircleImageView library
You can implement Circular Transformation check the below gist
https://gist.github.com/julianshen/5829333
then
Picasso.with(activity).load(image link here)
.transform(new CircleTransform()).into(ImageView);
Use This
Activity Class
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String imageUrl = "https://www.baby-connect.com/images/baby2.gif";
CircleImageView imageView = (CircleImageView) findViewById(R.id.image);
Picasso.with(getApplicationContext()).load(imageUrl)
.placeholder(R.drawable.images).error(R.drawable.ic_launcher)
.into(imageView);
}
}
Layout File
<de.hdodenhof.circleimageview.CircleImageView
android:id="#+id/image"
android:layout_width="160dp"
android:layout_height="160dp"
android:layout_centerInParent="true"
android:src="#drawable/images"
app:border_color="#ffffff"
app:border_width="2dp" />
This is Working fine.
Use this code to create Circular Imageview ....
public class RoundedImageView extends ImageView {
public RoundedImageView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public RoundedImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public RoundedImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#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(), h = getHeight();
Bitmap roundBitmap = getCroppedBitmap(bitmap, w);
canvas.drawBitmap(roundBitmap, 0,0, null);
}
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;
}
}
Take the ID of CircleImageView first :
CircleImageView mCircleImageView = (CircleImageView)findViewById(R.id.circleImageView);
And pass the ID to Picasso library :
Picasso.with(mContext).load(Uri.parse(link)).placeholder(drawable).into(mCircleImageView);
This worked for me.
I have created a target class that uses native Android's RoundedBitmapDrawable class to make image round (removes the need to add a circle transform class to code), see solution below:
public class RoundedImageBitmapTarget implements Target {
private final Context context;
private final ImageView view;
public RoundedImageBitmapTarget(Context context, ImageView view) {
this.context = context;
this.view = view;
}
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
setBitmap(bitmap);
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
setBitmap(((BitmapDrawable) errorDrawable).getBitmap());
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
setBitmap(((BitmapDrawable) placeHolderDrawable).getBitmap());
}
public void setBitmap(Bitmap bitmap) {
view.setImageDrawable(getRoundBitmap(context, bitmap));
}
public static RoundedBitmapDrawable getRoundBitmap(Context context, Bitmap bitmap) {
Resources res = context.getResources();
RoundedBitmapDrawable round = RoundedBitmapDrawableFactory.create(res, bitmap);
round.setCircular(true);
round.setTargetDensity(context.getResources().getDisplayMetrics());
return round;
}
public static void load(Context context, ImageView view, String url, int size, #DrawableRes int placeholder) {
RoundedImageBitmapTarget target;
Picasso.with(context).load(url)
.resize(0, size)
.placeholder(placeholder)
.error(placeholder)
.into(target = new RoundedImageBitmapTarget(context, view));
view.setTag(target);
}
}
I have a Custom Class extending Drawable as seen below which is meant to draw an Arc. From my MainActivity controlling the Drawable, I am redrawing it based on some input values that I translate to the appropriate "angle" for the shape. E.g.
This is the initial state of the drawable:
This is the second state of the drawable:
Red arrow indicates the motion i am trying to achieve
I am trying to "animate" the change from state 1 to state 2 with a sweeping motion. Any ideas on how to do that? Should I redraw the shape a number of times gradually transitioning between state one and two?
My Drawable Code:
public class CircleLoadingBar extends Drawable implements Drawable.Callback{
private Paint paint;
private Canvas canvas;
private float angle;
private RectF outterCircle;
private float padding=30;
public void invalidateDrawable(Drawable drawable){
final Callback callback = getCallback();
if (callback != null) {
callback.invalidateDrawable(this);
}
}
public void scheduleDrawable(Drawable drawable, Runnable runnable, long l){
invalidateDrawable(drawable);
}
public void unscheduleDrawable(Drawable drawable,Runnable runnable){
//empty
}
public CircleLoadingBar(){
this(0);
}
public CircleLoadingBar( int angle){
this.angle=angle;
this.canvas=new Canvas();
paint=new Paint();
paint.setColor(Color.GREEN);
paint.setStrokeWidth(padding);
paint.setAntiAlias(true);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStyle(Paint.Style.STROKE);
outterCircle = new RectF();
}
public void setAngle(float angle){
this.angle=angle;
}
#Override
public void draw(Canvas canvas){
canvas.save();
Rect bounds = getBounds();
outterCircle.set(bounds.left+padding,bounds.top+padding,bounds.right-padding,bounds.bottom-padding);
int[] colors = {Color.RED, Color.GREEN, Color.RED};
float[] positions = {0,0.2f,1.3f};
SweepGradient gradient3 = new SweepGradient(innerCircle.centerX(), innerCircle.centerY(),colors,positions);
paint.setShader(gradient3);
canvas.drawArc(outterCircle,90,angle,true,paint);
}
#Override
public void setAlpha(int alpha) {
// Has no effect
}
#Override
public void setColorFilter(ColorFilter cf) {
// Has no effect
}
#Override
public int getOpacity() {
// Not Implemented
return 0;
}
}
My MainActivity Code:
public class MainActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate();
setContentView(R.layout.main);
textView= (TextView) findViewById(R.id.textview);
circleLoadingBar= new CircleLoadingBar(10);
textView.setBackgroundDrawable(circleLoadingBar);
}
public void stateUpdate(float angle) {
circleLoadingBar.setAngle(angle);
textView.invalidate();
}
}
After a lot of searching around, I found the answer.
Summary:
I needed my Custom Drawable class to implement Drawable.Callback and Runnable interfaces (see code below).
CustomDrawable.class
public class CustomDrawable extends Drawable implements Drawable.Callback, Runnable{
private Paint paint;
private Canvas canvas;
private int angle;
private RectF circle;
private float cx,cy;
private float mHeight,mWidth=100;
private float mRadius=20;
private Drawable.Callback cb;
private boolean running=false;
public void invalidateDrawable(Drawable drawable){
super.invalidateSelf(); //This was done for my specific example. I wouldn't use it otherwise
}
public void scheduleDrawable(Drawable drawable, Runnable runnable, long l){
invalidateDrawable(drawable);
}
public void unscheduleDrawable(Drawable drawable,Runnable runnable){
super.unscheduleSelf(runnable);
}
public CircleLoadingBar(){
this(0);
}
public CircleLoadingBar(int angle){
this.angle=angle;
paint=new Paint();
paint.setColor(Color.GREEN);
paint.setAntiAlias(true);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStyle(Paint.Style.STROKE);
circle= new RectF();
}
#Override
public void draw(Canvas canvas){
canvas.save();
Rect bounds = getBounds();
circle.set(bounds);
canvas.drawArc(circle, 90, angle, true, paint);
}
public void nextFrame(){
unscheduleSelf(this);
scheduleSelf(this, SystemClock.uptimeMillis() + 250);
}
public void stop(){
running=false;
unscheduleSelf(this);
}
public void start(){
if(!running){
running=true;
nextFrame();
}
}
public void run(){
angle++;
invalidate();
nextFrame();
}
#Override
public void setAlpha(int alpha) {
// Has no effect
}
#Override
public void setColorFilter(ColorFilter cf) {
// Has no effect
}
#Override
public int getOpacity() {
// Not Implemented
return 0;
}
}
Now that your Drawable implements both, you can just "run" it as a separate thread, initiated and controlled by your activity via start and stop functions.
I am working on an Android application in which I am doing rounded image view. It is working fine with some images, but for the images like 160x120 resolution it shows an oval shaped.
My code for the custom imageview is given below:
public class RoundImage extends Drawable {
private final Bitmap mBitmap;
private final Paint mPaint;
private final RectF mRectF;
private final int mBitmapWidth;
private final int mBitmapHeight;
public RoundImage(Bitmap bitmap) {
mBitmap = bitmap;
mRectF = new RectF();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
final BitmapShader shader = new BitmapShader(bitmap,
Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint.setShader(shader);
mBitmapWidth = mBitmap.getWidth();
mBitmapHeight = mBitmap.getHeight();
}
#Override
public void draw(Canvas canvas) {
canvas.drawOval(mRectF, mPaint);
}
#Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mRectF.set(bounds);
}
#Override
public void setAlpha(int alpha) {
if (mPaint.getAlpha() != alpha) {
mPaint.setAlpha(alpha);
invalidateSelf();
}
}
#Override
public void setColorFilter(ColorFilter cf) {
mPaint.setColorFilter(cf);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
#Override
public int getIntrinsicWidth() {
return mBitmapWidth;
}
#Override
public int getIntrinsicHeight() {
return mBitmapHeight;
}
public void setAntiAlias(boolean aa) {
mPaint.setAntiAlias(aa);
invalidateSelf();
}
#Override
public void setFilterBitmap(boolean filter) {
mPaint.setFilterBitmap(filter);
invalidateSelf();
}
#Override
public void setDither(boolean dither) {
mPaint.setDither(dither);
invalidateSelf();
}
public Bitmap getBitmap() {
return mBitmap;
}
}
// My main Activity Class
public class MainActivity extends Activity {
ImageView imageView1, imageView2;
RoundImage roundedImage, roundedImage1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView1 = (ImageView) findViewById(R.id.imageView1);
Bitmap bm = BitmapFactory.decodeResource(getResources(),R.drawable.tt);
roundedImage = new RoundImage(bm);
imageView1.setImageDrawable(roundedImage);
}
}
// My xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/White"
android:orientation="vertical" >
<ImageView
android:id="#+id/imageView1"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:layout_gravity="center"
android:background="#color/Brown"
android:src="#drawable/image" />
</LinearLayout>
Add this gradle in your android project
compile 'com.mikhaellopez:circularimageview:3.0.2'
and added this line in your xml file (layout files)
<com.mikhaellopez.circularimageview.CircularImageView
android:layout_width="250dp"
android:layout_height="250dp"
android:src="#drawable/image"
app:civ_border_color="#EEEEEE"
app:civ_border_width="4dp"
app:civ_shadow="true"
app:civ_shadow_radius="10"
app:civ_shadow_color="#8BC34A"/>
In your java code
CircularImageView circularImageView = (CircularImageView)findViewById(R.id.yourCircularImageView);
// Set Border
circularImageView.setBorderColor(getResources().getColor(R.color.GrayLight));
circularImageView.setBorderWidth(10);
// Add Shadow with default param
circularImageView.addShadow();
// or with custom param
circularImageView.setShadowRadius(15);
circularImageView.setShadowColor(Color.RED);
From here
First of all use Picasso image loading library, for that you have to add one dependency like in build.gradle:
compile 'com.squareup.picasso:picasso:2.5.2'
than create a seperate class called "CircleTransform" for displaying image into round shape and implements interface "Transformation"
than add following conde into it.
#Override
public Bitmap transform(Bitmap source) {
int size = Math.min(source.getWidth(), source.getHeight());
int x = (source.getWidth() - size) / 2;
int y = (source.getHeight() - size) / 2;
Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);
if (squaredBitmap != source) {
source.recycle();
}
Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig());
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
BitmapShader shader = new BitmapShader(squaredBitmap, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
paint.setShader(shader);
paint.setAntiAlias(true);
float r = size/2f;
canvas.drawCircle(r, r, r, paint);
squaredBitmap.recycle();
return bitmap;
}
#Override
public String key() {
return "circle";
}
After that you can set image into imageview like this :
Picasso.with(this)
.load(R.drawable.tt)
.transform(new CircleTransform())
.into(imageView1);
That's it. i hope it helps.
Background
I've been searching in plenty of places to find out how to animate a drawable without animating the view and without using the built in drawables.
The reason is that I will need to prepare a customized animation within the drawable, and I might have different requirements for it later.
For now, I'm making a basic animated drawable that just spins a given bitmap inside it.
I've set it on an imageView, but I wish to be able to use it on any kind of view, even customized views that have overridden the onDraw function.
The problem
I can't find out how to show the drawable without being cut, no matter what the size of the view is. Here's what I see:
The code
Here's the code:
private class CircularAnimatedDrawable extends Drawable implements Animatable {
private static final Interpolator ANGLE_INTERPOLATOR = new LinearInterpolator();
private static final int ANGLE_ANIMATOR_DURATION = 2000;
private final RectF fBounds = new RectF();
private float angle = 0;
private ObjectAnimator mObjectAnimatorAngle;
private final Paint mPaint;
private boolean mRunning;
private final Bitmap mBitmap;
public CircularAnimatedDrawable(final Bitmap bitmap) {
this.mBitmap = bitmap;
mPaint = new Paint();
setupAnimations();
}
public float getAngle() {
return this.angle;
}
public void setAngle(final float angle) {
this.angle = angle;
invalidateSelf();
}
#Override
public Callback getCallback() {
return mCallback;
}
#Override
public void draw(final Canvas canvas) {
canvas.save();
canvas.rotate(angle);
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
canvas.restore();
}
#Override
public void setAlpha(final int alpha) {
mPaint.setAlpha(alpha);
}
#Override
public void setColorFilter(final ColorFilter cf) {
mPaint.setColorFilter(cf);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSPARENT;
}
#Override
protected void onBoundsChange(final Rect bounds) {
super.onBoundsChange(bounds);
fBounds.left = bounds.left;
fBounds.right = bounds.right;
fBounds.top = bounds.top;
fBounds.bottom = bounds.bottom;
}
private void setupAnimations() {
mObjectAnimatorAngle = ObjectAnimator.ofFloat(this, "angle", 360f);
mObjectAnimatorAngle.setInterpolator(ANGLE_INTERPOLATOR);
mObjectAnimatorAngle.setDuration(ANGLE_ANIMATOR_DURATION);
mObjectAnimatorAngle.setRepeatMode(ValueAnimator.RESTART);
mObjectAnimatorAngle.setRepeatCount(ValueAnimator.INFINITE);
}
#Override
public void start() {
if (isRunning())
return;
mRunning = true;
mObjectAnimatorAngle.start();
invalidateSelf();
}
#Override
public void stop() {
if (!isRunning())
return;
mRunning = false;
mObjectAnimatorAngle.cancel();
invalidateSelf();
}
#Override
public boolean isRunning() {
return mRunning;
}
}
and the usage :
final ImageView imageView = (ImageView) findViewById(R.id.imageView);
final Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.spinner_76_inner_holo);
final CircularAnimatedDrawable circularAnimatedDrawable = new CircularAnimatedDrawable(bitmap);
circularAnimatedDrawable.setCallback(imageView);
circularAnimatedDrawable.start();
imageView.setImageDrawable(circularAnimatedDrawable);
The question
How can I set it to make the drawable fit the view?
Should I use the bitmap size? the fBounds? both? Or maybe something else?
try this modified version of your Drawable:
class CircularAnimatedDrawable extends Drawable implements Animatable, TimeAnimator.TimeListener {
private static final float TURNS_PER_SECOND = 0.5f;
private Bitmap mBitmap;
private boolean mRunning;
private TimeAnimator mTimeAnimator = new TimeAnimator();
private Paint mPaint = new Paint();
private Matrix mMatrix = new Matrix();
public CircularAnimatedDrawable(final Bitmap bitmap) {
mBitmap = bitmap;
mTimeAnimator.setTimeListener(this);
}
#Override
public void draw(final Canvas canvas) {
canvas.drawBitmap(mBitmap, mMatrix, mPaint);
}
#Override
protected void onBoundsChange(Rect bounds) {
Log.d(TAG, "onBoundsChange " + bounds);
mMatrix.setRectToRect(new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight()),
new RectF(bounds),
Matrix.ScaleToFit.CENTER);
}
#Override
public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) {
Rect b = getBounds();
mMatrix.postRotate(360 * TURNS_PER_SECOND * deltaTime / 1000, b.centerX(), b.centerY());
invalidateSelf();
}
#Override
public void setAlpha(final int alpha) {
mPaint.setAlpha(alpha);
}
#Override
public void setColorFilter(final ColorFilter cf) {
mPaint.setColorFilter(cf);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSPARENT;
}
#Override
public void start() {
if (isRunning())
return;
mRunning = true;
mTimeAnimator.start();
invalidateSelf();
}
#Override
public void stop() {
if (!isRunning())
return;
mRunning = false;
mTimeAnimator.cancel();
invalidateSelf();
}
#Override
public boolean isRunning() {
return mRunning;
}
}
EDIT: version without Animator stuff (uses [un]scheduleSelf), NOTE it uses View's Drawable.Callback mechanism so it usually cannot be started directly from onCreate where View doesn't have attached Handler yet
class CircularAnimatedDrawable extends Drawable implements Animatable, Runnable {
private static final float TURNS_PER_SECOND = 0.5f;
private static final long DELAY = 50;
private Bitmap mBitmap;
private long mLastTime;
private boolean mRunning;
private Paint mPaint = new Paint();
private Matrix mMatrix = new Matrix();
public CircularAnimatedDrawable(final Bitmap bitmap) {
mBitmap = bitmap;
}
#Override
public void draw(final Canvas canvas) {
canvas.drawBitmap(mBitmap, mMatrix, mPaint);
}
#Override
protected void onBoundsChange(Rect bounds) {
Log.d(TAG, "onBoundsChange " + bounds);
mMatrix.setRectToRect(new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight()),
new RectF(bounds),
Matrix.ScaleToFit.CENTER);
}
#Override
public void setAlpha(final int alpha) {
mPaint.setAlpha(alpha);
}
#Override
public void setColorFilter(final ColorFilter cf) {
mPaint.setColorFilter(cf);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSPARENT;
}
#Override
public void start() {
if (isRunning())
return;
mRunning = true;
mLastTime = SystemClock.uptimeMillis();
scheduleSelf(this, 0);
invalidateSelf();
}
#Override
public void stop() {
if (!isRunning())
return;
mRunning = false;
unscheduleSelf(this);
invalidateSelf();
}
#Override
public boolean isRunning() {
return mRunning;
}
#Override
public void run() {
long now = SystemClock.uptimeMillis();
Rect b = getBounds();
long deltaTime = now - mLastTime;
mLastTime = now;
mMatrix.postRotate(360 * TURNS_PER_SECOND * deltaTime / 1000, b.centerX(), b.centerY());
scheduleSelf(this, now + DELAY);
invalidateSelf();
}
}
ok, the fix is:
#Override
public void draw(final Canvas canvas) {
canvas.save();
canvas.rotate(angle, fBounds.width() / 2 + fBounds.left, fBounds.height() / 2 + fBounds.top);
canvas.translate(fBounds.left, fBounds.top);
canvas.drawBitmap(mBitmap, null, new Rect(0, 0, (int) fBounds.width(), (int) fBounds.height()), mPaint);
canvas.restore();
}
#Override
public int getIntrinsicHeight() {
return mBitmap.getHeight();
}
#Override
public int getIntrinsicWidth() {
return mBitmap.getWidth();
}
It works fine. I hope it will be enough for the future changes.
EDIT: here's an optimization to the above, including all changes:
class CircularAnimatedDrawable extends Drawable implements Animatable {
private static final Interpolator ANGLE_INTERPOLATOR = new LinearInterpolator();
private static final int ANGLE_ANIMATOR_DURATION = 2000;
private float angle = 0;
private ObjectAnimator mObjectAnimatorAngle;
private final Paint mPaint;
private boolean mRunning;
private final Bitmap mBitmap;
private final Matrix mMatrix = new Matrix();
public CircularAnimatedDrawable(final Bitmap bitmap) {
this.mBitmap = bitmap;
mPaint = new Paint();
mPaint.setAntiAlias(true);
setupAnimations();
}
#SuppressWarnings("unused")
public float getAngle() {
return this.angle;
}
#SuppressWarnings("unused")
public void setAngle(final float angle) {
this.angle = angle;
invalidateSelf();
}
#Override
public void draw(final Canvas canvas) {
final Rect b = getBounds();
canvas.save();
canvas.rotate(angle, b.centerX(), b.centerY());
canvas.drawBitmap(mBitmap, mMatrix, mPaint);
canvas.restore();
}
#Override
public void setAlpha(final int alpha) {
mPaint.setAlpha(alpha);
}
#Override
public void setColorFilter(final ColorFilter cf) {
mPaint.setColorFilter(cf);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSPARENT;
}
#Override
protected void onBoundsChange(final Rect bounds) {
super.onBoundsChange(bounds);
mMatrix.setRectToRect(new RectF(0, 0, mBitmap.getWidth(), mBitmap.getHeight()), new RectF(bounds),
Matrix.ScaleToFit.CENTER);
}
#Override
public int getIntrinsicHeight() {
return mBitmap.getHeight();
}
#Override
public int getIntrinsicWidth() {
return mBitmap.getWidth();
}
private void setupAnimations() {
mObjectAnimatorAngle = ObjectAnimator.ofFloat(this, "angle", 360f);
mObjectAnimatorAngle.setInterpolator(ANGLE_INTERPOLATOR);
mObjectAnimatorAngle.setDuration(ANGLE_ANIMATOR_DURATION);
mObjectAnimatorAngle.setRepeatMode(ValueAnimator.RESTART);
mObjectAnimatorAngle.setRepeatCount(ValueAnimator.INFINITE);
}
#Override
public void start() {
if (isRunning())
return;
mRunning = true;
mObjectAnimatorAngle.start();
invalidateSelf();
}
#Override
public void stop() {
if (!isRunning())
return;
mRunning = false;
mObjectAnimatorAngle.cancel();
invalidateSelf();
}
#Override
public boolean isRunning() {
return mRunning;
}
}
I have a circular image, this circular image is inside linearlayout.
I want the linearlayout to be circular as well, how can i make a linearlayout circular?
not take recntgaular space, but circular space?
this is vital for me, cause am developing sthg with circular image..and next to circular image sthg tied to it which is a linearlayout as well
You can create a circular Bitmap to use in a ImageView, using a white stroke around the image.
Then you can use a RelativeLayout, to put the image on the left and other ui elements on the right.
There is a very interesting post about this feature:
http://www.curious-creature.org/2012/12/11/android-recipe-1-image-with-rounded-corners/ It is written by Romain Guy (ex android team at Google).
You can use something like this.
This example create a circular bitmap, with around a white stroke. You can change it, adding a white stroke with a black line.
public class CircleDrawable extends Drawable {
private final BitmapShader mBitmapShader;
private final Paint mPaint;
private Paint mWhitePaint;
int circleCenterX;
int circleCenterY;
int mRadus;
private boolean mUseStroke = false;
private int mStrokePadding = 0;
public CircleDrawable(Bitmap bitmap) {
mBitmapShader = new BitmapShader(bitmap,
Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setShader(mBitmapShader);
}
public CircleDrawable(Bitmap bitmap, boolean mUseStroke) {
this(bitmap);
if (mUseStroke) {
this.mUseStroke = true;
mStrokePadding = 4;
mWhitePaint = new Paint();
mWhitePaint.setStyle(Paint.Style.FILL_AND_STROKE);
mWhitePaint.setStrokeWidth(0.75f);
mWhitePaint.setColor(Color.WHITE);
}
}
#Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
circleCenterX = bounds.width() / 2;
circleCenterY = bounds.height() / 2;
if (bounds.width() >= bounds.height())
mRadus = bounds.width() / 2;
else
mRadus = bounds.height() / 2;
}
#Override
public void draw(Canvas canvas) {
if (mUseStroke) {
canvas.drawCircle(circleCenterX, circleCenterY, mRadus, mWhitePaint);
}
canvas.drawCircle(circleCenterX, circleCenterY, mRadus - mStrokePadding, 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);
}
public boolean ismUseStroke() {
return mUseStroke;
}
public void setmUseStroke(boolean mUseStroke) {
this.mUseStroke = mUseStroke;
}
}
To use it:
CircleDrawable circle = new CircleDrawable(bitmap,true);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
imageView.setBackground(circle);
else
imageView.setBackgroundDrawable(circle);