Search code examples
androidkotlinlayoutimageview

Anchor an ImageView to corner of another ImageView


How to anchor an ImageView to bottom right corner of another ImageView like the ones in the above picture (1-man and 2-pencil)

I am using retrofit to load my sources as oval into ImageViews

I do this :

   Glide.with(context)
        .asBitmap()
        .load(model)
        .fitCenter()
        .apply(RequestOptions.circleCropTransform())
        .into(object : BitmapImageViewTarget(this) {
            override fun setResource(resource: Bitmap?) {
                setImageDrawable(
                    resource?.run {
                        RoundedBitmapDrawableFactory.create(
                            resources,
                            if (borderSize > 0) {
                                createBitmapWithBorder(borderSize, borderColor)
                            } else {
                                this
                            }
                        ).apply {
                            isCircular = true
                        }
                    }
                )
            }
        })

Solution

  • Base concept

    To build such layout you can use for example ConstraintLayout or RelativeLayout.

    Using RelativeLayout you can build such structure:

    <RelativeLayout
        android:layout_width="200dp"
        android:layout_height="200dp">
    
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginEnd="20dp"
            android:layout_marginBottom="20dp"
            android:background="#00f" />
    
        <ImageView
            android:layout_width="70dp"
            android:layout_height="70dp"
            android:layout_alignParentEnd="true"
            android:layout_alignParentBottom="true"
            android:background="#f0f" />
    
    </RelativeLayout>
    

    Which should look like there: enter image description here

    Small circle

    For smaller circle (grey one) you can use only one ImageView. Just set light background - as background and pen icon - as src.

    1) For ImageView background you have to create new drawable with two shapes. First ont is for white border around, second it's just light gray background.

    You can set padding second shape (here, in example, there is 5dp)

    <?xml version="1.0" encoding="utf-8"?><?xml version="1.0" encoding="utf-8"?>
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    
        <item>
            <shape android:shape="oval">
                <solid android:color="#FFFFFF" />
            </shape>
        </item>
    
        <item
            android:bottom="5dp"
            android:left="5dp"
            android:right="5dp"
            android:top="5dp">
            <shape android:shape="oval">
                <solid android:color="#CACACA" />
            </shape>
        </item>
    
    </layer-list>
    

    2) As icon you can use drawable (e.g. vector) or just image and set it as src

    After this point it should look like there:

    enter image description here

    3) Set padding in ImageView to scale your (pen) icon correctly:

    <ImageView
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:layout_alignParentEnd="true"
        android:layout_alignParentBottom="true"
        android:background="@drawable/small_circle_background"
        android:padding="16dp"
        android:src="@drawable/small_circle_icon" />
    

    Big circle

    Best approach is to use some library like Picasso or Glide.

    For Glide, you can use CircleTransform() to crop received image in to circle. When loading image you can just use transform() method:

    Glide.with(this)
        .load(URL)
        .transform(new CircleTransform(context))
        .into(imageView);
    

    When CircleTransform is:

    import android.graphics.Bitmap;
    import android.graphics.BitmapShader;
    import android.graphics.Canvas;
    import android.graphics.Paint;
    
    import androidx.annotation.NonNull;
    
    import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
    import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
    
    import org.jetbrains.annotations.NotNull;
    
    import java.security.MessageDigest;
    
    public class CircleTransform extends BitmapTransformation {
    
        @Override protected Bitmap transform(@NotNull BitmapPool pool, @NotNull Bitmap toTransform, int outWidth, int outHeight) {
            return circleCrop(pool, toTransform);
        }
    
        private static Bitmap circleCrop(BitmapPool pool, Bitmap source) {
            if (source == null) return null;
    
            int size = Math.min(source.getWidth(), source.getHeight());
            int x = (source.getWidth() - size) / 2;
            int y = (source.getHeight() - size) / 2;
    
            Bitmap squared = Bitmap.createBitmap(source, x, y, size, size);
    
            Bitmap result = pool.get(size, size, Bitmap.Config.ARGB_8888);
    
            Canvas canvas = new Canvas(result);
            Paint paint = new Paint();
            paint.setShader(new BitmapShader(squared, BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP));
            paint.setAntiAlias(true);
            float r = size / 2f;
            canvas.drawCircle(r, r, r, paint);
            return result;
        }
    
        @Override
        public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
    
        }
    }
    

    Final result

    enter image description here