I'm trying to create a custom ImageView or Drawable in Kotlin which enables dynamic file extensions can be drawn on a base image at runtime. The end result will look like this. Tried creating custom AppCompatImageView class and overriding onDraw() with no luck. Being a novice in this area, can you suggest me a good starting point to achieve this?
EDIT
The file extension is a text that needs to be drawn on the base image with a background as shown in the attachment.
You could create a LayerDrawable at runtime resulting in the superposition of two drawables (one for the background and one for the extension) and position the extension drawable at the bottom right.
It would look like this
val layerDrawable = LayerDrawable(
arrayOf(
AppCompatResources.getDrawable(context, R.drawable.ic_base_sound_file),
AppCompatResources.getDrawable(context, R.drawable.ic_aiff_extension)
)
).apply {
setLayerInset(1, 20, 40, 0, 10)
}
imageView.setImageDrawable(layerDrawable)
The method setLayerInset(index, left, top, right, bottom) will add insets to the drawable at position 'index' (here 1 -> the extension drawable).
You can also use a remote image if needed for the base image.
I prefer to use a custom view than a custom drawable. because of its flexibility in measuring and customizing height and width.
So I've created the FileView:
import android.content.Context
import android.content.res.Resources
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.text.TextPaint
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatImageView
class FileView #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr) {
init {
setImageResource(R.drawable.ic_file)
}
var icon: Drawable? = null
set(value) {
field = value
postInvalidate()
}
var ext: CharSequence? = null
set(value) {
field = value
postInvalidate()
}
private val iconRect = Rect()
private val extRect = Rect()
private val extPaint by lazy {
TextPaint().apply {
style = Paint.Style.FILL
color = Color.WHITE
isAntiAlias = true
textAlign = Paint.Align.CENTER
textSize = 12f * Resources.getSystem().displayMetrics.density + 0.5f
}
}
private val extBackgroundPaint by lazy {
TextPaint().apply {
style = Paint.Style.FILL
color = Color.BLACK
isAntiAlias = true
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val centerX = width / 2
val centerY = height / 2
icon?.let { icon ->
iconRect.set(
centerX - icon.intrinsicWidth / 2,
centerY - icon.intrinsicHeight / 2,
centerX + icon.intrinsicWidth / 2,
centerY + icon.intrinsicHeight / 2
)
icon.bounds = iconRect
icon.draw(canvas)
}
ext?.let { ext ->
val truncatedExt =
if (ext.length > 6) ext.subSequence(0, 6).toString().plus('…')
else ext
// extRect is used for measured ext height
extPaint.getTextBounds("X", 0, 1, extRect)
val extHeight = extRect.height() // keep ext height
val extWidth = extPaint.measureText(truncatedExt, 0, truncatedExt.length).toInt() // keep ext width
val extPadding = 4.toPx
val extMargin = 4.toPx
val extRight = width - extMargin
val extBottom = height - extMargin
// extRect is reused for ext background bound
extRect.set(
extRight - extWidth - extPadding * 2,
extBottom - extHeight - extPadding * 2,
extRight,
extBottom
)
canvas.drawRect(extRect, extBackgroundPaint)
canvas.drawText(
truncatedExt,
0,
truncatedExt.length,
extRect.exactCenterX(),
extRect.bottom - ((extRect.height() - extHeight) / 2f),
extPaint
)
}
}
private val Int.toPx get() = (this * Resources.getSystem().displayMetrics.density).toInt()
}
and use it:
with(binding.fileView) {
icon = ContextCompat.getDrawable(context, R.drawable.ic_music)
ext = ".aiff"
}
Output:
I can think of 2 solutions for this. I am simply sharing ideas/approaches here and related code can easily be found.
Simpler approach would be to have this layout designed in your xml. Then create your custom class extending ViewGroup and in its constructor you can inflate the view xml and initialise things. Then you can define any helper method ,say setData() where you can pass the file extension info and/or thumb image. Then you can update your view right there.
Another approach would be to not create any xml but programmatically create them in your custom ViewGroup constructor. Then you can have a similar helper method as above to set values to various view components. After you have set everything, call requestLayout() at the end. You can then, if required, update views in onLayout() and perform any spacing/margin calculations. Then using these values draw them inside onDraw().
Related
I need to compare, for example, two straight lines. One of these lines will be set by me initially (or drawn), will have certain coordinates and thickness. The second one will be drawn by the user. Finally, I need to see the result of comparing these lines as a percentage. Can it be implement like in the following code below? I had an idea to draw my own line, put its coordinates from Path into an array somehow, and then compare it with the new one. But i don't know.
And, and yet, can you tell me how to make the background transparent? I am not satisfied with the replacement of a white background with a static picture, the animation will take place in the far background
Thank you!
enter image description here
import android.content.Context
import android.graphics.*
import android.util.Log
import android.view.View
import android.view.MotionEvent
import android.view.ViewConfiguration
import androidx.core.content.res.ResourcesCompat
class SomeDraw(context: Context) : View(context) {
// Holds the path you are currently drawing.
private var path = Path()
private val drawColor = ResourcesCompat.getColor(resources, R.color.purple_700, null)
private val backgroundColor = ResourcesCompat.getColor(resources, R.color.transparent, null)
private lateinit var extraCanvas: Canvas
private lateinit var extraBitmap: Bitmap
private lateinit var frame: Rect
// Set up the paint with which to draw.
private val paint = Paint().apply {
color = drawColor
// Smooths out edges of what is drawn without affecting shape.
isAntiAlias = true
// Dithering affects how colors with higher-precision than the device are down-sampled.
isDither = true
style = Paint.Style.STROKE // default: FILL
strokeJoin = Paint.Join.ROUND // default: MITER
strokeCap = Paint.Cap.ROUND // default: BUTT
strokeWidth = 70.0F // default: Hairline-width (really thin)
}
/**
* Don't draw every single pixel.
* If the finger has has moved less than this distance, don't draw. scaledTouchSlop, returns
* the distance in pixels a touch can wander before we think the user is scrolling.
*/
private val touchTolerance = ViewConfiguration.get(context).scaledTouchSlop
private var currentX = 0f
private var currentY = 0f
private var motionTouchEventX = 0f
private var motionTouchEventY = 0f
/**
* Called whenever the view changes size.
* Since the view starts out with no size, this is also called after
* the view has been inflated and has a valid size.
*/
override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
super.onSizeChanged(width, height, oldWidth, oldHeight)
if (::extraBitmap.isInitialized) extraBitmap.recycle()
extraBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
extraCanvas = Canvas(extraBitmap)
extraCanvas.drawColor(backgroundColor)
// Calculate a rectangular frame around the picture.
val inset = 0
frame = Rect(inset, inset, width - inset, height - inset)
}
override fun onDraw(canvas: Canvas) {
// Draw the bitmap that has the saved path.
canvas.drawBitmap(extraBitmap, 0f, 0f, null)
// Draw a frame around the canvas.
extraCanvas.drawRect(frame, paint)
}
/**
* No need to call and implement MyCanvasView#performClick, because MyCanvasView custom view
* does not handle click actions.
*/
override fun onTouchEvent(event: MotionEvent): Boolean {
motionTouchEventX = event.x
motionTouchEventY = event.y
when (event.action) {
MotionEvent.ACTION_DOWN -> touchStart()
MotionEvent.ACTION_MOVE -> touchMove()
MotionEvent.ACTION_UP -> touchUp()
}
return true
}
/**
* The following methods factor out what happens for different touch events,
* as determined by the onTouchEvent() when statement.
* This keeps the when conditional block
* concise and makes it easier to change what happens for each event.
* No need to call invalidate because we are not drawing anything.
*/
private fun touchStart() {
path.reset()
path.moveTo(motionTouchEventX, motionTouchEventY)
currentX = motionTouchEventX
currentY = motionTouchEventY
}
private fun touchMove() {
val dx = Math.abs(motionTouchEventX - currentX)
val dy = Math.abs(motionTouchEventY - currentY)
if (dx >= touchTolerance || dy >= touchTolerance) {
// QuadTo() adds a quadratic bezier from the last point,
// approaching control point (x1,y1), and ending at (x2,y2).
path.quadTo(currentX, currentY, (motionTouchEventX + currentX) / 2, (motionTouchEventY + currentY) / 2)
currentX = motionTouchEventX
currentY = motionTouchEventY
// Draw the path in the extra bitmap to save it.
extraCanvas.drawPath(path, paint)
val pathTrue = path
Log.d("AAA", ""+pathTrue.equals(path))
}
// Invalidate() is inside the touchMove() under ACTION_MOVE because there are many other
// types of motion events passed into this listener, and we don't want to invalidate the
// view for those.
invalidate()
}
private fun touchUp() {
// Reset the path so it doesn't get drawn again.
path.reset()
}
}
I tried to work with Path but nothing worked. Tried various ways to set transparency to the background (color, apply alpha, delete) - nothing helped
I have built a custom view class called progressCircle which is a snapchat-like, circular progress bar - this is inside a constraint layout, over a circular button.
This view has parameter angle which when called from onViewCreated(), will work perfectly if run
progressCircle.angle = 100f
However, I am trying to animate this onClick. If I run this same code, onClick, the progressCircle will not show up?! After trial and error, I found that updating the background colour here made the view visible & it was updated. IE;
button.setOnClickListener {
progressCircle.setBackgroundColor(android.R.color.transparent)
progressCircle.angle = 270f
}
Whats going on here, and how can I fix this so I can animate it properly...
Edit:
class ProgressCircle(context: Context, attrs: AttributeSet) : View(context, attrs) {
private val paint: Paint
private val rect: RectF
private val fillPaint: Paint
private val fillRect: RectF
var angle: Float
var startAngle: Float = 0f
init {
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ProgressCircle)
angle = typedArray.getFloat(R.styleable.ProgressCircle_angle, 0f)
typedArray.recycle()
val offsetAngle = 0f
val color = getColor(context, R.color.outputON)
val strokeWidth = 15f
val circleSize = 276f
paint = Paint().apply {
setAntiAlias(true)
setStyle(Paint.Style.STROKE)
setStrokeWidth(strokeWidth)
setColor(color)
}
rect = RectF(
strokeWidth,
strokeWidth,
(circleSize - strokeWidth),
(circleSize - strokeWidth)
)
fillPaint = Paint().apply {
setAntiAlias(true)
setStyle(Paint.Style.FILL)
setColor(getColor(context, R.color.flat_blue_1))
}
val offsetFill = strokeWidth
fillRect = RectF(
offsetFill,
offsetFill,
(circleSize - offsetFill),
(circleSize - offsetFill)
)
//Initial Angle (optional, it can be zero)
angle = offsetAngle
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (getColor(context, R.color.flat_blue_1) > 0) {
canvas.drawArc(rect, 0f, 360f, false, fillPaint)
}
canvas.drawArc(rect, startAngle, angle, false, paint)
}
}
TIA
By default, a View only redraws itself when something has changed - i.e., when the view is "invalidated" as per the View documentation:
Drawing is handled by walking the tree and recording the drawing commands of any View that needs to update. After this, the drawing commands of the entire tree are issued to screen, clipped to the newly damaged area.
If you want your custom view to redraw itself when the angle property is changed, you need to call invalidate():
var angle: Float
set(value) {
// Do the default behavior of setting the value
field = value
// Then call invalidate() to force a redraw
invalidate()
}
}
Suppose I have a single TextView like this
As you can see, the text is broken into three lines.
Is there any way that I can get the text areas in Rects? As this text is broken into three lines, I would need three Rects.
It is important to highlight that the left of a Rect is the left of the first character of the line, and the right is the right of the last character of the line.
Onik has the right idea, but the results will all be relative to zero. You will have to do a little more computation if you want to know where in the canvas the text of your TextView lies.
Here is a custom TextView that will outline the text on the screen.
class CustomTextView #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = android.R.attr.textViewStyle
) : AppCompatTextView(context, attrs, defStyleAttr) {
private val mPaint = Paint().apply {
strokeWidth = 2f
style = Paint.Style.STROKE
color = Color.RED
}
private val mLineOutline = Rect()
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.save()
// This is the view's padding which we want to ignore.
canvas.translate(totalPaddingLeft.toFloat(), totalPaddingTop.toFloat())
for (line in 0 until lineCount) {
// This gets the outline of the text on a line but it is all relative to zero.
paint.getTextBounds(
text.toString(), layout.getLineStart(line), layout.getLineEnd(line), mLineOutline
)
canvas.save()
// We have the outline relative to zero, shift it so it outlines the text.
canvas.translate(layout.getLineLeft(line), layout.getLineBaseline(line).toFloat())
canvas.drawRect(mLineOutline, mPaint)
canvas.restore()
}
canvas.restore()
}
}
This is what is displayed:
You might not need this TextView, but you can grab its computations.
I find this posting very helpful when thinking about Android typography.
I'd do it as follows (in Kotlin):
var lineStart = 0
var lineEnd = 0
var lineText = ""
val paint = textView.paint
val rectList = arrayListOf<Rect>()
for (i in 0 until textView.lineCount) {
lineStart = textView.layout.getLineStart(i)
lineEnd = textView.layout.getLineEnd(i)
lineText = textView.text.substring(lineStart, lineEnd)
val rect = Rect()
paint.getTextBounds(lineText, 0, lineText.length - 1, rect)
rectList.add(rect)
}
I'm trying to replace menu icon with cross icon, but I don't know better solution than replacing source in ImageView and futher more I cannot find done libraries which converts images.
Any help will be appreciateg.
Android has a drawable for animating between hamburger and arrow: android.support.v7.graphics.drawable.DrawerArrowDrawable
This drawable uses very generic approach with canvas drawing. If you have some spare time and ready for some tedious work, you can animate pretty much anything by looking at this example.
For instance, here is "hamburger" to cross drawable:
/**
* Simple animating drawable between the "hamburger" icon and cross icon
*
* Based on [android.support.v7.graphics.drawable.DrawerArrowDrawable]
*/
class HamburgerCrossDrawable(
/** Width and height of the drawable (the drawable is always square) */
private val size: Int,
/** Thickness of each individual line */
private val barThickness: Float,
/** The space between bars when they are parallel */
private val barGap: Float
) : Drawable() {
private val paint = Paint()
private val thick2 = barThickness / 2.0f
init {
paint.style = Paint.Style.STROKE
paint.strokeJoin = Paint.Join.MITER
paint.strokeCap = Paint.Cap.BUTT
paint.isAntiAlias = true
paint.strokeWidth = barThickness
}
override fun draw(canvas: Canvas) {
if (progress < 0.5) {
drawHamburger(canvas)
} else {
drawCross(canvas)
}
}
private fun drawHamburger(canvas: Canvas) {
val bounds = bounds
val centerY = bounds.exactCenterY()
val left = bounds.left.toFloat() + thick2
val right = bounds.right.toFloat() - thick2
// Draw middle line
canvas.drawLine(
left, centerY,
right, centerY,
paint)
// Calculate Y offset to top and bottom lines
val offsetY = barGap * (2 * (0.5f - progress))
// Draw top & bottom lines
canvas.drawLine(
left, centerY - offsetY,
right, centerY - offsetY,
paint)
canvas.drawLine(
left, centerY + offsetY,
right, centerY + offsetY,
paint)
}
private fun drawCross(canvas: Canvas) {
val bounds = bounds
val centerX = bounds.exactCenterX()
val centerY = bounds.exactCenterY()
val crossHeight = barGap * 2 + barThickness * 3
val crossHeight2 = crossHeight / 2
// Calculate current cross position
val distanceY = crossHeight2 * (2 * (progress - 0.5f))
val top = centerY - distanceY
val bottom = centerY + distanceY
val left = centerX - crossHeight2
val right = centerX + crossHeight2
// Draw cross
canvas.drawLine(
left, top,
right, bottom,
paint)
canvas.drawLine(
left, bottom,
right, top,
paint)
}
override fun setAlpha(alpha: Int) {
if (alpha != paint.alpha) {
paint.alpha = alpha
invalidateSelf()
}
}
override fun setColorFilter(colorFilter: ColorFilter?) {
paint.colorFilter = colorFilter
invalidateSelf()
}
override fun getIntrinsicWidth(): Int {
return size
}
override fun getIntrinsicHeight(): Int {
return size
}
override fun getOpacity(): Int {
return PixelFormat.TRANSLUCENT
}
/**
* Drawable color
* Can be animated
*/
var color: Int = 0xFFFFFFFF.toInt()
set(value) {
field = value
paint.color = value
invalidateSelf()
}
/**
* Animate this property to transition from hamburger to cross
* 0 = hamburger
* 1 = cross
*/
var progress: Float = 0.0f
set(value) {
field = value.coerceIn(0.0f, 1.0f)
invalidateSelf()
}
}
You can use this drawable like any other drawable, for example by setting ImageView src to this drawable:
imageView = AppCompatImageView(context)
addView(imageView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
hamburgerDrawable = HamburgerCrossDrawable(
size = dpToPx(20).toInt(),
barThickness = dpToPx(2),
barGap = dpToPx(5)
)
hamburgerDrawable.color = hamburgerColor
imageView.setImageDrawable(hamburgerDrawable)
To make the drawable actually change from hamburger to cross and back you'll need to change HamburgerCrossDrawable.progress (0 stands for hamburger and 1 stands for cross):
val animator = ValueAnimator.ofFloat(0.0f, 1.0f)
animator.interpolator = AccelerateDecelerateInterpolator()
animator.duration = 300
animator.addUpdateListener {
val progress = it.animatedValue as Float
val color = interpolateColor(hamburgerColor, crossColor, progress)
hamburgerDrawable.color = color
hamburgerDrawable.progress = progress
}
animator.start()
I seem to be a little late to the show, but I prefer a declarative approach with an animated selector for icon animations. It seems a lot clearer and the only thing you need to care about are the View or Button's states.
TL;DR: I've created a gist with all of the classes needed to achieve the animation.
Here's an example of the selector which you use as a drawable:
<?xml version="1.0" encoding="utf-8"?>
<animated-selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/open"
android:drawable="#drawable/ic_drawer_closed"
android:state_selected="true"/>
<item
android:id="#+id/closed"
android:drawable="#drawable/ic_drawer"/>
<transition
android:drawable="#drawable/avd_drawer_close"
android:fromId="#id/open"
android:toId="#id/closed"/>
<transition
android:drawable="#drawable/avd_drawer_open"
android:fromId="#id/closed"
android:toId="#id/open"/>
</animated-selector>
And here's the animation itself:
You shoud use existing AnimatedVectorDrawable on the internet. Or you should create it with your icons on Shape Shifter web site. I recommend you to watch a tutrial on youtube for this process: ShapeShifter Tutorial.
Handle the state change of the button by setting a state value in the tag of the button and change the Animatable vector drawable resource according to the value of the current solution may also work.
btnMenuView.setOnClickListener {
val state = it.getTag(R.string.meta_tag_menu_button_state).toString().trim()
context?.let {
var animDrawable =
AnimatedVectorDrawableCompat.create(it, R.drawable.avd_drawer_open)
if (state != "open") {
animDrawable =
AnimatedVectorDrawableCompat.create(it, R.drawable.avd_drawer_close)
btnMenuView.setTag(R.string.meta_tag_menu_button_state, "open")
} else {
btnMenuView.setTag(R.string.meta_tag_menu_button_state, "close")
}
btnMenuView.setImageDrawable(animDrawable)
val animatable: Drawable? = btnMenuView.drawable
if (animatable is Animatable) {
animatable.start()
}
}
---Rest of your code
}
I need insert a space between the stars of my ratingBar, example the ratingbar is well:
but I need it thus:
how i can do this?
I don't know if it will be useful anymore, but I made a custom library which allows you to change space beetwen stars programatically and in XML (among other stuff): SimpleRatingBar.
It features:
Fully working android:layout_width: it can be set to wrap_content, match_parent or abritary dp.
Arbitrary number of stars.
Arbitrary step size.
Size of stars can be controlled exactly or by setting a maximum size.
Customizable colors in normal state (border, fill and background of stars and rating bar).
Customizable colors in pressed state (border, fill and background of stars and rating bar).
Customizable size separation between stars.
Customizable border width of stars.
Customizable stars corner radius.
Allows to set OnRatingBarChangeListener
Stars fill can be set to start from left to right or from right to left (RTL language support).
AnimationBuilder integrated in the view to set rating programatically with animation.
Here is a preview of it.
In your case, you would just have to do:
ratingbar.setStarsSeparation(20, Dimension.DP);
or, for example, in pixels:
ratingbar.setStarsSeparation(100, Dimension.PX);
You can find it either in jcenter or in Maven Central. So in your build.gradle file just add to your dependencies:
compile 'com.iarcuschin:simpleratingbar:0.1.+'
You have a next property.
android:progressDrawable = "#drawable/rating_stars"
android:indeterminateDrawable = "#drawable/rating_stars"
#drawable/rating_stars :
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#+android:id/background"
android:drawable="#drawable/star_empty" />
<item android:id="#+android:id/secondaryProgress"
android:drawable="#drawable/star_empty" />
<item android:id="#+android:id/progress"
android:drawable="#drawable/star" />
</layer-list>
star_empty and star are the images which are in your drawable directory.
So, you can change star and star_empty in a graphic editor and add a spassing if you need.
Just use the custom icon for it and do use the style , i mean Photoshop it as you want it to look and replace with the system rating style icon.
I think you'd have to grab a copy of the systems star png file and manually add the padding that you want to it with a photoshop / gimp / some other editor.
I agree to Tim
i applied same logic and it worked.
Here in my project i am using my own star images for the star ratin
I made star images having extra space (padding) on the right side that resulted in space between the stars
You can use Custom SVG and Set Your Separation value
By using this class, you can fix Android custom SVG RatingBar and set Drawable End by replacing value(I marked this value as There_You_Can_Set_Your_Value) inside the class.
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapShader
import android.graphics.Canvas
import android.graphics.Shader
import android.graphics.drawable.*
import android.graphics.drawable.shapes.RoundRectShape
import android.graphics.drawable.shapes.Shape
import android.util.AttributeSet
import android.view.Gravity
import androidx.appcompat.graphics.drawable.DrawableWrapper
import androidx.appcompat.widget.AppCompatRatingBar
class RatingBarSvg #JvmOverloads
constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = R.attr.ratingBarStyle,
) : AppCompatRatingBar(context, attrs, defStyleAttr) {
private var mSampleTile: Bitmap? = null
private val drawableShape: Shape
get() {
val roundedCorners = floatArrayOf(5f, 5f, 5f, 5f, 5f, 5f, 5f, 5f)
return RoundRectShape(roundedCorners, null, null)
}
init {
val drawable = tileify(progressDrawable, false) as LayerDrawable
//drawable.findDrawableByLayerId(android.R.id.background).setColorFilter(backgroundTintColor, PorterDuff.Mode.SRC_ATOP);
//drawable.findDrawableByLayerId(android.R.id.progress).setColorFilter(progressTintColor, PorterDuff.Mode.SRC_ATOP);
progressDrawable = drawable
}
/**
* Converts a drawable to a tiled version of itself. It will recursively
* traverse layer and state list drawables.
*/
#SuppressLint("RestrictedApi")
private fun tileify(drawable: Drawable, clip: Boolean): Drawable {
if (drawable is DrawableWrapper) {
var inner: Drawable? = drawable.wrappedDrawable
if (inner != null) {
inner = tileify(inner, clip)
drawable.wrappedDrawable = inner
}
} else if (drawable is LayerDrawable) {
val numberOfLayers = drawable.numberOfLayers
val outDrawables = arrayOfNulls<Drawable>(numberOfLayers)
for (i in 0 until numberOfLayers) {
val id = drawable.getId(i)
outDrawables[i] = tileify(
drawable.getDrawable(i),
id == android.R.id.progress || id == android.R.id.secondaryProgress
)
}
val newBg = LayerDrawable(outDrawables)
for (i in 0 until numberOfLayers) {
newBg.setId(i, drawable.getId(i))
}
return newBg
} else if (drawable is BitmapDrawable) {
val tileBitmap = drawable.bitmap
if (mSampleTile == null) {
mSampleTile = tileBitmap
}
val shapeDrawable = ShapeDrawable(drawableShape)
val bitmapShader = BitmapShader(
tileBitmap,
Shader.TileMode.REPEAT, Shader.TileMode.CLAMP
)
shapeDrawable.paint.shader = bitmapShader
shapeDrawable.paint.colorFilter = drawable.paint.colorFilter
return if (clip)
ClipDrawable(
shapeDrawable, Gravity.START,
ClipDrawable.HORIZONTAL
)
else
shapeDrawable
} else {
return tileify(getBitmapDrawableFromVectorDrawable(drawable), clip)
}
return drawable
}
private fun getBitmapDrawableFromVectorDrawable(drawable: Drawable): BitmapDrawable {
val bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth + (**There_You_Can_Set_Your_Value**).toInt(), //dp between svg images //* resources.displayMetrics.density
drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
drawable.draw(canvas)
return BitmapDrawable(resources, bitmap)
}
#Synchronized
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
if (mSampleTile != null) {
val width = mSampleTile!!.width * numStars
setMeasuredDimension(
resolveSizeAndState(width, widthMeasureSpec, 0),
measuredHeight
)
}
}
}