Как создать собственную панель рейтингов в Android
80
Привет всем, что мне нужно для проведения оценок в моем приложении ... Итак, мне нужно создать собственную панель оценок ... Кто-нибудь может мне в этом помочь?
Некоторые фрагменты кода были бы полезны (возможно, макеты вашей цели), чтобы увидеть, где вы находитесь. Глядя на ваш пост, вы не чувствуете себя проблемой, которую нужно решать, это скорее предложение о работе, пожалуйста, поясните это подробнее.
<?xml version="1.0" encoding="utf-8"?><!-- This is the rating bar drawable that is used to
show a filled cookie. --><selectorxmlns:android="http://schemas.android.com/apk/res/android"><itemandroid:state_pressed="true"android:state_window_focused="true"android:drawable="@drawable/cookiee" /><itemandroid:state_focused="true"android:state_window_focused="true"android:drawable="@drawable/cookiee" /><itemandroid:state_selected="true"android:state_window_focused="true"android:drawable="@drawable/cookiee" /><itemandroid: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. --><selectorxmlns:android="http://schemas.android.com/apk/res/android"><itemandroid:state_pressed="true"android:state_window_focused="true"android:drawable="@drawable/cookie" /><itemandroid:state_focused="true"android:state_window_focused="true"android:drawable="@drawable/cookie" /><itemandroid:state_selected="true"android:state_window_focused="true"android:drawable="@drawable/cookie" /><itemandroid:drawable="@drawable/cookie" /></selector>
import android.app.Activity;
import android.os.Bundle;
import android.widget.RatingBar;
import android.widget.RatingBar.OnRatingBarChangeListener;
import android.widget.Toast;
publicclassMainActivityextendsActivity{
/** Called when the activity is first created. */
RatingBar rb;
@OverridepublicvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
rb=(RatingBar)findViewById(R.id.ratingBar1);
rb.setOnRatingBarChangeListener(newOnRatingBarChangeListener(){
@OverridepublicvoidonRatingChanged(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
Эти два изображения имеют одинаковый размер, одно используется для идентификации выбранной панели рейтинга, а другое - для идентификации невыбранной панели рейтинга.
Это сработало для меня после изменения в файле food_rating_bar_full.xml идентификатора элемента. Я заменил <item android: id = "@ + android: id / background" на <item android: id = "@ android: id / background" (без символа +).
то же самое, что предложил @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
Мне нужно добавить свое решение, которое намного проще, чем приведенное выше. Нам даже не нужно использовать стили.
Создайте файл селектора в папке с возможностью переноса:
+1, но извините, это должно работать, но это не так. Сначала вы сказали, что добавляете селектор, но на самом деле вы добавляете список слоев. Во-вторых, я точно попробовал ваш код, но все, что я получил, - это пустой компонент (вообще без изображения). Может быть, вы ответите на мой вопрос: stackoverflow.com/questions/14251092/… Спасибо: D
Blaze Tama
3
Этот простой подход приводит к проблемам с размещением шкалы рейтинга на макете. Лучше использовать более традиционный подход к определению стиля.
javaxian
3
он показывает линию под всеми звездами шкалы рейтинга
Сунил
3
Отлично работает, спасибо. ВНИМАНИЕ: Не забывайте, что он не работает с векторными изображениями, только с .png!
Это похоже на проверенный ответ. Но есть картинки (показывает дробные звезды) и <! - подавить 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.
publicclassCustomRatingBarextendsRatingBar{
@Nullableprivate Bitmap mSampleTile;
publicShapeDrawableRatingBar(final Context context, final AttributeSet attrs){
super(context, attrs);
setProgressDrawable(createProgressDrawable());
}
@OverrideprotectedsynchronizedvoidonMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mSampleTile != null) {
finalint 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);
returnnew ClipDrawable(shapeDrawable, Gravity.LEFT, ClipDrawable.HORIZONTAL);
}
Shape getDrawableShape(){
finalfloat[] roundedCorners = newfloat[]{5, 5, 5, 5, 5, 5, 5, 5};
returnnew RoundRectShape(roundedCorners, null, null);
}
publicstatic 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. Надеюсь это поможет.
Обратите внимание, что я добавил фактическую высоту (13,4 dp) полосы рейтинга в layout_heightсвойство, потому что в wrap_contentэтом случае линии будут отображаться под звездами. (в моем случае только в превью Android Studio)
Я сделал что-то похожее, RatingBar с отдельными значками рейтинга, я использую VectorDrawables для значков рейтинга, но вы можете использовать любой тип рисованного
Вы можете создать настраиваемую полосу рейтинга материалов, определив drawable xml с помощью значка материала по вашему выбору, а затем применив настраиваемый drawable к панели рейтинга с помощью атрибута progressDrawable.
При создании настраиваемой панели рейтинга, которая отображает сплошную линию градиента, идущую по дорожке, подобной SeekBar, а не звездочкам, я также столкнулся с проблемой, связанной с вертикальным центрированием фона (дорожка с возможностью рисования). Это ошибочный код, который я использовал изначально (который породил проблему), как было предложено разработчиком Android и другими записями StackOverflow:
Проблема здесь в первом элементе, который относится к фону настраиваемого RatingBar. Многие записи говорят вам установить для свойства layout_minHeight какое-то большое значение, чтобы избежать вертикального пространственного разъединения между ползунком и его дорожкой. Это не было решением для меня - при просмотре на планшете фон все еще рисовался до своего меньшего размера, основанного на телефоне, поэтому дорожка всегда располагалась значительно выше центра дорожки RatingBar. Решение состоит в том, чтобы удалить эту запись в выводимом элементе RatingBar, чтобы теперь он выглядел так:
Итак, подведем итоги: не устанавливайте функцию фона (дорожки) в настраиваемом чертеже RatingBar, а установите ее в функции layout_background вашего пользовательского стиля RatingBar. Это гарантирует, что дорожка всегда будет центрирована по вертикали в горизонтальной панели RatingBar. (Помните, что в этой настраиваемой строке рейтинга вместо звездочек или других изолированных изображений в качестве рейтинга я использую линию градиента, которая «растет» или «сжимается» по горизонтали для отображения рейтинга - в этой строке рейтинга используется большой палец в стиле SeekBar. работает по «треку», подобному SeekBar.)
Вы можете использовать для этого решение @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 просмотров изображений с дефалтингом изображения как пустая звезда и заполнение шкалы оценок половиной или полной базой изображений в зависимости от рейтинга.
Ответы:
редактировать
Взгляните на пользовательский рейтинг в 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
Эти два изображения имеют одинаковый размер, одно используется для идентификации выбранной панели рейтинга, а другое - для идентификации невыбранной панели рейтинга.
источник
Мне нужно добавить свое решение, которое намного проще, чем приведенное выше. Нам даже не нужно использовать стили.
Создайте файл селектора в папке с возможностью переноса:
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" />
И это все, что нам нужно.
источник
сначала добавьте изображения в 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);
источник
res/values/styles
он определяетandroid:minHeight
иandroid:maxHeight
изменит это, чтобы увеличить размер звездыСоздание настраиваемой панели рейтинга со списком слоев и селекторами сложно, лучше переопределить класс 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; } }
источник
Для 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) } } }
источник
Вы можете попробовать эту панель рейтинга с гораздо лучшей анимацией.
СмайликРейтинг
источник
Я исследовал первоисточник,
и вот мой результат.
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>
Вот результат.
layout_height
свойство, потому что вwrap_content
этом случае линии будут отображаться под звездами. (в моем случае только в превью Android Studio)источник
Я сделал что-то похожее, RatingBar с отдельными значками рейтинга, я использую VectorDrawables для значков рейтинга, но вы можете использовать любой тип рисованного
https://github.com/manmountain/emoji-ratingbar
источник
Следующий код работает:
@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); }
источник
Вы можете создать настраиваемую полосу рейтинга материалов, определив 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>
источник
При создании настраиваемой панели рейтинга, которая отображает сплошную линию градиента, идущую по дорожке, подобной 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.)
источник
Вы можете использовать для этого решение @erdomester. Но если вы столкнулись с проблемами, связанными с высотой панели рейтинга, вы можете программно использовать высоту значков панели оценок.
В Котлине,
val drawable = ContextCompat.getDrawable(context, R.drawable.rating_filled) val drawableHeight = drawable.intrinsicHeight rating_bar.layoutParams.height = drawableHeight
источник
android:minHeight
в макет. Если изменить программно, следует добавитьrating_bar.requestLayout()
.У вас может быть 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; }
источник
RatingBar
виджет. И это не то, о чем спрашивает ОП.