自定义View系列(6)--RatingBarView

来源:互联网 发布:电脑设计绘图软件 编辑:程序博客网 时间:2024/05/17 01:37

写在前面

这个控件是一个组合控件,继承于LinearLayout,实现的效果类似于Android中的RatingBar,但是比RatingBar更加灵活,使用起来也更加方便.现在已经上传到github,地址:RatingBarView,同时也上传到jCenter,在Android Studio中使用RatingBarView只需要以如下方式引入即可:

compile 'com.qiangxi.ratingbarview:ratingbarview:x.y.z'

x.y.z代表可用版本,可点击这里查看最新稳定可用版本.

效果图

这里写图片描述

实现思路

实现思路其实很简单,通过继承LinearLayout,动态添加子View即可.虽然思路简单,但是实现的过程中还是有一些需要注意的地方.

1.设置自定义属性

通过在attr.xml中定义相关属性,并在RatingBarView中进行引用.代码如下:

<?xml version="1.0" encoding="utf-8"?><resources>    <item name="RatingBarViewTagId" type="id"/>    <declare-styleable name="RatingBarView">        <attr name="totalCount" format="integer"/>        <attr name="selectedCount" format="integer"/>        <attr name="selectedDrawable" format="reference"/>        <attr name="normalDrawable" format="reference"/>        <attr name="clickable" format="boolean"/>        <attr name="childMargin" format="dimension"/>        <attr name="childPadding" format="dimension"/>        <attr name="childDimension" format="dimension"/>    </declare-styleable></resources>

这里需要说明的是对于format为reference类型的属性,在获取属性值的时候需要具体对待,因为TypedArray 没有提供直接获取引用的的方法,即没有提供类似a.getReference(attr, DEFAULT);这样的方法,所以我们在获取reference类型的属性时,如果我们目标是需要一个Drawable,那么可以使用a.getDrawable()方法,获取资源id时可以使用a.getResourceId()方法等.

2. 获取到点击的view

因为RatingBarView可以添加N个子view(虽然子View就是ImageView),当点击某个子view时,我们需要获取到这个view,还要知道这个子view处在第几个位置.这两个难点可以通过给View设置TAG解决.代码如下:

 private void initLayout() {        if (getChildCount() != 0) {            removeAllViews();        }        final LayoutParams lp = new LayoutParams(dpToPx(mChildDimension), dpToPx(mChildDimension));        lp.gravity = Gravity.CENTER_VERTICAL;        lp.rightMargin = dpToPx(childMargin);        for (int position = 0; position < mTotalCount; position++) {            final ImageView imageView = new ImageView(getContext());            imageView.setLayoutParams(lp);            imageView.setTag(DEFAULT_TAG_ID, position);            imageView.setPadding(dpToPx(childPadding), dpToPx(childPadding), dpToPx(childPadding), dpToPx(childPadding));            imageView.setOnClickListener(this);            if (position < mSelectedCount) {                imageView.setImageResource(mSelectedIconResId);            } else {                imageView.setImageResource(mNormalIconResId);            }            addView(imageView);        }    }

可以看到,在初始化时,我们给每个子view设置了一个tag,tag的键就是默认的键,tag的值就是position值,当我们点击某个子view时,通过默认的键获取到当前点击的view所处的位置.这样就解决了上面的两个问题.

这里需要注意的是给view设置tag时,键的值不可随便赋值一个int常量,如果键的值不合适就会抛出异常.那么什么样的键才是合适的呢?通过该方法的注释可以了解到,键的值必须为一个资源的id值才可以,那么怎么定义这样一个id值,要求这个id值不能与任意其他id冲突,且要保证唯一性呢?最简单的方式就是自己定义一个id值,可以通过在attr文件中定义一个item并指定type为id类型来实现,代码如下:
<item name="RatingBarViewTagId" type="id"/>

自定义exception

其实这个属于Java中的知识,在学习Java的过程中应该早就了解了这个知识.Java中有各种异常机制,其中最为典型的就是RuntimeException,当我们自定义一个exception时,可以继承Exception.也可以继承RuntimeException或者其他Exception的子类.但是区别在哪里呢?

作用域不同

RuntimeException顾名思义,是在程序运行时才会抛出的异常,比如除数不可为0之类的.其他异常有些是在编译时抛出的.

编译时处理不同

JVM在处理exception时,如果不是RuntimeException,编译器会提示错误,让你捕获异常才可以运行.但是在处理RuntimeException及其子类时,JVM不会提示错误.其实也很好理解,既然是运行时才会出现的异常,在编译时期肯定是发现不了的,比如除数不可为0,在语法上是没有一点问题的,但是在实际的运行中,除数为0是绝对不会被允许的,

总结

自定义RatingBarView其实是很简单的,因为它是个组合控件,但仍有一些细节需要注意.
最后再贴一下RatingBarView的github地址:RatingBarView,如果这个library帮助到了你,还请点个star或fork.不胜感激!!!

原创粉丝点击