0

Edit 4/5: My getViewAt function is now this, and still no effect occurs

    override fun getViewAt(position: Int): RemoteViews {
        val v = (context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater)
                .inflate(R.layout.widget_item, null)
                .findViewById<ProgressBar>(R.id.progressBar)
                .apply {
            this.progressBackgroundTintList = ColorStateList.valueOf(0x00ff00)
            this.progressTintList = ColorStateList.valueOf(0xffff00)
            this.max = 100
            this.progress = 0
            this.isIndeterminate = false
            this.progressDrawable = context.resources.getDrawable(R.drawable.circle_progress_bar, null)
        }

        val layer = context.resources.getDrawable(R.drawable.circle_progress_bar, null) as LayerDrawable
        layer.getDrawable(0).setColorFilter(0x03a9f4, android.graphics.PorterDuff.Mode.SRC_IN)
        layer.getDrawable(1).setColorFilter(0x8bc34a, android.graphics.PorterDuff.Mode.SRC_IN)

        View.inflate(context, R.layout.widget_item, null)
                .findViewById<ProgressBar>(R.id.progressBar)
                .progressDrawable = layer


        val r = RemoteViews(context.packageName, R.layout.widget_item).apply {
            val event = MiniModel.events[position]

//          setProgressBar(
//                  R.id.progressBar,
//                  event.totalDuration.toInt(unit = DurationUnit.SECONDS),
//                  event.timeRemaining.toInt(unit = DurationUnit.SECONDS),
//                  false
//          )

            reapply(context, v)

            setTextViewText(R.id.item_text, event.title)

            setTextViewText(R.id.details_text, if (event.isOver) "Event Complete" else "in " + event.timeRemaining.asPrettyString)
        }

        View.inflate(context, r.layoutId, null).findViewById<ProgressBar>(R.id.progressBar)
                .apply {
                    this.progressBackgroundTintList = ColorStateList.valueOf(0x00ff00)
                    this.progressTintList = ColorStateList.valueOf(0xffff00)
                    this.max = 100
                    this.progress = 0
                    this.isIndeterminate = false
                    this.progressDrawable = context.resources.getDrawable(R.drawable.circle_progress_bar, null)
                }

        return r
    }

I'm making an Android app widget which is a ListView with n rows. In each row, I have a custom circular progress indicator, which has two colors; one for the background and one for the foreground. The progress indicator's colors are different for each row, but are based on the same Drawable resource.

My question is, when setting up / updating my widget, how can I change the progress indicators colors in each row? Another challenge is that I don't have access to findViewById since I construct my widget using a RemoteViewsFactory.

@drawable/circle_progress_bar.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@android:id/background">
        <shape
            android:innerRadiusRatio="1000"
            android:shape="ring"
            android:thicknessRatio="10"
            android:useLevel="false">
            <solid android:color="#0000ff" />  <!-- I want this to be set programatically per widget row -->
        </shape>
    </item>
    <item android:id="@android:id/progress">
        <rotate
            android:fromDegrees="270"
            android:pivotX="50%"
            android:pivotY="50%"
            android:toDegrees="270">
            <shape
                android:innerRadiusRatio="1000"
                android:shape="ring"
                android:thicknessRatio="10"
                android:useLevel="true">
                <solid android:color="#ff0000" />  <!-- this too -->
            </shape>
        </rotate>
    </item>

</layer-list>

@layout/widget_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="80dp"
    android:background="@drawable/shape"
    android:gravity="center_vertical">

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:scaleX="2"
        android:scaleY="2"
        android:layout_width="100dp"
        android:layout_height="60dp"
        android:layout_weight="0"
        android:indeterminate="false"
        android:max="100"
        android:progress="25"
        android:progressDrawable="@drawable/circle_progress_bar" />
</LinearLayout>

EventWidgetService.kt

class EventWidgetService : RemoteViewsService() {
    override fun onGetViewFactory(intent: Intent): RemoteViewsFactory =
            EventRemoteViewsFactory(this.applicationContext, intent)
}

class EventRemoteViewsFactory(private val context: Context, intent: Intent) : RemoteViewsService.RemoteViewsFactory {
    private val prefsName = "FlutterSharedPreferences"

    override fun onCreate() {
        MiniModel.initialize(context.getSharedPreferences(prefsName, Context.MODE_PRIVATE))
    }

    override fun getLoadingView(): RemoteViews = RemoteViews(context.packageName, R.id.emptyView)

    override fun getItemId(position: Int): Long = position.toLong()

    override fun onDataSetChanged() {
    }

    override fun hasStableIds(): Boolean = true

    @ExperimentalTime
    override fun getViewAt(position: Int): RemoteViews {
        return RemoteViews(context.packageName, R.layout.widget_item).apply {
            val event = MiniModel.events[position]

            setProgressBar(
                    R.id.progressBar,
                    (event.end.epochSecond - event.start.epochSecond).toInt(),
                    event.secondsRemaining.toInt(unit = TimeUnit.SECONDS),
                    false
            )

            setTextViewText(R.id.item_text, event.title)

            setTextViewText(R.id.details_text, if (event.isOver) "Event Complete" else "in " + event.secondsRemaining.asPrettyString)
        }
    }

    override fun getCount(): Int = MiniModel.events.count()

    override fun getViewTypeCount(): Int = 1

    override fun onDestroy() {
    }

}

Edits 1 / 2 Summary Reflection doesn't work

Edit 3: In my getViewAt(Int): RemoteViews function, I also tried adding

View.inflate(context, R.layout.widget_item, null).findViewById<ProgressBar(R.id.progressBar).apply {
    progressBackgroundTintList = ColorStateList.valueOf(0x00ff00)
    progressTintList = ColorStateList.valueOf(0xffff00)
}

however this does not have any effect.

Richard Robinson
  • 867
  • 1
  • 11
  • 38
  • Then use 2 progress bars in xml with different progress drawables and changing the visibility is the only work around I can think of. – sravs Jan 03 '20 at 07:36
  • this won't work either because the progress bar color should be different for each row in the listView, and changing the visibility of one would change it throughout the list – Richard Robinson Jan 04 '20 at 00:00

2 Answers2

0

Use setProgressBackgroundTintList method of RemoteViews to change blue color and setProgressTintList to change red color.

sravs
  • 330
  • 2
  • 14
  • These methods aren't for `RemoteViews` though; they're for `ProgressBar`, however my Progress Bar is implemented via XML and not programatically so I can't call them – Richard Robinson Jan 03 '20 at 04:40
  • similar to setProgressBar those are methods in RemoteViews.java. You can call them. Just write it below setProgressBar method and see the parameter need to be passed. Just give it a try – sravs Jan 03 '20 at 04:43
  • I did, and it is an 'Unresolved reference' error (https://developer.android.com/reference/android/widget/ProgressBar.html#setProgressBackgroundTintList(android.content.res.ColorStateList)) – Richard Robinson Jan 03 '20 at 04:46
  • I'm using Kotlin if that makes any difference – Richard Robinson Jan 03 '20 at 04:47
  • No, Kotlin doesn't make any difference. They used @hide annotation for those methods. Don't know why. – sravs Jan 03 '20 at 05:24
  • Try this https://stackoverflow.com/questions/28136963/how-to-change-the-progressbar-color-in-a-widget answered by Oleksandr Albul – sravs Jan 03 '20 at 05:26
0

Keep the same xml and use the below code to change color at runtime.

val layerDrawable = resources.getDrawable(R.drawable.circle_progress_bar) as LayerDrawable

layerDrawable.getDrawable(0).setColorFilter(Color.parseColor("#FF03A9F4"), android.graphics.PorterDuff.Mode.SRC_IN) layerDrawable.getDrawable(1).setColorFilter(Color.parseColor("#FF8BC34A"), android.graphics.PorterDuff.Mode.SRC_IN)

progressBar.progressDrawable = layerDrawable

Akarsh M
  • 1,629
  • 2
  • 24
  • 47