Как создать собственную панель рейтингов в Android

80

Привет всем, что мне нужно для проведения оценок в моем приложении ... Итак, мне нужно создать собственную панель оценок ... Кто-нибудь может мне в этом помочь?

Coder_sLaY
источник
6
Некоторые фрагменты кода были бы полезны (возможно, макеты вашей цели), чтобы увидеть, где вы находитесь. Глядя на ваш пост, вы не чувствуете себя проблемой, которую нужно решать, это скорее предложение о работе, пожалуйста, поясните это подробнее.
rekaszeru

Ответы:

153

редактировать

Взгляните на пользовательский рейтинг в Motorola http://community.developer.motorola.com/t5/Android-App-Development-for/custom-rating-bar-style-using-android-s-ratingBar-small-style/ td-p / 10462

Обновлено

styles.xml

Он должен находиться в папке значений.

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

Этот файл должен находиться в папке Drawable.

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

Этот файл должен находиться в папке 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

Этот файл должен находиться в папке Drawable.

<?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 должен выглядеть так:

<?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 должен выглядеть так:

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

        }

    }); 
}
}

Я использовал два изображения:

cookie.jpg

cookiee.jpg

Эти два изображения имеют одинаковый размер, одно используется для идентификации выбранной панели рейтинга, а другое - для идентификации невыбранной панели рейтинга.

Картик Домадия
источник
16
Это сработало для меня после изменения в файле food_rating_bar_full.xml идентификатора элемента. Я заменил <item android: id = "@ + android: id / background" на <item android: id = "@ android: id / background" (без символа +).
Р. Кампос
1
первая ссылка в посте не работает
Саймон Шнелл
1
Ссылка не работает kozyr.zydako.net/2010/05/23/pretty-ratingbar
Jay Rathod RJ
4
то же самое, что предложил @erdomester, но больше кода. ВНИМАНИЕ: Не забывайте, что он не работает с векторными изображениями, только с .png!
Кирилл Кармазин
1
У меня не сработало, но все было нормально, когда я заменил: - android: id = "@ + id / background на @android: id / background - android: id =" @ + id / secondaryProgress "на android: id = "@ android: id / secondaryProgress" - android: id = "@ + id / progress" с android: id = "@ android: id / progress"
Харис Даутович,
56

Мне нужно добавить свое решение, которое намного проще, чем приведенное выше. Нам даже не нужно использовать стили.

Создайте файл селектора в папке с возможностью переноса:

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>

В макете установите файл селектора как 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" />

И это все, что нам нужно.

эрдомер
источник
1
+1, но извините, это должно работать, но это не так. Сначала вы сказали, что добавляете селектор, но на самом деле вы добавляете список слоев. Во-вторых, я точно попробовал ваш код, но все, что я получил, - это пустой компонент (вообще без изображения). Может быть, вы ответите на мой вопрос: stackoverflow.com/questions/14251092/… Спасибо: D
Blaze Tama
3
Этот простой подход приводит к проблемам с размещением шкалы рейтинга на макете. Лучше использовать более традиционный подход к определению стиля.
javaxian
3
он показывает линию под всеми звездами шкалы рейтинга
Сунил
3
Отлично работает, спасибо. ВНИМАНИЕ: Не забывайте, что он не работает с векторными изображениями, только с .png!
Кирилл Кармазин
Благодаря! Для SVG см. Мой ответ здесь: stackoverflow.com/a/53589663/2914140 .
CoolMind
48

сначала добавьте изображения в drawable:

введите описание изображения здесь введите описание изображения здесь

первое изображение "ratingbar_staroff.png" и второе "ratingbar_staron.png"

После этого создайте "ratingbar.xml" в 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>

следующий xml такой же на 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>

следующее, что нужно сделать, добавьте эти строки кода в 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>

Теперь уже можно добавить стиль к ресурсу панели рейтинга

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

наконец, только о вашей деятельности объявляется:

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

введите описание изображения здесь

Алекс Зараос
источник
5
Это похоже на проверенный ответ. Но есть картинки (показывает дробные звезды) и <! - подавить AndroidDomInspection -> в списке слоев. Вместо этого вы можете удалить знак плюса в "@ + android: id / background", чтобы избежать ошибок Android. Также следует изменить minHeight и maxHeight на 50dp.
CoolMind
Спасибо за решение. Могу я узнать, как отрегулировать размер звездочек? мои подошли очень маленькие. Спасибо.
jay
@jay в, res/values/stylesон определяет android:minHeightи android:maxHeightизменит это, чтобы увеличить размер звезды
Инзимам Тарик IT
7

Создание настраиваемой панели рейтинга со списком слоев и селекторами сложно, лучше переопределить класс RatingBar и создать настраиваемый RatingBar. createBackgroundDrawableShape () - это функция, в которую вы должны поместить свое пустое состояние png, а createProgressDrawableShape () - это функция, в которую вы должны поместить свое заполненное состояние png.

Примечание. Этот код пока не работает с 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;
    }
}
Гаурав Салуджа
источник
как можно увеличить пространство между предметами?
Али Резайян
1
@AliRezaiyan: Поскольку мы используем наши собственные чертежи, то есть ic_star_full и ic_star_empty, вы можете просто добавить пустое пространство (заполнение) слева и справа от вашего PNG. Вы можете использовать такие инструменты, как GIMP или Android Asset Studio, чтобы добиться этого, отредактировав свой PNG. Надеюсь это поможет.
Gaurav Saluja
Я ищу программный путь
Али Резайян
5

Для SVG RatingBar я использовал наложение пользовательских векторных чертежей RatingBar и ответ erdomester здесь. Это решение SvgRatingBarпросматривает все чертежи внутри вашего макета, поэтому в RecyclerViewнем есть накладные расходы.

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

В вашем макете:

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

Вам также необходимо создать rating_bar.xml с двумя чертежами 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>

введите описание изображения здесь

Если вы видите в режиме «Дизайн / Разделение» только одну звездочку, обновите макет:

введите описание изображения здесь

В Котлине.

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
источник
@Michalsx, спасибо, удачи! Добавлена ​​версия Kotlin.
CoolMind 02 окт.2020,
4

Вы можете попробовать эту панель рейтинга с гораздо лучшей анимацией.

СмайликРейтинг

введите описание изображения здесь

Суджит Нираикулатан
источник
Слушатель щелчка значка не работает даже после установки setIndicator (false).
Ахмад Шахвайз
4

Я исследовал первоисточник,
и вот мой результат.

styles.xml (разрешение / значения)

<!-- 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 (разрешение / вытяжка)

<?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 (разрешение / макет)

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

Вот результат.

введите описание изображения здесь

  • Обратите внимание, что я добавил фактическую высоту (13,4 dp) полосы рейтинга в layout_heightсвойство, потому что в wrap_contentэтом случае линии будут отображаться под звездами. (в моем случае только в превью Android Studio)
Wonsuc
источник
3

Я сделал что-то похожее, RatingBar с отдельными значками рейтинга, я использую VectorDrawables для значков рейтинга, но вы можете использовать любой тип рисованного

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

введите описание изображения здесь

Горан
источник
2

Следующий код работает:

@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);
}
Мани Агарвал
источник
1

Вы можете создать настраиваемую полосу рейтинга материалов, определив drawable xml с помощью значка материала по вашему выбору, а затем применив настраиваемый drawable к панели рейтинга с помощью атрибута progressDrawable.

Для получения информации о настройке панели рейтинга см. Http://www.zoftino.com/android-ratingbar-and-custom-ratingbar-example

Ниже вытягиваемого 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">
        <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>
Арнав Рао
источник
0

При создании настраиваемой панели рейтинга, которая отображает сплошную линию градиента, идущую по дорожке, подобной SeekBar, а не звездочкам, я также столкнулся с проблемой, связанной с вертикальным центрированием фона (дорожка с возможностью рисования). Это ошибочный код, который я использовал изначально (который породил проблему), как было предложено разработчиком Android и другими записями 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>

Проблема здесь в первом элементе, который относится к фону настраиваемого RatingBar. Многие записи говорят вам установить для свойства layout_minHeight какое-то большое значение, чтобы избежать вертикального пространственного разъединения между ползунком и его дорожкой. Это не было решением для меня - при просмотре на планшете фон все еще рисовался до своего меньшего размера, основанного на телефоне, поэтому дорожка всегда располагалась значительно выше центра дорожки RatingBar. Решение состоит в том, чтобы удалить эту запись в выводимом элементе RatingBar, чтобы теперь он выглядел так:

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

Затем в определении стиля пользовательского RatingBar установите для layout_background значение track drawable. Мой выглядит так:

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

(Ранее настройка фона здесь не была определена.).

Это запись в моем макете, в которой используются как стиль, так и чертежи:

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

Итак, подведем итоги: не устанавливайте функцию фона (дорожки) в настраиваемом чертеже RatingBar, а установите ее в функции layout_background вашего пользовательского стиля RatingBar. Это гарантирует, что дорожка всегда будет центрирована по вертикали в горизонтальной панели RatingBar. (Помните, что в этой настраиваемой строке рейтинга вместо звездочек или других изолированных изображений в качестве рейтинга я использую линию градиента, которая «растет» или «сжимается» по горизонтали для отображения рейтинга - в этой строке рейтинга используется большой палец в стиле SeekBar. работает по «треку», подобному SeekBar.)

Эпсилон3
источник
0

Вы можете использовать для этого решение @erdomester. Но если вы столкнулись с проблемами, связанными с высотой панели рейтинга, вы можете программно использовать высоту значков панели оценок.

В Котлине,

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

rating_bar.layoutParams.height = drawableHeight
Кишан Соланки
источник
Я тоже столкнулся с этой проблемой, но добавил android:minHeightв макет. Если изменить программно, следует добавить rating_bar.requestLayout().
CoolMind
-11

У вас может быть 5 просмотров изображений с дефалтингом изображения как пустая звезда и заполнение шкалы оценок половиной или полной базой изображений в зависимости от рейтинга.

 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
источник
Это не способ сделать это. Для этого есть RatingBarвиджет. И это не то, о чем спрашивает ОП.
4gus71n