Jak utworzyć pasek ocen niestandardowych w systemie Android

80

Witam, wszystko, czego potrzebuję, aby przeprowadzić oceny w mojej aplikacji ... Więc muszę utworzyć niestandardowy pasek ocen ... Czy ktoś może mi w tym pomóc?

Coder_sLaY
źródło
6
Przydałyby się niektóre fragmenty kodu (np. Makiety celu), aby zobaczyć, gdzie jesteś do tej pory. Patrząc na Twój post, nie wydaje się, że jest to problem do rozwiązania, a raczej oferta pracy, opowiedz o tym.
rekaszeru

Odpowiedzi:

153

Edytować

Spójrz na niestandardową ocenę w motorola http://community.developer.motorola.com/t5/Android-App-Development-for/custom-rating-bar-style-using-android-s-ratingBar-small-style/ td-p / 10462

Zaktualizowano

style.xml

Musi znajdować się w folderze wartości

 <?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

Ten plik musi znajdować się w folderze do rysowania.

<?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

Ten plik musi znajdować się w folderze Drawable.

<?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

Ten plik musi znajdować się w folderze do rysowania.

<?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>

Plik main.xml powinien wyglądać następująco:

<?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 powinien wyglądać następująco:

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();

        }

    }); 
}
}

Użyłem dwóch obrazów:

cookie.jpg

cookiee.jpg

Te dwa obrazy mają ten sam rozmiar, jeden służy do identyfikacji wybranego paska oceny, a drugi do identyfikowania niewybranego paska oceny

Kartik Domadiya
źródło
16
To zadziałało dla mnie, po zmianie w pliku food_rating_bar_full.xml, identyfikator pozycji. Zastąpiłem <item android: id = "@ + android: id / background" na <item android: id = "@ android: id / background" (bez symbolu +).
R. Campos,
1
pierwszy link w poście jest niedostępny
Simon Schnell
1
Link nie działa kozyr.zydako.net/2010/05/23/pretty-ratingbar
Jay Rathod RJ
4
to samo, co sugerował @erdomester, ale więcej kodu. UWAGA: Nie zapominaj, że nie działa z obrazami wektorowymi, tylko z .png!
Kirill Karmazin
1
Nie zadziałało, ale było dobrze, gdy zamieniłem: - android: id = "@ + id / background na @android: id / background - android: id =" @ + id / secondaryProgress "na android: id = "@ android: id / secondaryProgress" - android: id = "@ + id / progress" with android: id = "@ android: id / progress"
Haris Dautović
56

Muszę dodać moje rozwiązanie, które jest O WIELE łatwiejsze niż to powyżej. Nie musimy nawet używać stylów.

Utwórz plik selektora w folderze do rysowania:

custom_ratingbar_selector.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"
    android:drawable="@drawable/star_off" />

 <item android:id="@android:id/secondaryProgress"
    android:drawable="@drawable/star_off" />

 <item android:id="@android:id/progress"
    android:drawable="@drawable/star_on" />

</layer-list>

W układzie ustaw plik selektora jako progressDrawable:

 <RatingBar
        android:id="@+id/ratingBar2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dp"
        android:progressDrawable="@drawable/custom_ratingbar_selector"
        android:numStars="8"
        android:stepSize="0.2"
        android:rating="3.0" />

I to wszystko, czego potrzebujemy.

erdomester
źródło
1
+1, ale przepraszam, to powinno działać, ale nie. Najpierw powiedziałeś, że dodajesz Selektor, ale w rzeczywistości dodajesz listę warstw. Po drugie, wypróbowałem dokładnie twój kod, ale wszystko, co otrzymałem, to pusty komponent (w ogóle brak obrazu). Może możesz odpowiedzieć na moje pytanie: stackoverflow.com/questions/14251092/... Dzięki: D
Blaze Tama
3
To proste podejście powoduje problemy z pozycjonowaniem paska oceny w układzie. Bardziej tradycyjne podejście do definiowania stylu jest lepsze.
javaxian
3
poniżej znajduje się linia ze wszystkimi gwiazdami na pasku oceny
Sunil
3
Działa świetnie, dzięki. UWAGA: Nie zapominaj, że nie działa z obrazami wektorowymi, tylko z .png!
Kirill Karmazin
Dzięki! W przypadku SVG zobacz moją odpowiedź tutaj: stackoverflow.com/a/53589663/2914140 .
CoolMind
48

najpierw dodaj obrazy do rysowania:

wprowadź opis obrazu tutaj wprowadź opis obrazu tutaj

pierwsze zdjęcie „ratingbar_staroff.png”, a drugie „ratingbar_staron.png”

Następnie utwórz plik „ratingbar.xml” na 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>

następny plik xml to samo na 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>

W następnej kolejności dodaj te wiersze kodu do 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>

Teraz możesz już dodać styl do zasobu ratingbar

        <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"/>

na koniec tylko o swojej działalności zadeklaruj:

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

wprowadź opis obrazu tutaj

Alex Zaraos
źródło
5
Jest podobny do zaznaczonej odpowiedzi. Ale ma zdjęcia (pokazuje ułamkowe gwiazdki) i <! - pomija AndroidDomInspection -> na liście warstw. Zamiast tego możesz usunąć znak plusa „@ + android: id / background”, aby uniknąć błędów Androida. Należy również zmienić minHeight i maxHeight na 50dp.
CoolMind
Dzięki za rozwiązanie. czy mogę wiedzieć, jak dopasować rozmiar gwiazd? mój wyszedł bardzo mały. dzięki.
jay
@jay w the res/values/styleson definiuje android:minHeighti android:maxHeightzmienia to, aby zwiększyć rozmiar gwiazdy
Inzimam Tariq IT
7

Tworzenie niestandardowego paska oceny z listą warstw i selektorami jest skomplikowane, lepiej jest zastąpić klasę RatingBar i utworzyć niestandardowy RatingBar. createBackgroundDrawableShape () to funkcja, w której należy umieścić swój pusty stan png, a createProgressDrawableShape () to funkcja, w której należy umieścić wypełniony stan png.

Uwaga: ten kod na razie nie będzie działał z SVG.

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;
    }
}
Gaurav Saluja
źródło
jak zwiększyć przestrzeń między przedmiotami?
Ali Rezaiyan
1
@AliRezaiyan: Ponieważ używamy naszych własnych rysunków, tj. Ic_star_full i ic_star_empty, możesz po prostu dodać puste miejsce (dopełnienie) po lewej i prawej stronie pliku PNG. Możesz użyć narzędzi takich jak GIMP lub Android Asset Studio, aby to osiągnąć, edytując plik PNG. Mam nadzieję że to pomoże.
Gaurav Saluja
Szukam sposobu programistycznego
Ali Rezaiyan
5

W przypadku SVG RatingBar użyłem nakładania niestandardowych wektorów RatingBar i odpowiedź erdomester tutaj. To rozwiązanie przechodzi przez wszystkie elementy rysunkowe wewnątrz SvgRatingBarwidoku układu, więc RecyclerViewma narzut.

SvgRatingBar.java:

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ClipDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.VectorDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.graphics.drawable.shapes.Shape;
import android.os.Build;
import android.util.AttributeSet;
import android.view.Gravity;

import androidx.appcompat.graphics.drawable.DrawableWrapper;
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat;

import com.example.R; // Your R.java file for R.attr.ratingBarStyle.

public class SvgRatingBar extends androidx.appcompat.widget.AppCompatRatingBar {

    private Bitmap sampleTile;

    public SvgRatingBar(Context context) {
        this(context, null);
    }

    public SvgRatingBar(Context context, AttributeSet attrs) {
        this(context, attrs, R.attr.ratingBarStyle);
    }

    public SvgRatingBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        LayerDrawable drawable = (LayerDrawable) createTile(getProgressDrawable(), false);
        setProgressDrawable(drawable);
    }

    /**
     * Converts a drawable to a tiled version of itself. It will recursively
     * traverse layer and state list drawables.
     */
    @SuppressLint("RestrictedApi")
    private Drawable createTile(Drawable drawable, boolean clip) {
        if (drawable instanceof DrawableWrapper) {
            Drawable inner = ((DrawableWrapper) drawable).getWrappedDrawable();
            if (inner != null) {
                inner = createTile(inner, clip);
                ((DrawableWrapper) drawable).setWrappedDrawable(inner);
            }
        } else if (drawable instanceof LayerDrawable) {
            LayerDrawable background = (LayerDrawable) drawable;
            final int n = background.getNumberOfLayers();
            Drawable[] outDrawables = new Drawable[n];

            for (int i = 0; i < n; i++) {
                int id = background.getId(i);
                outDrawables[i] = createTile(background.getDrawable(i),
                        (id == android.R.id.progress || id == android.R.id.secondaryProgress));
            }
            LayerDrawable newBg = new LayerDrawable(outDrawables);

            for (int i = 0; i < n; i++) {
                newBg.setId(i, background.getId(i));
            }

            return newBg;

        } else if (drawable instanceof BitmapDrawable) {
            final BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
            final Bitmap tileBitmap = bitmapDrawable.getBitmap();
            if (sampleTile == null) {
                sampleTile = tileBitmap;
            }

            final ShapeDrawable shapeDrawable = new ShapeDrawable(getDrawableShape());
            final BitmapShader bitmapShader = new BitmapShader(tileBitmap,
                    Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
            shapeDrawable.getPaint().setShader(bitmapShader);
            shapeDrawable.getPaint().setColorFilter(bitmapDrawable.getPaint().getColorFilter());
            return (clip) ? new ClipDrawable(shapeDrawable, Gravity.START,
                    ClipDrawable.HORIZONTAL) : shapeDrawable;
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && drawable instanceof VectorDrawable) {
            return createTile(getBitmapDrawableFromVectorDrawable(drawable), clip);
        } else if (drawable instanceof VectorDrawableCompat) {
            // API 19 support.
            return createTile(getBitmapDrawableFromVectorDrawable(drawable), clip);
        }
        return drawable;
    }

    private BitmapDrawable getBitmapDrawableFromVectorDrawable(Drawable drawable) {
        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);
        return new BitmapDrawable(getResources(), bitmap);
    }

    @Override
    protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (sampleTile != null) {
            final int width = sampleTile.getWidth() * getNumStars();
            setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, 0),
                    getMeasuredHeight());
        }
    }

    private Shape getDrawableShape() {
        final float[] roundedCorners = new float[]{5, 5, 5, 5, 5, 5, 5, 5};
        return new RoundRectShape(roundedCorners, null, null);
    }
}

W swoim układzie:

<com.example.common.control.SvgRatingBar
    android:id="@+id/rate"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:minHeight="13dp"
    android:numStars="5"
    android:progressDrawable="@drawable/rating_bar"
    android:rating="3.5"
    android:stepSize="0.01"
    />

Musisz także utworzyć rating_bar.xml z dwoma rysunkami SVG:

<?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/ic_unfilled_star"
        />

    <item
        android:id="@android:id/secondaryProgress"
        android:drawable="@drawable/ic_unfilled_star"
        />

    <item
        android:id="@android:id/progress"
        android:drawable="@drawable/ic_filled_star"
        />

</layer-list>

wprowadź opis obrazu tutaj

Jeśli widzisz w widoku Projekt / Podziel tylko jedną gwiazdkę, odśwież układ:

wprowadź opis obrazu tutaj

W Kotlinie.

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapShader
import android.graphics.Canvas
import android.graphics.Shader
import android.graphics.drawable.*
import android.graphics.drawable.shapes.RoundRectShape
import android.os.Build
import android.util.AttributeSet
import android.view.Gravity
import androidx.appcompat.graphics.drawable.DrawableWrapper
import androidx.appcompat.widget.AppCompatRatingBar
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
import com.example.R; // Your R.java file for R.attr.ratingBarStyle.

class SvgRatingBar @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null,
                                             defStyleAttr: Int = R.attr.ratingBarStyle) :
    AppCompatRatingBar(context, attrs, defStyleAttr) {

    private var sampleTile: Bitmap? = null
    private val roundedCorners = floatArrayOf(5f, 5f, 5f, 5f, 5f, 5f, 5f, 5f)
    private val roundRectShape = RoundRectShape(roundedCorners, null, null)

    init {
        progressDrawable = createTile(progressDrawable, false) as LayerDrawable
    }

    /**
     * Converts a drawable to a tiled version of itself. It will recursively
     * traverse layer and state list drawables.
     */
    private fun createTile(drawable: Drawable, clip: Boolean): Drawable =
        when {
            drawable is DrawableWrapper -> {
                @SuppressLint("RestrictedApi")
                var inner = drawable.wrappedDrawable
                if (inner != null) {
                    inner = createTile(inner, clip)
                    @SuppressLint("RestrictedApi")
                    drawable.wrappedDrawable = inner
                }
                drawable
            }
            drawable is LayerDrawable -> {
                val n = drawable.numberOfLayers
                val outDrawables = arrayOfNulls<Drawable>(n)
                for (i in 0 until n) {
                    val id = drawable.getId(i)
                    outDrawables[i] = createTile(drawable.getDrawable(i),
                        id == android.R.id.progress || id == android.R.id.secondaryProgress)
                }
                val newBg = LayerDrawable(outDrawables)
                for (i in 0 until n) {
                    newBg.setId(i, drawable.getId(i))
                }
                newBg
            }
            drawable is BitmapDrawable -> {
                val tileBitmap = drawable.bitmap
                if (sampleTile == null) {
                    sampleTile = tileBitmap
                }
                val bitmapShader = BitmapShader(tileBitmap, Shader.TileMode.REPEAT,
                    Shader.TileMode.CLAMP)
                val shapeDrawable = ShapeDrawable(roundRectShape).apply {
                    paint.shader = bitmapShader
                    paint.colorFilter = drawable.paint.colorFilter
                }
                if (clip) ClipDrawable(shapeDrawable, Gravity.START, ClipDrawable.HORIZONTAL)
                else shapeDrawable
            }
            Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && drawable is VectorDrawable -> {
                createTile(getBitmapDrawableFromVectorDrawable(drawable), clip)
            }
            drawable is VectorDrawableCompat -> {
                // Pre-Lollipop support.
                createTile(getBitmapDrawableFromVectorDrawable(drawable), clip)
            }
            else -> drawable
        }

    private fun getBitmapDrawableFromVectorDrawable(drawable: Drawable): BitmapDrawable {
        val bitmap = Bitmap.createBitmap(drawable.intrinsicWidth, drawable.intrinsicHeight,
            Bitmap.Config.ARGB_8888)
        val canvas = Canvas(bitmap)
        drawable.setBounds(0, 0, canvas.width, canvas.height)
        drawable.draw(canvas)
        return BitmapDrawable(resources, bitmap)
    }

    @Synchronized override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        if (sampleTile != null) {
            val width = sampleTile!!.width * numStars
            setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, 0),
                measuredHeight)
        }
    }
}
CoolMind
źródło
@Michalsx, dzięki, powodzenia! Dodano wersję Kotlin.
CoolMind
4

Możesz wypróbować ten pasek oceny ze znacznie lepszymi animacjami

SmileyRating

wprowadź opis obrazu tutaj

Sujith Niraikulathan
źródło
Odbiornik kliknięć ikon nie działa, nawet po ustawieniu setIndicator (false).
Ahmad Shahwaiz
4

Zbadałem oryginalne źródło
i oto mój wynik.

style.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)

wprowadź opis obrazu tutaj

btn_rating_star_on_normal.png (res / drawable-xxhdpi)

wprowadź opis obrazu tutaj

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>

To jest wynik.

wprowadź opis obrazu tutaj

  • Zwróć uwagę, że dodałem rzeczywistą wysokość (13,4 dp) paska ratingowego we layout_heightwłaściwości, ponieważ jeśli tak, wrap_contentto narysuje linie poniżej gwiazdek. (w moim przypadku tylko w podglądzie Android Studio)
wonsuc
źródło
3

Zrobiłem coś podobnego, pasek RatingBar z indywidualnymi ikonami ocen. Używam VectorDrawables jako ikon ocen, ale możesz użyć dowolnego typu do rysowania

https://github.com/manmountain/emoji-ratingbar

wprowadź opis obrazu tutaj

Goran
źródło
2

Poniższy kod działa:

@Override
protected synchronized void onDraw(Canvas canvas)
{
    int stars = getNumStars();
    float rating = getRating();
    try
    {
        bitmapWidth = getWidth() / stars;
    }
    catch (Exception e)
    {
        bitmapWidth = getWidth();
    }
    float x = 0;

    for (int i = 0; i < stars; i++)
    {
        Bitmap bitmap;
        Resources res = getResources();
        Paint paint = new Paint();

        if ((int) rating > i)
        {
            bitmap = BitmapFactory.decodeResource(res, starColor);
        }
        else
        {
            bitmap = BitmapFactory.decodeResource(res, starDefault);
        }
        Bitmap scaled = Bitmap.createScaledBitmap(bitmap, getHeight(), getHeight(), true);
        canvas.drawBitmap(scaled, x, 0, paint);
        canvas.save();
        x += bitmapWidth;
    }

    super.onDraw(canvas);
}
Mani Agarwal
źródło
1

Możesz utworzyć niestandardowy pasek oceny materiału, definiując plik XML do rysowania za pomocą wybranej ikony materiału, a następnie stosując atrybut progressDrawable, stosując niestandardowy pasek oceny materiału.

Aby uzyskać informacje na temat dostosowywania paska oceny, zobacz http://www.zoftino.com/android-ratingbar-and-custom-ratingbar-example

Poniższy plik XML z możliwością rysowania używa ikony kciuka w górę na pasku oceny.

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@android:id/background">
        <bitmap
            android:src="@drawable/thumb_up"
            android:tint="?attr/colorControlNormal" />
    </item>
    <item android:id="@android:id/secondaryProgress">
        <bitmap
            android:src="@drawable/thumb_up"
            android:tint="?attr/colorControlActivated" />
    </item>
    <item android:id="@android:id/progress">
        <bitmap
            android:src="@drawable/thumb_up"
            android:tint="?attr/colorControlActivated" />
    </item>
</layer-list>
Arnav Rao
źródło
0

Podczas tworzenia niestandardowego paska oceny, który wyświetla ciągłą linię gradientu biegnącą po torze podobnym do SeekBar, a nie gwiazd, napotkałem również problem związany z pionowym wyśrodkowaniem tła (ścieżka do rysowania). Oto wadliwy kod, którego użyłem pierwotnie (który spowodował problem), zgodnie z sugestią programisty Androida i innych wpisów StackOverflow:

<?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>

Problem dotyczy pierwszego elementu, który dotyczy tła niestandardowego paska RatingBar. Wiele wpisów mówi o ustawieniu funkcji layout_minHeight na jakąś dużą wartość, aby uniknąć pionowego przestrzennego rozłączenia między kciukiem a jego ścieżką. To nie było dla mnie rozwiązanie - podczas oglądania na tablecie tło wciąż rysowało się do mniejszego rozmiaru w telefonie - więc ścieżka była konsekwentnie umieszczana znacznie powyżej środka toru RatingBar. Rozwiązaniem jest usunięcie tego wpisu z rysunkowego paska RatingBar, aby teraz wyglądał tak:

<?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>

Następnie w definicji stylu niestandardowego RatingBar ustaw layout_background na ścieżkę do rysowania. Mój wygląda tak:

<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>

(Wcześniej ustawienie tła w tym miejscu było niezdefiniowane).

To jest wpis w moim układzie, który wykorzystuje zarówno styl, jak i elementy do rysowania:

<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" />

Podsumowując, nie ustawiaj funkcji tła (ścieżki) w niestandardowym drawable RatingBar, ale ustaw ją w funkcji layout_background własnego stylu RatingBar. Dzięki temu ścieżka jest zawsze wyśrodkowana w pionie na poziomym pasku RatingBar. (Pamiętaj, że w tym niestandardowym pasku RatingBar zamiast używać gwiazdek lub innych izolowanych obrazów jako oceny, używam linii gradientu, która „rośnie” lub „kurczy się” w poziomie, aby wyświetlić ocenę - ta linia oceny używa kciuka podobnego do paska SeekBar działa na „ścieżce” przypominającej SeekBar).

Epsilon3
źródło
0

Możesz użyć do tego rozwiązania podanego przez @erdomester. Ale jeśli napotykasz problemy z wysokością paska oceny, możesz programowo użyć wysokości ikon na pasku oceny.

W Kotlinie

val drawable = ContextCompat.getDrawable(context, R.drawable.rating_filled)
val drawableHeight = drawable.intrinsicHeight

rating_bar.layoutParams.height = drawableHeight
Kishan Solanki
źródło
Miałem też do czynienia z tym problemem, ale dodałem android:minHeightw układzie. Jeśli zmieniasz programowo, należy dodać rating_bar.requestLayout().
CoolMind,
-11

Możesz mieć 5 widoków obrazu z domyślnym obrazem jako gwiazdą, która jest pusta i wypełnij pasek oceny połową lub pełnym obrazem w oparciu o ocenę.

 public View getView(int position, View convertView, ViewGroup parent) {
     LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
     View grid=inflater.inflate(R.layout.griditem, parent, false);
     imageView=(ImageView)grid.findViewById(R.id.grid_prod);
     imageView.setImageResource(imgId[position]);
     imgoff =(ImageView)grid.findViewById(R.id.offer);
     tv=(TextView)grid.findViewById(R.id.grid_text);
     tv.setText(namesArr[position]);
     tv.setTextColor(Color.BLACK);
     tv.setPadding(0, 2, 0, 0);
   sta=(ImageView)grid.findViewById(R.id.imageView);
    sta1=(ImageView)grid.findViewById(R.id.imageView1);
    sta2=(ImageView)grid.findViewById(R.id.imageView2);
    sta3=(ImageView)grid.findViewById(R.id.imageView3);
    sta4=(ImageView)grid.findViewById(R.id.imageView4);
    Float rate=rateFArr[position];


   if(rate==5 || rate==4.5)
    {
        sta.setImageResource(R.drawable.full__small);
        sta1.setImageResource(R.drawable.full__small);
        sta2.setImageResource(R.drawable.full__small);
        sta3.setImageResource(R.drawable.full__small);
        if(rate==4.5)
        {
            sta4.setImageResource(R.drawable.half_small);
        }
        else
        {
            sta4.setImageResource(R.drawable.full__small);
        }
    }
    if(rate==4 || rate==3.5)
    {
        sta.setImageResource(R.drawable.full__small);
        sta1.setImageResource(R.drawable.full__small);
        sta2.setImageResource(R.drawable.full__small);
     if(rate==3.5)
        {
            sta3.setImageResource(R.drawable.half_small);
        }
        else
        {
            sta3.setImageResource(R.drawable.full__small);
        }
    }
    if(rate==3 || rate==2.5)
    {
        sta.setImageResource(R.drawable.full__small);
        sta1.setImageResource(R.drawable.full__small);
       if(rate==2.5)
        {
            sta2.setImageResource(R.drawable.half_small);
        }
        else
        {
            sta2.setImageResource(R.drawable.full__small);
        }
    }
    if(rate==2 || rate==1.5)
    {
    sta.setImageResource(R.drawable.full__small);
     if(rate==1.5)
        {
            sta1.setImageResource(R.drawable.half_small);
        }
        else
        {
            sta1.setImageResource(R.drawable.full__small);
        }
    }
    if(rate==1 || rate==0.5)
    {
        if(rate==1)
        sta.setImageResource(R.drawable.full__small);
        else
            sta.setImageResource(R.drawable.half_small);

    }
    if(rate>5)
    {
        sta.setImageResource(R.drawable.full__small);
        sta1.setImageResource(R.drawable.full__small);
        sta2.setImageResource(R.drawable.full__small);
        sta3.setImageResource(R.drawable.full__small);
        sta4.setImageResource(R.drawable.full__small);
    }

    // rb=(RatingBar)findViewById(R.id.grid_rating);
     //rb.setRating(rateFArr[position]);
        return grid;
    }
user2609258
źródło
To nie jest sposób na zrobienie tego. Do tego służy RatingBarwidget. Nie o to również prosi PO.
4gus71n