Set space between stars
106 | * 107 | * @param space space between stars in pixel unit 108 | */ 109 | public void setSpace(int space) { 110 | space = Math.max(0, space); 111 | if (mSpace == space) { 112 | return; 113 | } 114 | 115 | mSpace = space; 116 | updateStarViews(); 117 | } 118 | 119 | public Drawable getFillDrawable() { 120 | return mFillDrawable; 121 | } 122 | 123 | public void setFillDrawable(Drawable fillDrawable) { 124 | if (mFillDrawable == fillDrawable) { 125 | return; 126 | } 127 | mFillDrawable = fillDrawable; 128 | updateStarViews(); 129 | } 130 | 131 | /** 132 | *Set the fill star drawable resource
133 | * 134 | * @param res drawable resource 135 | */ 136 | public void setFillDrawableRes(@DrawableRes int res) { 137 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 138 | setFillDrawable(mContext.getDrawable(res)); 139 | } else { 140 | setFillDrawable(mContext.getResources().getDrawable(res)); 141 | } 142 | } 143 | 144 | public Drawable getEmptyDrawable() { 145 | return mEmptyDrawable; 146 | } 147 | 148 | public void setEmptyDrawable(@Nullable Drawable emptyDrawable) { 149 | mEmptyDrawable = emptyDrawable; 150 | updateStarViews(); 151 | } 152 | 153 | /** 154 | *Set the empty star drawable resource
155 | * 156 | * @param res drawable resource 157 | */ 158 | public void setEmptyDrawableRes(@DrawableRes int res) { 159 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 160 | setEmptyDrawable(mContext.getDrawable(res)); 161 | } else { 162 | setEmptyDrawable(mContext.getResources().getDrawable(res)); 163 | } 164 | } 165 | 166 | public void setOnRatingChangeListener(@Nullable OnRatingChangeListener listener) { 167 | mOnRatingChangeListener = listener; 168 | } 169 | 170 | /** 171 | *Get max rating count
172 | * 173 | * @return the max rating count 174 | */ 175 | public int getMaxCount() { 176 | return mMaxCount; 177 | } 178 | 179 | /** 180 | *Set max rating count which will lead to RatingBar refreshing immediately
181 | * 182 | * @param maxCount 183 | */ 184 | public void setMaxCount(int maxCount) { 185 | maxCount = Math.max(0, maxCount); 186 | if (maxCount == mMaxCount) { 187 | return; 188 | } 189 | mMaxCount = maxCount; 190 | createStarViews(maxCount); 191 | 192 | if (maxCount < mCount) { 193 | setCount(maxCount); 194 | } 195 | } 196 | 197 | /** 198 | *Get rating count
199 | * 200 | * @return the rating count. 201 | */ 202 | public int getCount() { 203 | return mCount; 204 | } 205 | 206 | /** 207 | *Set rating count, this will update rating bar immediately.
208 | * 209 | * @param count the new rating count. If count small than 0 it will be set to 0, or if count 210 | * large than max count it will be set to the max count. 211 | */ 212 | public void setCount(int count) { 213 | count = Math.max(0, Math.min(mMaxCount, count)); 214 | if (count == mCount) { 215 | return; 216 | } 217 | 218 | int oldCount = mCount; 219 | mCount = count; 220 | updateStarViews(); 221 | if (mOnRatingChangeListener != null) { 222 | mOnRatingChangeListener.onChange(this, oldCount, mCount); 223 | } 224 | } 225 | 226 | private void init(Context context, AttributeSet attrs) { 227 | mContext = context; 228 | 229 | // Retrieve attributes 230 | if (attrs == null) { 231 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 232 | mFillDrawable = context.getDrawable(R.drawable.fill); 233 | mEmptyDrawable = context.getDrawable(R.drawable.empty); 234 | } else { 235 | mFillDrawable = context.getResources().getDrawable(R.drawable.fill); 236 | mEmptyDrawable = context.getResources().getDrawable(R.drawable.empty); 237 | } 238 | } else { 239 | TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RatingBar); 240 | mMaxCount = typedArray.getInteger(R.styleable.RatingBar_rb_max_count, MAX_COUNT); 241 | mCount = typedArray.getInteger(R.styleable.RatingBar_rb_count, 0); 242 | mFillDrawable = typedArray.getDrawable(R.styleable.RatingBar_rb_fill); 243 | mEmptyDrawable = typedArray.getDrawable(R.styleable.RatingBar_rb_empty); 244 | mSpace = typedArray.getDimensionPixelSize(R.styleable.RatingBar_rb_space, 0); 245 | mClickRating = typedArray.getBoolean(R.styleable.RatingBar_rb_click_rating, true); 246 | mTouchRating = typedArray.getBoolean(R.styleable.RatingBar_rb_touch_rating, true); 247 | typedArray.recycle(); 248 | 249 | if (mFillDrawable == null) { 250 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 251 | mFillDrawable = context.getDrawable(R.drawable.fill); 252 | } else { 253 | mFillDrawable = context.getResources().getDrawable(R.drawable.fill); 254 | } 255 | } 256 | 257 | mMaxCount = Math.max(0, mMaxCount); 258 | mCount = Math.max(0, Math.min(mCount, mMaxCount)); 259 | } 260 | 261 | // Create root layout (LinearLayout) used to contain stars 262 | mRootLayout = new LinearLayout(context); 263 | addView(mRootLayout, new ViewGroup.LayoutParams( 264 | ViewGroup.LayoutParams.MATCH_PARENT, 265 | ViewGroup.LayoutParams.MATCH_PARENT 266 | )); 267 | 268 | createStarViews(mMaxCount); 269 | } 270 | 271 | // create star views 272 | private void createStarViews(int count) { 273 | // remove previous child views 274 | if (mRootLayout.getChildCount() > 0) { 275 | mRootLayout.removeAllViews(); 276 | } 277 | 278 | // create new image views 279 | mImageViews = new ImageView[count]; 280 | for (int i = 0; i < mImageViews.length; ++i) { 281 | // Use FrameLayout to wrap the star view 282 | FrameLayout frameLayout = new FrameLayout(getContext()); 283 | LinearLayout.LayoutParams llp = new LinearLayout.LayoutParams( 284 | 0, ViewGroup.LayoutParams.MATCH_PARENT, 1 285 | ); 286 | mRootLayout.addView(frameLayout, llp); 287 | 288 | mImageViews[i] = new ImageView(getContext()); 289 | ImageView imageView = mImageViews[i]; 290 | imageView.setOnClickListener(this); 291 | imageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE); 292 | imageView.setTag(i); 293 | FrameLayout.LayoutParams flp = new FrameLayout.LayoutParams( 294 | ViewGroup.LayoutParams.MATCH_PARENT, 295 | ViewGroup.LayoutParams.MATCH_PARENT, 296 | Gravity.CENTER 297 | ); 298 | frameLayout.addView(imageView, flp); 299 | } 300 | 301 | updateStarViews(); 302 | } 303 | 304 | /** 305 | *Update the rating bar with current rating count.
306 | */ 307 | private void updateStarViews() { 308 | ImageView imgView; 309 | // update drawable 310 | for (int i = 0; i < mImageViews.length; ++i) { 311 | imgView = mImageViews[i]; 312 | imgView.setImageDrawable((i < mCount) ? mFillDrawable : mEmptyDrawable); 313 | 314 | // update margin between the stars whose drawable is not null 315 | ViewGroup parent = (ViewGroup) imgView.getParent(); 316 | MarginLayoutParams mlp = (MarginLayoutParams) parent.getLayoutParams(); 317 | if (imgView.getDrawable() != null 318 | && (i - 1) >= 0 319 | && mImageViews[i - 1].getDrawable() != null) { 320 | mlp.setMargins(mSpace, 0, 0, 0); 321 | } else { 322 | mlp.setMargins(0, 0, 0, 0); 323 | } 324 | parent.setLayoutParams(mlp); 325 | } 326 | } 327 | 328 | @Override 329 | public void onClick(View v) { 330 | Integer index = (Integer) v.getTag(); 331 | setCount(index + 1); 332 | } 333 | 334 | @Override 335 | public void setClickable(boolean clickable) { 336 | } 337 | 338 | @Override 339 | public boolean onInterceptTouchEvent(MotionEvent ev) { 340 | return true; 341 | } 342 | 343 | @Override 344 | public boolean onTouchEvent(MotionEvent event) { 345 | switch (event.getAction()) { 346 | case MotionEvent.ACTION_DOWN: 347 | mOldX = event.getX(); 348 | mOldY = event.getY(); 349 | mOldStarCount = getTouchStarCount(event); 350 | return true; 351 | case MotionEvent.ACTION_MOVE: 352 | if (mTouchRating) { 353 | float deltaX = event.getX() - mOldX; 354 | float deltaY = event.getY() - mOldY; 355 | int distance = (int) Math.round(Math.sqrt(Math.pow(deltaX, 2) + Math.pow(deltaY, 2))); 356 | if (distance >= ViewConfiguration.getTouchSlop()) { 357 | setCount(getTouchStarCount(event)); 358 | } 359 | } 360 | mOldX = event.getX(); 361 | mOldY = event.getY(); 362 | break; 363 | case MotionEvent.ACTION_UP: 364 | if (mClickRating) { 365 | int starCount = getTouchStarCount(event); 366 | // if touch down and touch up hit the same view think it is a click event 367 | if (starCount == mOldStarCount) { 368 | setCount(starCount); 369 | } 370 | } 371 | break; 372 | default: 373 | break; 374 | } 375 | return super.onTouchEvent(event) && false; 376 | } 377 | 378 | private View getStarView(int index) { 379 | return mRootLayout.getChildAt(index); 380 | } 381 | 382 | /** 383 | *Get the star count on specified touch position
384 | * @param event touch event 385 | * @return selected star count 386 | */ 387 | private int getTouchStarCount(MotionEvent event) { 388 | int count = 1; 389 | 390 | float rawX = event.getRawX(); 391 | for (int i = 0; i < getMaxCount(); ++i) { 392 | Rect rect = new Rect(); 393 | View view = getStarView(i); 394 | view.getGlobalVisibleRect(rect); 395 | MarginLayoutParams mlp = (MarginLayoutParams) view.getLayoutParams(); 396 | if (rawX > rect.right + mlp.rightMargin) { 397 | count += 1; 398 | } 399 | } 400 | 401 | return count; 402 | } 403 | 404 | public interface OnRatingChangeListener { 405 | /** 406 | *This method will be execute after every change of rating bar
407 | * 408 | * @param preCount previous rating count 409 | * @param curCount current rating count 410 | */ 411 | void onChange(RatingBar ratingBar, int preCount, int curCount); 412 | } 413 | 414 | } 415 | 416 | --------------------------------------------------------------------------------