Draw line with shadow - android

Right now i draw line without shadow:
override fun onDraw(canvas: Canvas, parent: RecyclerView, state: RecyclerView.State) {
val dividerStart = parent.paddingStart
val dividerEnd = parent.width - parent.paddingEnd
val endIndex = parent.adapter?.itemCount ?: 0
for (index in 0 until endIndex) {
val currentChild = parent.getChildAt(index)
if (needDrawBottomDelimiter(parent, currentChild)) {
val topOfCurrentView = currentChild.top
val startX = dividerStart.toFloat()
val topY = topOfCurrentView.toFloat() - rectHeight
val endX = dividerEnd.toFloat()
val bottomY = topOfCurrentView.toFloat()
canvas.drawRect(startX, topY, endX, bottomY, spacePaint)
canvas.drawLine(startX, bottomY, endX, bottomY, dividerPaint)
}
}
}
I know what can help me shader, but doesn't know how i can use it here.
Let me know how to use shader here.

In your layout you can do:
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:elevation="5dp"
android:background="#color/colorBlack" />

You just try the below method:
Paint paint;
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setShadowLayer(12, 0, 0, Color.YELLOW);
// Important for certain APIs
setLayerType(LAYER_TYPE_SOFTWARE, paint);
#Override
protected void onDraw(Canvas canvas)
{
canvas.drawRect(20, 20, 100, 100, paint);
}

I solved problem with comment deepak kumar
dividerPaint.setShadowLayer(12F, 0F, 0F, Color.YELLOW)
setLayerType(parent, LAYER_TYPE_SOFTWARE, dividerPaint)

Related

Path is filled with color instead showing as contour

I faced the following problem: I need to show the contour of the human face over bitmap. I have coordinates of the face contour points. Here's my code:
private fun detectContours(
it: FirebaseVisionFace,
icon: Bitmap
) {
val faceContour = it.getContour(FirebaseVisionFaceContour.FACE)
val mutableBitmap = icon.copy(
Bitmap.Config.ARGB_8888, true
)
val canvas = Canvas(mutableBitmap)
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.color = Color.parseColor("#99ff0000")
val path = Path()
path.moveTo(faceContour.points[0].x, faceContour.points[0].y)
faceContour.points.forEach {
path.lineTo(it.x, it.y)
}
path.close()
canvas.drawPath(path, paint)
binding.ivPhoto.setImageBitmap(mutableBitmap)
}
Here I use path.moveTo(...) and I expect that mutableBitmap will look like a face with the contour. But when I run the app I see the following result:
So, why the path is filled with the colour. Thanks in advance, I will appreciate any help
Fixed by changing path.drawline to canvas.drawLine
val faceContour = it.getContour(FirebaseVisionFaceContour.FACE)
var startX = faceContour.points[0].x
var startY = faceContour.points[0].y
val initialX = faceContour.points[0].x
val initialY = faceContour.points[0].y
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.color = Color.parseColor("#ff0000")
faceContour.points.forEach {
canvas.drawLine(startX, startY, it.x, it.y, paint)
startX = it.x
startY = it.y
}
canvas.drawLine(startX, startY, initialX, initialY, paint)

How to make circle image in custom image view with center crop?

I want to create Custom Image View for avatars which can work in two modes:
If android:src is empty display circle with first letter of name
Else display circle avatar
At now if I use android:src my drawable is not in center, how can i make central crop of it?
Example
Here is code of custom view:
class AvatarImageView : AppCompatImageView{
private var letter = ""
private var avatarBackgroundColor = 0
private var avatarLetterColor = 0
private val paintBackground = Paint()
private val paintLetter = Paint(Paint.LINEAR_TEXT_FLAG)
private val paintAvatarImage = Paint()
private var circleX = 0f
private var circleY = 0f
private var circleRadius = 0f
private var letterX = 0f
private var letterY = 0f
private val avatarImageRect = RectF(0f, 0f, 0f, 0f)
constructor(context : Context)
: this(context, null)
constructor(context: Context, attrs : AttributeSet?)
: this(context, attrs, 0)
constructor(context: Context, attrs : AttributeSet?, defStyleAttr : Int)
: super(context, attrs, defStyleAttr){
attrs?.let{
val array = context.obtainStyledAttributes(it, R.styleable.AvatarImageView)
letter = array.getString(R.styleable.AvatarImageView_signature)?.trim()?.substring(0..0)?.toUpperCase(Locale.ROOT) ?: ""
avatarBackgroundColor = array.getColor(R.styleable.AvatarImageView_background_color, Color.BLUE)
avatarLetterColor = array.getColor(R.styleable.AvatarImageView_letter_color, Color.WHITE)
array.recycle()
}
init()
}
private fun init(){
paintBackground.style = Paint.Style.FILL
paintBackground.color = avatarBackgroundColor
paintLetter.color = avatarLetterColor
paintLetter.textAlign = Paint.Align.CENTER
paintLetter.isAntiAlias = true
paintLetter.style = Paint.Style.FILL
drawable?.let{
paintAvatarImage.isAntiAlias = true
val shader = BitmapShader(getBitmapFromDrawable(it), Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
paintAvatarImage.shader = shader
}
}
private fun getBitmapFromDrawable(drawable: Drawable): Bitmap{
if (drawable is BitmapDrawable) {
drawable.bitmap?.let {
return drawable.bitmap
}
}
val bitmap: Bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
return bitmap
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
val availableWidth = width
val availableHeight = height
circleX = availableWidth / 2f
circleY = availableHeight / 2f
circleRadius = min(availableWidth, availableHeight) / 2f
paintLetter.textSize = availableHeight / 2f
letterX = availableWidth / 2f
letterY = availableHeight / 2f - ((paintLetter.descent() + paintLetter.ascent()) / 2f)
avatarImageRect.right = width.toFloat()
avatarImageRect.bottom = height.toFloat()
}
override fun onDraw(canvas: Canvas?) {
drawable?.let{
canvas?.drawRoundRect(avatarImageRect, width.toFloat(), height.toFloat(), paintAvatarImage)
} ?:run {
canvas?.drawCircle(circleX, circleY, circleRadius, paintBackground)
canvas?.drawText(letter, letterX, letterY, paintLetter)
}
}
}
My xml file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:padding="16dp"
tools:context=".ui.AvatarsFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<com.test.test.views.AvatarImageView
android:layout_width="32dp"
android:layout_height="32dp"
android:src="#drawable/avatar"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="10dp"
style="#style/TextAppearance.AppCompat.Medium"
android:text="Ivan Ivanov"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="10dp">
<com.test.test.views.AvatarImageView
android:layout_width="32dp"
android:layout_height="32dp"
app:signature="Ivan Ivanov"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="10dp"
style="#style/TextAppearance.AppCompat.Medium"
android:text="Ivan Ivanov"/>
</LinearLayout>
</LinearLayout>
UPDATED: I know about other libraries, but I want to do it without them.
I used a CircleImageView to display images and picasso to load images into the CircleImageView
Picasso centers and crops the image
It worked for me...
SOLVED:
I`m now using this code for crop my image from https://stackoverflow.com/a/12089127/12209565:
private fun getCircleBitmap(bitmap: Bitmap): Bitmap {
val output = Bitmap.createBitmap(
bitmap.width,
bitmap.height,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(output)
val color = -0xbdbdbe
val paint = Paint()
val rect = Rect(0, 0, bitmap.width, bitmap.height)
paint.isAntiAlias = true
canvas.drawARGB(0, 0, 0, 0)
paint.color = color
canvas.drawCircle(
bitmap.width / 2f, bitmap.height / 2f,
bitmap.width / 2f, paint
)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
canvas.drawBitmap(bitmap, rect, rect, paint)
return output
}

Android - in Android Pie (API 28) RadialGradient draws a rectangle instead of a circle

I'm trying to simulate a TorchView with a background image and a foreground image. It works well on API 27 and below, but draw a rectangle on API 28.
Any idea why it doesn't work on Android Pie?
On API 27 and below
API 28
Torch View class:
class TorchView : View, OnTouchListener {
var mBitmapBackground: Bitmap? = null
var mBitmapForeground: Bitmap? = null
var mMask: Bitmap? = null
private var mPosX = 0f
private var mPosY = 0f
private lateinit var paintMask: Paint
private lateinit var paintBackground: Paint
private lateinit var paintForeground: Paint
private var radius = 150
constructor(context: Context) : super(context) {
init()
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init()
}
fun initBitmaps(bitmapBackground: Bitmap, bitmapForeground: Bitmap, radius: Int){
this.radius = radius
mBitmapBackground = bitmapBackground
mBitmapForeground = bitmapForeground
mMask = makeRadGrad()
mPosX = (bitmapBackground.width/2 - radius).toFloat()
mPosY = (bitmapBackground.height/2 - radius).toFloat()
invalidate()
}
fun init() {
paintBackground = Paint()
paintMask = Paint()
paintMask.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
paintForeground = Paint()
paintForeground.xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_OVER)
isFocusable = true
isFocusableInTouchMode = true
this.setOnTouchListener(this)
}
public override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val mask = mMask
val bitmapForeground = mBitmapBackground
val bitmapBackground = mBitmapForeground
if(mask != null && bitmapForeground != null && bitmapBackground != null){
canvas.save()
canvas.drawBitmap(bitmapBackground, 0f, 0f, paintBackground)
canvas.drawBitmap(mask, mPosX, mPosY, paintMask)
canvas.drawBitmap(bitmapForeground, 0f, 0f, paintForeground)
canvas.restore()
}
}
private fun makeRadGrad(): Bitmap {
val gradient = RadialGradient(
radius.toFloat(), radius.toFloat(), radius.toFloat(), -0xff0100,
0x00000000, android.graphics.Shader.TileMode.CLAMP
)
val p = Paint()
p.isDither = true
p.shader = gradient
val bitmap = Bitmap.createBitmap(radius*2, radius*2, Config.ARGB_8888)
val c = Canvas(bitmap)
c.drawCircle(radius.toFloat(), radius.toFloat(), radius.toFloat(), p)
return bitmap
}
override fun onTouch(v: View?, event: MotionEvent): Boolean {
mPosX = event.x - radius
mPosY = event.y - radius
invalidate()
return true
}
}
check this https://issuetracker.google.com/issues/111819103
try this
public override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val mask = mMask
val bitmapForeground = mBitmapBackground
val bitmapBackground = mBitmapForeground
if(mask != null && bitmapForeground != null && bitmapBackground != null){
canvas.save()
canvas.drawBitmap(bitmapBackground, 0f, 0f, paintBackground)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
makeRadGradP(canvas, bitmapForeground)
} else {
canvas.drawBitmap(mask, mPosX, mPosY, paintMask)
canvas.drawBitmap(bitmapForeground, 0f, 0f, paintForeground)
}
canvas.restore()
}
}
private fun makeRadGradP(canvas: Canvas, bm: Bitmap) {
val paint = Paint()
paint.style = Paint.Style.FILL
val shader = BitmapShader(bm, TileMode.CLAMP, TileMode.CLAMP)
paint.shader = shader
val corners = Path()
corners.addCircle(mPosX + radius, mPosY + radius, radius.toFloat(), Path.Direction.CW)
canvas.drawPath(corners, paint)
val gradient = RadialGradient(
mPosX + radius, mPosY + radius, radius.toFloat(), 0x00000000,
Color.parseColor("#df000000"), TileMode.CLAMP
)
val p = Paint()
p.isDither = true
p.shader = gradient
canvas.drawCircle(mPosX + radius, mPosY + radius, radius.toFloat(), p)
}
According to the Official Document
PorterDuff was deprecated in Android API 28
You should replace PorterDuff with BlendMode
Maybe you should do some compatible work on Android API 28

Android Bitmap Circle And Draw Border [duplicate]

What i have:: I have a Imageview for which i am making image as a circle using picassso
What i what to do:: I want to add a black border for rounded image using my current implementation, how to achieve this without using third party library
Picasso.with(this)
.load("http://i.imgur.com/DvpvklR.png")
.transform(new RoundedTransformation(50, 4))
.resize(100, 100)
.centerCrop().into(imageView1);
RoundedTransformation.java
// enables hardware accelerated rounded corners
// original idea here : http://www.curious-creature.org/2012/12/11/android-recipe-1-image-with-rounded-corners/
public class RoundedTransformation implements com.squareup.picasso.Transformation {
private final int radius;
private final int margin; // dp
// radius is corner radii in dp
// margin is the board in dp
public RoundedTransformation(final int radius, final int margin) {
this.radius = radius;
this.margin = margin;
}
#Override
public Bitmap transform(final Bitmap source) {
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
Bitmap output = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
canvas.drawRoundRect(new RectF(margin, margin, source.getWidth() - margin, source.getHeight() - margin), radius, radius, paint);
if (source != output) {
source.recycle();
}
return output;
}
#Override
public String key() {
return "rounded";
}
}
EDIT
public class RoundedTransformation implements com.squareup.picasso.Transformation {
private final int radius;
private final int margin; // dp
// radius is corner radii in dp
// margin is the board in dp
public RoundedTransformation(final int radius, final int margin) {
this.radius = radius;
this.margin = margin;
}
#Override
public Bitmap transform(final Bitmap source) {
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
Bitmap output = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
canvas.drawRoundRect(new RectF(margin, margin, source.getWidth() - margin, source.getHeight() - margin), radius, radius, paint);
if (source != output) {
source.recycle();
}
Paint paint1 = new Paint();
paint1.setColor(Color.RED);
paint1.setStyle(Style.STROKE);
paint1.setAntiAlias(true);
paint1.setStrokeWidth(2);
canvas.drawCircle((source.getWidth() - margin)/2, (source.getHeight() - margin)/2, radius-2, paint1);
return output;
}
#Override
public String key() {
return "rounded";
}
}
Final transformation class, thanks to blackbelt
public class RoundedTransformation implements com.squareup.picasso.Transformation {
private final int radius;
private final int margin; // dp
// radius is corner radii in dp
// margin is the board in dp
public RoundedTransformation(final int radius, final int margin) {
this.radius = radius;
this.margin = margin;
}
#Override
public Bitmap transform(final Bitmap source) {
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
Bitmap output = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
canvas.drawCircle((source.getWidth() - margin)/2, (source.getHeight() - margin)/2, radius-2, paint);
if (source != output) {
source.recycle();
}
Paint paint1 = new Paint();
paint1.setColor(Color.RED);
paint1.setStyle(Style.STROKE);
paint1.setAntiAlias(true);
paint1.setStrokeWidth(2);
canvas.drawCircle((source.getWidth() - margin)/2, (source.getHeight() - margin)/2, radius-2, paint1);
return output;
}
#Override
public String key() {
return "rounded";
}
}
you can use drawCircle with another Paint object. For instance:
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Style.STROKE);
paint.setAntiAlias(true);
paint.setStrokeWidth(2);
canvas.drawCircle((source.getWidth() - margin)/2, (source.getHeight() - margin)/2, radius-2, paint);
Also, instead of using a drawRoundRect to draw a circle, you can use drawCircle
Answers of BlackBelt and Devrath are great. And if you are looking for a Kotlin version of this class, here it is:
class RoundedBorderTransform(private val radius: Int, private val margin: Int) : com.squareup.picasso.Transformation {
override fun transform(source: Bitmap?): Bitmap {
val paint = Paint()
paint.isAntiAlias = true
paint.shader = BitmapShader(source!!, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
val output = Bitmap.createBitmap(source.width, source.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(output)
canvas.drawCircle((source.width - margin) / 2f, (source.height - margin) / 2f, radius - 2f, paint)
if (source != output) {
source.recycle()
}
val borderPaint = Paint()
borderPaint.color = Color.RED
borderPaint.style = Paint.Style.STROKE
borderPaint.isAntiAlias = true
borderPaint.strokeWidth = 2f
canvas.drawCircle((source.width - margin) / 2f, (source.height - margin) / 2f, radius - 2f, borderPaint)
return output
}
override fun key(): String {
return "roundedBorder"
}
}
This is solution for circle and rectangle shapes. Also it's useful not only for Picasso usage, but for general android Canvas-tasks.
I've created them very voluminous and detailed only for yours understanding of processes, shorten as you want.
But I want to clarify a main problem that many people faced with.
In android there no possibilities to create inner or outer border - only centered:
And this is the reason why you receive border elements cut off like these:
So there only a single way to recalculate position of border: in case of
circle you have to decrease circle radius by HALF OF BORDER WIDTH
rectangle you have to 1) shift border lines "inside" by HALF OF BORDER WIDTH; 2) decrease corner radius by HALF OF BORDER WIDTH
These actions will provide you an expected results:
If you're need only code for you border - pick only correspondent drawBorder() method.
Here is an example for empty Fragment with two images:
ViewsCroppingAndBorderingTestFragment.kt
class ViewsCroppingAndBorderingTestFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_test_views_cropping_and_bordering, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val urlCircle = "https://images-na.ssl-images-amazon.com/images/I/71zLQIfmTlL._AC_SX466_.jpg"
val urlRectRounded = "https://www.gardendesign.com/pictures/images/675x529Max/site_3/helianthus-yellow-flower-pixabay_11863.jpg"
val ivCircle = view.findViewById<ImageView>(R.id.ivCircle)
val ivRectRounded = view.findViewById<ImageView>(R.id.ivRectRounded)
val trCircle = CircleTransformation()
val trRectRounded = RectRoundedTransformation()
Picasso.get().load(urlCircle).transform(trCircle).into(ivCircle)
Picasso.get().load(urlRectRounded).transform(trRectRounded).into(ivRectRounded)
}
class CircleTransformation() : Transformation {
override fun transform(source: Bitmap): Bitmap {
val size = Math.min(source.width, source.height)
val x = (source.width - size) / 2
val y = (source.height - size) / 2
val w = size
val h = size
val squaredBitmap = Bitmap.createBitmap(source, x, y, w, h)
if (squaredBitmap != source) {
source.recycle()
}
val bitmap = Bitmap.createBitmap(w, h, source.config)
val canvas = Canvas(bitmap)
// >> Draw original rectangle image
// val paint = Paint(Paint.ANTI_ALIAS_FLAG)
// canvas.drawBitmap(squaredBitmap, 0F, 0F, paint)
val rImage = w / 2f
cropWithCircle(squaredBitmap, canvas, rImage)
squaredBitmap.recycle()
drawBorder(canvas, rImage)
return bitmap
}
private fun cropWithCircle(bitmapSource: Bitmap, canvas: Canvas, rImage: Float) {
val shader = BitmapShader(bitmapSource, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.shader = shader
paint.isAntiAlias = true
val xCenter = rImage
val yCenter = rImage
canvas.drawCircle(xCenter, yCenter, rImage, paint)
}
private fun drawBorder(canvas: Canvas, rImage: Float) {
val borderWidth = 30F
val paintBorder = Paint()
paintBorder.strokeWidth = borderWidth
paintBorder.style = Paint.Style.STROKE
paintBorder.color = Color.GREEN
paintBorder.isAntiAlias = true
val cCenter = rImage
val yCenter = rImage
// ANDROID'S BORDER IS ALWAYS CENTERED.
// So have to reduce radius by half of border's width
val rBorder = rImage - borderWidth / 2
canvas.drawCircle(cCenter, yCenter, rBorder, paintBorder)
}
override fun key(): String {
return "circle"
}
}
class RectRoundedTransformation() : Transformation {
override fun transform(source: Bitmap): Bitmap {
val x = 0
val y = 0
val w = source.width
val h = source.height
val squaredBitmap = Bitmap.createBitmap(source, x, y, w, h)
if (squaredBitmap != source) {
source.recycle()
}
val bitmap = Bitmap.createBitmap(w, h, source.config)
val canvas = Canvas(bitmap)
// >> Draw original rectangle image
// val paint = Paint(Paint.ANTI_ALIAS_FLAG)
// canvas.drawBitmap(squaredBitmap, 0F, 0F, paint)
val rCorner = 80F
cropCorners(squaredBitmap, canvas, rCorner)
squaredBitmap.recycle()
drawBorder(canvas, rCorner)
return bitmap
}
private fun cropCorners(bitmapSource: Bitmap, canvas: Canvas, rCorner: Float) {
val width = canvas.width
val height = canvas.height
val maskBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8)
val maskCanvas = Canvas(maskBitmap)
val maskPaint = Paint(Paint.ANTI_ALIAS_FLAG)
val maskRect = RectF(0F, 0F, width.toFloat(), height.toFloat())
// 1. draw base rect
maskCanvas.drawRect(maskRect, maskPaint)
// 2. cut off inner rounded rect, leave corners
maskPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
maskCanvas.drawRoundRect(maskRect, rCorner, rCorner, maskPaint)
// 3. draw original rectangle image
val paintSource = Paint(Paint.ANTI_ALIAS_FLAG)
canvas.drawBitmap(bitmapSource, 0f, 0f, paintSource)
// 4.cut off corners
val paintCropped = Paint(Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG)
paintCropped!!.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
canvas.drawBitmap(maskBitmap!!, 0f, 0f, paintCropped)
}
private fun drawBorder(canvas: Canvas, rCorner: Float) {
val borderWidth = 30F
val paintBorder = Paint()
paintBorder.strokeWidth = borderWidth
paintBorder.style = Paint.Style.STROKE
paintBorder.color = Color.RED
paintBorder.isAntiAlias = true
// // ANDROID'S BORDER IS ALWAYS CENTERED.
// // So have to shift every side by half of border's width
val rectLeft = 0 + borderWidth / 2
val rectTop = 0 + borderWidth / 2
val rectRight = canvas.width.toFloat() - borderWidth / 2
val rectBottom = canvas.height.toFloat() - borderWidth / 2
val rectF = RectF(rectLeft, rectTop, rectRight, rectBottom)
// // So have to corner reduce radius by half of border's width
val rectRCorner = rCorner - borderWidth / 2
canvas.drawRoundRect(rectF, rectRCorner, rectRCorner, paintBorder)
}
override fun key(): String {
return "rectRounded"
}
}
}
fragment_test_views_cropping_and_bordering.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/temp"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="200dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:background="#android:color/darker_gray">
<ImageView
android:id="#+id/ivCircle"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_margin="10dp"/>
<ImageView
android:id="#+id/ivRectRounded"
android:layout_width="200dp"
android:layout_height="160dp"
android:layout_margin="10dp"/>
</LinearLayout>

Adding borders for image rounded image android

What i have:: I have a Imageview for which i am making image as a circle using picassso
What i what to do:: I want to add a black border for rounded image using my current implementation, how to achieve this without using third party library
Picasso.with(this)
.load("http://i.imgur.com/DvpvklR.png")
.transform(new RoundedTransformation(50, 4))
.resize(100, 100)
.centerCrop().into(imageView1);
RoundedTransformation.java
// enables hardware accelerated rounded corners
// original idea here : http://www.curious-creature.org/2012/12/11/android-recipe-1-image-with-rounded-corners/
public class RoundedTransformation implements com.squareup.picasso.Transformation {
private final int radius;
private final int margin; // dp
// radius is corner radii in dp
// margin is the board in dp
public RoundedTransformation(final int radius, final int margin) {
this.radius = radius;
this.margin = margin;
}
#Override
public Bitmap transform(final Bitmap source) {
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
Bitmap output = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
canvas.drawRoundRect(new RectF(margin, margin, source.getWidth() - margin, source.getHeight() - margin), radius, radius, paint);
if (source != output) {
source.recycle();
}
return output;
}
#Override
public String key() {
return "rounded";
}
}
EDIT
public class RoundedTransformation implements com.squareup.picasso.Transformation {
private final int radius;
private final int margin; // dp
// radius is corner radii in dp
// margin is the board in dp
public RoundedTransformation(final int radius, final int margin) {
this.radius = radius;
this.margin = margin;
}
#Override
public Bitmap transform(final Bitmap source) {
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
Bitmap output = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
canvas.drawRoundRect(new RectF(margin, margin, source.getWidth() - margin, source.getHeight() - margin), radius, radius, paint);
if (source != output) {
source.recycle();
}
Paint paint1 = new Paint();
paint1.setColor(Color.RED);
paint1.setStyle(Style.STROKE);
paint1.setAntiAlias(true);
paint1.setStrokeWidth(2);
canvas.drawCircle((source.getWidth() - margin)/2, (source.getHeight() - margin)/2, radius-2, paint1);
return output;
}
#Override
public String key() {
return "rounded";
}
}
Final transformation class, thanks to blackbelt
public class RoundedTransformation implements com.squareup.picasso.Transformation {
private final int radius;
private final int margin; // dp
// radius is corner radii in dp
// margin is the board in dp
public RoundedTransformation(final int radius, final int margin) {
this.radius = radius;
this.margin = margin;
}
#Override
public Bitmap transform(final Bitmap source) {
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
Bitmap output = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(output);
canvas.drawCircle((source.getWidth() - margin)/2, (source.getHeight() - margin)/2, radius-2, paint);
if (source != output) {
source.recycle();
}
Paint paint1 = new Paint();
paint1.setColor(Color.RED);
paint1.setStyle(Style.STROKE);
paint1.setAntiAlias(true);
paint1.setStrokeWidth(2);
canvas.drawCircle((source.getWidth() - margin)/2, (source.getHeight() - margin)/2, radius-2, paint1);
return output;
}
#Override
public String key() {
return "rounded";
}
}
you can use drawCircle with another Paint object. For instance:
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Style.STROKE);
paint.setAntiAlias(true);
paint.setStrokeWidth(2);
canvas.drawCircle((source.getWidth() - margin)/2, (source.getHeight() - margin)/2, radius-2, paint);
Also, instead of using a drawRoundRect to draw a circle, you can use drawCircle
Answers of BlackBelt and Devrath are great. And if you are looking for a Kotlin version of this class, here it is:
class RoundedBorderTransform(private val radius: Int, private val margin: Int) : com.squareup.picasso.Transformation {
override fun transform(source: Bitmap?): Bitmap {
val paint = Paint()
paint.isAntiAlias = true
paint.shader = BitmapShader(source!!, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
val output = Bitmap.createBitmap(source.width, source.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(output)
canvas.drawCircle((source.width - margin) / 2f, (source.height - margin) / 2f, radius - 2f, paint)
if (source != output) {
source.recycle()
}
val borderPaint = Paint()
borderPaint.color = Color.RED
borderPaint.style = Paint.Style.STROKE
borderPaint.isAntiAlias = true
borderPaint.strokeWidth = 2f
canvas.drawCircle((source.width - margin) / 2f, (source.height - margin) / 2f, radius - 2f, borderPaint)
return output
}
override fun key(): String {
return "roundedBorder"
}
}
This is solution for circle and rectangle shapes. Also it's useful not only for Picasso usage, but for general android Canvas-tasks.
I've created them very voluminous and detailed only for yours understanding of processes, shorten as you want.
But I want to clarify a main problem that many people faced with.
In android there no possibilities to create inner or outer border - only centered:
And this is the reason why you receive border elements cut off like these:
So there only a single way to recalculate position of border: in case of
circle you have to decrease circle radius by HALF OF BORDER WIDTH
rectangle you have to 1) shift border lines "inside" by HALF OF BORDER WIDTH; 2) decrease corner radius by HALF OF BORDER WIDTH
These actions will provide you an expected results:
If you're need only code for you border - pick only correspondent drawBorder() method.
Here is an example for empty Fragment with two images:
ViewsCroppingAndBorderingTestFragment.kt
class ViewsCroppingAndBorderingTestFragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_test_views_cropping_and_bordering, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val urlCircle = "https://images-na.ssl-images-amazon.com/images/I/71zLQIfmTlL._AC_SX466_.jpg"
val urlRectRounded = "https://www.gardendesign.com/pictures/images/675x529Max/site_3/helianthus-yellow-flower-pixabay_11863.jpg"
val ivCircle = view.findViewById<ImageView>(R.id.ivCircle)
val ivRectRounded = view.findViewById<ImageView>(R.id.ivRectRounded)
val trCircle = CircleTransformation()
val trRectRounded = RectRoundedTransformation()
Picasso.get().load(urlCircle).transform(trCircle).into(ivCircle)
Picasso.get().load(urlRectRounded).transform(trRectRounded).into(ivRectRounded)
}
class CircleTransformation() : Transformation {
override fun transform(source: Bitmap): Bitmap {
val size = Math.min(source.width, source.height)
val x = (source.width - size) / 2
val y = (source.height - size) / 2
val w = size
val h = size
val squaredBitmap = Bitmap.createBitmap(source, x, y, w, h)
if (squaredBitmap != source) {
source.recycle()
}
val bitmap = Bitmap.createBitmap(w, h, source.config)
val canvas = Canvas(bitmap)
// >> Draw original rectangle image
// val paint = Paint(Paint.ANTI_ALIAS_FLAG)
// canvas.drawBitmap(squaredBitmap, 0F, 0F, paint)
val rImage = w / 2f
cropWithCircle(squaredBitmap, canvas, rImage)
squaredBitmap.recycle()
drawBorder(canvas, rImage)
return bitmap
}
private fun cropWithCircle(bitmapSource: Bitmap, canvas: Canvas, rImage: Float) {
val shader = BitmapShader(bitmapSource, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
val paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.shader = shader
paint.isAntiAlias = true
val xCenter = rImage
val yCenter = rImage
canvas.drawCircle(xCenter, yCenter, rImage, paint)
}
private fun drawBorder(canvas: Canvas, rImage: Float) {
val borderWidth = 30F
val paintBorder = Paint()
paintBorder.strokeWidth = borderWidth
paintBorder.style = Paint.Style.STROKE
paintBorder.color = Color.GREEN
paintBorder.isAntiAlias = true
val cCenter = rImage
val yCenter = rImage
// ANDROID'S BORDER IS ALWAYS CENTERED.
// So have to reduce radius by half of border's width
val rBorder = rImage - borderWidth / 2
canvas.drawCircle(cCenter, yCenter, rBorder, paintBorder)
}
override fun key(): String {
return "circle"
}
}
class RectRoundedTransformation() : Transformation {
override fun transform(source: Bitmap): Bitmap {
val x = 0
val y = 0
val w = source.width
val h = source.height
val squaredBitmap = Bitmap.createBitmap(source, x, y, w, h)
if (squaredBitmap != source) {
source.recycle()
}
val bitmap = Bitmap.createBitmap(w, h, source.config)
val canvas = Canvas(bitmap)
// >> Draw original rectangle image
// val paint = Paint(Paint.ANTI_ALIAS_FLAG)
// canvas.drawBitmap(squaredBitmap, 0F, 0F, paint)
val rCorner = 80F
cropCorners(squaredBitmap, canvas, rCorner)
squaredBitmap.recycle()
drawBorder(canvas, rCorner)
return bitmap
}
private fun cropCorners(bitmapSource: Bitmap, canvas: Canvas, rCorner: Float) {
val width = canvas.width
val height = canvas.height
val maskBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8)
val maskCanvas = Canvas(maskBitmap)
val maskPaint = Paint(Paint.ANTI_ALIAS_FLAG)
val maskRect = RectF(0F, 0F, width.toFloat(), height.toFloat())
// 1. draw base rect
maskCanvas.drawRect(maskRect, maskPaint)
// 2. cut off inner rounded rect, leave corners
maskPaint.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
maskCanvas.drawRoundRect(maskRect, rCorner, rCorner, maskPaint)
// 3. draw original rectangle image
val paintSource = Paint(Paint.ANTI_ALIAS_FLAG)
canvas.drawBitmap(bitmapSource, 0f, 0f, paintSource)
// 4.cut off corners
val paintCropped = Paint(Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG)
paintCropped!!.xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
canvas.drawBitmap(maskBitmap!!, 0f, 0f, paintCropped)
}
private fun drawBorder(canvas: Canvas, rCorner: Float) {
val borderWidth = 30F
val paintBorder = Paint()
paintBorder.strokeWidth = borderWidth
paintBorder.style = Paint.Style.STROKE
paintBorder.color = Color.RED
paintBorder.isAntiAlias = true
// // ANDROID'S BORDER IS ALWAYS CENTERED.
// // So have to shift every side by half of border's width
val rectLeft = 0 + borderWidth / 2
val rectTop = 0 + borderWidth / 2
val rectRight = canvas.width.toFloat() - borderWidth / 2
val rectBottom = canvas.height.toFloat() - borderWidth / 2
val rectF = RectF(rectLeft, rectTop, rectRight, rectBottom)
// // So have to corner reduce radius by half of border's width
val rectRCorner = rCorner - borderWidth / 2
canvas.drawRoundRect(rectF, rectRCorner, rectRCorner, paintBorder)
}
override fun key(): String {
return "rectRounded"
}
}
}
fragment_test_views_cropping_and_bordering.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/temp"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="200dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:background="#android:color/darker_gray">
<ImageView
android:id="#+id/ivCircle"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_margin="10dp"/>
<ImageView
android:id="#+id/ivRectRounded"
android:layout_width="200dp"
android:layout_height="160dp"
android:layout_margin="10dp"/>
</LinearLayout>

Categories

Resources