Why don't set CircleImageView of android support v4 library public? - android

I have noticed android.support.v4.widget.CircleImageView for long. Every time I want to use an ImageView in a round shape, CircleImageView would appear in my mind. I have tried to use it for many times, but every time, I failed. Because the access permission of android.support.v4.widget.CircleImageView is default which means only classes in the same package with CircleImageView, namely, android.support.v4.widget, are able to access it.
I can't understand now that round ImageView is used in common, why don't set CircleImageView to public so that developers don't have to override an ImageView into a Round ImageView? Isn't it that Google Android team force us to reinvent wheels?
Or, don't I know this CircleImageView well?
Any tips will be appreciated. Thanks in advance.

I tried to copy and paste source codes of android.support.v4.widget.CircleImageView to make it public just like this:
package me.danielpan.youtubelike.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RadialGradient;
import android.graphics.Shader;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.support.v4.view.ViewCompat;
import android.view.animation.Animation;
import android.widget.ImageView;
/**
* Private class created to work around issues with AnimationListeners being
* called before the animation is actually complete and support shadows on older
* platforms.
*
* #hide
*/
public class CircleImageView extends ImageView {
private static final int KEY_SHADOW_COLOR = 0x1E000000;
private static final int FILL_SHADOW_COLOR = 0x3D000000;
// PX
private static final float X_OFFSET = 0f;
private static final float Y_OFFSET = 1.75f;
private static final float SHADOW_RADIUS = 3.5f;
private static final int SHADOW_ELEVATION = 4;
private Animation.AnimationListener mListener;
private int mShadowRadius;
public CircleImageView(Context context, int color, final float radius) {
super(context);
final float density = getContext().getResources().getDisplayMetrics().density;
final int diameter = (int) (radius * density * 2);
final int shadowYOffset = (int) (density * Y_OFFSET);
final int shadowXOffset = (int) (density * X_OFFSET);
mShadowRadius = (int) (density * SHADOW_RADIUS);
ShapeDrawable circle;
if (elevationSupported()) {
circle = new ShapeDrawable(new OvalShape());
ViewCompat.setElevation(this, SHADOW_ELEVATION * density);
} else {
OvalShape oval = new OvalShadow(mShadowRadius, diameter);
circle = new ShapeDrawable(oval);
ViewCompat.setLayerType(this, ViewCompat.LAYER_TYPE_SOFTWARE, circle.getPaint());
circle.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset,
KEY_SHADOW_COLOR);
final int padding = mShadowRadius;
// set padding so the inner image sits correctly within the shadow.
setPadding(padding, padding, padding, padding);
}
circle.getPaint().setColor(color);
setBackgroundDrawable(circle);
}
private boolean elevationSupported() {
return android.os.Build.VERSION.SDK_INT >= 21;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (!elevationSupported()) {
setMeasuredDimension(getMeasuredWidth() + mShadowRadius*2, getMeasuredHeight()
+ mShadowRadius*2);
}
}
public void setAnimationListener(Animation.AnimationListener listener) {
mListener = listener;
}
#Override
public void onAnimationStart() {
super.onAnimationStart();
if (mListener != null) {
mListener.onAnimationStart(getAnimation());
}
}
#Override
public void onAnimationEnd() {
super.onAnimationEnd();
if (mListener != null) {
mListener.onAnimationEnd(getAnimation());
}
}
/**
* Update the background color of the circle image view.
*
* #param colorRes Id of a color resource.
*/
public void setBackgroundColorRes(int colorRes) {
setBackgroundColor(getContext().getResources().getColor(colorRes));
}
#Override
public void setBackgroundColor(int color) {
if (getBackground() instanceof ShapeDrawable) {
((ShapeDrawable) getBackground()).getPaint().setColor(color);
}
}
private class OvalShadow extends OvalShape {
private RadialGradient mRadialGradient;
private Paint mShadowPaint;
private int mCircleDiameter;
public OvalShadow(int shadowRadius, int circleDiameter) {
super();
mShadowPaint = new Paint();
mShadowRadius = shadowRadius;
mCircleDiameter = circleDiameter;
mRadialGradient = new RadialGradient(mCircleDiameter / 2, mCircleDiameter / 2,
mShadowRadius, new int[] {
FILL_SHADOW_COLOR, Color.TRANSPARENT
}, null, Shader.TileMode.CLAMP);
mShadowPaint.setShader(mRadialGradient);
}
#Override
public void draw(Canvas canvas, Paint paint) {
final int viewWidth = CircleImageView.this.getWidth();
final int viewHeight = CircleImageView.this.getHeight();
canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2 + mShadowRadius),
mShadowPaint);
canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2), paint);
}
}
}
It looks well, right? It has no customized attributes and seems able to be used as a normal ImageView.
But if you have tried it, you will find out that NoSuchMethodException is thrown. This exception implies that necessary constructors are not overridden. So that you can't even instantiate it as a normal View.
After reading these source codes, I realize that CircleImageView only adds shadow behind ImageView, whose result is not a RoundCornerImageView or RoundImageView. So if I want a RoundImageView, I have to forget this class and implement this effect by overriding an ImageView.
At last, there is the file comment, which points out the use of android.support.v4.widget.CircleImageView:
Private class created to work around issues with AnimationListeners
being called before the animation is actually complete and support
shadows on older platforms.
And I hope no one will ask such a stupid question again and let it end here, ^_^, Haha~

As per the documentation this class is private class used for work around and we cannot instantiate it. May be that class will be removed sooner I suspect.
There are ways to create the circular background for a view.
file : drawable/contact_badge_round.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<size android:width="32dp" android:height="32dp"/>
<gradient android:centerColor="#123456" <!--Put your custom color for bg -->
android:startColor="#123456"
android:endColor="#123456"
/>
</shape>
then in your layout create a Button and set the background as contact_badge_round
<ImageView
android:id="#+id/roundContact"
android:layout_width="32dp"
android:layout_height="32dp"
android:src="#drawalbe/your_image"
android:gravity="center"
android:background="#drawable/background_new_entity_symbol"
/>

Related

In Android, How do I create a ring with dynamically changing colors and a transparent center / outside?

I am trying to copy this dynamic image:
The goal here is to alter the percentage of the circle with a certain color and the rest of a circle the other color depending on circumstances via java code in real-time. (IE, setting 50/50 would be half purple and half blue)
The tricky part is that the circle itself has solid colors but both outside and inside are transparent as there are items behind it that need to be seen; which is where I am getting stuck.
I would love some help figuring out how to try and make this work using either native Android properties or using a library if someone can recommend one.
What I have tried so far:
1) Making 2 circles with transparent outer and inner rings:
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Outer layer of circle -->
<item>
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thicknessRatio="2"
android:useLevel="false">
<solid android:color="#android:color/transparent" />
<stroke
android:width="2dp"
android:color="#A9A9A9" />
</shape>
</item>
<!-- Inner layer of Circle -->
<item
android:left="12dp"
android:right="12dp"
android:top="12dp"
android:bottom="12dp">
<shape
android:innerRadius="0dp"
android:shape="ring"
android:thicknessRatio="2"
android:useLevel="false">
<solid android:color="#android:color/transparent" />
<stroke
android:width="2dp"
android:color="#A9A9A9" />
</shape>
</item>
</layer-list>
And then included them in a layout with a split linear layout on each side like this:
But I am unsure how to make sections on the outside and inside of the circles invisible AND transparent while making the circle portion visible and NOT transparent.
2) mimicking this code to try and adapt to my own: https://github.com/DmitryMalkovich/circular-with-floating-action-button/blob/master/progress-fab/src/main/java/com/dmitrymalkovich/android/ProgressFloatingActionButton.java
3) Working with progress bars to try and set a percentage and then work the "not set" percentage part to the other color.
All three have not gotten me very far :(
Has anyone successfully done something like this and if so can they tell me the best way to go about recreating it? Thank you!
I ended up making a custom class that was derived from this answer: Android: looking for a drawArc() method with inner & outer radius
Code is below:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Silmarilos on 2017-05-22.
*/
public class MultiColorCircle extends View {
private RectF rect, outerRect, innerRect;
private Paint perimeterPaint;
private List<CustomStrokeObject> strokeObjects;
private int widthOfCircleStroke, widthOfBoarderStroke,
colorOfBoarderStroke, onePercentPixels;
public MultiColorCircle(Context context) {
super(context);
init();
}
public MultiColorCircle(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public MultiColorCircle(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
#RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public MultiColorCircle(Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
/**
* Setter for the width of the circle stroke. Affects all arcs drawn. This is the width of
* the various arcs that make up the actual circle, this is NOT the boarder, that is different
* #param widthOfCircleStroke
*/
public void setWidthOfCircleStroke(int widthOfCircleStroke){
this.widthOfCircleStroke = widthOfCircleStroke;
}
/**
* Setter for the width of the boarder stroke. This is the width of the boarder strokes used
* to make the inner and outer boarder of the rings that surround the main body circle.
* They will default to black and 1 pixel in width. To hide them, pass null as the color
* #param widthOfBoarderStroke
*/
public void setWidthOfBoarderStroke(int widthOfBoarderStroke){
this.widthOfBoarderStroke = widthOfBoarderStroke;
this.perimeterPaint.setStrokeWidth(this.widthOfBoarderStroke);
}
/**
* Set the color of the boarder stroke. Send in null if you want it to be hidden
* #param colorOfBoarderStroke
*/
public void setColorOfBoarderStroke(Integer colorOfBoarderStroke){
if(colorOfBoarderStroke == null){
//Set to transparent
this.colorOfBoarderStroke = Color.parseColor("#00000000");
} else {
this.colorOfBoarderStroke = colorOfBoarderStroke;
}
this.perimeterPaint.setColor(this.colorOfBoarderStroke);
}
private void init(){
this.strokeObjects = new ArrayList<>();
this.onePercentPixels = 0;
this.widthOfCircleStroke = 1; //Default
this.widthOfBoarderStroke = 1; //Default
this.colorOfBoarderStroke = Color.parseColor("#000000"); //Default, black
this.rect = new RectF();
this.outerRect = new RectF();
this.innerRect = new RectF();
this.perimeterPaint = new Paint();
this.perimeterPaint.setStrokeWidth(widthOfBoarderStroke);
this.perimeterPaint.setColor(colorOfBoarderStroke);
this.perimeterPaint.setAntiAlias(true);
this.perimeterPaint.setStyle(Paint.Style.STROKE);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = this.getWidth();
int left = 0;
int top = 0;
int right = (left + width);
int bottom = (top + width);
onePercentPixels = (int)(this.getWidth() * 0.01);
left = left + onePercentPixels + widthOfCircleStroke;
top = top + onePercentPixels + widthOfCircleStroke;
right = right - onePercentPixels - widthOfCircleStroke;
bottom = bottom - onePercentPixels - widthOfCircleStroke;
drawCircle(canvas, left, top, right, bottom);
}
private void drawCircle(Canvas canvas, int left, int top, int right, int bottom){
//Base rect for sides of circle parameters
rect.set(left, top, right, bottom);
if(this.strokeObjects.size() <= 0){
return;
}
for(CustomStrokeObject strokeObject : this.strokeObjects){
if(strokeObject == null){
continue;
}
Paint paint = strokeObject.paint;
paint.setStrokeWidth(this.widthOfCircleStroke);
canvas.drawArc(rect, strokeObject.percentToStartAt,
strokeObject.percentOfCircle, false, paint);
}
drawPerimeterCircle(canvas, left, top, right, bottom);
}
/**
* Draws the outer and inner boarder arcs of black to create a boarder
*/
private void drawPerimeterCircle(Canvas canvas, int left, int top, int right, int bottom){
//Base inner and outer rectanges for circles to be drawn
outerRect.set(
(left - (widthOfCircleStroke / 2)),
(top - (widthOfCircleStroke / 2)),
(right + (widthOfCircleStroke / 2)),
(bottom + (widthOfCircleStroke / 2))
);
innerRect.set(
(left + (widthOfCircleStroke / 2)),
(top + (widthOfCircleStroke / 2)),
(right - (widthOfCircleStroke / 2)),
(bottom - (widthOfCircleStroke / 2))
);
canvas.drawArc(outerRect, 0, 360, false, perimeterPaint);
canvas.drawArc(innerRect, 0, 360, false, perimeterPaint);
}
/**
* Setter method for setting the various strokes on the circle
* #param strokeObjects {#link CustomStrokeObject}
*/
public void setCircleStrokes(List<CustomStrokeObject> strokeObjects){
if(strokeObjects == null){
return;
}
if(strokeObjects.size() == 0){
return;
}
this.strokeObjects = new ArrayList<>();
this.strokeObjects = strokeObjects;
invalidate();
}
/**
* Class used in drawing arcs of circle
*/
public static class CustomStrokeObject {
float percentOfCircle;
float percentToStartAt;
Integer colorOfLine;
Paint paint;
/**
* Constructor. This will also do the calculations to convert the percentages into the
* circle numbers so that passing in 50 will be converted into 180 for mapping on to a
* circle. Also, I am adding in a very tiny amount of overlap (a couple pixels) so that
* there will not be a gap between the arcs because the whitespace gap of a couple pixels
* does not look very good. To remove this, just remove the -.1 and .1 to startAt and circle
* #param percentOfCircle Percent of the circle to fill.
* NOTE! THIS IS BASED OFF OF 100%!
* This is not based off of a full 360 circle so if you want something
* to fill half the circle, pass 50, not 180.
* #param percentToStartAt Percent to start at (for filling multiple colors).
* NOTE! THIS IS BASED OFF OF 100%!
* This is not based off of a full 360 circle so if you want something
* to fill half the circle, pass 50, not 180.
* #param colorOfLine Int color of the line to use
*/
public CustomStrokeObject(float percentOfCircle, float percentToStartAt, Integer colorOfLine){
this.percentOfCircle = percentOfCircle;
this.percentToStartAt = percentToStartAt;
this.colorOfLine = colorOfLine;
if(this.percentOfCircle < 0 || this.percentOfCircle > 100){
this.percentOfCircle = 100; //Default to 100%
}
this.percentOfCircle = (float)((360 * (percentOfCircle + 0.1)) / 100);
if(this.percentToStartAt < 0 || this.percentToStartAt > 100){
this.percentToStartAt = 0;
}
//-90 so it will start at top, Ex: http://www.cumulations.com/images/blog/screen1.png
this.percentToStartAt = (float)((360 * (percentToStartAt - 0.1)) / 100) - 90;
if(this.colorOfLine == null){
this.colorOfLine = Color.parseColor("#000000"); //Default to black
}
paint = new Paint();
paint.setColor(colorOfLine);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
}
/**
* Overloaded setter, in case you want to set a custom paint object here
* #param paint Paint object to overwrite one set by constructor
*/
public void setPaint(Paint paint){
this.paint = paint;
}
}
}
To use it, define it in the xml:
<com.yourpackage.goeshere.MultiColorCircle
android:id="#+id/my_circle"
android:padding="8dp"
android:layout_margin="8dp"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Then in your Java:
MultiColorCircle my_circle = (MultiColorCircle) this.findViewById(R.id.my_circle);
my_circle.setWidthOfCircleStroke(60);
my_circle.setWidthOfBoarderStroke(2);
my_circle.setColorOfBoarderStroke(ContextCompat.getColor(this, R.color.purple));
MultiColorCircle.CustomStrokeObject s1 = new MultiColorCircle.CustomStrokeObject(
50, 0, ContextCompat.getColor(this, R.color.blue)
);
MultiColorCircle.CustomStrokeObject s2 = new MultiColorCircle.CustomStrokeObject(
30, 50, ContextCompat.getColor(this, R.color.red)
);
MultiColorCircle.CustomStrokeObject s3 = new MultiColorCircle.CustomStrokeObject(
20, 80, ContextCompat.getColor(this, R.color.green)
);
List<MultiColorCircle.CustomStrokeObject> myList = new ArrayList<>();
myList.add(s1);
myList.add(s2);
myList.add(s3);
my_circle.setCircleStrokes(myList);
Adjust values accordingly.
Sil

Blurry image after canvas rotate, only in Android 6

I've got a custom view with the following code:
private final Drawable outerGauge;
private final Drawable innerGauge;
private float rotateX;
private float rotateY;
private int rotation = 0;
{
outerGauge = getContext().getDrawable(R.drawable.gauge_outer);
innerGauge = getContext().getDrawable(R.drawable.gauge_inner);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
outerGauge.draw(canvas);
canvas.rotate(rotation, rotateX, rotateY);
innerGauge.draw(canvas);
canvas.rotate(-rotation, rotateX, rotateY);
}
Most of the time this produces perfectly clear images. However, sometimes the result looks like this:
This only seems to happen on one of my two test devices. The device is a Motorola moto G, with the Android 6 upgrade. The other test device, which always seems to produce perfectly clear images, is an Oneplus X, Android 5. It's also not consistent, it happens sometimes, and then doesn't again the next moment. From what I've been able to test, it does not even depend on the amount of rotation applied. I've never seen it happen on straight angles though, (0, 90, 180 degrees,) and it does seem to be worse at angles closer to 45 or 135 degrees.
The image in question is an imported SVG, placed directly in the res/drawable folder. Therefore it can't be the resolution. (Also, gauge_outer is placed in exactly the same folder and made exactly the same way, though this one does not become blurry.)
Any ideas on how to solve this?
Edit:
Okay, never mind what I said about the complete inconsistency. It appears to be fully consistent, and be worst when the rotation comes closer and closer to 90 degrees. Also, as soon as the rotation is exactly 90 degrees, the indicator completely disappears.
Edit:
Behold: two emulators, one running Android 5 and one running Android 6:
The full source code is as follows:
package nl.dvandenberg.gauge;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
public class GaugeView extends View {
private static final int ORIGINAL_ROTATE_Y = 510;
private static final int ORIGINAL_IMAGE_HEIGHT = 613;
private static final int ORIGINAL_IMAGE_WIDTH = 1046;
private final Drawable outerGauge;
private final Drawable innerGauge;
private float rotateX;
private float rotateY;
private int rotation = 0;
{
outerGauge = getContext().getDrawable(R.drawable.gauge_outer);
innerGauge = getContext().getDrawable(R.drawable.gauge_inner);
}
public GaugeView(Context context) {
super(context);
setProgress(48);
}
public GaugeView(Context context, AttributeSet attrs) {
super(context, attrs);
setProgress(48);
}
public GaugeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setProgress(48);
}
public void setProgress(double percentage) {
this.rotation = (int) (180 * Math.min(100, Math.max(0, percentage)) / 100);
invalidate();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
double width = MeasureSpec.getSize(widthMeasureSpec);
double idealHeight = ORIGINAL_IMAGE_HEIGHT * width / ORIGINAL_IMAGE_WIDTH;
double height = Math.min(idealHeight, MeasureSpec.getSize(heightMeasureSpec));
width = width * height / idealHeight;
heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) height, MeasureSpec.getMode(heightMeasureSpec));
rotateX = (float) (width / 2f);
rotateY = (float) (height / ORIGINAL_IMAGE_HEIGHT * ORIGINAL_ROTATE_Y);
outerGauge.setBounds(0, 0, (int) width, (int) height);
innerGauge.setBounds(0, 0, (int) width, (int) height);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
outerGauge.draw(canvas);
canvas.rotate(rotation, rotateX, rotateY);
innerGauge.draw(canvas);
}
}
with drawable/gauge_inner.xml
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="1046dp"
android:height="613dp"
android:viewportWidth="1046"
android:viewportHeight="613">
<path
android:fillColor="#aa3939"
android:pathData="M142.541,516.071 C145.053,517.623,156.088,519.334,183.255,522.586
C203.832,525.024,251.438,530.676,289.03,535.184
C326.708,539.641,359.782,543.523,362.537,543.896
C365.292,544.268,388.127,547.018,413.445,550.067 L459.289,555.468
L462.946,560.401 C468.075,567.485,479.691,577.405,489.255,582.968
C499.701,589.062,520.069,594.737,531.817,594.883
C571.623,595.225,607.57,570.083,620.01,533.226
C624.956,518.592,626.123,507.412,624.269,492.201
C622.686,479.259,620.262,472.461,612.212,458.518
C602.012,440.852,592.681,431.69,575.424,422.602
C537.988,402.763,489.163,413.401,462.78,447.108 L458.957,452.086
L449.523,453.146 C444.316,453.727,420.115,456.614,395.829,459.552
C371.456,462.538,346.451,465.429,340.177,466.165
C333.904,466.9,293.067,471.772,249.427,476.991
C205.788,482.211,164.951,487.082,158.678,487.817
C144.122,489.408,139.036,491.998,136.796,498.719
C134.433,505.626,136.72,512.388,142.541,516.07 Z" />
</vector>
and drawable/gauge_outer.xml
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="1046dp"
android:height="613dp"
android:viewportWidth="1046"
android:viewportHeight="613">
<path
android:fillColor="#aa3939"
android:pathData="M488.981,0.56719 C465.882,2.06727,430.783,6.96753,412.984,11.0677
C392.285,15.768,387.285,17.0681,375.285,20.6683
C231.691,63.4706,113.696,164.376,49.898,299.183
C16.6993,369.187,0,444.491,0,523.495
C0,540.296,0.0999961,541.696,1.99992,543.596
C3.99984,545.596,5.29979,545.596,59.4977,545.596
C113.696,545.596,114.996,545.596,116.995,543.596
C118.895,541.696,118.995,540.296,118.995,522.595
C118.995,504.894,118.895,503.494,116.995,501.594
C115.095,499.694,113.695,499.594,85.2962,499.594 L55.6974,499.594
L56.2974,489.793 C60.0973,433.69,76.3966,372.387,101.396,320.384
C103.996,314.984,106.496,310.383,106.896,310.183
C107.396,309.883,110.796,311.483,114.596,313.683
C118.396,315.983,124.396,319.483,127.995,321.583
C131.595,323.583,139.195,328.083,144.994,331.484
C155.694,337.684,159.993,338.884,163.193,336.284
C164.893,334.984,171.293,324.483,177.992,312.083
C183.292,302.282,183.092,299.882,176.492,295.782
C173.992,294.282,162.593,287.582,151.093,281.081 L130.294,269.08 L135.294,261.58
C166.593,214.877,210.691,170.375,258.589,137.273
C268.189,130.673,269.889,129.873,270.489,131.273
C272.389,136.273,298.388,179.776,299.988,180.576
C300.988,181.176,302.788,181.576,303.888,181.576
C306.288,181.576,334.787,165.275,336.787,162.775
C339.187,159.575,337.987,155.575,330.887,143.274
C326.987,136.574,322.987,129.773,322.087,128.273
C321.187,126.673,318.087,121.273,315.287,116.372
C312.387,111.372,309.987,107.072,309.987,106.671
C309.987,105.371,342.586,90.7702,360.385,84.0698
C388.684,73.5692,427.382,63.5687,455.981,59.6685
C468.68,57.8684,490.98,55.5683,495.579,55.5683 L499.979,55.5683 L499.979,85.0699
C499.979,113.271,500.079,114.671,501.979,116.572
C503.879,118.472,505.279,118.572,522.978,118.572
C540.677,118.572,542.077,118.472,543.977,116.572
C545.877,114.672,545.977,113.272,545.977,84.8703 L545.977,55.2687
L555.977,55.9687 C581.776,57.5688,617.875,63.7691,644.874,71.0695
C670.273,77.9699,702.072,89.7705,722.771,99.871
C729.071,102.971,734.671,105.671,735.271,105.871
C735.871,106.071,730.171,117.072,722.172,131.072
C713.772,145.773,707.973,156.973,707.973,158.573
C707.973,162.273,709.373,163.573,718.973,169.274
C741.272,182.375,743.072,183.075,746.772,179.775
C748.472,178.375,765.571,149.773,773.871,134.373 L776.471,129.773
L787.471,137.373 C834.969,170.075,877.067,212.377,910.266,260.98
C912.866,264.78,914.866,268.28,914.766,268.78
C914.566,269.28,903.866,275.78,890.967,283.181
C878.068,290.581,866.668,297.582,865.768,298.782
C862.268,302.782,863.268,305.182,878.268,330.084
C884.168,339.785,886.468,339.885,900.967,331.484
C906.767,328.084,914.366,323.584,917.966,321.583
C921.566,319.483,927.566,315.983,931.365,313.683
C935.265,311.383,938.565,309.583,938.865,309.583
C939.565,309.583,946.665,324.184,952.164,337.084
C972.463,383.986,986.363,440.49,989.663,489.792 L990.263,499.592
L960.664,499.592 C932.265,499.592,930.865,499.692,928.965,501.592
C927.065,503.492,926.965,504.892,926.965,522.593
C926.965,540.294,927.065,541.694,928.965,543.594
C930.965,545.594,932.265,545.594,986.463,545.594
C1041.86,545.594,1041.96,545.594,1044.06,543.494
C1046.26,541.294,1046.26,540.994,1045.66,513.192
C1044.76,470.69,1040.36,436.088,1031.36,398.586
C1027.46,382.685,1026.86,380.485,1020.26,360.084
C1009.06,325.382,990.461,284.58,971.762,253.578
C923.864,174.276,855.866,108.873,775.07,64.3706
C712.572,29.8688,645.075,8.96764,574.477,2.06727
C555.278,0.16716,507.68,-0.63288,488.981,0.56719 Z" />
</vector>
Though not an answer, I have managed to find a workaround. This workaround relies on drawing the image onto a canvas, which is linked to a bitmap, which is then drawn onto the final, rotated canvas in the onDraw method.
It seems like this problem really only arises with nodpi-drawables, in other words, imported svg's. It is however, very consistent. Whether the shape is a multi-path vector or a simple square does not matter, the problem will always take exactly the same shape, with images disappearing entirely when the canvas is rotated 90°.
The full code I used to bypass this problem is as follows:
package nl.dvandenberg.energymonitor.customViews;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import nl.dvandenberg.energymonitor.R;
public class GaugeView extends View {
private static final int ORIGINAL_ROTATE_Y = 510;
private static final int ORIGINAL_IMAGE_HEIGHT = 613;
private static final int ORIGINAL_IMAGE_WIDTH = 1046;
private final Drawable outerGauge, innerGauge;
private float rotateX;
private float rotateY;
private int rotation = 0;
private Bitmap innerGaugeBitmap;
private final Canvas innerGaugeCanvas;
{
outerGauge = getContext().getDrawable(R.drawable.gauge_outer);
innerGauge = getContext().getDrawable(R.drawable.gauge_inner);
innerGaugeCanvas = new Canvas();
}
public GaugeView(Context context) {
super(context);
}
public GaugeView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public GaugeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setProgress(double percentage) {
this.rotation = (int) (180 * Math.min(100, Math.max(0, percentage)) / 100);
invalidate();
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
double width = MeasureSpec.getSize(widthMeasureSpec);
double idealHeight = ORIGINAL_IMAGE_HEIGHT * width / ORIGINAL_IMAGE_WIDTH;
double height = Math.min(idealHeight, MeasureSpec.getSize(heightMeasureSpec));
width = width * height / idealHeight;
heightMeasureSpec = MeasureSpec.makeMeasureSpec((int) height, MeasureSpec.getMode(heightMeasureSpec));
rotateX = (float) (width / 2f);
rotateY = (float) (height / ORIGINAL_IMAGE_HEIGHT * ORIGINAL_ROTATE_Y);
outerGauge.setBounds(0, 0, (int) width, (int) height);
innerGauge.setBounds(0, 0, (int) width, (int) height);
if (innerGaugeBitmap != null){
innerGaugeBitmap.recycle();
}
innerGaugeBitmap = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888); // Gives LINT-warning draw-allocation, but no other way to upscale bitmaps exists.
innerGaugeCanvas.setBitmap(innerGaugeBitmap);
innerGaugeBitmap.eraseColor(Color.TRANSPARENT);
innerGauge.draw(innerGaugeCanvas);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
#Override
protected void onDraw(Canvas canvas) {
outerGauge.draw(canvas);
canvas.rotate(rotation, rotateX, rotateY);
canvas.drawBitmap(innerGaugeBitmap,0,0,null);
}
}
with the important part occuring in the onMeasure method:
if (innerGaugeBitmap != null){
innerGaugeBitmap.recycle();
}
innerGaugeBitmap = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888); // Gives LINT-warning draw-allocation, but no other way to upscale bitmaps exists.
innerGaugeCanvas.setBitmap(innerGaugeBitmap);
innerGaugeBitmap.eraseColor(Color.TRANSPARENT);
innerGauge.draw(innerGaugeCanvas);
I have filed a bugreport at https://code.google.com/p/android/issues/detail?id=208453

Android : Semi Circle Progress Bar

I want semi circle progress bar in background of image. just like below image.
i have tried to draw using canvas but can't get success. i have also tired some custom progress bar library but result is same.
any suggestions.
looking for one time development and used in every screen size.
This can be implemented by clipping a canvas containing an image at an angle (By drawing an arc).
You can use an image something like this
And clip that image by drawing an arc.
Here is how you can do it.
//Convert the progress in range of 0 to 100 to angle in range of 0 180. Easy math.
float angle = (progress * 180) / 100;
mClippingPath.reset();
//Define a rectangle containing the image
RectF oval = new RectF(mPivotX, mPivotY, mPivotX + mBitmap.getWidth(), mPivotY + mBitmap.getHeight());
//Move the current position to center of rect
mClippingPath.moveTo(oval.centerX(), oval.centerY());
//Draw an arc from center to given angle
mClippingPath.addArc(oval, 180, angle);
//Draw a line from end of arc to center
mClippingPath.lineTo(oval.centerX(), oval.centerY());
And once you get the path, you can use clipPath function to clip the canvas in that path.
canvas.clipPath(mClippingPath);
Here is the Complete code
SemiCircleProgressBarView.java
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
public class SemiCircleProgressBarView extends View {
private Path mClippingPath;
private Context mContext;
private Bitmap mBitmap;
private float mPivotX;
private float mPivotY;
public SemiCircleProgressBarView(Context context) {
super(context);
mContext = context;
initilizeImage();
}
public SemiCircleProgressBarView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
initilizeImage();
}
private void initilizeImage() {
mClippingPath = new Path();
//Top left coordinates of image. Give appropriate values depending on the position you wnat image to be placed
mPivotX = getScreenGridUnit();
mPivotY = 0;
//Adjust the image size to support different screen sizes
Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.circle);
int imageWidth = (int) (getScreenGridUnit() * 30);
int imageHeight = (int) (getScreenGridUnit() * 30);
mBitmap = Bitmap.createScaledBitmap(bitmap, imageWidth, imageHeight, false);
}
public void setClipping(float progress) {
//Convert the progress in range of 0 to 100 to angle in range of 0 180. Easy math.
float angle = (progress * 180) / 100;
mClippingPath.reset();
//Define a rectangle containing the image
RectF oval = new RectF(mPivotX, mPivotY, mPivotX + mBitmap.getWidth(), mPivotY + mBitmap.getHeight());
//Move the current position to center of rect
mClippingPath.moveTo(oval.centerX(), oval.centerY());
//Draw an arc from center to given angle
mClippingPath.addArc(oval, 180, angle);
//Draw a line from end of arc to center
mClippingPath.lineTo(oval.centerX(), oval.centerY());
//Redraw the canvas
invalidate();
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//Clip the canvas
canvas.clipPath(mClippingPath);
canvas.drawBitmap(mBitmap, mPivotX, mPivotY, null);
}
private float getScreenGridUnit() {
DisplayMetrics metrics = new DisplayMetrics();
((Activity)mContext).getWindowManager().getDefaultDisplay().getMetrics(metrics);
return metrics.widthPixels / 32;
}
}
And using it in any activity is very easy.
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<com.example.progressbardemo.SemiCircleProgressBarView
android:id="#+id/progress"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
Note that clipPath function doesn't work if the hardware acceleration is turned on. You can turn off the hardware acceleration only for that view.
//Turn off hardware accleration
semiCircleProgressBarView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
MainActivity.java
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SemiCircleProgressBarView semiCircleProgressBarView = (SemiCircleProgressBarView) findViewById(R.id.progress);
semiCircleProgressBarView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
semiCircleProgressBarView.setClipping(70);
}
}
As and when the progress changes you can set the progressbar by calling function,
semiCircleProgressBarView.setClipping(progress);
Ex: semiCircleProgressBarView.setClipping(50); //50% progress
semiCircleProgressBarView.setClipping(70); //70% progress
You can use your own Image to match the requirements. Hope it helps!!
Edit : To move the semi circle to bottom of the screen, change mPivotY value. Something like this
//In `SemiCircleProgressBarView.java`
//We don't get the canvas width and height initially, set `mPivoyY` inside `onWindowFocusChanged` since `getHeight` returns proper results by that time
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
mPivotX = getScreenGridUnit();
mPivotY = getHeight() - (mBitmap.getHeight() / 2);
}
You can try SeekArc Library. I know its a different kind of seekbar, but with some minor customization, you can use it for your app as a progressbar. I've done the same. You just need to change some properties like seekarc:touchInside="false".
Its fairly simple.
Now the custom implementation on my app looks somewhat like this:
img src: CleanMaster at Google Play
You can also use native ProgressBar to achieve semi circle.
Define ProgressBar like this:
<ProgressBar
android:id="#+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:max="200"
android:progress="0"
android:progressDrawable="#drawable/circular" />
Create drawable:
circular (API Level < 21):
<shape
android:innerRadiusRatio="2.3"
android:shape="ring"
android:thickness="5sp" >
<solid android:color="#color/someColor" />
</shape>
circular (API Level >= 21):
<shape
android:useLevel="true"
android:innerRadiusRatio="2.3"
android:shape="ring"
android:thickness="5sp" >
<solid android:color="#color/someColor" />
</shape>
useLevel is false by default in API Level 21.
Now since we have set max = 200, to achieve semi circle, range of the progress should be 0 to 100. You can play around with these values to achieve desired shape.
Thus use it like this:
ProgressBar progressBar = (Progressbar) view.findViewById(R.id.progressBar);
progressBar.setProgress(value); // 0 <= value <= 100
This is a view which has height equal to half its width.
Use the setters to adjust the behaviour as desired.
By default the progress is 0 and the width of the arc is 20.
Calling setProgress() will invalidate the view with the progress given.
Adding a background drawable is possible and the progress bar will be draw on top.
public class SemicircularProgressBar extends View {
private int mProgress;
private RectF mOval;
private RectF mOvalInner;
private Paint mPaintProgress;
private Paint mPaintClip;
private float ovalsDiff;
private Path clipPath;
public SemicircularProgressBar(Context context) {
super(context);
init();
}
public SemicircularProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public SemicircularProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mProgress = 0;
ovalsDiff = 20;
mOval = new RectF();
mOvalInner = new RectF();
clipPath = new Path();
mPaintProgress = new Paint();
mPaintProgress.setColor(Color.GREEN);
mPaintProgress.setAntiAlias(true);
mPaintClip = new Paint();
mPaintClip.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mPaintClip.setAlpha(0);
mPaintClip.setAntiAlias(true);
}
// call this from the code to change the progress displayed
public void setProgress(int progress) {
this.mProgress = progress;
invalidate();
}
// sets the width of the progress arc
public void setProgressBarWidth(float width) {
this.ovalsDiff = width;
invalidate();
}
// sets the color of the bar (#FF00FF00 - Green by default)
public void setProgressBarColor(int color){
this.mPaintProgress.setColor(color);
}
#Override
public void onDraw(Canvas c) {
super.onDraw(c);
mOval.set(0, 0, this.getWidth(), this.getHeight()*2);
mOvalInner.set(0+ovalsDiff, 0+ovalsDiff, this.getWidth()-ovalsDiff, this.getHeight()*2);
clipPath.addArc(mOvalInner, 180, 180);
c.clipPath(clipPath, Op.DIFFERENCE);
c.drawArc(mOval, 180, 180f * ((float) mProgress / 100), true, mPaintProgress);
}
// Setting the view to be always a rectangle with height equal to half of its width
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
this.setMeasuredDimension(parentWidth/2, parentHeight);
ViewGroup.LayoutParams params = this.getLayoutParams();
params.width = parentWidth;
params.height = parentWidth/2;
this.setLayoutParams(params);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
You can use this library :
compile 'com.github.lzyzsd:circleprogress:1.1.1'
for example :
<com.github.lzyzsd.circleprogress.DonutProgress
android:layout_marginLeft="50dp"
android:id="#+id/donut_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
custom:donut_progress="30"/>
<com.github.lzyzsd.circleprogress.ArcProgress
android:id="#+id/arc_progress"
android:background="#214193"
android:layout_marginLeft="50dp"
android:layout_width="100dp"
android:layout_height="100dp"
custom:arc_progress="55"
custom:arc_bottom_text="MEMORY"/>
For more information see the following website :
https://github.com/lzyzsd/CircleProgress
You may be able to use this github library - circularseekbar. To achieve the half circle, you will need to manipulate the following attributes: "app:start_angle" & "app:end_angle"
More Options:
The Holo Seekbar library
Tutorial showing semi-circular seekbar link to tutorial

Getting square images like gmail app

I want to show images with alphabets like gmail app as shown in the below figure.
Are all those images are images to be kept in drawable folder or they are drawn as square shapes and then letters are drawn to them? Below is what I tried so far to do dynamically. I got just a square shape. Can someone suggest the way to achieve like in gmail app?
GradientDrawable gd = new GradientDrawable();
gd.mutate();
gd.setColor(getResources().getColor(gColors[i]));
button.setBackgroundDrawable(gd);
Update 2:
I have fixed some of the bugs and released the code as an open source library at: https://github.com/amulyakhare/TextDrawable. It also include some other features that you might want to check out.
Old Answer:
I recommend you to use the following class CharacterDrawable (just copy-paste this):
public class CharacterDrawable extends ColorDrawable {
private final char character;
private final Paint textPaint;
private final Paint borderPaint;
private static final int STROKE_WIDTH = 10;
private static final float SHADE_FACTOR = 0.9f;
public CharacterDrawable(char character, int color) {
super(color);
this.character = character;
this.textPaint = new Paint();
this.borderPaint = new Paint();
// text paint settings
textPaint.setColor(Color.WHITE);
textPaint.setAntiAlias(true);
textPaint.setFakeBoldText(true);
textPaint.setStyle(Paint.Style.FILL);
textPaint.setTextAlign(Paint.Align.CENTER);
// border paint settings
borderPaint.setColor(getDarkerShade(color));
borderPaint.setStyle(Paint.Style.STROKE);
borderPaint.setStrokeWidth(STROKE_WIDTH);
}
private int getDarkerShade(int color) {
return Color.rgb((int)(SHADE_FACTOR * Color.red(color)),
(int)(SHADE_FACTOR * Color.green(color)),
(int)(SHADE_FACTOR * Color.blue(color)));
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
// draw border
canvas.drawRect(getBounds(), borderPaint);
// draw text
int width = canvas.getWidth();
int height = canvas.getHeight();
textPaint.setTextSize(height / 2);
canvas.drawText(String.valueOf(character), width/2, height/2 - ((textPaint.descent() + textPaint.ascent()) / 2) , textPaint);
}
#Override
public void setAlpha(int alpha) {
textPaint.setAlpha(alpha);
}
#Override
public void setColorFilter(ColorFilter cf) {
textPaint.setColorFilter(cf);
}
#Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
Then using this is simple: new CharacterDrawable('A', 0xFF805781); by passing the character and the color value (example Color.RED or some other color in hex 0xFF805781):
ImageView imageView = (ImageView) findViewById(R.id.imageView);
CharacterDrawable drawable = new CharacterDrawable('A', 0xFF805781);
imageView.setImageDrawable(drawable);
or based on your question:
CharacterDrawable drawable = new CharacterDrawable('A', 0xFF805781);
button.setBackgroundDrawable(drawable);
The drawable will scale to fit the size of the ImageView. Result will be:
Update: Updated code for adding a border which is of darker shade (automatically picks a dark shade based on the fill color).
1) Change the value of STROKE_WIDTH based on your needs for the border thikness.
2) Change the value of SHADE_FACTOR for border darkness. If SHADE_FACTOR is small (eg. 0.2f), the border will be darker and vice versa.
Note: You can easily vary the size and font of the character
Simple thing is that you have use Linear Layout and set that background color and set TectView inside that root layout. Its Over.
You should use ColorCode Intesed of images that will good thing compare to use images in terms of loading on UI thread.
<LinearLayout
android:id="#+id/get_more"
android:layout_width="70dp" // this root layout will set your square
android:layout_height="70dp"
android:background="#654321" // set background color of square
android:orientation="horizontal" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textSize="24sp"
android:text="C"
android:background="#ffffff" // Text Color , set as White
android:textAppearance="?android:attr/textAppearanceLarge" />
</LinearLayout>
I tweak the code a little bit..., and it works everytime even with different screen sizes. The trick is to obtain the ImageView canvas size in pixels (which sometimes is density dependent on various devices)
package net.mypapit.android.ui;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.drawable.ColorDrawable;
public class CharacterDrawable extends ColorDrawable {
private final char character;
private final Paint textPaint;
private final Paint borderPaint;
private static final int STROKE_WIDTH = 10;
private static final float SHADE_FACTOR = 0.9f;
private int mwidth, mheight;
public CharacterDrawable(char character, int color, int width, int height) {
super(color);
this.character = character;
this.textPaint = new Paint();
this.borderPaint = new Paint();
this.mwidth = width;
this.mheight = height;
// text paint settings
textPaint.setColor(Color.WHITE);
textPaint.setAntiAlias(true);
textPaint.setFakeBoldText(true);
textPaint.setStyle(Paint.Style.FILL);
textPaint.setTextAlign(Paint.Align.CENTER);
// border paint settings
borderPaint.setColor(getDarkerShade(color));
borderPaint.setStyle(Paint.Style.STROKE);
borderPaint.setStrokeWidth(STROKE_WIDTH);
}
private int getDarkerShade(int color) {
return Color.rgb((int)(SHADE_FACTOR * Color.red(color)),
(int)(SHADE_FACTOR * Color.green(color)),
(int)(SHADE_FACTOR * Color.blue(color)));
}
public void draw(Canvas canvas) {
super.draw(canvas);
// draw border
canvas.drawRect(getBounds(), borderPaint);
// draw text
int width = this.mwidth;
int height = this.mheight;
textPaint.setTextSize(height / 2);
canvas.drawText(String.valueOf(character), width/2, height/2 - ((textPaint.descent() + textPaint.ascent()) / 2) , textPaint);
}
public void setAlpha(int alpha) {
textPaint.setAlpha(alpha);
}
public void setColorFilter(ColorFilter cf) {
textPaint.setColorFilter(cf);
}
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
}
Then, refer back to the original Amulya Khare answer:
ImageView imageView = (ImageView) findViewById(R.id.imageView);
CharacterDrawable drawable = new CharacterDrawable('A', 0xFF805781,imageView.getWidth(),imageView.getHeight());
imageView.setImageDrawable(drawable);
It should work on different screen density by now =)

Android RatingBar change star colors [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
How can I change the star colors and size of the stars?
It's a little complicated at the mentioned blog, I've used a similar but simplier way.
You do need 3 star images (red_star_full.png, red_star_half.png and red_star_empty.png) and one xml, that's all.
Put these 3 images at res/drawable.
Put there the following ratingbar_red.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/red_star_empty" />
<item android:id="#android:id/secondaryProgress" android:drawable="#drawable/red_star_half" />
<item android:id="#android:id/progress" android:drawable="#drawable/red_star_full" />
</layer-list>
and, finally, tell your ratingbar definition to use this, i.e.
<RatingBar android:progressDrawable="#drawable/ratingbar_red"/>
That's it.
Try this, if you only want to change color:
RatingBar ratingBar = (RatingBar) findViewById(R.id.ratingBar);
LayerDrawable stars = (LayerDrawable) ratingBar.getProgressDrawable();
stars.getDrawable(2).setColorFilter(Color.YELLOW, PorterDuff.Mode.SRC_ATOP);
The easiest way that worked for me...if you are extending AppCompat Activity
In your build.gradle add latest appcompat library.
dependencies {
compile 'com.android.support:appcompat-v7:X.X.X' // where X.X.X version
}
Make your activity extend android.support.v7.app.AppCompatActivity
public class MainActivity extends AppCompatActivity {
...
}
Declare custom style in your styles.xml file.
<style name="RatingBar" parent="Theme.AppCompat">
<item name="colorControlNormal">#color/indigo</item>
<item name="colorControlActivated">#color/pink</item>
</style>
Apply this style to your RatingBar via android:theme attribute.
<RatingBar
android:theme="#style/RatingBar"
android:rating="3"
android:stepSize="0.5"
android:numStars="5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
From the API 21 on it's very easy to change the color of the stars with this three lines of code:
android:progressTint="#android:color/holo_red_dark"
android:progressBackgroundTint="#android:color/holo_red_dark"
android:secondaryProgressTint="#android:color/holo_red_dark"
Doing it like this, you'll change:
the filled stars color (progressTint)
the unfilled stars color (progressBackgroundTint)
and the border color (secondaryProgressTint) of the stars
2015 Update
Now you can use DrawableCompat to tint all kind of drawables. For example:
Drawable progress = ratingBar.getProgressDrawable();
DrawableCompat.setTint(progress, Color.WHITE);
This is backwards compatible up to API 4
If you want to change color for all stars states you my use:
LayerDrawable stars = (LayerDrawable) ratingBar.getProgressDrawable();
stars.getDrawable(2).setColorFilter(getResources().getColor(R.color.starFullySelected), PorterDuff.Mode.SRC_ATOP);
stars.getDrawable(1).setColorFilter(getResources().getColor(R.color.starPartiallySelected), PorterDuff.Mode.SRC_ATOP);
stars.getDrawable(0).setColorFilter(getResources().getColor(R.color.starNotSelected), PorterDuff.Mode.SRC_ATOP);
Step #1: Create your own style, by cloning one of the existing styles (from $ANDROID_HOME/platforms/$SDK/data/res/values/styles.xml), putting it in your own project's styles.xml, and referencing it when you add the widget to a layout.
Step #2: Create your own LayerDrawable XML resources for the RatingBar, pointing to appropriate images to use for the bar. The original styles will point you to the existing resources that you can compare with. Then, adjust your style to use your own LayerDrawable resources, rather than built-in ones.
The solutions that Alex and CommonsWares have posted are correct. One thing that the Android never talks about though is proper pixel sizes for different densities. Here are the required dimensions for each density based on halo light.
Small Star
mdpi: 16px
hdpi: 24px
xhdpi: 32px
xxhdpi: 48px
Medium Star
mdpi: 24px
hdpi: 36px
xhdpi: 48px
xxhdpi: 72px
Large Star
mdpi: 35px
hdpi: 52px
xhdpi: 69px
xxhdpi: 105px
So I have been struggling with this issue for two hours and I have come up with a working solution for all API versions, where half stars ratings are also shown.
private void setRatingStarColor(Drawable drawable, #ColorInt int color)
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
{
DrawableCompat.setTint(drawable, color);
}
else
{
drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN);
}
}
You call the method with this order of drawables:
LayerDrawable stars = (LayerDrawable) ratingBar.getProgressDrawable();
// Filled stars
setRatingStarColor(stars.getDrawable(2), ContextCompat.getColor(getContext(), R.color.foreground));
// Half filled stars
setRatingStarColor(stars.getDrawable(1), ContextCompat.getColor(getContext(), R.color.background));
// Empty stars
setRatingStarColor(stars.getDrawable(0), ContextCompat.getColor(getContext(), R.color.background));
NOTE: Also you must specify attributes "max" and "numStars" in XML, otherwise half stars aren't shown.
Now you can use DrawableCompat from AppCompat v22.1.0 onwards to dynamically tint all kind of drawables, useful when you're supporting multiple themes with a single set of drawables. For example:
LayerDrawable layerDrawable = (LayerDrawable) ratingBar.getProgressDrawable();
DrawableCompat.setTint(DrawableCompat.wrap(layerDrawable.getDrawable(0)), Color.RED); // Empty star
DrawableCompat.setTint(DrawableCompat.wrap(layerDrawable.getDrawable(1)), Color.GREEN); // Partial star
DrawableCompat.setTint(DrawableCompat.wrap(layerDrawable.getDrawable(2)), Color.BLUE); // Full star
This is backwards compatible down to API 4. Also see Chris Banes' blog post on Support Libraries v22.1.0
For the actual size and shape you will need to define a new style and layer-list drawables for the appropriate size, as others have already answered above.
For just changing the color of Rating bar from xml:-
android:progressTint="#color/your_color"
android:backgroundTint="#color/your_color"
android:secondaryProgressTint="#color/your_color"
Use android:theme attribute:
styles.xml
<style name="Theme.Rating" parent="Theme.AppCompat.Light">
<item name="colorAccent">#color/rating</item>
</style>
layout.xml
<android.support.v7.widget.AppCompatRatingBar
android:theme="#style/Theme.Rating"
android:numStars="5"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
To change the color you just have to put set the parameter android:progressTint
<RatingBar
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginTop="15dp"
android:numStars="5"
android:rating="1"
android:progressTint="#android:/color/black"
android:layout_gravity="center"
/>
For the size the style property.
The simpliest way:
android:progressTint="#color/color"
Withou adding a new style you can use the tint color within the RatingBar
<RatingBar
android:id="#+id/ratingBar"
style="#android:style/Widget.Holo.RatingBar.Small"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:numStars="5"
android:rating="4.5"
android:stepSize="0.5"
android:progressTint="#color/colorPrimary"/>
Building #lgvalle's answer.
2015 Update
Now you can use DrawableCompat to tint all kind of drawables. For example:
Drawable progress = ratingBar.getProgressDrawable();
DrawableCompat.setTint(progress, Color.WHITE);
This is backwards compatible up to API 4
LayerDrawable drawable = (LayerDrawable) getProgressDrawable();
Drawable progress = drawable.getDrawable(2);
DrawableCompat.setTint(progress, getResources().getColor(COLOR1));
progress = drawable.getDrawable(1);
DrawableCompat.setTintMode(progress, PorterDuff.Mode.DST_ATOP);
DrawableCompat.setTint(progress, getResources().getColor(COLOR1));
DrawableCompat.setTintMode(progress, PorterDuff.Mode.SRC_ATOP);
DrawableCompat.setTint(progress, getResources().getColor(COLOR2));
progress = drawable.getDrawable(0);
DrawableCompat.setTint(progress, getResources().getColor(COLOR2));
This will keep the fraction steps colors.
<!--For rating bar -->
<style name="RatingBarfeed" parent="Theme.AppCompat">
<item name="colorControlNormal">#color/colorPrimary</item>
<item name="colorControlActivated">#color/colorPrimary</item>
</style>
use your own color
Works for Android below and above version 21
After some research I've come up with this method to set the background tint, the gap tint (ex: half star) and the star tint color.
LayerDrawable layers = (LayerDrawable) ratingBar.getProgressDrawable();
DrawableCompat.setTint(layers.getDrawable(0), 0x33000000); // The background tint
DrawableCompat.setTint(layers.getDrawable(1), 0x00000000); // The gap tint (Transparent in this case so the gap doesnt seem darker than the background)
DrawableCompat.setTint(layers.getDrawable(2), 0xffFED80A); // The star tint
Simple solution, use AppCompatRatingBar and its setProgressTintList method to achieve this, see this answer for reference.
I solve this issue this following:
LayerDrawable layerDrawable = (LayerDrawable) ratingBar.getProgressDrawable();
DrawableCompat.setTint(DrawableCompat.wrap(layerDrawable.getDrawable(0)),
Color.WHITE); // Empty star
DrawableCompat.setTint(DrawableCompat.wrap(layerDrawable.getDrawable(1)),
Color.YELLOW); // Partial star
DrawableCompat.setTint(DrawableCompat.wrap(layerDrawable.getDrawable(2)),
Color.YELLOW);
RatingBar mRating=(RatingBar)findViewById(R.id.rating);
LayerDrawable layerDrawable=(LayerDrawable)mRating.getProgressDrawable();
layerDrawable.getDrawable(2).setColorFilter(Color.parseColor
("#32CD32"), PorterDuff.Mode.SRC_ATOP);
for me its working....
I found a simple solution for changing the color of the star according to your theme.
Goto this site : http://android-holo-colors.com/
Choose your theme color and get your star images created.
Using the answers above, I created a quick static method that can easily be re-used. It only aims at tinting the progress color for the activated stars. The stars that are not activated remain grey.
public static RatingBar tintRatingBar (RatingBar ratingBar, int progressColor)if (ratingBar.getProgressDrawable() instanceof LayerDrawable) {
LayerDrawable progressDrawable = (LayerDrawable) ratingBar.getProgressDrawable();
Drawable drawable = progressDrawable.getDrawable(2);
Drawable compat = DrawableCompat.wrap(drawable);
DrawableCompat.setTint(compat, progressColor);
Drawable[] drawables = new Drawable[3];
drawables[2] = compat;
drawables[0] = progressDrawable.getDrawable(0);
drawables[1] = progressDrawable.getDrawable(1);
LayerDrawable layerDrawable = new LayerDrawable(drawables);
ratingBar.setProgressDrawable(layerDrawable);
return ratingBar;
}
else {
Drawable progressDrawable = ratingBar.getProgressDrawable();
Drawable compat = DrawableCompat.wrap(progressDrawable);
DrawableCompat.setTint(compat, progressColor);
ratingBar.setProgressDrawable(compat);
return ratingBar;
}
}
Just pass your rating bar and a Color using getResources().getColor(R.color.my_rating_color)
As you can see, I use DrawableCompat so it's backward compatible.
EDIT : This method does not work on API21 (go figure why). You end up with a NullPointerException when calling setProgressBar. I ended up disabling the whole method on API >= 21.
For API >= 21, I use SupperPuccio solution.
The rating bar is used automatically at run time for change color on touch star.
First add style in app\src\main\res\values\styles.xml file:
<style name="RatingBar" parent="Theme.AppCompat">
<item name="colorControlNormal">#android:color/darker_gray</item>
<item name="colorControlActivated">#color/com_facebook_blue</item>
</style>
Then your rating bar add theme like this:
<RatingBar
android:id="#+id/rating"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:numStars="5"
android:stepSize="1"
android:theme="#style/RatingBar"/>
1) declare this xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="horizontal"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:layout_marginBottom="20dp"
android:background="#323232"
android:gravity="center_horizontal">
<com.example.android.custom_ratingbar.CustomRatingBar
android:id="#+id/coloredRatingBar5"
style="#style/coloredRatingBarStyleSmall"
android:layout_width="wrap_content"
android:layout_height="match_parent"
/>
</LinearLayout>
2) in style.xml
<style name="coloredRatingBarStyleSmall">
<item name="indicator">false</item>
<item name="type">small</item>
</style>
3)
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class CustomRatingBar extends View{
private static final String TAG="ColoredRatingBar";
private static final int NORMAL = 0;
private static final int SMALL = 1;
Bitmap[] drawables;
Bitmap progressBackground;
Context mContext;
private int mNumStars =9;
private float mRating =0;
private boolean mIndicator;
private float slidePosition;
private int mType;
/**
* A callback that notifies clients when the rating has been changed. This
* includes changes that were initiated by the user through a touch gesture
* or arrow key/trackball as well as changes that were initiated
* programmatically.
*/
public interface OnRatingBarChangeListener {
/**
* Notification that the rating has changed. Clients can use the
* fromUser parameter to distinguish user-initiated changes from those
* that occurred programmatically. This will not be called continuously
* while the user is dragging, only when the user finalizes a rating by
* lifting the touch.
*
* #param ratingBar The RatingBar whose rating has changed.
* #param rating The current rating. This will be in the range
* 0..numStars.
* #param fromUser True if the rating change was initiated by a user's
* touch gesture or arrow key/horizontal trackbell movement.
*/
void onRatingChanged(CustomRatingBar ratingBar, float rating, boolean fromUser);
}
private OnRatingBarChangeListener mOnRatingBarChangeListener;
public CustomRatingBar(Context context) {
this(context, null);
}
public CustomRatingBar(Context context, AttributeSet attrs) {
this(context, attrs,0);//R.attr.coloredRatingBarStyle
}
public CustomRatingBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomRatingBar,defStyle, 0);
final boolean indicator = a.getBoolean(R.styleable.CustomRatingBar_indicator, false);
final float rating = a.getFloat(R.styleable.CustomRatingBar_setrating, -1);
final int type = a.getInt(R.styleable.CustomRatingBar_type, 0);
a.recycle();
setIndicator(indicator);
setRating(rating);
setType(type);
init(context);
}
public int getType() {
return mType;
}
public void setType(int type) {
this.mType = type;
}
private void init(Context context) {
mContext = context;
Resources res = getResources();
if(mType==SMALL){
drawables = new Bitmap[]{BitmapFactory.decodeResource(res, R.drawable.rating_inactive),BitmapFactory.decodeResource(res, R.drawable.rating_active)};
progressBackground = BitmapFactory.decodeResource(res, R.drawable.rating_inactive);
}else{
drawables = new Bitmap[]{BitmapFactory.decodeResource(res, R.drawable.rating_inactive),BitmapFactory.decodeResource(res, R.drawable.rating_active)};
progressBackground = BitmapFactory.decodeResource(res, R.drawable.rating_inactive);
}
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//draw empty stars bg
for(int i=0;i< mNumStars;i++){
drawStar(canvas,i);
}
}
private void drawStar(Canvas canvas, int position) {
float fraction = mRating -(position);
Bitmap ratedStar1 = getRatedStar();
Paint paint=getPaint(position);
int division=getSize();
Bitmap ratedStar=null;
Bitmap emptyStar=null;
if(!isInEditMode()){
ratedStar=Bitmap.createScaledBitmap(ratedStar1, division, division, false);
emptyStar=Bitmap.createScaledBitmap(progressBackground, division, division, false);
}
if((position)< mRating){
if(!isInEditMode()){
canvas.drawBitmap(ratedStar,(position* division),0,paint);
}
} else{
if(!isInEditMode()){
canvas.drawBitmap(emptyStar,(position*division),0,null);
}
}
}
private int getSize(){
return (getWidth()/mNumStars);
}
private Bitmap getRatedStar() {
if(mRating==0){
return drawables[0];
}
else{
return drawables[1];
}
}
private Paint getPaint(int position){
int value=(255*(position+1))/mNumStars;
String hexString=Integer.toHexString(value).equals("0")?"00":Integer.toHexString(value);
String hexvalue="#"+hexString+"000000";//FEE98E
//Log.e("TAG", position+"/"+value+"/"+hexvalue);
Paint paint=new Paint();
paint.setColor(Color.parseColor(hexvalue));
return paint;
}
public int getNumStars() {
return mNumStars;
}
public void setNumStars(int numStars) {
this.mNumStars = numStars;
}
public float getRating() {
return mRating;
}
public void setRating(float rating) {
setRating(rating,false);
}
void setRating(float rating,boolean fromUser) {
if(rating>mNumStars){
this.mRating = mNumStars;
}
this.mRating = rating;
invalidate();
dispatchRatingChange(fromUser);
}
public boolean isIndicator() {
return mIndicator;
}
public void setIndicator(boolean indicator) {
this.mIndicator = indicator;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (progressBackground != null) {
final int width = progressBackground.getWidth() * mNumStars;
final int height = progressBackground.getHeight();
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
Bitmap emptyStar=Bitmap.createScaledBitmap(progressBackground, widthSize/mNumStars, widthSize/mNumStars, false);
int heightSize = emptyStar.getHeight();
setMeasuredDimension(resolveSizeAndState(widthSize, widthMeasureSpec, 0),
resolveSizeAndState(heightSize, heightMeasureSpec, 0));
}
else{
int desiredWidth = 100;
int desiredHeight = 50;
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int width;
int height;
//Measure Width
if (widthMode == MeasureSpec.EXACTLY) {
//Must be this size
width = widthSize;
} else if (widthMode == MeasureSpec.AT_MOST) {
//Can't be bigger than...
width = Math.min(desiredWidth, widthSize);
} else {
//Be whatever you want
width = desiredWidth;
}
//Measure Height
if (heightMode == MeasureSpec.EXACTLY) {
//Must be this size
height = heightSize;
} else if (heightMode == MeasureSpec.AT_MOST) {
//Can't be bigger than...
height = Math.min(desiredHeight, heightSize);
} else {
//Be whatever you want
height = desiredHeight;
}
//MUST CALL THIS
setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, 0),resolveSizeAndState(height, heightMeasureSpec, 0));
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
if(mIndicator){
return false;
}
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
slidePosition = getRelativePosition(event.getX());
int newRating = (int)(slidePosition>0?slidePosition+1:0) ;
if(newRating>mNumStars){
newRating=mNumStars;
}
// Log.e("TAG", ""+newRating);
if (newRating != mRating) {
setRating(newRating,true);
}
break;
case MotionEvent.ACTION_CANCEL:
break;
default:
break;
}
return true;
}
private float getRelativePosition(float x) {
Bitmap emptyStar=Bitmap.createScaledBitmap(progressBackground, getWidth()/mNumStars, getWidth()/mNumStars, false);
int widthSize = emptyStar.getWidth();
// Log.e("TAG", widthSize+"/"+x);
float position = x / widthSize;
position = Math.max(position, 0);
return Math.min(position, mNumStars);
}
/**
* Sets the listener to be called when the rating changes.
*
* #param listener The listener.
*/
public void setOnRatingBarChangeListener(OnRatingBarChangeListener listener) {
mOnRatingBarChangeListener = listener;
}
/**
* #return The listener (may be null) that is listening for rating change
* events.
*/
public OnRatingBarChangeListener getOnRatingBarChangeListener() {
return mOnRatingBarChangeListener;
}
void dispatchRatingChange(boolean fromUser) {
if (mOnRatingBarChangeListener != null) {
mOnRatingBarChangeListener.onRatingChanged(this, getRating(),
fromUser);
}
}
}
5) then in calling activity---
CustomRatingBar coloredRatingBar5=(CustomRatingBar)findViewById(R.id.coloredRatingBar5);
coloredRatingBar5.setOnRatingBarChangeListener(new OnRatingBarChangeListener() {
#Override
public void onRatingChanged(CustomRatingBar ratingBar, float rating,boolean fromUser) {
// TODO Auto-generated method stub
Log.e("RATING", ""+rating);
}
});
6) rating active---take any image with dark color coz it will be used as color transparency for different rating
rating_inactive--take any image of same size of above image with light background..it will used when no rating is selected
A very easy way to change the border colour of the stars is using the xml parameter:
android:progressBackgroundTint=""
in the ratingBar view. The value should be a hexadecimal code for a color.
I was looking for a reliable method to do this all the way down to API 9 at least.
The "casting to LayerDrawble" solution seemed like a risky solution to me, and when I tested it out on an Android phone on 2.3, it casted successfully but the call to DrawableCompat.setTint(...) did not have any effect.
The need to load drawable assets did not seem like a good solution to me either.
I decided to code my own solution which is a class extending AppCompatRatingBar, using a custom Drawable taking care of drawing the stars programmatically. It works perfectly for my needs, I'll post it in case it helps anyone:
https://gist.github.com/androidseb/2b8044c90a07c7a52b4bbff3453c8460
The link is easier because you can get the full file directly, but here is the full code just in case:
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.AppCompatRatingBar;
import android.util.AttributeSet;
/**
* #author androidseb
* <p/>
* Extends AppCompatRatingBar with the ability to tint the drawn stars when selected, pressed and un-selected.
* Limitation: Only draws full stars.
*/
public class TintableRatingBar extends AppCompatRatingBar {
private TintableRatingBarProgressDrawable progressDrawable;
public TintableRatingBar(final Context context) {
super(context);
init();
}
public TintableRatingBar(final Context context, final AttributeSet attrs) {
super(context, attrs);
init();
}
public TintableRatingBar(final Context context, final AttributeSet attrs, final int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
progressDrawable = new TintableRatingBarProgressDrawable();
setProgressDrawable(progressDrawable);
}
public void setCustomTintColors(final int _uncheckedColor, final int _pressedColor, final int _checkedColor) {
progressDrawable.setRatingMaxLevelValue(getMax() * 1000);
progressDrawable.setUnCheckedColor(_uncheckedColor);
progressDrawable.setPressedColor(_pressedColor);
progressDrawable.setCheckedColor(_checkedColor);
invalidate();
}
public class TintableRatingBarProgressDrawable extends Drawable {
private static final int STAR_COUNT = 5;
private static final int STAR_BRANCHES_COUNT = 5;
/** Sets the max level value: if the level is at the max, then all stars are selected. */
private int ratingMaxLevelValue = 10000;
/** Color to be painted for unselected stars */
private int uncheckedColor = Color.GRAY;
/** Color to be painted for unselected stars when the ratingbar is pressed */
private int pressedColor = Color.CYAN;
/** Color to be painted for selected stars */
private int checkedColor = Color.BLUE;
#Override
public void setAlpha(final int _i) {
}
#Override
public void setColorFilter(final ColorFilter _colorFilter) {
}
#Override
public boolean isStateful() {
return true;
}
#Override
public boolean setState(final int[] stateSet) {
final boolean res = super.setState(stateSet);
invalidateSelf();
return res;
}
#Override
public int getOpacity() {
return 255;
}
public void setRatingMaxLevelValue(final int _ratingMaxLevelValue) {
ratingMaxLevelValue = _ratingMaxLevelValue;
}
public void setUnCheckedColor(final int _uncheckedColor) {
uncheckedColor = _uncheckedColor;
}
public void setPressedColor(final int _pressedColor) {
pressedColor = _pressedColor;
}
public void setCheckedColor(final int _checkedColor) {
checkedColor = _checkedColor;
}
#Override
public void draw(final Canvas _canvas) {
boolean pressed = false;
for (int i : getState()) {
if (i == android.R.attr.state_pressed) {
pressed = true;
}
}
final int level = (int) Math.ceil(getLevel() / (double) ratingMaxLevelValue * STAR_COUNT);
final int starRadius = Math.min(getBounds().bottom / 2, getBounds().right / STAR_COUNT / 2);
for (int i = 0; i < STAR_COUNT; i++) {
final int usedColor;
if (level >= i + 1) {
usedColor = checkedColor;
} else if (pressed) {
usedColor = pressedColor;
} else {
usedColor = uncheckedColor;
}
drawStar(_canvas, usedColor, (i * 2 + 1) * starRadius, getBounds().bottom / 2, starRadius,
STAR_BRANCHES_COUNT);
}
}
private void drawStar(final Canvas _canvas, final int _color, final float _centerX, final float _centerY,
final float _radius, final int _branchesCount) {
final double rotationAngle = Math.PI * 2 / _branchesCount;
final double rotationAngleComplement = Math.PI / 2 - rotationAngle;
//Calculating how much space is left between the bottom of the star and the bottom of the circle
//In order to be able to center the star visually relatively to the square when drawn
final float bottomOffset = (float) (_radius - _radius * Math.sin(rotationAngle / 2) / Math.tan(
rotationAngle / 2));
final float actualCenterY = _centerY + (bottomOffset / 2);
final Paint paint = new Paint();
paint.setColor(_color);
paint.setStyle(Style.FILL);
final Path path = new Path();
final float relativeY = (float) (_radius - _radius * (1 - Math.sin(rotationAngleComplement)));
final float relativeX = (float) (Math.tan(rotationAngle / 2) * relativeY);
final PointF a = new PointF(-relativeX, -relativeY);
final PointF b = new PointF(0, -_radius);
final PointF c = new PointF(relativeX, -relativeY);
path.moveTo(_centerX + a.x, actualCenterY + a.y);
_canvas.save();
for (int i = 0; i < _branchesCount; i++) {
path.lineTo(_centerX + b.x, actualCenterY + b.y);
path.lineTo(_centerX + c.x, actualCenterY + c.y);
rotationToCenter(b, rotationAngle);
rotationToCenter(c, rotationAngle);
}
_canvas.drawPath(path, paint);
_canvas.restore();
}
private void rotationToCenter(final PointF _point, final double _angleRadian) {
final float x = (float) (_point.x * Math.cos(_angleRadian) - _point.y * Math.sin(_angleRadian));
final float y = (float) (_point.x * Math.sin(_angleRadian) + _point.y * Math.cos(_angleRadian));
_point.x = x;
_point.y = y;
}
}
}
A bit late answer but i hope it will help some folks.
<RatingBar
android:id="#+id/rating"
style="#style/Base.Widget.AppCompat.RatingBar.Small"
android:theme="#style/WhiteRatingStar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/profil_name"
android:layout_centerHorizontal="true"
android:layout_marginLeft="#dimen/dimen_4"
android:rating="3" />
And here is what the WhiteRatingStar looks like
<style name="WhiteRatingStar" parent="Base.Widget.AppCompat.RatingBar.Small">
<item name="colorAccent">#android:color/white</item>
</style>
With this the stars will be coloured in white for example.
Use this link
Android RatingBar change star colors
set your style inside value/style(v-21);
As the previous answer implies, it is not easy to change the color of the ratingbar. The stars are not programmatically drawn, they are images with fixed size and specific color gradients. To change the color you have to create your own star images with different colors then proceed to create your own drawable XML resource and pass it to the ratingsBar class using setProgressDrawable(Drawable d) or XML attribute android:progressDrawable.

Categories

Resources