1

I need to change the colors of the stars ratingBar but get an exception, why it occurs and that is perfectly accepted?

ResultActivity

private void setDados() {
        TextView text = (TextView) findViewById(R.id.textView3);
        text.setText((text.getText()) + "" + corretas);
        RatingBar ratingBar = (RatingBar) findViewById(R.id.ratingBar);
        ratingBar.setRating((100*corretas)/(corretas+erradas));
        LayerDrawable stars = (LayerDrawable) ratingBar.getProgressDrawable();
        try {
            stars.getDrawable(2).setColorFilter(Color.YELLOW, PorterDuff.Mode.SRC_ATOP);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

Log

04-18 22:02:39.828  31732-31732/com.morais.daniela.doctorquiz E/AndroidRuntime﹕ FATAL EXCEPTION: main
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.morais.daniela.doctorquiz/com.morais.daniela.doctorquiz.Activity.ResultActivity}: java.lang.ClassCastException: android.support.v7.internal.widget.TintDrawableWrapper cannot be cast to android.graphics.drawable.LayerDrawable
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1967)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1992)
            at android.app.ActivityThread.access$600(ActivityThread.java:127)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1158)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:4448)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:511)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:823)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:590)
            at dalvik.system.NativeStart.main(Native Method)
     Caused by: java.lang.ClassCastException: android.support.v7.internal.widget.TintDrawableWrapper cannot be cast to android.graphics.drawable.LayerDrawable
            at com.morais.daniela.doctorquiz.Activity.ResultActivity.setDados(ResultActivity.java:44)
            at com.morais.daniela.doctorquiz.Activity.ResultActivity.onCreate(ResultActivity.java:36)
            at android.app.Activity.performCreate(Activity.java:4465)
            at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1049)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1931)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1992)
            at android.app.ActivityThread.access$600(ActivityThread.java:127)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1158)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:4448)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:511)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:823)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:590)
            at dalvik.system.NativeStart.main(Native Method)

UPDATE: This doesn't work in API 5.0 and higher, how I do for works in Ice Cream and higher? Without need to create a star image

Daniela Morais
  • 2,125
  • 7
  • 28
  • 49
  • Post all of your code for `ResultActivity`. Plus your error seems to be about: `android.support.v7.internal.widget.TintDrawableWrapper cannot be cast to android.graphics.drawable.LayerDrawable`. – Jared Burrows Apr 19 '15 at 01:14
  • Similar question and answer: http://stackoverflow.com/a/17267921/950427. – Jared Burrows Apr 19 '15 at 01:19
  • The line 44 is `LayerDrawable stars = (LayerDrawable) ratingBar.getProgressDrawable();`. according to a comment in another question doesn't work on API 5.0 and up http://stackoverflow.com/questions/2446270/android-ratingbar-change-star-colors – Daniela Morais Apr 19 '15 at 01:21
  • 2
    This really sucks. A method for "higher versions" and another method for older versions is necessary? – Daniela Morais Apr 19 '15 at 01:30

4 Answers4

1

Because tinting drawables was introduced with Android 5 the appcompat-v7 library wraps drawables in TintDrawable class. You can get around it using reflection. Use this gist and then this snippet:

LayerDrawable stars = (LayerDrawable) XpeceDrawableWrapper.getDrawable(ratingBar.getProgressDrawable());

Be sure to call ratingBar.setProgressDrawable(stars) to replace the originally tinted drawable.

EDIT: The gist as it is is probably incompatible with proguard (a couple of keep class rules should mend it).

Eugen Pechanec
  • 37,669
  • 7
  • 103
  • 124
0

getPorgressDrawable returns a Drawable object. LayerDrawable extends Drawable.

You are explicitly casting a Drawable object to LayerDrawable, which concludes to runtime error, because a Drawable object is not a LayerDrawable. Only a LayerDrawable object is Drawable.

You might want to take a look at this about Type Casting in Java.

Community
  • 1
  • 1
Menelaos Kotsollaris
  • 5,776
  • 9
  • 54
  • 68
0

The cleanest way would be to define your own Drawable like suggested here: Android RatingBar change star colors

The casting error is because of android support library. The support library wraps the most of android widgets drawables into a TintDrawableWrapper which extends DrawableWrapper. You can access the real (wrapped) drawable only by using reflections to access field

private final Drawable mDrawable;

which should be the LayerDrawable you are looking for.

But be aware that the name of the field could be changed with any support library update at any time.

Another "hacky" way is to use the tinting methods

public void setTint(int tint);
public void setTintList(ColorStateList tint)

which are defined in DrawableWrapper. Since DrawableWrapper has package visiblity you have to write a little helper class and put it into android.support.v7.internal.widget package to access these methods

Community
  • 1
  • 1
sockeqwe
  • 15,574
  • 24
  • 88
  • 144
0

Came accross a similar issue today when creating a RatingBar programmatically. The following workaround is working for me also on Android 4.x devices using the AppCompatRatingBar:

AppCompatRatingBar rb = new AppCompatRatingBar(activity);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
layoutParams.gravity = Gravity.CENTER;
rb.setLayoutParams(layoutParams);
rb.setStepSize(1.0f);
rb.setMax(5);

LayerDrawable layerDrawable = null;
if (rb.getProgressDrawable() instanceof LayerDrawable) {
    layerDrawable = (LayerDrawable) rb.getProgressDrawable();
} else if (rb.getProgressDrawable() instanceof DrawableWrapper) {
    DrawableWrapper wrapper = (DrawableWrapper) rb.getProgressDrawable();
    if (wrapper.getWrappedDrawable() instanceof LayerDrawable) {
        layerDrawable = (LayerDrawable) wrapper.getWrappedDrawable();
    }
}

if (layerDrawable != null) {
    layerDrawable.getDrawable(2).setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP);
    layerDrawable.getDrawable(0).setColorFilter(Color.WHITE, PorterDuff.Mode.SRC_ATOP);
    layerDrawable.getDrawable(1).setColorFilter(ContextCompat.getColor(activity, R.color.primary), PorterDuff.Mode.SRC_ATOP);
}
Tobias
  • 7,282
  • 6
  • 63
  • 85