自定義簡單的RatingBar

黎偉傑發表於2017-06-05

樣式是:

自定義簡單的RatingBar

需求概述

我們需要一個類似原生RatingBar的這樣一個評分View,但是RatingBar不夠靈活,我們需要有不同顏色,不同間距,而且可調的一個評分Bar。

分析

我們主要是定義ratingbar,不和文字一起定義。
首先是:單個星星的高和寬,然後是需要知道每個星星之間的間距,還有就是那幾顆星星是active那幾顆是disactivie的。同時,我們需要計算在這個View中的什麼位置開始畫,最後就是需要指定我們的星星的resource了。

實現

屬性定義

需要定義的屬性有:

 <declare-styleable name="CustomRatingStyle">
        <!--寬,高,間距,啟用的數量,沒有啟用的數量,啟用的icon,沒有啟用的icon-->
        <!--屬性分別是:單個的寬,高,之間的距離,啟用的數量,總數量,啟用的drawable,沒有啟用的drawable-->
        <attr name="custom_rate_width" format="dimension"/>
        <attr name="custom_rate_height" format="dimension"/>
        <attr name="custom_rate_padding" format="dimension"/>
        <attr name="custom_rate_active_size" format="integer"/>
        <attr name="custom_rate_size" format="integer"/>
        <attr name="custom_rate_active_drawable" format="reference"/>
        <attr name="custom_rate_disactive_drawable" format="reference"/>
    </declare-styleable>複製程式碼

程式碼實現:

我們繼承自View就好,不要ViewGroup。然後獲取xml檔案中的自定義屬性值,獲取屬性值完成,同時給定預設值。同時,我們需要計算一個步長,作用是知道我們畫下一個星星開始的座標,他的計算是星星的width+星星之間的padding。同時,還有一點是可能active和disactivie的drawable大小不同,所以我們需要按照其中一個區計算步長,同時縮放到等同大小。比如我們按照active的drawable大小為標準,當disactive的drawable大小不同的時候,就對他進行比例縮放,直到相同。程式碼如下:

程式碼實現

   //單個高
    private float singleWidth;
    //單個寬
    private float singleHeight;
    //間距
    private int padding;
    //總數量
    private int size = 5;
    //預設啟用數量
    private int activeSize = 3;
    //啟用的bitmap
    private Bitmap activeBitmap;
    //沒有啟用的bitmap
    private Bitmap disactiveBitmap;
    //畫筆
    private Paint mPaint;
    //步長
    private int stepSize;
    //開始畫的x座標
    private int drawStartX;
    //開始畫的y座標
    private int drawStartY;複製程式碼

初始化程式碼:

    int activeId = 0;
        int disactiveId = 0;
        if (attrs != null) {
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CustomRatingStyle);
            singleWidth = array.getDimensionPixelOffset(R.styleable.CustomRatingStyle_custom_rate_width, 0);
            singleHeight = array.getDimensionPixelOffset(R.styleable.CustomRatingStyle_custom_rate_height, 0);
            activeId = array.getResourceId(R.styleable.CustomRatingStyle_custom_rate_active_drawable, R.drawable.custom_rateingbar_active);
            disactiveId = array.getResourceId(R.styleable.CustomRatingStyle_custom_rate_disactive_drawable, R.drawable.custom_rateingbar_disactive);
            size = array.getInteger(R.styleable.CustomRatingStyle_custom_rate_size, 5);
            activeSize = array.getInteger(R.styleable.CustomRatingStyle_custom_rate_active_size, 3);
            padding = array.getDimensionPixelOffset(R.styleable.CustomRatingStyle_custom_rate_padding, 10);
            array.recycle();
        }
        activeBitmap = BitmapFactory.decodeResource(getResources(), activeId);
        disactiveBitmap = BitmapFactory.decodeResource(getResources(), disactiveId);
        stepSize = padding + activeBitmap.getWidth();
        mPaint = new Paint();
        if (singleHeight <= 0) {
            singleHeight = activeBitmap.getHeight();
        }
        if (singleWidth <= 0) {
            singleWidth = activeBitmap.getWidth();
        }
        //大小不同的話,對Bitmap進行壓縮,
        if (activeBitmap.getWidth() != disactiveBitmap.getWidth() || activeBitmap.getHeight() != disactiveBitmap.getHeight()) {
            //把dis壓縮或者是放大的active的大小
            disactiveBitmap = Bitmap.createScaledBitmap(disactiveBitmap, activeBitmap.getWidth(), activeBitmap.getHeight(), false);
        }複製程式碼

邏輯實現

測量步長計算:我們是把五角星放在這個View的center位置,然後他所佔的長度是5width+4padding。

onMeasure 方法:

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int width;
        int height;
        //計算寬
        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
        } else {
            width = 5 * stepSize;
        }
        //計算高
        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            height = (int) singleHeight;
        }
        //總共應該的寬
        int drawWidth = (int) (size * singleWidth + (size <= 1 ? 0 : (size - 1) * padding));
        //開始的y位置
        int drawHeight = (int) ((height - singleHeight) / 2);
        drawStartX = (width - drawWidth) / 2;
        drawStartY = drawHeight > 0 ? drawHeight : 0;
        setMeasuredDimension(width, height);
    }複製程式碼

onDraw方法:

  @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //開始畫active
        for (int i = 0; i < activeSize; i++) {
            canvas.drawBitmap(activeBitmap, drawStartX + i * stepSize, drawStartY, mPaint);
        }
        //開始畫disactive
        for (int i = activeSize; i < size; i++) {
            canvas.drawBitmap(disactiveBitmap, drawStartX + i * stepSize, drawStartY, mPaint);
        }
    }複製程式碼

然後,我們在對外提供一個設定activie的方法:

    /**
     * 設定啟用的數量
     *
     * @param activeSize
     */
    public void setActiveSize(int activeSize) {
        this.activeSize = activeSize;
        if (CommentUtil.isUIThread()) {
            invalidate();
        } else {
            postInvalidate();
        }
    }複製程式碼

這樣子就完成了星星評分的UI了。
結果截圖:

自定義簡單的RatingBar

原始碼下載

相關文章