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.

2
  • Then use 2 progress bars in xml with different progress drawables and changing the visibility is the only work around I can think of. Commented Jan 3, 2020 at 7: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 Commented Jan 4, 2020 at 0:00

2 Answers 2

0

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

Sign up to request clarification or add additional context in comments.

6 Comments

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
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
I did, and it is an 'Unresolved reference' error (developer.android.com/reference/android/widget/…)
I'm using Kotlin if that makes any difference
No, Kotlin doesn't make any difference. They used @hide annotation for those methods. Don't know why.
|
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

5 Comments

what're resources and layer_list in this case? and does this change the background or foreground color?
wait @RichardRobinson
I have tested now, color is changing at runtime @RichardRobinson
Nope, doesn't work. Keep in mind that this is in a RemoteViewsFactory class and hence findViewById doesn't work
Edit 3 -> have to try in this section

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.