How to create Custom Ratings bar in Android

前端 未结 13 1231
故里飘歌
故里飘歌 2020-11-22 06:28

Hello all i need to perform Ratings in my application... SO i need to create custom Ratings bar... Can Anyone Help me in this?

相关标签:
13条回答
  • 2020-11-22 07:13

    Making the custom rating bar with layer list and selectors is complex, it is better to override the RatingBar class and create a custom RatingBar. createBackgroundDrawableShape() is the function where you should put your empty state png and createProgressDrawableShape() is the function where you should put your filled state png.

    Note: This code will not work with svg for now.

    public class CustomRatingBar extends RatingBar {
    
        @Nullable
        private Bitmap mSampleTile;
    
        public ShapeDrawableRatingBar(final Context context, final AttributeSet attrs) {
            super(context, attrs);
            setProgressDrawable(createProgressDrawable());
        }
    
        @Override
        protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    
            if (mSampleTile != null) {
                final int width = mSampleTile.getWidth() * getNumStars();
                setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, 0), getMeasuredHeight());
            }
        }
    
        protected LayerDrawable createProgressDrawable() {
            final Drawable backgroundDrawable = createBackgroundDrawableShape();
            LayerDrawable layerDrawable = new LayerDrawable(new Drawable[]{
                backgroundDrawable,
                backgroundDrawable,
                createProgressDrawableShape()
            });
    
            layerDrawable.setId(0, android.R.id.background);
            layerDrawable.setId(1, android.R.id.secondaryProgress);
            layerDrawable.setId(2, android.R.id.progress);
            return layerDrawable;
        }
    
        protected Drawable createBackgroundDrawableShape() {
            final Bitmap tileBitmap = drawableToBitmap(getResources().getDrawable(R.drawable.ic_star_empty));
            if (mSampleTile == null) {
                mSampleTile = tileBitmap;
            }
            final ShapeDrawable shapeDrawable = new ShapeDrawable(getDrawableShape());
            final BitmapShader bitmapShader = new BitmapShader(tileBitmap, Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
            shapeDrawable.getPaint().setShader(bitmapShader);
            return shapeDrawable;
        }
    
        protected Drawable createProgressDrawableShape() {
            final Bitmap tileBitmap = drawableToBitmap(getResources().getDrawable(R.drawable.ic_star_full));
            final ShapeDrawable shapeDrawable = new ShapeDrawable(getDrawableShape());
            final BitmapShader bitmapShader = new BitmapShader(tileBitmap, Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
            shapeDrawable.getPaint().setShader(bitmapShader);
            return new ClipDrawable(shapeDrawable, Gravity.LEFT, ClipDrawable.HORIZONTAL);
        }
    
        Shape getDrawableShape() {
            final float[] roundedCorners = new float[]{5, 5, 5, 5, 5, 5, 5, 5};
            return new RoundRectShape(roundedCorners, null, null);
        }
    
        public static Bitmap drawableToBitmap(Drawable drawable) {
            if (drawable instanceof BitmapDrawable) {
                return ((BitmapDrawable) drawable).getBitmap();
            }
    
            int width = drawable.getIntrinsicWidth();
            width = width > 0 ? width : 1;
            int height = drawable.getIntrinsicHeight();
            height = height > 0 ? height : 1;
    
            final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            final Canvas canvas = new Canvas(bitmap);
            drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
            drawable.draw(canvas);
    
            return bitmap;
        }
    }
    
    0 讨论(0)
  • 2020-11-22 07:13

    I investigated the original source,
    and here is my result.

    styles.xml (res/values)

    <!-- RatingBar -->
    <style name="RatingBar" parent="@android:style/Widget.RatingBar">
        <item name="android:progressDrawable">@drawable/ratingbar_full</item>
        <item name="android:indeterminateDrawable">@drawable/ratingbar_full</item>
        <item name="android:minHeight">13.4dp</item>
        <item name="android:maxHeight">13.4dp</item>
    </style>
    

    ratingbar_full.xml (res/drawable)

    <?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/btn_rating_star_off_normal" />
        <item android:id="@android:id/secondaryProgress" android:drawable="@drawable/btn_rating_star_off_normal" />
        <item android:id="@android:id/progress" android:drawable="@drawable/btn_rating_star_on_normal" />
    </layer-list>
    

    btn_rating_star_off_normal.png (res/drawable-xxhdpi)

    btn_rating_star_on_normal.png (res/drawable-xxhdpi)

    activity_ratingbar.xml (res/layout)

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <androidx.appcompat.widget.AppCompatRatingBar
            android:id="@+id/ratingbar"
            style="@style/RatingBar"
            android:layout_width="wrap_content"
            android:layout_height="13.4dp"
            android:isIndicator="false"
            android:numStars="5"
            android:rating="2.6"
            android:secondaryProgressTint="#00000000"
            android:stepSize="0.1" />
    </FrameLayout>
    

    This is the result.

    • Note that I added the actual height(13.4dp) of ratingbar in layout_height property, because if it is wrap_content it will draw lines below stars. (in my case only in a preview of Android Studio)
    0 讨论(0)
  • 2020-11-22 07:14

    first add images to drawable:

    the first picture "ratingbar_staroff.png" and the second "ratingbar_staron.png"

    After, create "ratingbar.xml" on res/drawable

    <?xml version="1.0" encoding="utf-8"?>
    <!--suppress AndroidDomInspection -->
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:id="@+android:id/background"
            android:drawable="@drawable/ratingbar_empty" />
        <item android:id="@+android:id/secondaryProgress"
            android:drawable="@drawable/ratingbar_empty" />
        <item android:id="@+android:id/progress"
            android:drawable="@drawable/ratingbar_filled" />
    </layer-list>
    

    the next xml the same on res/drawable

    "ratingbar_empty.xml"

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
    
        <item android:state_pressed="true"
            android:state_window_focused="true"
            android:drawable="@drawable/ratingbar_staroff" />
    
        <item android:state_focused="true"
            android:state_window_focused="true"
            android:drawable="@drawable/ratingbar_staroff" />
    
        <item android:state_selected="true"
            android:state_window_focused="true"
            android:drawable="@drawable/ratingbar_staroff" />
    
        <item android:drawable="@drawable/ratingbar_staroff" />
    
    </selector>
    

    "ratingbar_filled"

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="http://schemas.android.com/apk/res/android">
    
        <item android:state_pressed="true"
            android:state_window_focused="true"
            android:drawable="@drawable/ratingbar_staron" />
    
        <item android:state_focused="true"
            android:state_window_focused="true"
            android:drawable="@drawable/ratingbar_staron" />
    
        <item android:state_selected="true"
            android:state_window_focused="true"
            android:drawable="@drawable/ratingbar_staron" />
    
        <item android:drawable="@drawable/ratingbar_staron" />
    
    </selector>
    

    the next to do, add these lines of code on res/values/styles

    <style name="CustomRatingBar" parent="@android:style/Widget.RatingBar">
        <item name="android:progressDrawable">@drawable/ratingbar</item>
        <item name="android:minHeight">18dp</item>
        <item name="android:maxHeight">18dp</item>
    </style>
    

    Now, already can add style to ratingbar resource

            <RatingBar
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                style= "@style/CustomRatingBar"
                android:id="@+id/ratingBar"
                android:numStars="5"
                android:stepSize="0.01"
                android:isIndicator="true"/>
    

    finally on your activity only is declare:

    RatingBar ratingbar = (RatingBar) findViewById(R.id.ratingbar);
    ratingbar.setRating(3.67f);
    

    0 讨论(0)
  • 2020-11-22 07:14

    You can use @erdomester 's given solution for this. But if you are facing issues with rating bar height then you can use ratingbar's icons height programmatically.

    In Kotlin,

    val drawable = ContextCompat.getDrawable(context, R.drawable.rating_filled)
    val drawableHeight = drawable.intrinsicHeight
    
    rating_bar.layoutParams.height = drawableHeight
    
    0 讨论(0)
  • 2020-11-22 07:18

    Edit

    Have a look at custom rating in motorola http://community.developer.motorola.com/t5/Android-App-Development-for/custom-rating-bar-style-using-android-s-ratingBar-small-style/td-p/10462

    Updated

    styles.xml

    This must be located in your values folder

     <?xml version="1.0" encoding="utf-8"?>
      <resources>
        <style name="foodRatingBar" parent="@android:style/Widget.RatingBar">
           <item name="android:progressDrawable">@drawable/food_rating_bar_full</item>
           <item name="android:minHeight">23dip</item>
           <item name="android:maxHeight">25dip</item>
       </style>
      </resources>
    

    food_rating_bar_full.xml

    This file must be in Drawable folder.

    <?xml version="1.0" encoding="utf-8"?>
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:id="@+id/background"
        android:drawable="@drawable/food_ratingbar_full_empty" />
        <item android:id="@+id/secondaryProgress"
        android:drawable="@drawable/food_ratingbar_full_empty" />
        <item android:id="@+id/progress"
        android:drawable="@drawable/food_ratingbar_full_filled" />
    </layer-list>
    

    food_ratingbar_full_empty.xml

    This file must be inside Drawable folder.

    <?xml version="1.0" encoding="utf-8"?>
    
    <!-- This is the rating bar drawable that is used to
     show a filled cookie. -->
    <selector
    xmlns:android="http://schemas.android.com/apk/res/android">
    
    <item android:state_pressed="true"
          android:state_window_focused="true"
          android:drawable="@drawable/cookiee" />
    
    <item android:state_focused="true"
          android:state_window_focused="true"
          android:drawable="@drawable/cookiee" />
    
    <item android:state_selected="true"
          android:state_window_focused="true"
          android:drawable="@drawable/cookiee" />
    
    <item android:drawable="@drawable/cookiee" />
    
    </selector>
    

    food_ratingbar_full_filled.xml

    This file must be located in Drawable folder.

    <?xml version="1.0" encoding="utf-8"?>
    
     <!-- This is the rating bar drawable that is used to
     show a unfilled cookie. -->
    <selector
    xmlns:android="http://schemas.android.com/apk/res/android">
    
    <item android:state_pressed="true"
          android:state_window_focused="true"
          android:drawable="@drawable/cookie" />
    
    <item android:state_focused="true"
          android:state_window_focused="true"
          android:drawable="@drawable/cookie" />
    
    <item android:state_selected="true"
          android:state_window_focused="true"
          android:drawable="@drawable/cookie" />
    
    <item android:drawable="@drawable/cookie" />
    
    </selector>
    

    main.xml file should look like :

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
    
        <RatingBar android:id="@+id/ratingBar1"
            style="@style/foodRatingBar"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
    
        </RatingBar>
    </LinearLayout>
    

    MainActivity.class should look like :

    import android.app.Activity;
    import android.os.Bundle;
    import android.widget.RatingBar;
    import android.widget.RatingBar.OnRatingBarChangeListener;
    import android.widget.Toast;
    
    public class MainActivity extends Activity {
    /** Called when the activity is first created. */
    
    RatingBar rb;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    
        rb=(RatingBar)findViewById(R.id.ratingBar1);
    
        rb.setOnRatingBarChangeListener(new OnRatingBarChangeListener(){
    
            @Override
            public void onRatingChanged(RatingBar ratingBar, float rating,
                    boolean fromUser) {
                // TODO Auto-generated method stub
                    Toast.makeText(getApplicationContext(),Float.toString(rating),Toast.LENGTH_LONG).show();
    
            }
    
        }); 
    }
    }
    

    I have used two images:

    cookie.jpg

    cookiee.jpg

    This two images are of same size one is used for identifying selected Rating Bar and other for identifying unselected RatingBar

    0 讨论(0)
  • 2020-11-22 07:19

    When creating a custom rating bar that displays a solid gradient line running on a SeekBar-like track, rather than stars, I also encountered a problem related to the vertical centering of the background (track drawable). This is the flawed drawable code I used originally (which generated the problem), as suggested by Android developer and other StackOverflow entries:

    <?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/seekbar_track"/>
        <item android:id="@android:id/secondaryProgress">
            <scale
                android:drawable="@drawable/seekbar_progress2"
                android:scaleWidth="100%" />
        </item>
    
        <item android:id="@android:id/progress" >
            <clip android:clipOrientation="horizontal" android:gravity="left" >
                <shape>
                    <gradient
                        android:startColor="@color/ratingbar_bg_start"
                        android:centerColor="@color/ratingbar_bg_center"
                        android:centerX="0.5"
                        android:endColor="@color/ratingbar_bg_end"
                        android:angle="0"
                        />
                </shape>
            </clip>
        </item>    
    
     </layer-list>
    

    The problem here is the first item, which relates to the background of the custom RatingBar. Many entries will tell you to set the layout_minHeight feature to some large value to avoid a vertical spatial disconnect between the thumb and its track. This was not the solution for me - when viewed on a tablet, the background was still drawing to its smaller phone-based size - so the track was consistently positioned well above the center of the RatingBar track. The solution is to remove this entry in the RatingBar drawable, so it now looks like this:

    <?xml version="1.0" encoding="utf-8"?>
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    
        <item android:id="@android:id/secondaryProgress">
            <scale
                android:drawable="@drawable/seekbar_progress2"
                android:scaleWidth="100%" />
        </item>
    
        <item android:id="@android:id/progress" >
            <clip android:clipOrientation="horizontal" android:gravity="left" >
                <shape>
                    <gradient
                        android:startColor="@color/ratingbar_bg_start"
                        android:centerColor="@color/ratingbar_bg_center"
                        android:centerX="0.5"
                        android:endColor="@color/ratingbar_bg_end"
                        android:angle="0"
                        />
                </shape>
            </clip>
        </item>    
    
     </layer-list>
    

    Then, in the style definition of the custom RatingBar, set the layout_background to the the track drawable. Mine looks like this:

    <style name="styleRatingBar" parent="@android:style/Widget.RatingBar">
        <item name="android:indeterminateOnly">false</item>
        <item name="android:background">@drawable/seekbar_track</item>
        <item name="android:progressDrawable">@drawable/abratingbar</item>
        <item name="android:thumb">@drawable/abseekbar_thumb</item>
        <item name="android:minHeight">@dimen/base_29dp</item>
        <item name="android:maxHeight">@dimen/base_29dp</item>
        <item name="android:layout_marginLeft">@dimen/base_10dp</item>
        <item name="android:layout_marginRight">@dimen/base_10dp</item>
        <item name="android:layout_marginTop">@dimen/base_10dp</item>
        <item name="android:layout_marginBottom">@dimen/base_10dp</item>
        <item name="android:scaleType">fitXY</item>
    </style>
    

    (Previously, the background setting here was undefined.).

    This is the entry in my layout, which uses both the style and the drawables:

    <RatingBar
        android:id="@+id/ratingbar_vote"
        style="@style/styleRatingBar"
        android:hint="@string/ratingbar_vote"
        android:contentDescription="@string/ratingbar_vote"
        android:numStars="5"
        android:rating="5"
        android:stepSize="1"
        android:layout_width="match_parent"
        android:layout_height="@dimen/base_29dp"
        android:layout_marginLeft="@dimen/base_120dp"
        android:layout_gravity="bottom|right" />
    

    So, to summarize, do not set the background (track) feature in your custom RatingBar drawable, set it in the layout_background feature of your custom RatingBar style. This ensures the track is always vertically centered in a horizontal RatingBar. (Remember, in this custom RatingBar, instead of using stars or other isolated images as the rating, I'm using a gradient line that "grows" or "shrinks" horizontally to display the rating - this rating line uses a SeekBar-like thumb running on a SeekBar-like "track".)

    0 讨论(0)
提交回复
热议问题