Android RatingBar painting itself wrong

1.4k views Asked by At

I need to force a repaint of the RatingBar control.

After many problems with the ratingbar and styles, I managed to get almost everything working.

I use it in a listview item. Due to how it works, one has to "fight" a little with its look and behavior. I ended up using a soluion I found on SO where one sets it to work as an indicator, but where oneself manually calculates what score the click on the rating bar corresponds to. The code always yield the correct result when steeping through the code, but the control painting of itself is wrong the first time. Here's my code in getView "part one":

  final RatingBar rating = (RatingBar)view.findViewById(R.id.listitem_catalog_rating);

  rating.setOnTouchListener(new OnTouchListener() {
    @Override

    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP) {
             float touchPositionX = event.getX();
             float width = rating.getWidth();
             float starsf = (touchPositionX / width);
             starsf = starsf * param_final__data.score_max; // 5
             int starsint = (int) starsf + param_final__data.score_min;                                   
             byte starsbyte = (byte) starsint; 
             param_final__data.score_cur = starsbyte;
             starsf = starsint; 
             rating.setRating(starsf);
             rating.setVisibility(View.INVISIBLE);
             // force repaint and set visible - how?
        }
        else
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
          param_final__view.setPressed(true);
        }
        else
        if (event.getAction() == MotionEvent.ACTION_CANCEL) {
          param_final__view.setPressed(false);
        }
        return true;      
    }                
  });

The problem is.When the ratingbar is initially shown, the first time when clicking anywhere on it will make it draw itself as if one always chosen the max score. However, if one hides the control and shows it again - it draws everything correctly. However, this is with "natural user interaction delay" - e.g. by clicking a button that switches the visibility state. If I try to force a repaint in code using invalidate or setvisibility instructions, nothing happens.

This is the code "part 2" where I initialize the ratingbar in getView when it is shown:

              rating.setIsIndicator(true);
              rating.setVisibility(View.VISIBLE);
              rating.setStepSize(1);
              //rating.setMax(data.score_max);
              rating.setNumStars(data.score_max);
              rating.setRating(data.score_cur);

And this is its XML:

   <RatingBar
        android:id="@+id/listitem_catalog_rating"        
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"        
        android:numStars="1"
        android:stepSize="1.0"
        android:rating="1.0" 
        style="@style/MicRatingBar"         
        android:visibility="gone"
        android:layout_marginTop="10dip"               
        android:layout_marginBottom="10dip"
        />

...

<style name="MicRatingBar" parent="@android:style/Widget.RatingBar">
    <item name="android:progressDrawable">@drawable/micratingstars</item>
    <item name="android:minHeight">34dip</item>
    <item name="android:maxHeight">34dip</item>               
</style>

...

<?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_background" />
    <item android:id="@+android:id/secondaryProgress" android:drawable="@drawable/star_secondaryprogress" />
    <item android:id="@+android:id/progress" android:drawable="@drawable/star_progress" /> </layer-list>

For reference, these are some of the stackoverflows which got me as far as I am:

(but unfortunately does not solve my specific problem.)

I have tried lots of different combinations, and in my case, the code posted here is what got closest to the desired behavior and look. Just with the problem the score is drawn incorrectly on "first show".

I have tried to use e.g. invalidate, but I believe its internal flags makes it ignore he invalidate the request.

1

There are 1 answers

2
Vikram On BEST ANSWER

I think the problem lies in the order of these statements:

// Fine
rating.setIsIndicator(true);

// Fine
rating.setVisibility(View.VISIBLE);

// Problem - Although not documented, I think this should be 
// called after `setNumStars(int)`
rating.setStepSize(1);

// Switch with the statement above
rating.setNumStars(data.score_max);

// Fine
rating.setRating(data.score_cur);

So, try this order:

rating.setIsIndicator(true);
rating.setVisibility(View.VISIBLE);
rating.setNumStars(data.score_max);
rating.setStepSize(1);
rating.setRating(data.score_cur);

As for // force repaint and set visible - how?, this shouldn't be required. setRating(float) should force an update. Just remove rating.setVisibility(View.INVISIBLE); from ACTION_UP part of your code. If the RatingBar still does not update, try rating.requestLayout(). But, please read further for a cleaner solution.

You said that int starsint = (int) starsf + param_final__data.score_min; is getting the correct value. And I am guessing that param_final__data.score_cur = starsbyte; updates your ListView data. Then why not just call notifyDataSetChanged(), and let the adapter update the view with the correct value? The change in the order of statements will be required though.