Android 自定义view之星级评分控件实现

先看效果图


实现逻辑,先自定义一个单独的五角星view,然后使用组合控件的放式实现最终效果。

比较初级,适合新手看一看,大牛见笑了,哈哈哈。

自定义view无非就是用测量onMeasure() 绘制 ondraw()位置onlayout()三个方法。

补充一句,本人英语实在垃圾,见笑见笑。

废话不多说,上代码。

StarView 五角星view

public class StarView extends View implements View.OnClickListener {

    Context mContext;

    private int centerX;

    Paint.Style style;

    private int mIndex;

    boolean fill;

    boolean noChooseFill;

    //是否选中

    boolean choose;

    //颜色

    private int color=0XE3E3E3;

    private int noChooseColor=0XFF5722;

    //默认颜色白色

    private int defuatColor=0XFFFFFF;

    //宽度

    private int wight;

    //高度

    private int height;

    //画笔

    Paint mPaint;

    public StarView(Context context) {

super(context);

        this.mContext=context;

        initPaint();

        this.setOnClickListener(this);

    }

public StarView(Context context,  AttributeSet attrs) {

super(context, attrs);

        this.mContext=context;

        setAttributes(attrs);

        initPaint();

        this.setOnClickListener(this);

    }

public StarView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

        this.mContext=context;

        setAttributes(attrs);

        initPaint();

        this.setOnClickListener(this);

    }

@Override

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int wightSize=MeasureSpec.getSize(widthMeasureSpec);

        int wightMode=MeasureSpec.getMode(widthMeasureSpec);

        switch (wightMode){

case MeasureSpec.EXACTLY:

wight=wightSize;

break;

            case MeasureSpec.AT_MOST:

wight=50;

break;

            case MeasureSpec.UNSPECIFIED:

wight=wightSize;

break;

        }

int heightSize=MeasureSpec.getSize(heightMeasureSpec);

        int heightMode=MeasureSpec.getMode(heightMeasureSpec);

        switch (heightMode){

case MeasureSpec.EXACTLY:

height=heightSize;

break;

            case MeasureSpec.AT_MOST:

height=50;

break;

            case MeasureSpec.UNSPECIFIED:

height=heightSize;

break;

        }

}

/*

*

*绘制五角星的方式根据正无边形的外切圆形来计算五角星的十个坐标点,用moveto,lineto进行绘制

*/

@Override

    protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

        if(fill){

isFill();

            mPaint.setColor(color);

        }else{

mPaint.setColor(noChooseColor);

            mPaint.setStyle(style);

        }

        centerX=wight/2;

        int radius=wight/2;

        float sideLength= (float) (radius*Math.sin(Math.PI/5));

        float starLenght= (float) (sideLength/Math.cos(Math.PI/5));//五角星边长

        float commentLenght= (float) (starLenght+(Math.sin(Math.PI/10)*starLenght));

        float sin36= (float) (Math.sin(Math.PI/5)*starLenght);

        float sin18= (float) (Math.sin(Math.PI/10)*starLenght);

        float cos18= (float) (Math.cos(Math.PI/10)*starLenght);

        float cos36= (float) (Math.cos(Math.PI/5)*starLenght);

        float  margin_top=(height-(cos18+sin36+cos18))/2;

        Path path=new Path();

        //逆时针绘制

        path.moveTo(centerX,margin_top);//上顶点坐标。

        path.lineTo((centerX-sin18), cos18+margin_top);//2

        path.lineTo(centerX-commentLenght, cos18+margin_top);//3

        path.lineTo(centerX-commentLenght+cos36,cos18+sin36+margin_top);//4

        path.lineTo(centerX-commentLenght+cos36-sin18,cos18+sin36+cos18+margin_top);//5

        path.lineTo(centerX,cos18+cos18+margin_top);//6

        path.lineTo(centerX+cos36,cos18+sin36+cos18+margin_top);//7

        path.lineTo(centerX+cos36-sin18,cos18+sin36+margin_top);//8

        path.lineTo(wight-(centerX-commentLenght),cos18+margin_top);//9

        path.lineTo(centerX+sin18,cos18+margin_top);//10

        path.lineTo(centerX,margin_top);//上顶点坐标。

        canvas.drawPath(path,mPaint);

    }

/**

* 获取自定义属性

*/

    public  void  setAttributes(AttributeSet attrs){

TypedArray typedArray=mContext.obtainStyledAttributes(attrs,R.styleable.StarView);

        color=typedArray.getColor(R.styleable.StarView_color,defuatColor);

        typedArray.recycle();

    }

/**

* 初始化paint

*/

    public void initPaint(){

mPaint=new Paint();

        mPaint.setAntiAlias(true);

        mPaint.setStrokeWidth(5f);

        mPaint.setDither(true); //设置防抖动

    }

/**

* 是否填充五角星

*/

    public void  isFill(){

if(fill){

style=Paint.Style.FILL;

        }else{

style=Paint.Style.STROKE;

        }

mPaint.setStyle(style);

    }

@Override

    public void onClick(View v) {

clickCallBack.choose(mIndex);

    }

/**

* 为单个五角星添加下标进行标记

*/

    public  void setIndex(int index){

mIndex=index;

    }

/**

* 设置星星颜色

*/

    public void setColor(int mColor){

color=mColor;

    }

/**

    * @param _noChooseColor 未选中时候画笔的颜色

*/

    public void setNoChooseColor(int _noChooseColor) {

noChooseColor=_noChooseColor;

    }

/**

    * @param _noChooseFill 未选中时候是否进行填充

*/

    public void setNoChooseFill(boolean _noChooseFill) {

noChooseFill=_noChooseFill;

        if(_noChooseFill){

style=Paint.Style.FILL;//填充

        }else{

style=Paint.Style.STROKE;//不填充

        }

}

public interface  ClickCallBack{

void choose(int index);

    }

private ClickCallBackclickCallBack;

    public void setClickCallBack(ClickCallBack clickCallBack){

this.clickCallBack=clickCallBack;

    }

/**

* 选中

*/

    public void choose(boolean mFill){

fill=mFill;

        invalidate();

    }

}

组合控件 StarBar。

public class StarBar extends LinearLayout implements StarView.ClickCallBack {

Context mContext;

    private int color;

    private int noChooseColor;

    private int starNum=1;//星星数目

    private float size;//星星大小

    private StarView starView;

    private float margin;

    private boolean noChooseFill;

    private ListstarViews=new ArrayList<>();

    public StarBar(Context context) {

super(context);

        this.mContext=context;

    }

private int chooseNum;//选中了几个,默认为0

    public StarBar(Context context, AttributeSet attrs) {

super(context, attrs);

        this.mContext=context;

        setAttr(attrs);

        addStar();

    }

public StarBar(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

        this.mContext=context;

        setAttr(attrs);

        addStar();

    }

public void setAttr(AttributeSet attrs){

TypedArray typedArray=mContext.obtainStyledAttributes(attrs,R.styleable.StarBar);

        color=typedArray.getColor(R.styleable.StarBar_starcolor,0XFFFFFF);

        starNum=typedArray.getInt(R.styleable.StarBar_star_num,0xFFFFFF);

        size=typedArray.getDimension(R.styleable.StarBar_size,0f);

        margin=typedArray.getDimension(R.styleable.StarBar_star_margin,10f);

        noChooseColor=typedArray.getColor(R.styleable.StarBar_no_choose_color,0XEEEEEE);

        noChooseFill=typedArray.getBoolean(R.styleable.StarBar_no_choose_fill,false);

        typedArray.recycle();

    }

public void addStar(){

for(int i=0;i

starView=new StarView(mContext);

            starView.setColor(color);

            starView.setNoChooseColor(noChooseColor);

            starView.setNoChooseFill(noChooseFill);

            starView.setIndex(i);

            LinearLayout.LayoutParams layoutParams=new LinearLayout.LayoutParams((int)size,(int)size);

            layoutParams.rightMargin= (int)margin;

            starView.setLayoutParams(layoutParams);

            starView.setClickCallBack(this);

            addView(starView);

            starViews.add(starView);

        }

}

@Override

    public void choose(int index) {

for(int i=0;i

if(i<=index){

starViews.get(i).choose(true);

                }else{

starViews.get(i).choose(false);

                }

}

chooseNumCallBack.chooseNum(index+1);

    }

public interface  ChooseNumCallBack{

/**

* 返回选择的数目

*/

        void chooseNum(int chooseStarNum);

    }

private ChooseNumCallBackchooseNumCallBack;

    public void setChooseNumCallBack(ChooseNumCallBack chooseNumCallBack){

this.chooseNumCallBack=chooseNumCallBack;

    }

}

使用方法

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    xmlns:app="http://schemas.android.com/apk/res-auto"

    xmlns:tools="http://schemas.android.com/tools"

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    tools:context=".MainActivity">

      android:id="@+id/stars"

      android:layout_width="match_parent"

      android:layout_height="100dp"

      app:starcolor="#E00404"

      android:orientation="horizontal"

      app:size="50dp"

      android:gravity="center"

      app:star_num="6"

      app:star_margin="10dp"

      app:no_choose_fill="true"

      app:no_choose_color="#D1D1D1"

      />

</RelativeLayout>

其中

app:starcolor为选中时五角星的颜色

app:size="50dp"五角星的尺寸   注:不应超过布局的尺寸

app:star_num="6" 五角星的数量   根据自己实际情况使用    

app:star_margin="10dp"  每个五角星之间的距离

app:no_choose_fill="true"  没有被选择的五角星是否为填充模式

app:no_choose_color="#D1D1D1" 没有被选择的五角星的颜色


MainActivity看效果。

public class MainActivityextends AppCompatActivity {

@Override

    protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        StarBar starBar=findViewById(R.id.stars);

        starBar.setChooseNumCallBack(new StarBar.ChooseNumCallBack() {

@Override

            public void chooseNum(int chooseStarNum) {

            Toast.makeText(MainActivity.this,chooseStarNum+"星评价",Toast.LENGTH_SHORT).show();

            }

});

    }

}


自定义的属性

<?xml version="1.0" encoding="utf-8"?>

    <declare-styleable name="StarView">


        <attr name="color" format="color"/>

    <declare-styleable name="StarBar">


        <attr name="starcolor" format="color"/>


        <attr name="size" format="dimension" />


        <attr name="star_num" format="integer"/>


        <attr name="star_margin" format="dimension"/>


        <attr name="no_choose_color" format="color"/>


        <attr name="no_choose_fill" format="boolean"/>

需要源码的可给我留言。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容