个人开发中自定义View系列(有需要的可以点击查看收藏)
- Android自定义view第一弹(防小米计步)
- Android自定义View第二弹(旋转的体重)
- Android自定义View第三弹(反人类尺子)
- Android自定义View第四弹(Kotlin流式布局)
距离上一篇自定义view已经过去了一年多了,这次主要给大家介绍的是可滑动的星星评价,虽然Google官方也提供了 RatingBar 但是没办法满足我的需要只能自己定义一个了,废话不多说先上图: 这个选中以及默认的心型都是UI提供的图片,上代码:
1.自定义view的代码
import android.content.Context
import android.graphics.*
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import cn.neoclub.uki.R
import kotlin.math.abs
import kotlin.math.ceil
import kotlin.math.roundToInt
class HeartRatingBar : View {
private var starDistance = 0
private var starCount = 5
private var starSize = 0
private var starMark = 0
private var starFillBitmap: Bitmap? = null
private var starEmptyDrawable : Drawable? = null
private var onStarChangeListener : OnStarChangeListener? = null
private var paint : Paint? = null
private var integerMark = false
private var scaledTouchSlop:Int=0
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
scaledTouchSlop=ViewConfiguration.get(context).scaledTouchSlop
isClickable = true
val mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.HeartRatingBar)
starDistance = mTypedArray.getDimension(R.styleable.HeartRatingBar_starDistance, 0f).toInt()
starSize = mTypedArray.getDimension(R.styleable.HeartRatingBar_starSize, 20f).toInt()
starCount = mTypedArray.getInteger(R.styleable.HeartRatingBar_starCount, 5)
starEmptyDrawable = mTypedArray.getDrawable(R.styleable.HeartRatingBar_starEmpty)
starFillBitmap = drawableToBitmap(mTypedArray.getDrawable(R.styleable.HeartRatingBar_starFill))
mTypedArray.recycle()
paint = Paint()
paint?.isAntiAlias = true
paint?.shader = BitmapShader(starFillBitmap!!, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
}
fun setIntegerMark(integerMark: Boolean) {
this.integerMark = integerMark
}
private fun setStarMark(mark: Int) {
starMark = if (integerMark) {
ceil(mark.toDouble()).toInt()
} else {
(mark * 10).toFloat().roundToInt() * 1 / 10
}
if (onStarChangeListener != null) {
onStarChangeListener?.onStarChange(starMark)
}
invalidate()
}
fun getStarMark(): Int {
return starMark
}
interface OnStarChangeListener {
fun onStarChange(mark: Int)
}
fun setOnStarChangeListener(onStarChangeListener: OnStarChangeListener?) {
this.onStarChangeListener = onStarChangeListener
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
setMeasuredDimension(starSize * starCount + starDistance * (starCount - 1), starSize)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (starFillBitmap == null || starEmptyDrawable == null) {
return
}
for (i in 0 until starCount) {
starEmptyDrawable?.setBounds(
(starDistance + starSize) * i,
0,
(starDistance + starSize) * i + starSize,
starSize
)
starEmptyDrawable?.draw(canvas)
}
if (starMark > 1) {
canvas.drawRect(0f, 0f, starSize.toFloat(), starSize.toFloat(), paint!!)
if (starMark - starMark == 0) {
for (i in 1 until starMark) {
canvas.translate((starDistance + starSize).toFloat(), 0f)
canvas.drawRect(0f, 0f, starSize.toFloat(), starSize.toFloat(), paint!!)
}
} else {
for (i in 1 until starMark - 1) {
canvas.translate((starDistance + starSize).toFloat(), 0f)
canvas.drawRect(0f, 0f, starSize.toFloat(), starSize.toFloat(), paint!!)
}
canvas.translate((starDistance + starSize).toFloat(), 0f)
canvas.drawRect(
0f,
0f,
starSize * (((starMark - starMark) * 10).toFloat().roundToInt() * 1.0f / 10),
starSize.toFloat(),
paint!!
)
}
} else {
canvas.drawRect(0f, 0f, (starSize * starMark).toFloat(), starSize.toFloat(), paint!!)
}
}
private var downX:Int=0
override fun onTouchEvent(event: MotionEvent): Boolean {
var x = event.x.toInt()
if (x < 0) x = 0
if (x > measuredWidth) x = measuredWidth
when (event.action) {
MotionEvent.ACTION_DOWN -> {
downX=x
if(starCount==0||(measuredWidth * 1 / starCount)==0){
return false
}
val count=x * 1 / (measuredWidth * 1 / starCount)
setStarMark(count+1)
}
MotionEvent.ACTION_MOVE -> {
if(abs(event.x-downX)<scaledTouchSlop){
return false
}
if(starCount==0||(measuredWidth * 1 / starCount)==0){
return false
}
setStarMark(x * 1 / (measuredWidth * 1 / starCount))
}
MotionEvent.ACTION_UP -> {}
}
invalidate()
return super.onTouchEvent(event)
}
private fun drawableToBitmap(drawable: Drawable?): Bitmap? {
if (drawable == null) return null
val bitmap = Bitmap.createBitmap(starSize, starSize, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, starSize, starSize)
drawable.draw(canvas)
return bitmap
}
}
2.自定义View的使用
<cn.neoclub.uki.message.widget.HeartRatingBar
android:id="@+id/rb_rating_bar"
android:layout_width="match_parent"
android:layout_height="40dp"
app:starCount="5"
app:starDistance="7dp"
app:starEmpty="@drawable/icon_heart_rating_default"
app:starFill="@drawable/icon_heart_rating_select"
app:starSize="40dp" />
3.attrs.xml文件中的属性
<declare-styleable name="HeartRatingBar">
<attr name="starDistance" format="dimension"/>
<attr name="starSize" format="dimension"/>
<attr name="starCount" format="integer"/>
<attr name="starEmpty" format="reference"/>
<attr name="starFill" format="reference"/>
</declare-styleable>
4.送你两张图,怕你运行不起来
1.icon_heart_rating_select.png 2.icon_heart_rating_default.png 是不是感觉这边少了个icon,对了就是少一张😄(其实是有图的)
|