I am here not for looking how add border to imageView I know 10000 methods.
I am here for how add rounded border ImageView specific on HOME WIDGET.
Since currently I found no way to do this. I have tried:
Using xml shape as ImageView background, it works for TextView, but not ImageView;
I tried using Glide programatically add rounded border, but that things need a bounch of thing, and I can not make it work.
Here is current code that doesn't work:
import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.SharedPreferences
import android.net.Uri
import android.widget.RemoteViews
import cn.manaai.daybreak.R
import es.antonborri.home_widget.HomeWidgetBackgroundIntent
import es.antonborri.home_widget.HomeWidgetLaunchIntent
import es.antonborri.home_widget.HomeWidgetProvider
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
import com.bumptech.glide.load.resource.bitmap.TransformationUtils
import com.bumptech.glide.load.resource.bitmap.RoundedCorners
class HomeWidgetGlanceProvider : HomeWidgetProvider(), AppCompatActivity {
// this load a todo widget, showing todos here
// so the layout here is different.
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray, widgetData: SharedPreferences) {
appWidgetIds.forEach { widgetId ->
val views = RemoteViews(context.packageName, R.layout.glance_app_widget).apply {
// Open App on Widget Click
val pendingIntent = HomeWidgetLaunchIntent.getActivity(
context,
MainActivity::class.java)
setOnClickPendingIntent(R.id.widget_container, pendingIntent)
// Swap Title Text by calling Dart Code in the Background
setTextViewText(R.id.nickname, widgetData.getString("title", null)
?: "No Title Set")
val backgroundIntent = HomeWidgetBackgroundIntent.getBroadcast(
context,
Uri.parse("homeWidgetExample://titleClicked2")
)
setOnClickPendingIntent(R.id.nickname, backgroundIntent)
val message = widgetData.getString("message", null)
setTextViewText(R.id.todonum, message
?: "12")
// Detect App opened via Click inside Flutter
val pendingIntentWithData = HomeWidgetLaunchIntent.getActivity(
context,
MainActivity::class.java,
Uri.parse("homeWidgetExample://message?message=$message"))
setOnClickPendingIntent(R.id.todonum, pendingIntentWithData)
var avatar = findViewById(R.id.avatar) as ImageView;
// avatar
Glide.with(this).load("http://p15.qhimg.com/bdm/720_444_0/t01b12dfd7f42342197.jpg")
.apply(RequestOptions.bitmapTransform(RoundedCorners(20)))
// .circleCrop()
.into(avatar)
}
appWidgetManager.updateAppWidget(widgetId, views)
}
}
}
I simplfy want my avatar to be rounded, any idea??
PS: DO NOT SEND ME ANY JAVA CODE.
I am using kotlin only.
You need to make a bitmap from the URL. And pass that bitmap to the function that I made.
fun createRoundedImage(bitmap: Bitmap): Bitmap {
val imageRounded =
Bitmap.createBitmap(bitmap.width, bitmap.height, bitmap.config)
val canvas = Canvas(imageRounded)
val paint = Paint()
paint.isAntiAlias = true
paint.shader = BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
canvas.drawRoundRect(
RectF(0f, 0f, bitmap.width.toFloat(), bitmap.height.toFloat()),
(bitmap.width / 2).toFloat(),
(bitmap.height / 2).toFloat(),
paint
)
return imageRounded
}
Now just set the round bitmap returned by the function to your image view.
Related
I have checked my code several times and don't know where is it going wrong. i am trying to build a drawing app and after doing all this code I am unable to get any output. Not able to draw on the canvas.
please help me get rid of this issue.
This is my DrawingView.kt
package com.example.drawingapp
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import androidx.core.content.res.ResourcesCompat
class DrawingView(context : Context, attrs:AttributeSet): View(context, attrs){
private var myDrawPath: CustomPath? = null
private var myCanvasBitmap: Bitmap? = null
private var myDrawPaint: Paint? = null
private var myCanvasPaint: Paint? = null
private var myCanvas: Canvas?= null
private var myBrushSize :Float = 0.toFloat()
private var myColor = Color.BLACK
init{
drawingSetUp()
}
private fun drawingSetUp(){
myDrawPaint = Paint()
myDrawPath = CustomPath(myColor,myBrushSize)
myDrawPaint!!.color= myColor
myDrawPaint!!.style = Paint.Style.STROKE
myDrawPaint!!.strokeJoin =Paint.Join.ROUND
myDrawPaint!!.strokeCap =Paint.Cap.ROUND
myCanvasPaint= Paint(Paint.DITHER_FLAG)
myBrushSize=20.toFloat()
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
myCanvasBitmap= Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
myCanvas = Canvas(myCanvasBitmap!!)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
myCanvas!!.drawBitmap(myCanvasBitmap!!, 0f,0f , myCanvasPaint)
if (!myDrawPath!!.isEmpty){
myDrawPaint!!.strokeWidth = myDrawPath!!.brushThickness
myDrawPaint!!.color= myDrawPath!!.color
myCanvas!!.drawPath(myDrawPath!!, myDrawPaint!!)
}
}
#SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent?): Boolean {
val touchX = event?.x
val touchY = event?.y
when (event?.action) {
MotionEvent.ACTION_DOWN -> {
myDrawPath!!.color = myColor
myDrawPath!!.brushThickness = myBrushSize
myDrawPath!!.reset()
if (touchX != null) {
if (touchY != null) {
myDrawPath!!.moveTo(touchX, touchX)
}
}
}
MotionEvent.ACTION_MOVE -> {
if (touchX != null) {
if (touchY != null) {
myDrawPath!!.lineTo(touchX, touchY)
}
}
}
MotionEvent.ACTION_UP->{
myDrawPath = CustomPath(myColor, myBrushSize)
}
else->return false
}
invalidate()
return true
}
internal inner class CustomPath(var color:Int, var brushThickness:Float): Path(){
}
}
This is my activity_main.xml file
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.example.drawingapp.DrawingView
android:id="#+id/drawing_view"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
And this is my MainActivity.kt file
package com.example.drawingapp
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
You're drawing to the wrong Canvas. You need to draw to the one passed in to onDraw, that's the one linked to what your View will actually display.
When you do this:
myCanvasBitmap= Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888)
myCanvas = Canvas(myCanvasBitmap!!)
you're creating your own, personal Bitmap, and creating your own personal Canvas to draw stuff on that bitmap. You can do that - but unless you draw that bitmap to the canvas onDraw gives you at some point, you'll never see it.
I'm assuming you wanted to do this:
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// draw your stored bitmap to the view's canvas
canvas.drawBitmap(myCanvasBitmap!!, 0f,0f , myCanvasPaint)
if (!myDrawPath!!.isEmpty){
myDrawPaint!!.strokeWidth = myDrawPath!!.brushThickness
myDrawPaint!!.color= myDrawPath!!.color
// draw this stored path to the view's canvas, on top of
// the stored bitmap you just drew
canvas.drawPath(myDrawPath!!, myDrawPaint!!)
}
}
i.e. drawing to canvas and not myCanvas. I don't know where you're actually drawing stuff to myCanvas though (to build up your stored image), you still need to do that so you have something to paint on the view's canvas. I'm assuming that happens elsewhere and the path you're drawing in onDraw is like a temporary overlay that isn't part of the stored image yet
Also just as a piece of advice, you need to avoid that !! stuff everywhere - that's a big sign you've made something nullable that shouldn't be nullable, and now you have to fight with the null-checking system because you're sure it can't be null but the system thinks it could be.
If you just move all your drawingSetup code into init, it'll see that you're assigning everything a value and you won't need to make them null. It's because you're assigning them through another function called from init that it can't be sure you're doing that - it's a limitation of the language unfortunately. Or you could just assign them directly:
private var myDrawPath = CustomPath(myColor,myBrushSize)
private lateinit var myCanvasBitmap: Bitmap
private var myDrawPaint = Paint().apply {
color= myColor
style = Paint.Style.STROKE
strokeJoin = Paint.Join.ROUND
strokeCap = Paint.Cap.ROUND
}
// etc
If you do want to keep that initialisation function (e.g. because you want to reset everything to normal) at least make your vars lateinit non-nullable types - if they're never going to be null when they're accessed, don't make them nullable! You just have to make sure lateinit stuff is assigned a value before something tries to read it - but the same is true for making something null and doing !! every time you access it, aka "trust me bro it's not null"
How do I show a location marker for a fixed location? All am getting from the docs is user location or current location. The task I want to achieve is to get coordinates of a certain location and have a marker appear at that particular location.
Below is the code I tried with but the marker appears somewhere else:
package com.show.show_map
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.os.Bundle
import androidx.annotation.DrawableRes
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.content.res.AppCompatResources
import com.mapbox.geojson.Point
import com.mapbox.maps.MapView
import com.mapbox.maps.Style
import com.mapbox.maps.plugin.annotation.annotations
import com.mapbox.maps.plugin.annotation.generated.PointAnnotationOptions
import com.mapbox.maps.plugin.annotation.generated.createPointAnnotationManager
import okhttp3.logging.HttpLoggingInterceptor
class AbsaLocationActivity : AppCompatActivity() {
private var mapView: MapView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_absa_location)
val BASIC = "mapbox://styles/stunupps/cl2f3q6b1002d14o6wpu3m6bq"
mapView = findViewById(R.id.mapView)
mapView?.getMapboxMap()?.loadStyleUri(
HttpLoggingInterceptor.Level.BASIC,
object : Style.OnStyleLoaded {
override fun onStyleLoaded(style: Style) {
addAnnotationToMap()
}
}
)
}
private fun addAnnotationToMap() {
// Create an instance of the Annotation API and get the PointAnnotationManager.
bitmapFromDrawableRes(
this#AbsaLocationActivity,
R.drawable.red_marker
)?.let {
val annotationApi = mapView?.annotations
val pointAnnotationManager = annotationApi?.createPointAnnotationManager()
// Set options for the resulting symbol layer.
val pointAnnotationOptions: PointAnnotationOptions = PointAnnotationOptions()
// Define a geographic coordinate.
.withPoint(Point.fromLngLat(-15.39, 28.31))
// Specify the bitmap you assigned to the point annotation
// The bitmap will be added to map style automatically.
.withIconImage(it)
// Add the resulting pointAnnotation to the map.
pointAnnotationManager?.create(pointAnnotationOptions)
}
}
private fun bitmapFromDrawableRes(context: Context, #DrawableRes resourceId: Int) =
convertDrawableToBitmap(AppCompatResources.getDrawable(context, resourceId))
private fun convertDrawableToBitmap(sourceDrawable: Drawable?): Bitmap? {
if (sourceDrawable == null) {
return null
}
return if (sourceDrawable is BitmapDrawable) {
sourceDrawable.bitmap
} else {
// copying drawable object to not manipulate on the same reference
val constantState = sourceDrawable.constantState ?: return null
val drawable = constantState.newDrawable().mutate()
val bitmap: Bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth, drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
bitmap
}
}
}
I have an App that lets users drag and draw boxes on a custom view. I want to persist the state of these boxes(list of boxes) across orientation change using onSavedInstanceState(): Parcelable and onRestoreInstanceState(state: Parcelable). However, I don't know how to store a MutableList because the only available function is putParcelableArrayList. Please how do I parcelize a Mutable List to persist the boxes across rotation? I know the docs said its possible but I don't know how to. Here is the code.
#Parcelize
class Box(private val start: PointF) : Parcelable {
// When a user touches BoxDrawingView, a new box will be created and added to the list of existing boxes.
var end: PointF = start
val left: Float
get() = start.x.coerceAtMost(end.x)
val right: Float
get() = start.x.coerceAtLeast(end.x)
val top: Float
get() = start.y.coerceAtMost(end.y)
val bottom: Float
get() = start.y.coerceAtLeast(end.y)
}
My custom View
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.PointF
import android.os.Bundle
import android.os.Parcelable
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View
/** This Class is where we setup our custom View and write the Implementation for listening to touch events from the USER and draw boxes on the Screen.**/
private const val TAG = "BoxDrawingView"
private const val BOX_STATE = "box"
private const val VIEW_STATE = "view"
class BoxDrawingView(context: Context, attrs: AttributeSet? = null) :
View(context, attrs) {
private var currentBox: Box? = null
private var boxen = mutableListOf<Box>() // list of boxes to be drawn out on the screen
private val boxPaint = Paint().apply {
color = 0x22ff0000
}
private val backGroundPaint = Paint().apply {
color = 0xfff8efe0.toInt()
}
init {
isSaveEnabled = true
}
override fun onSaveInstanceState(): Parcelable {
val bundle = Bundle()
bundle.putParcelableArrayList(BOX_STATE, boxen) // type mismatch error because of mutableList passed to ArrayList
bundle.putParcelable(VIEW_STATE, super.onSaveInstanceState())
return bundle
}
override fun onRestoreInstanceState(state: Parcelable?) {
var viewState = state
if (viewState is Bundle) {
boxen = viewState.getParcelableArrayList<Box>(BOX_STATE)?.toMutableList() ?: mutableListOf()
viewState = viewState.getParcelable(VIEW_STATE)
}
super.onRestoreInstanceState(state)
}
override fun onDraw(canvas: Canvas) {
// Fill in the background
canvas.drawPaint(backGroundPaint)
boxen.forEach { box ->
canvas.drawRect(box.left, box.top, box.right, box.bottom, boxPaint)
}
}
override fun onTouchEvent(event: MotionEvent): Boolean {
val current = PointF(event.x, event.y)
var action = ""
when(event.action) {
MotionEvent.ACTION_DOWN -> {
action = "ACTION_DOWN"
// Reset drawing state
currentBox = Box(current).also {
boxen.add(it)
}
}
MotionEvent.ACTION_MOVE -> {
action = "ACTION_MOVE"
// update the currentBox.end as the user moves his/her finger around the screen
updateCurrentBox(current)
}
MotionEvent.ACTION_UP -> {
action = "ACTION_UP"
// tells the last report of the currentBox as the user's finger leaves the screen
updateCurrentBox(current)
currentBox = null
}
MotionEvent.ACTION_CANCEL -> {
action = "ACTION_CANCEL"
currentBox = null
}
}
// this is a log message for each of the 4 Event actions
Log.i(TAG, "$action at x=${current.x}, y=${current.y}")
return true
}
Changing the boxen type to arrayList() worked. Turns out arrayList works as a mutable list under the hood. Also David wasser's answer in the comments also worked.
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().
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
)
}
}
}