简体   繁体   English

ShapeDrawable作为Android中RatingBar的progressDrawable?

[英]ShapeDrawable as progressDrawable for RatingBar in Android?

It seems I cannot set ShapeDrawable as progressDrawable for Ratingbar. 似乎我不能将ShapeDrawable设置为Ratingbar的progressDrawable。 I tried the following but failed: 我尝试了以下但失败了:

<RatingBar
android:id="@+id/ratingbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:numStars="5"
android:stepSize="1.0"
android:rating="3.0"
style="@style/myRatingBar"
/>

myRatingbar style: myRatingbar风格:

<style name="myRatingBar" 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">48dip</item>
<item name="android:maxHeight">48dip</item>
<item name="android:scaleType">center</item>
</style>

ratingbar_full.xml: ratingbar_full.xml:

    <shape 
       android:shape="ring"
       android:innerRadius="10dip"
       android:thickness="1dip">
       <solid android:color="@color/red" />
       <size
           android:width="48dip"
           android:height="48dip"
       />
    </shape>

Nothing shows on screen. 屏幕上没有显示任何内容。

EDIT: use .png instead of shape works for this line: 编辑:使用.png而不是形状适用于此行:

@drawable/ratingbar_full @绘制/ ratingbar_full

Shape XML Drawables + RatingBar a complete mess. 形状XML Drawables + RatingBar完全混乱。

This is as close as I could get to a solution without writing a whole new class. 这就像我可以在不编写全新类的情况下获得解决方案一样接近。

My extended class builds the progress drawable correctly for the ProgressBar clamping it as required. 我的扩展类为ProgressBar正确构建了可绘制的ProgressBar并根据需要将其钳制。

Replace your empty and full states with the ones I've prepopulated. 用我预先填充的状态替换您的空状态和完整状态。 Not very flexible at the moment, you could easily abstract the setting of empty/full star states. 目前不是很灵活,你可以很容易地抽象出空/满星状态的设置。

/**
 * Created by chris on 28/08/2014.
 * For Yoyo-Android.
 */
public class ShapeDrawableRatingBar extends RatingBar {


    /**
     * TileBitmap to base the width off of.
     */
    @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_stamp_circle_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_stamp_circle_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;
    }

} 

Calling setMax setMaxStars calls requestLayout so you it will measure the width correctly. 调用setMax setMaxStars调用requestLayout以便您正确测量宽度。 No need to work out the android:minWidth , Just set android:layout_width="wrap_content" . 无需解决android:minWidth ,只需设置android:layout_width="wrap_content"

Just remember you will need to add a bit of padding to your ShapeDrawables as they get repeated edge to edge. 记住你需要在ShapeDrawables添加一些填充,因为它们会重复边缘到边缘。

I customized the @ChrisJenkins method, so you can set the size of the RatingBar icons and custom tint colors for rating images. 我自定义了@ChrisJenkins方法,因此您可以设置RatingBar图标的大小和评级图像的自定义色调颜色。 You can set padding on your RatingBar or set your custom width and height. 您可以在RatingBar上设置填充或设置自定义宽度和高度。 @ChrisJenkins solution helped me a lot to finally come up with a totally customizable RatingBar. @ChrisJenkins解决方案帮助我最终想出了一个完全可定制的RatingBar。 You can set the parameters in your XML also. 您也可以在XML中设置参数。

public class DrawableRatingBar extends RatingBar
{
// TileBitmap to base the width and hight off of.
@Nullable
private Bitmap iconTile;
private float scaleIconFactor;
private @ColorInt int iconBackgroundColor;
private @ColorInt int iconForegroundColor;
private @DrawableRes int iconDrawable;

public DrawableRatingBar(Context context)
{
    super(context);
    init();
}

public DrawableRatingBar(Context context, AttributeSet attrs)
{
    super(context, attrs);

    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DrawableRatingBarItem);
    scaleIconFactor = a.getFloat(R.styleable.DrawableRatingBarItem_scaleIconFactor, 1);
    iconBackgroundColor = a.getColor(R.styleable.DrawableRatingBarItem_iconBackgroundColor, Color.BLACK);
    iconForegroundColor = a.getColor(R.styleable.DrawableRatingBarItem_iconForegroundColor, Color.WHITE);
    iconDrawable = a.getResourceId(R.styleable.DrawableRatingBarItem_iconDrawable, -1);
    a.recycle();

    init();
}

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

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public DrawableRatingBar(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
{
    super(context, attrs, defStyleAttr, defStyleRes);
    init();
}

private void init()
{
    setProgressDrawable(createProgressDrawable());
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    if (iconTile != null)
    {
        final int width = iconTile.getWidth() * getNumStars();
        final int height = iconTile.getHeight();
        setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, 0),
                             resolveSizeAndState(height, heightMeasureSpec, 0));
    }
}

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()
{
    Drawable drawable = ContextCompat.getDrawable(getContext(), iconDrawable);

    final Bitmap tileBitmap = scaleImageWithColor(drawable, scaleIconFactor, iconBackgroundColor);
    if (iconTile == null)
    {
        iconTile = 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;
}


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

protected Drawable createProgressDrawableShape()
{
    Drawable drawable = ContextCompat.getDrawable(getContext(), iconDrawable);

    final Bitmap tileBitmap = scaleImageWithColor(drawable, scaleIconFactor, iconForegroundColor);
    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()
{
    return new RectShape();
}

public Bitmap scaleImageWithColor(Drawable drawable, float scaleFactor, @ColorInt int color)
{
    Bitmap b = ((BitmapDrawable) drawable).getBitmap();

    int sizeX = Math.round(drawable.getIntrinsicWidth() * scaleFactor);
    int sizeY = Math.round(drawable.getIntrinsicHeight() * scaleFactor);

    Bitmap bitmapResized = Bitmap.createScaledBitmap(b, sizeX, sizeY, true);

    Canvas canvas = new Canvas(bitmapResized);
    Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG);
    paint.setAntiAlias(true);
    ColorFilter filter = new LightingColorFilter(color, 1);
    paint.setColorFilter(filter);
    canvas.drawBitmap(bitmapResized, 0, 0, paint);

    return bitmapResized;

}

protected Bitmap fromDrawable(final Drawable drawable, final int height, final int width) {
    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;
}

public float getScaleIconFactor() {
    return scaleIconFactor;
}

public void setScaleIconFactor(float scaleIconFactor) {
    this.scaleIconFactor = scaleIconFactor;
}

public int getIconForegroundColor() {
    return iconForegroundColor;
}

public void setIconForegroundColor(int iconForegroundColor) {
    this.iconForegroundColor = iconForegroundColor;
}

public int getIconBackgroundColor() {
    return iconBackgroundColor;
}

public void setIconBackgroundColor(int iconBackgroundColor) {
    this.iconBackgroundColor = iconBackgroundColor;
}

public int getIconDrawable() {
    return iconDrawable;
}

public void setIconDrawable(int iconDrawable) {
    this.iconDrawable = iconDrawable;
}

}

Custom attributes: 自定义属性:

<declare-styleable name="DrawableRatingBarItem">
    <attr name="scaleIconFactor" format="float"/>
    <attr name="iconBackgroundColor" format="color"/>
    <attr name="iconForegroundColor" format="color"/>
    <attr name="iconDrawable" format="reference"/>
</declare-styleable>

I dig RatingBar(and its parent class ProgressBar) source code, and found that ShapeDrawable is missing from tileify() which is called before setting progressDrawable in ProgressBar constructor: 我挖掘了RatingBar(及其父类ProgressBar)的源代码,发现tileify()中缺少ShapeDrawable,在ProgressBar构造函数中设置progressDrawable之前调用它:

private Drawable tileify(Drawable drawable, boolean clip) {

     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] = tileify(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 StateListDrawable) {
         StateListDrawable in = (StateListDrawable) drawable;
         StateListDrawable out = new StateListDrawable();
         int numStates = in.getStateCount();
         for (int i = 0; i < numStates; i++) {
             out.addState(in.getStateSet(i), tileify(in.getStateDrawable(i), clip));
         }
         return out;

     } else if (drawable instanceof BitmapDrawable) {
         final Bitmap tileBitmap = ((BitmapDrawable) drawable).getBitmap();
         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 (clip) ? new ClipDrawable(shapeDrawable, Gravity.LEFT,
                 ClipDrawable.HORIZONTAL) : shapeDrawable;
     }

     return drawable;
}

I don't know why ShapeDrawable was not put there but I am considering to add it in subclassing code. 我不知道为什么ShapeDrawable没有放在那里,但我正在考虑在子类代码中添加它。

You can find a step by step ratingBar styling here 您可以在这里找到一步一步的ratingBar造型
try to set <item name="android:progressDrawable"> to an image instead of of shape, if it works that means your shape is causing the problem 尝试将<item name="android:progressDrawable">设置为图像而不是形状,如果它的工作意味着你的形状导致问题

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM