1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.support.v7.internal.widget; 18 19import android.content.Context; 20import android.graphics.Bitmap; 21import android.graphics.BitmapShader; 22import android.graphics.Shader; 23import android.graphics.drawable.AnimationDrawable; 24import android.graphics.drawable.BitmapDrawable; 25import android.graphics.drawable.ClipDrawable; 26import android.graphics.drawable.Drawable; 27import android.graphics.drawable.LayerDrawable; 28import android.graphics.drawable.ShapeDrawable; 29import android.graphics.drawable.shapes.RoundRectShape; 30import android.graphics.drawable.shapes.Shape; 31import android.support.v4.view.ViewCompat; 32import android.util.AttributeSet; 33import android.view.Gravity; 34import android.widget.RatingBar; 35 36/** 37 * An tint aware {@link android.widget.RatingBar}. 38 * 39 * @hide 40 */ 41public class TintRatingBar extends RatingBar { 42 43 private static final int[] TINT_ATTRS = { 44 android.R.attr.indeterminateDrawable, 45 android.R.attr.progressDrawable 46 }; 47 48 private Bitmap mSampleTile; 49 50 public TintRatingBar(Context context) { 51 this(context, null); 52 } 53 54 public TintRatingBar(Context context, AttributeSet attrs) { 55 this(context, attrs, android.R.attr.ratingBarStyle); 56 } 57 58 public TintRatingBar(Context context, AttributeSet attrs, int defStyleAttr) { 59 super(context, attrs, defStyleAttr); 60 61 TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, TINT_ATTRS, 62 defStyleAttr, 0); 63 64 Drawable drawable = a.getDrawable(0); 65 if (drawable != null) { 66 setIndeterminateDrawable(tileifyIndeterminate(drawable)); 67 } 68 69 drawable = a.getDrawable(1); 70 if (drawable != null) { 71 setProgressDrawable(tileify(drawable, false)); 72 } 73 74 a.recycle(); 75 } 76 77 /** 78 * Converts a drawable to a tiled version of itself. It will recursively 79 * traverse layer and state list drawables. 80 */ 81 private Drawable tileify(Drawable drawable, boolean clip) { 82 if (drawable instanceof DrawableWrapper) { 83 Drawable inner = ((DrawableWrapper) drawable).getWrappedDrawable(); 84 if (inner != null) { 85 inner = tileify(inner, clip); 86 ((DrawableWrapper) drawable).setWrappedDrawable(inner); 87 } 88 } else if (drawable instanceof LayerDrawable) { 89 LayerDrawable background = (LayerDrawable) drawable; 90 final int N = background.getNumberOfLayers(); 91 Drawable[] outDrawables = new Drawable[N]; 92 93 for (int i = 0; i < N; i++) { 94 int id = background.getId(i); 95 outDrawables[i] = tileify(background.getDrawable(i), 96 (id == android.R.id.progress || id == android.R.id.secondaryProgress)); 97 } 98 LayerDrawable newBg = new LayerDrawable(outDrawables); 99 100 for (int i = 0; i < N; i++) { 101 newBg.setId(i, background.getId(i)); 102 } 103 104 return newBg; 105 106 } else if (drawable instanceof BitmapDrawable) { 107 final Bitmap tileBitmap = ((BitmapDrawable) drawable).getBitmap(); 108 if (mSampleTile == null) { 109 mSampleTile = tileBitmap; 110 } 111 112 final ShapeDrawable shapeDrawable = new ShapeDrawable(getDrawableShape()); 113 final BitmapShader bitmapShader = new BitmapShader(tileBitmap, 114 Shader.TileMode.REPEAT, Shader.TileMode.CLAMP); 115 shapeDrawable.getPaint().setShader(bitmapShader); 116 return (clip) ? new ClipDrawable(shapeDrawable, Gravity.LEFT, 117 ClipDrawable.HORIZONTAL) : shapeDrawable; 118 } 119 120 return drawable; 121 } 122 123 /** 124 * Convert a AnimationDrawable for use as a barberpole animation. 125 * Each frame of the animation is wrapped in a ClipDrawable and 126 * given a tiling BitmapShader. 127 */ 128 private Drawable tileifyIndeterminate(Drawable drawable) { 129 if (drawable instanceof AnimationDrawable) { 130 AnimationDrawable background = (AnimationDrawable) drawable; 131 final int N = background.getNumberOfFrames(); 132 AnimationDrawable newBg = new AnimationDrawable(); 133 newBg.setOneShot(background.isOneShot()); 134 135 for (int i = 0; i < N; i++) { 136 Drawable frame = tileify(background.getFrame(i), true); 137 frame.setLevel(10000); 138 newBg.addFrame(frame, background.getDuration(i)); 139 } 140 newBg.setLevel(10000); 141 drawable = newBg; 142 } 143 return drawable; 144 } 145 146 private Shape getDrawableShape() { 147 final float[] roundedCorners = new float[] { 5, 5, 5, 5, 5, 5, 5, 5 }; 148 return new RoundRectShape(roundedCorners, null, null); 149 } 150 151 @Override 152 protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 153 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 154 155 if (mSampleTile != null) { 156 final int width = mSampleTile.getWidth() * getNumStars(); 157 setMeasuredDimension(ViewCompat.resolveSizeAndState(width, widthMeasureSpec, 0), 158 getMeasuredHeight()); 159 } 160 } 161 162} 163