I need to paint an overlay image onto a SeekBar
in my ListView
rows. This isn't too hard to do and the code in this question accomplishes that by painting the row number in a bitmap and placing it as the progress drawable.
The problem that I am having is that this only works one time. When the view is recycled to be used for another row, the drawable disappears.
The first 10 views (positions 0 through 9) worked. Everything after that fails. There is no error or exception. Android just stops drawing that layer. If I scroll down and then back up, none of them work (as they have all been recycled).
How do I get Android to always draw this overlayed image? I imagine there is some sort of caching or invalidation that I need to perform, but I just don't know what it is yet.
public class ViewTest extends ListActivity {
// replace the progress drawable with my custom drawable
public void setSeekBarOverlay(SeekBar seekBar, String overlayText) {
Bitmap bitmap = Bitmap.createBitmap(100, 12, Bitmap.Config.ARGB_4444);
new Canvas(bitmap).drawText(overlayText, 10, 12, new Paint());
Drawable d = new BitmapDrawable(bitmap);
((LayerDrawable) seekBar.getProgressDrawable()).setDrawableByLayerId(android.R.id.progress, d);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setListAdapter(new ListAdapter() {
// return the view. Recycle them if possible.
public View getView(int pos, View v, ViewGroup vg) {
if (v == null) {
v = View.inflate(ViewTest.this, R.layout.seekbar, null);
}
String s = String.valueOf(pos);
((TextView) v.findViewById(android.R.id.text1)).setText(s);
setSeekBarOverlay((SeekBar) v.findViewById(R.id.seekbar), s);
return v;
}
... cut ...
});
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<ListView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
seekbar.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="wrap_content"
android:gravity="center_vertical"
android:minHeight="?android:attr/listPreferredItemHeight">
<TextView
android:id="@android:id/text1"
android:textAppearance="?android:attr/textAppearanceLarge"
android:layout_width="0dp"
android:layout_weight="1"
android:gravity="center"
android:layout_height="wrap_content" />
<SeekBar
android:id="@+id/seekbar"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content" />
</LinearLayout>
This is a tough one that I don't think will get much traffic, so I'm going to answer it myself since I found the solution.
Another SO question/answer, Android ProgressBar.setProgressDrawable only works once?, got me halfway there but the solution didn't work for me. In the other question, the code was trying to replace the entire progressDrawable (setProgressDrawable()
). I'm not doing that here as I'm just updating a layer in the existing progressDrawable. After some playing around, with similar approaches, I found something that worked. You have to get the bounds of the drawable that is being updated (in this case, one of the layers) and set the bounds later.
Seems like a bug in the platform, but this workaround seems to make it work. Hope this helps someone else someday.
public void setSeekBarOverlay(SeekBar seekBar, String overlayText) {
LayerDrawable layerDrawable = (LayerDrawable) seekBar.getProgressDrawable();
Drawable overlayDrawable = layerDrawable.findDrawableByLayerId(android.R.id.progress);
Rect bounds = overlayDrawable.getBounds();
Bitmap bitmap = Bitmap.createBitmap(100, 12, Bitmap.Config.ARGB_4444);
new Canvas(bitmap).drawText(overlayText, 10, 12, new Paint());
overlayDrawable = new BitmapDrawable(bitmap);
overlayDrawable.setBounds(bounds);
layerDrawable.setDrawableByLayerId(android.R.id.progress, overlayDrawable);
}