So erstellen Sie eine benutzerdefinierte Bewertungsleiste in Android
80
Hallo, alles was ich brauche, um Bewertungen in meiner Anwendung durchzuführen ... SO muss ich eine benutzerdefinierte Bewertungsleiste erstellen ... Kann mir jemand dabei helfen?
Einige Codefragmente wären nützlich (möglicherweise Modelle Ihres Ziels), um zu sehen, wo Sie sich bisher befinden. Wenn Sie sich Ihren Beitrag ansehen, ist dies kein zu lösendes Problem, sondern eher ein Stellenangebot. Bitte erläutern Sie dies.
Diese Datei muss sich im Ordner Drawable befinden.
<?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
Diese Datei muss sich im Ordner Drawable befinden.
<?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>
Die Datei main.xml sollte folgendermaßen aussehen:
MainActivity.class sollte folgendermaßen aussehen:
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();
}
});
}
}
Ich habe zwei Bilder verwendet:
cookie.jpg
cookiee.jpg
Diese beiden Bilder haben dieselbe Größe. Eines dient zur Identifizierung der ausgewählten Bewertungsleiste und das andere zur Identifizierung der nicht ausgewählten Bewertungsleiste
Dies funktionierte für mich, nachdem ich in der Datei food_rating_bar_full.xml die Artikel-IDs geändert hatte. Ich habe <item android: id = "@ + android: id / background" durch <item android: id = "@ android: id / background" (ohne das + Symbol) ersetzt.
das gleiche wie @erdomester vorgeschlagen, aber mehr Code. VORSICHT: Vergessen Sie nicht, dass es nicht mit Vektorbildern funktioniert, sondern nur mit .png!
Kirill Karmazin
1
Hat bei mir nicht funktioniert, aber es war in Ordnung, wenn ich Folgendes ersetzt habe: - android: id = "@ + id / background durch @android: id / background - android: id =" @ + id / SecondaryProgress "durch android: id = "@ android: id / SecondaryProgress" - android: id = "@ + id / progress" mit android: id = "@ android: id / progress"
Haris D.
56
Ich muss meine Lösung hinzufügen, die viel einfacher ist als die oben genannte. Wir müssen nicht einmal Stile verwenden.
+1, aber sorry, das sollte funktionieren, aber es ist nicht so. Zuerst sagten Sie, Sie fügen den Selektor hinzu, aber tatsächlich fügen Sie eine Ebenenliste hinzu. Zweitens habe ich Ihren Code genau ausprobiert, aber alles, was ich bekomme, ist eine leere Komponente (überhaupt kein Bild). Vielleicht können Sie meine Frage beantworten: stackoverflow.com/questions/14251092/… Danke: D
Blaze Tama
3
Dieser einfache Ansatz führt zu Problemen bei der Positionierung der Bewertungsleiste in einem Layout. Ein traditionellerer Ansatz mit der Definition eines Stils ist besser.
Javaxian
3
es zeigt eine Linie unten bei allen Sternen der Bewertungsleiste
Sunil
3
Funktioniert super, danke. VORSICHT: Vergessen Sie nicht, dass es nicht mit Vektorbildern funktioniert, sondern nur mit .png!
Es ähnelt der überprüften Antwort. Hat aber Bilder (zeigt Bruchsterne) und <! - AndroidDomInspection unterdrücken -> in Layer-Liste. Sie können stattdessen ein Pluszeichen in "@ + android: id / background" löschen, um Android-Fehler zu vermeiden. Sollte auch minHeight und maxHeight auf 50dp ändern.
CoolMind
Danke für die Lösung. Darf ich wissen, wie Sie die Größe der Sterne anpassen? meins kommt sehr klein hoch. Danke.
Jay
@ Jay in der res/values/styleser definiert android:minHeightund android:maxHeightändert, um
Sterngröße
7
Das Erstellen der benutzerdefinierten Bewertungsleiste mit Ebenenliste und Selektoren ist komplex. Es ist besser, die RatingBar-Klasse zu überschreiben und eine benutzerdefinierte Bewertungsleiste zu erstellen. createBackgroundDrawableShape () ist die Funktion, in die Sie Ihren leeren Status png einfügen sollten, und createProgressDrawableShape () ist die Funktion, in die Sie Ihren gefüllten Status png einfügen sollten.
Hinweis: Dieser Code funktioniert derzeit nicht mit 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;
}
}
Wie kann der Abstand zwischen Elementen vergrößert werden?
Ali Rezaiyan
1
@AliRezaiyan: Da wir unsere eigenen Drawables verwenden, dh ic_star_full und ic_star_empty, können Sie einfach links und rechts in Ihrem PNG einen leeren Bereich (Polsterung) hinzufügen. Sie können Tools wie GIMP oder Android Asset Studio verwenden, um dies zu erreichen, indem Sie Ihr PNG bearbeiten. Hoffe das hilft.
Beachten Sie, dass ich die tatsächliche Höhe (13,4 dp) der Bewertungsleiste in der layout_heightEigenschaft hinzugefügt habe , da in diesem Fall wrap_contentLinien unter den Sternen gezeichnet werden. (in meinem Fall nur in einer Vorschau von Android Studio)
Ich habe etwas Ähnliches gemacht, eine Bewertungsleiste mit einzelnen Bewertungssymbolen. Ich verwende VectorDrawables für die Bewertungssymbole, aber Sie können jede Art von Zeichenelement verwenden
Sie können eine benutzerdefinierte Materialbewertungsleiste erstellen, indem Sie eine zeichnbare XML mithilfe des Materialsymbols Ihrer Wahl definieren und anschließend mithilfe des Attributs progressDrawable eine benutzerdefinierte Zeichenleiste auf die Bewertungsleiste anwenden.
Beim Erstellen einer benutzerdefinierten Bewertungsleiste, die eine durchgezogene Verlaufslinie auf einer SeekBar-ähnlichen Spur anstelle von Sternen anzeigt, ist auch ein Problem im Zusammenhang mit der vertikalen Zentrierung des Hintergrunds aufgetreten (Spur zeichnbar). Dies ist der fehlerhafte Zeichencode, den ich ursprünglich verwendet habe (der das Problem verursacht hat), wie vom Android-Entwickler und anderen StackOverflow-Einträgen vorgeschlagen:
Das Problem hierbei ist das erste Element, das sich auf den Hintergrund der benutzerdefinierten Bewertungsleiste bezieht. In vielen Einträgen werden Sie aufgefordert, die Funktion layout_minHeight auf einen großen Wert zu setzen, um eine vertikale räumliche Trennung zwischen Daumen und Spur zu vermeiden. Dies war nicht die Lösung für mich - wenn ich es auf einem Tablet betrachtete, zeichnete sich der Hintergrund immer noch auf seine kleinere telefonbasierte Größe ab -, sodass die Spur durchweg weit über der Mitte der RatingBar-Spur positioniert war. Die Lösung besteht darin, diesen Eintrag in der ausziehbaren Ratingleiste zu entfernen, sodass er jetzt folgendermaßen aussieht:
Zusammenfassend lässt sich sagen, dass Sie die Hintergrund- (Spur-) Funktion nicht in Ihrer benutzerdefinierten RatingBar-Zeichnungsfunktion festlegen, sondern in der layout_background-Funktion Ihres benutzerdefinierten RatingBar-Stils. Dadurch wird sichergestellt, dass die Spur in einer horizontalen Bewertungsleiste immer vertikal zentriert ist. (Denken Sie daran, dass ich in dieser benutzerdefinierten Bewertungsleiste anstelle von Sternen oder anderen isolierten Bildern als Bewertung eine Verlaufslinie verwende, die horizontal "wächst" oder "schrumpft", um die Bewertung anzuzeigen. Diese Bewertungslinie verwendet einen SeekBar-ähnlichen Daumen Laufen auf einer SeekBar-ähnlichen "Spur".)
Sie können hierfür die von @erdomester angegebene Lösung verwenden. Wenn Sie jedoch Probleme mit der Höhe der Bewertungsleiste haben, können Sie die Höhe der Symbole der Bewertungsleiste programmgesteuert verwenden.
In Kotlin,
val drawable = ContextCompat.getDrawable(context, R.drawable.rating_filled)
val drawableHeight = drawable.intrinsicHeight
rating_bar.layoutParams.height = drawableHeight
Ich war auch mit diesem Problem konfrontiert, habe es aber android:minHeightim Layout hinzugefügt . Wenn Sie programmgesteuert ändern, sollten Sie hinzufügen rating_bar.requestLayout().
CoolMind
-11
Sie können 5 Bildansichten mit defalutem Bild als Stern haben, der leer ist, und die Bewertungsleiste mit der halben oder vollen Bildbasis basierend auf der Bewertung füllen.
Antworten:
Bearbeiten
Werfen Sie einen Blick auf die benutzerdefinierte Bewertung in Motorola http://community.developer.motorola.com/t5/Android-App-Development-for/custom-rating-bar-style-using-android-s-ratingBar-small-style/ td-p / 10462
Aktualisiert
styles.xml
Dies muss sich in Ihrem Werteordner befinden
<?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
Diese Datei muss sich im Ordner Drawable befinden.
<?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
Diese Datei muss sich im Ordner Drawable befinden.
<?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
Diese Datei muss sich im Ordner Drawable befinden.
<?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>
Die Datei main.xml sollte folgendermaßen aussehen:
<?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 sollte folgendermaßen aussehen:
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(); } }); } }
Ich habe zwei Bilder verwendet:
cookie.jpg
cookiee.jpg
Diese beiden Bilder haben dieselbe Größe. Eines dient zur Identifizierung der ausgewählten Bewertungsleiste und das andere zur Identifizierung der nicht ausgewählten Bewertungsleiste
quelle
Ich muss meine Lösung hinzufügen, die viel einfacher ist als die oben genannte. Wir müssen nicht einmal Stile verwenden.
Erstellen Sie eine Auswahldatei im Zeichenordner:
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>
Legen Sie im Layout die Auswahldatei als progressDrawable fest:
<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" />
Und das ist alles was wir brauchen.
quelle
Fügen Sie zuerst Bilder zum Zeichnen hinzu:
das erste Bild "Ratingbar_staroff.png" und das zweite "Ratingbar_staron.png"
Erstellen Sie anschließend "reviewbar.xml" auf 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>
die nächste xml gleich auf 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>
"bewertungsleiste_gefüllt"
<?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>
Fügen Sie als Nächstes diese Codezeilen in res / values / styles hinzu
<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>
Jetzt kann der Ressourcen der Bewertungsleiste bereits Stil hinzugefügt werden
<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"/>
Schließlich wird auf Ihrer Aktivität nur erklärt:
RatingBar ratingbar = (RatingBar) findViewById(R.id.ratingbar); ratingbar.setRating(3.67f);
quelle
res/values/styles
er definiertandroid:minHeight
undandroid:maxHeight
ändert, umDas Erstellen der benutzerdefinierten Bewertungsleiste mit Ebenenliste und Selektoren ist komplex. Es ist besser, die RatingBar-Klasse zu überschreiben und eine benutzerdefinierte Bewertungsleiste zu erstellen. createBackgroundDrawableShape () ist die Funktion, in die Sie Ihren leeren Status png einfügen sollten, und createProgressDrawableShape () ist die Funktion, in die Sie Ihren gefüllten Status png einfügen sollten.
Hinweis: Dieser Code funktioniert derzeit nicht mit 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; } }
quelle
Für SVG habe
RatingBar
ich die benutzerdefinierte Überlagerung von RatingBar-benutzerdefinierten Vektorzeichnungen und die Antwort von erdomester hier verwendet. Diese Lösung durchläuft alle Drawables in derSvgRatingBar
Innenansicht Ihres Layouts, sodassRecyclerView
ein Overhead entsteht.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); } }
In Ihrem Layout:
<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" />
Sie müssen außerdem grade_bar.xml mit zwei SVG-Drawables erstellen:
<?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>
Wenn in der Ansicht Design / Split nur ein Stern angezeigt wird, aktualisieren Sie das Layout:
In Kotlin.
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) } } }
quelle
Sie können diese Bewertungsleiste mit viel besseren Animationen ausprobieren
SmileyRating
quelle
Ich habe die Originalquelle untersucht
und hier ist mein Ergebnis.
styles.xml (res / values)
<!-- RatingBar --> <style name="RatingBar" parent="@android:style/Widget.RatingBar"> <item name="android:progressDrawable">@drawable/ratingbar_full</item> <item name="android:indeterminateDrawable">@drawable/ratingbar_full</item> <item name="android:minHeight">13.4dp</item> <item name="android:maxHeight">13.4dp</item> </style>
Ratingbar_full.xml (res / drawable)
<?xml version="1.0" encoding="utf-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@android:id/background" android:drawable="@drawable/btn_rating_star_off_normal" /> <item android:id="@android:id/secondaryProgress" android:drawable="@drawable/btn_rating_star_off_normal" /> <item android:id="@android:id/progress" android:drawable="@drawable/btn_rating_star_on_normal" /> </layer-list>
btn_rating_star_off_normal.png (res / drawable-xxhdpi)
btn_rating_star_on_normal.png (res / drawable-xxhdpi)
activity_ratingbar.xml (res / layout)
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.appcompat.widget.AppCompatRatingBar android:id="@+id/ratingbar" style="@style/RatingBar" android:layout_width="wrap_content" android:layout_height="13.4dp" android:isIndicator="false" android:numStars="5" android:rating="2.6" android:secondaryProgressTint="#00000000" android:stepSize="0.1" /> </FrameLayout>
Das ist das Ergebnis.
layout_height
Eigenschaft hinzugefügt habe , da in diesem Fallwrap_content
Linien unter den Sternen gezeichnet werden. (in meinem Fall nur in einer Vorschau von Android Studio)quelle
Ich habe etwas Ähnliches gemacht, eine Bewertungsleiste mit einzelnen Bewertungssymbolen. Ich verwende VectorDrawables für die Bewertungssymbole, aber Sie können jede Art von Zeichenelement verwenden
https://github.com/manmountain/emoji-ratingbar
quelle
Der folgende Code funktioniert:
@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); }
quelle
Sie können eine benutzerdefinierte Materialbewertungsleiste erstellen, indem Sie eine zeichnbare XML mithilfe des Materialsymbols Ihrer Wahl definieren und anschließend mithilfe des Attributs progressDrawable eine benutzerdefinierte Zeichenleiste auf die Bewertungsleiste anwenden.
Informationen zum Anpassen der Bewertungsleiste finden Sie unter http://www.zoftino.com/android-ratingbar-and-custom-ratingbar-example
Unterhalb der zeichnbaren XML wird das Daumen hoch-Symbol für die Bewertungsleiste verwendet.
<?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>
quelle
Beim Erstellen einer benutzerdefinierten Bewertungsleiste, die eine durchgezogene Verlaufslinie auf einer SeekBar-ähnlichen Spur anstelle von Sternen anzeigt, ist auch ein Problem im Zusammenhang mit der vertikalen Zentrierung des Hintergrunds aufgetreten (Spur zeichnbar). Dies ist der fehlerhafte Zeichencode, den ich ursprünglich verwendet habe (der das Problem verursacht hat), wie vom Android-Entwickler und anderen StackOverflow-Einträgen vorgeschlagen:
<?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>
Das Problem hierbei ist das erste Element, das sich auf den Hintergrund der benutzerdefinierten Bewertungsleiste bezieht. In vielen Einträgen werden Sie aufgefordert, die Funktion layout_minHeight auf einen großen Wert zu setzen, um eine vertikale räumliche Trennung zwischen Daumen und Spur zu vermeiden. Dies war nicht die Lösung für mich - wenn ich es auf einem Tablet betrachtete, zeichnete sich der Hintergrund immer noch auf seine kleinere telefonbasierte Größe ab -, sodass die Spur durchweg weit über der Mitte der RatingBar-Spur positioniert war. Die Lösung besteht darin, diesen Eintrag in der ausziehbaren Ratingleiste zu entfernen, sodass er jetzt folgendermaßen aussieht:
<?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>
Setzen Sie dann in der Stildefinition der benutzerdefinierten Bewertungsleiste den layout_background auf die zu zeichnende Spur. Meins sieht so aus:
<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>
(Bisher war die Hintergrundeinstellung hier undefiniert.)
Dies ist der Eintrag in meinem Layout, der sowohl den Stil als auch die Zeichen verwendet:
<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" />
Zusammenfassend lässt sich sagen, dass Sie die Hintergrund- (Spur-) Funktion nicht in Ihrer benutzerdefinierten RatingBar-Zeichnungsfunktion festlegen, sondern in der layout_background-Funktion Ihres benutzerdefinierten RatingBar-Stils. Dadurch wird sichergestellt, dass die Spur in einer horizontalen Bewertungsleiste immer vertikal zentriert ist. (Denken Sie daran, dass ich in dieser benutzerdefinierten Bewertungsleiste anstelle von Sternen oder anderen isolierten Bildern als Bewertung eine Verlaufslinie verwende, die horizontal "wächst" oder "schrumpft", um die Bewertung anzuzeigen. Diese Bewertungslinie verwendet einen SeekBar-ähnlichen Daumen Laufen auf einer SeekBar-ähnlichen "Spur".)
quelle
Sie können hierfür die von @erdomester angegebene Lösung verwenden. Wenn Sie jedoch Probleme mit der Höhe der Bewertungsleiste haben, können Sie die Höhe der Symbole der Bewertungsleiste programmgesteuert verwenden.
In Kotlin,
val drawable = ContextCompat.getDrawable(context, R.drawable.rating_filled) val drawableHeight = drawable.intrinsicHeight rating_bar.layoutParams.height = drawableHeight
quelle
android:minHeight
im Layout hinzugefügt . Wenn Sie programmgesteuert ändern, sollten Sie hinzufügenrating_bar.requestLayout()
.Sie können 5 Bildansichten mit defalutem Bild als Stern haben, der leer ist, und die Bewertungsleiste mit der halben oder vollen Bildbasis basierend auf der Bewertung füllen.
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; }
quelle
RatingBar
Widget. Auch das fragt das OP nicht.