自定义RatingBar

最近公司项目里增加了一个评价功能模块,就要用到咱们最常见的ratingBar了,大家都知道系统自带的ratingBar有多丑,所以打算自定义,然后翻看资料,说是在drawable里写个.xml文件,例如这样:

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@android:id/background" android:drawable="@drawable/startnormal"/>
    <item android:id="@android:id/progress" android:drawable="@drawable/startslected"/>
</layer-list>


然后在布局文件里android:progressDrawable="@drawable/rating_style",运行试试,恩,还不错,等等下面的流苏是什么鬼??


换个图片试试,恩这次好了,可是在不同分辨率的手机上
显示的大小不一致,恩,可能还是图片的问题,可是公司美工不给出适配各个手机分辨率的图,如果你们公司图片给好几套,那么这个方法是可行的,可以满足开发需求。



我选择了自定义控件来解决,效果图如下(模拟器有点卡,真机运行效果还不错):




下面来说说思路,我选择的是组合控件方法来实现这个自定义控件,也就是利用在linearlayout中摆放imageview,实现步骤是:

1.为每一个星星设置drawable,并且计算出每个星星的起始位置并保存在集合中;

2.重写onTouchEvent方法,
            当手指按下时:记录按下的横坐标downX,将所有星星设置为未选中状态,然后判断所有星星的起始坐标是否小于downX,如果小于则将星星改为选中状态;
            当手指滑动时:记录每次滑动到的横坐标moveX,如果moveX大于当前最后一个选中状态星星的坐标,则是向右滑动,否则向左滑动,然后根据滑动距离除上
                      每两颗星星之间的滑动距离计算出滑动了几颗星星,然后改变星星的对应状态;
听起来很简单是不是?



下面就是实现代码+注释

<declare-styleable name="CustomRatingBar">
    <attr name="crating"  format="integer"/>
    <attr name="cstartDrawable" format="reference"/>
    <attr name="cselectedDrawable" format="reference"/>
    <attr name="cspace" format="dimension"/>
    <attr name="cisIndicator" format="boolean"/>
    <attr name="cstartNum" format="integer"/>
</declare-styleable>

package com.example.ratingbar;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseIntArray;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.MotionEvent;
import android.widget.ImageView;
import android.widget.LinearLayout;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by pactera on 2017/5/11.
 * 自定义ratingbar,系统自带的ratingbar很难控制图片大小,希望自定义的控件可以根据图片大小设置控件大小
 * ,并且可以实现点击选中以及滑动选中的功能
 */

public class CustomRatingBar extends LinearLayout {

    /**星星的个数*/
    private int num;
    /**未选中的星星图片*/
    private Drawable startDrawable;
    /**选中的星星图片*/
    private Drawable selectedDrawable;
    /**选中的星星个数*/
    private int rating;
    /**星星的间隔*/
    private float space;
    /**是否是指示器,即是否可选*/
    private boolean indicator;
    /**盛放星星的imageview的集合*/
    private ArrayList<ImageView> startContains;
    /**盛放星星位置的集合*/
    private ArrayList<Integer> locations;
    /**每一个星星控件的布局参数*/
    private LayoutParams params;

    public CustomRatingBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    /**从布局文件中读取数据*/
    private void init(Context context, AttributeSet attrs) {

        setOrientation(LinearLayout.HORIZONTAL);
        setGravity(Gravity.CENTER_VERTICAL);

        TypedArray arr = context.obtainStyledAttributes(attrs, R.styleable.CustomRatingBar);
        num = arr.getInteger(R.styleable.CustomRatingBar_cstartNum, 5);
        startDrawable = arr.getDrawable(R.styleable.CustomRatingBar_cstartDrawable);
        selectedDrawable = arr.getDrawable(R.styleable.CustomRatingBar_cselectedDrawable);
        rating = arr.getInt(R.styleable.CustomRatingBar_crating,0);
        space = arr.getDimension(R.styleable.CustomRatingBar_cspace,
                TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,10,getResources().getDisplayMetrics()));
        indicator = arr.getBoolean(R.styleable.CustomRatingBar_cisIndicator,false);
        arr.recycle();

        startContains = new ArrayList<>();
        locations = new ArrayList<>();
        params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        params.leftMargin = (int) space;//设置左边距
        int locationLeft = 0;

        for (int i=0; i<num; i++) {
            ImageView iv = new ImageView(context);
            if (i < rating) {
                iv.setImageDrawable(selectedDrawable);//设置选中背景
            } else {
                iv.setImageDrawable(startDrawable);//设置未选中背景
            }
            iv.setLayoutParams(params);
            addView(iv);
            startContains.add(iv);
            locationLeft = (int) (i*startDrawable.getIntrinsicWidth() + (i+1)*space);
           // Log.i("zhangdi","locationLeft="+locationLeft+", intrinsicwidth="+startDrawable.getIntrinsicWidth());
            locations.add(locationLeft);//设置每个星星左侧的位置坐标
        }
    }

    private float downX;
    private float moveX;

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        if (indicator) {
            return true;
        }

        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN:
                downX = event.getX();
                Log.i("zhangdi","downX="+downX);
                handleDown(downX);
                break;

            case MotionEvent.ACTION_MOVE:
                moveX = event.getX() - downX;
                Log.i("zhangdi","moveX="+moveX+", x="+event.getX());
                if (Math.abs(moveX) > (startDrawable.getIntrinsicWidth()/2)) {
                    handleMove(event.getX());
                }
                break;

            case MotionEvent.ACTION_UP:
                break;

        }

        return true;
    }

    /**按下时判断按的位置*/
    private void handleDown(float downX) {

        rating = 0;

        for (ImageView iv: startContains) {//所有星星重置为未选状态
            iv.setImageDrawable(startDrawable);
        }

        //遍历所有星星的位置,将星星设置为选中状态并且设置rating的值直到星星所在位置大于按下位置为止
        for (int i=0; i<startContains.size(); i++) {
            if (locations.get(i) > downX) {
                break;
            }
            startContains.get(i).setImageDrawable(selectedDrawable);
            if (rating < (num - 1)) {//注意不要让下标越界
                rating++;
            }
        }
    }

    /**滑动控件设置是否选中*/
    private void handleMove(float moveX) {

        int move = 0;
        int unit = 0;
        Drawable drawable = null;

        Log.i("zhangdi","rating="+rating);
        if (moveX > locations.get(rating)) {//向右滑动
            //startDrawable.getIntrinsicWidth()/2+space为一颗星星与下一颗的距离,移动距离除上两个星星间的距离,
            // 计算出移动了几颗星星
            move = (int) ((moveX-locations.get(rating))/(startDrawable.getIntrinsicWidth()/2+space));
            Log.i("zhangdi","向右滑move="+move);
            unit = 1;
            drawable = selectedDrawable;
        } else {//向左滑动
            move = (int) ((moveX-locations.get(rating)-startDrawable.getIntrinsicWidth()/2)
                    /(startDrawable.getIntrinsicWidth()/2+space));
            Log.i("zhangdi","向左滑move="+move);
            unit = -1;
            drawable = startDrawable;
        }

        //根据滑动过了几颗星星设置星星的背景图片和rating
        if (move != 0) {
            for (int i = 0; i < Math.abs(move); i++) {
                startContains.get(rating).setImageDrawable(drawable);
                if (unit > 0 && rating < (num - 1)) {

                    rating += unit;
                }
                else if (unit < 0 && rating >0) {
                    rating += unit;
                }
            }
        }
    }
}

使用方法

<com.example.ratingbar.CustomRatingBar
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="120dp"
    app:cstartDrawable="@drawable/rating_star"
    app:cselectedDrawable="@drawable/rating_star_select"
    app:cisIndicator="false"
    android:layout_marginLeft="10dp"
    app:crating="3"/>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值