欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android自定义星星评分控件

程序员文章站 2024-02-24 13:14:10
下面为控件的实现历程: 此控件高效,直接使用ondraw绘制,先亮照: 由于android自身的星星评分控件样式可以改,但是他的大小不好调整的缺点,只能用s...

下面为控件的实现历程:
此控件高效,直接使用ondraw绘制,先亮照:

Android自定义星星评分控件

由于android自身的星星评分控件样式可以改,但是他的大小不好调整的缺点,只能用small normal这样的style调整,自定义不强,因此击发了我自定义星星控件的欲望。

星星评分控件的设计,大体规划为:

需要两张图片,一颗亮星星,一颗空星星;(当然图片不一定是星星,其他图片也可以,现在实验就用星星就好了)星星数量,间距可以自定义,星星的最小步进为0.1,在用户使用的时候与android自带的方法一样。

星星控件大体分为两层,第一层空星星,第二层亮星星,第一层固定,第二层动态绘制,这样就可以实现评分。

在画星星的时候,由于在xml得出回来的对象是drawable,不必再转换为bitmap绘制,故直接绘制drawable,并且提升效率。

绘制drawable需要两个方法就够了
1、设置绘制到那里:
setbounds(int left ,int top , int right ,int bottom);
2、绘制:
draw(canvas canvas);

Android自定义星星评分控件

经过一个for循环,五颗空星星就出来了,哈哈

for (int i = 0;i < starcount;i++) {
   staremptydrawable.setbounds(starsize * i, 0, starsize * (i + 1), starsize);
   staremptydrawable.draw(canvas);
  }

Android自定义星星评分控件

for (int i = 0;i < starcount;i++) {
   staremptydrawable.setbounds(starsize * i, 0, starsize * (i + 1), starsize);
   staremptydrawable.draw(canvas);
  }
  for (int i = 0;i < starcount -1;i++) {
   starfilldrawable.setbounds(starsize * i, 0, starsize * (i + 1), starsize);
   starfilldrawable.draw(canvas);
  }

上面几行代码成功强行装成了一个评了4分的Android自定义星星评分控件

现在,显示几颗几颗的星星无压力,但是我们目标是需要步进为0.1的星星。
but
经过一系列的实验,发现drawable对象没有能指定绘制需要的部分,也就是不能绘制半颗星星(反正找不到,找到可以评论告诉我),然后就采用了折中的方法,把drawable对象变为bitmap这样就好办了,再利用bitmapshader,想绘制多少就绘制多上(就是实现0.1步进),下面为1/3颗的效果:

Android自定义星星评分控件

转换方法:

private bitmap drawabletobitmap(drawable drawable)
 {
  if (drawable == null)return null;
  bitmap bitmap = bitmap.createbitmap(starsize, starsize, bitmap.config.argb_8888);
  canvas canvas = new canvas(bitmap);
  drawable.setbounds(0, 0, starsize, starsize);
  drawable.draw(canvas);
  return bitmap;
 }

把bitmap转换为画笔绘制:

paint = new paint();
paint.setantialias(true);
paint.setshader(new bitmapshader(starfillbitmap, bitmapshader.tilemode.clamp, bitmapshader.tilemode.clamp));

在ondraw()方法绘制(三分之一个)

canvas.drawrect(0,0,starsize/3,starsize,paint);

原理就是这样,剩下就是逻辑问题了,以下为星星控件代码:

package com.dming.starbar;

import android.content.context;
import android.content.res.typedarray;
import android.graphics.bitmap;
import android.graphics.bitmapshader;
import android.graphics.canvas;
import android.graphics.paint;
import android.graphics.drawable.drawable;
import android.util.attributeset;
import android.view.motionevent;
import android.view.view;

/**
 * created by dming on 2016/7/18.
 *
 */
public class starbar extends view{
 private int stardistance = 0; //星星间距
 private int starcount = 5; //星星个数
 private int starsize;  //星星高度大小,星星一般正方形,宽度等于高度
 private float starmark = 0.0f; //评分星星
 private bitmap starfillbitmap; //亮星星
 private drawable staremptydrawable; //暗星星
 private onstarchangelistener onstarchangelistener;//监听星星变化接口
 private paint paint;   //绘制星星画笔
 private boolean integermark = false;
 public starbar(context context, attributeset attrs) {
  super(context, attrs);
  init(context, attrs);
 }

 public starbar(context context, attributeset attrs, int defstyleattr) {
  super(context, attrs, defstyleattr);
  init(context, attrs);
 }

 /**
  * 初始化ui组件
  *
  * @param context
  * @param attrs
  */
 private void init(context context, attributeset attrs){
  setclickable(true);
  typedarray mtypedarray = context.obtainstyledattributes(attrs, r.styleable.ratingbar);
  this.stardistance = (int) mtypedarray.getdimension(r.styleable.ratingbar_stardistance, 0);
  this.starsize = (int) mtypedarray.getdimension(r.styleable.ratingbar_starsize, 20);
  this.starcount = mtypedarray.getinteger(r.styleable.ratingbar_starcount, 5);
  this.staremptydrawable = mtypedarray.getdrawable(r.styleable.ratingbar_starempty);
  this.starfillbitmap = drawabletobitmap(mtypedarray.getdrawable(r.styleable.ratingbar_starfill));
  mtypedarray.recycle();

  paint = new paint();
  paint.setantialias(true);
  paint.setshader(new bitmapshader(starfillbitmap, bitmapshader.tilemode.clamp, bitmapshader.tilemode.clamp));
 }

 /**
  * 设置是否需要整数评分
  * @param integermark
  */
 public void setintegermark(boolean integermark){
  this.integermark = integermark;
 }

 /**
  * 设置显示的星星的分数
  *
  * @param mark
  */
 public void setstarmark(float mark){
  if (integermark) {
   starmark = (int)math.ceil(mark);
  }else {
   starmark = math.round(mark * 10) * 1.0f / 10;
  }
  if (this.onstarchangelistener != null) {
   this.onstarchangelistener.onstarchange(starmark); //调用监听接口
  }
  invalidate();
 }

 /**
  * 获取显示星星的数目
  *
  * @return starmark
  */
 public float getstarmark(){
  return starmark;
 }


 /**
  * 定义星星点击的监听接口
  */
 public interface onstarchangelistener {
  void onstarchange(float mark);
 }

 /**
  * 设置监听
  * @param onstarchangelistener
  */
 public void setonstarchangelistener(onstarchangelistener onstarchangelistener){
  this.onstarchangelistener = onstarchangelistener;
 }

 @override
 protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
  super.onmeasure(widthmeasurespec, heightmeasurespec);
  setmeasureddimension(starsize * starcount + stardistance * (starcount - 1), starsize);
 }

 @override
 protected void ondraw(canvas canvas) {
  super.ondraw(canvas);
  if (starfillbitmap == null || staremptydrawable == null) {
   return;
  }
  for (int i = 0;i < starcount;i++) {
   staremptydrawable.setbounds((stardistance + starsize) * i, 0, (stardistance + starsize) * i + starsize, starsize);
   staremptydrawable.draw(canvas);
  }
  if (starmark > 1) {
   canvas.drawrect(0, 0, starsize, starsize, paint);
   if(starmark-(int)(starmark) == 0) {
    for (int i = 1; i < starmark; i++) {
     canvas.translate(stardistance + starsize, 0);
     canvas.drawrect(0, 0, starsize, starsize, paint);
    }
   }else {
    for (int i = 1; i < starmark - 1; i++) {
     canvas.translate(stardistance + starsize, 0);
     canvas.drawrect(0, 0, starsize, starsize, paint);
    }
    canvas.translate(stardistance + starsize, 0);
    canvas.drawrect(0, 0, starsize * (math.round((starmark - (int) (starmark))*10)*1.0f/10), starsize, paint);
   }
  }else {
   canvas.drawrect(0, 0, starsize * starmark, starsize, paint);
  }
 }


 @override
 public boolean ontouchevent(motionevent event) {
  int x = (int) event.getx();
  if (x < 0) x = 0;
  if (x > getmeasuredwidth()) x = getmeasuredwidth();
  switch(event.getaction()){
   case motionevent.action_down: {
    setstarmark(x*1.0f / (getmeasuredwidth()*1.0f/starcount));
    break;
   }
   case motionevent.action_move: {
    setstarmark(x*1.0f / (getmeasuredwidth()*1.0f/starcount));
    break;
   }
   case motionevent.action_up: {
    break;
   }
  }
  invalidate();
  return super.ontouchevent(event);
 }

 /**
  * drawable转bitmap
  *
  * @param drawable
  * @return
  */
 private bitmap drawabletobitmap(drawable drawable)
 {
  if (drawable == null)return null;
  bitmap bitmap = bitmap.createbitmap(starsize, starsize, bitmap.config.argb_8888);
  canvas canvas = new canvas(bitmap);
  drawable.setbounds(0, 0, starsize, starsize);
  drawable.draw(canvas);
  return bitmap;
 }
}

attrs的文件:

<?xml version="1.0" encoding="utf-8"?>
<resources>
 <declare-styleable name="ratingbar">
  <!--星星间距-->
  <attr format="dimension" name="stardistance"/>
  <!--星星大小-->
  <attr format="dimension" name="starsize"/>
  <!--星星个数-->
  <attr format="integer" name="starcount"/>
  <!--星星空图-->
  <attr format="reference" name="starempty"/>
  <!--星星满图-->
  <attr format="reference" name="starfill"/>
 </declare-styleable>

</resources>

xml的使用方式:

 <com.dming.starbar.starbar
  android:id="@+id/starbar"
  android:layout_below="@+id/display"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  ratingbar:starempty="@drawable/star_empty"
  ratingbar:starfill="@drawable/star_full"
  ratingbar:stardistance="5dp"
  ratingbar:starcount="8"
  ratingbar:starsize="30dp"/>

<重点>工程源码:http://xiazai.jb51.net/201701/yuanma/androidstarbar(jb51.net).rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。