有个需求涉及到类似这个视图,于是在网上找了个轮子,自己改吧改吧用,拿来主义当然后,但做事不仅要知其然,还要知其所以然,所以拿来用的同时还要理解。于是就有了本文。
参数
/**
* 控件宽度
*/
private var controlWidth = 0f
/**
* 控件高度
*/
private var controlHeight = 0f
/**
* 是否正在滑动
*
* @return
*/
/**
* 是否滑动中
*/
var isScrolling = false
/**
* 选择的内容
*/
private val itemList: MutableList<ItemObject>? = mutableListOf()
/**
* 设置数据
*/
private var dataList = mutableListOf<String>()
/**
* 按下的坐标
*/
private var downY = 0
/**
* 按下的时间
*/
private var downTime: Long = 0
/**
* 短促移动
*/
private val goonTime: Long = 200
/**
* 短促移动距离
*/
private val goonDistance = 100
/**
* 画线画笔
*/
private var linePaint: Paint? = null
/**
* 线的默认颜色
*/
private var lineColor = -0x1000000
/**
* 线的默认宽度
*/
private var lineHeight = 2f
/**
* 默认字体
*/
private var normalFont = 14.0f
/**
* 选中的时候字体
*/
private var selectedFont = 22.0f
/**
* 单元格高度
*/
private var unitHeight = 50
/**
* 显示多少个内容
*/
private var itemNumber = 7
/**
* 默认字体颜色
*/
private var normalColor = -0x1000000
/**
* 选中时候的字体颜色
*/
private var selectedColor = -0x10000
/**
* 蒙板高度
*/
private var maskHeight = 48.0f
/**
* 选择监听
*/
private var onSelectListener: OnSelectListener? = null
/**
* 设置是否可用
*
* @param isEnable
*/
var isEnable = true
/**
* 是否允许选空
*/
private var noEmpty = true
/**
* 正在修改数据,避免ConcurrentModificationException异常
*/
private var isClearing = false
/**
* 初始化,获取设置的属性
*
* @param context
* @param attrs
*/
private fun init(context: Context, attrs: AttributeSet?) {
val attribute = context.obtainStyledAttributes(attrs, R.styleable.WheelView)
unitHeight =
attribute.getDimension(R.styleable.WheelView_unitHeight, unitHeight.toFloat()).toInt()
itemNumber = attribute.getInt(R.styleable.WheelView_itemNumber, itemNumber)
normalFont = attribute.getDimension(R.styleable.WheelView_normalTextSize, normalFont)
selectedFont = attribute.getDimension(R.styleable.WheelView_selectedTextSize, selectedFont)
normalColor = attribute.getColor(R.styleable.WheelView_normalTextColor, normalColor)
selectedColor = attribute.getColor(R.styleable.WheelView_selectedTextColor, selectedColor)
lineColor = attribute.getColor(R.styleable.WheelView_lineColor, lineColor)
lineHeight = attribute.getDimension(R.styleable.WheelView_lineHeight, lineHeight)
maskHeight = attribute.getDimension(R.styleable.WheelView_maskHeight, maskHeight)
noEmpty = attribute.getBoolean(R.styleable.WheelView_noEmpty, true)
isEnable = attribute.getBoolean(R.styleable.WheelView_isEnable, true)
attribute.recycle()
controlHeight = (itemNumber * unitHeight).toFloat()
}
上面的代码在构造函数中调用,通过 context.obtainStyledAttributes(attrs, R.styleable.WheelView) 方法拿到我们在attrs.xml文件中自定义样式的一些参数。这些参数是可以在xml中配置的。这些都是自定义view的一些基础,属于老生常谈了。
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="WheelView">
<attr name="unitHeight" format="dimension" />
<attr name="itemNumber" format="integer"/>
<attr name="normalTextColor" format="color" />
<attr name="normalTextSize" format="dimension" />
<attr name="selectedTextColor" format="color" />
<attr name="selectedTextSize" format="dimension" />
<attr name="lineColor" format="color" />
<attr name="lineHeight" format="dimension" />
<attr name="maskHeight" format="dimension"/>
<attr name="noEmpty" format="boolean"/>
<attr name="isEnable" format="boolean"/>
</declare-styleable>
</resources>
/**
* 初始化数据
*/
private fun initData() {
isClearing = true
itemList!!.clear()
for (i in dataList.indices) {
val itemObject = ItemObject()
itemObject.id = i
itemObject.itemText = dataList[i]
itemObject.x = 0
itemObject.y = i * unitHeight
itemList.add(itemObject)
}
isClearing = false
}
这里就是初始化item数据,ItemObject是单个数据的绘制,后面再说。而isClearing 是为了避免 ConcurrentModificationException,在drawList()方法中有体现。
@Synchronized
private fun drawList(canvas: Canvas) {
if (isClearing) return
try {
for (itemObject in itemList!!) {
itemObject.drawSelf(canvas, measuredWidth)
}
} catch (e: Exception) {
Log.e("WheelView", "drawList: $e")
}
}
自定义view的三件套之一
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
controlWidth = measuredWidth.toFloat()
if (controlWidth != 0f) {
setMeasuredDimension(measuredWidth, itemNumber * unitHeight)
}
}
先用measuredWidth给 controlWidth 赋值 ,然后当宽度不为0的时候调用setMeasuredDimension方法给具体的测量值。我们来看看setMeasuredDimension方法
这是一个view的自带方法,onMeasure(int,int)必须调用此方法来存储测量的宽度和测量的高度。否则将在测量时触发异常。
参数:
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
drawLine(canvas)
drawList(canvas)
drawMask(canvas)
}
在其中绘制了三个东西,一个是绘制选中的两个线条
/**
* 绘制线条
*
* @param canvas
*/
private fun drawLine(canvas: Canvas) {
if (linePaint == null) {
linePaint = Paint()
linePaint!!.color = lineColor
linePaint!!.isAntiAlias = true
linePaint!!.strokeWidth = lineHeight
}
canvas.drawLine(
0f, controlHeight / 2 - unitHeight / 2 + lineHeight,
controlWidth, controlHeight / 2 - unitHeight / 2 + lineHeight, linePaint!!
)
canvas.drawLine(
0f, controlHeight / 2 + unitHeight / 2 - lineHeight,
controlWidth, controlHeight / 2 + unitHeight / 2 - lineHeight, linePaint!!
)
}
一个是绘制列表,上面已经说过了,还有一个就是绘制蒙层,我这边是有一个渐变的蒙层,也是我做过改动的地方
/**
* 绘制遮盖板
*
* @param canvas
*/
private fun drawMask(canvas: Canvas) {
val colorArray = intArrayOf(0x0042C8FF, 0x3D42C8FF, 0x0042C8FF)
val positionArray = floatArrayOf(0f, 0.5f, 1f)
val lg3 = LinearGradient(
0f, 0f,
controlWidth, 0f, colorArray, positionArray, TileMode.MIRROR
)
val paint3 = Paint()
paint3.shader = lg3
canvas.drawRect(
0f, controlHeight / 2 - unitHeight / 2 + lineHeight, controlWidth,
controlHeight / 2 + unitHeight / 2 - lineHeight, paint3
)
}
触摸事件
override fun onTouchEvent(event: MotionEvent): Boolean {
if (!isEnable) return true
val y = event.y.toInt()
when (event.action) {
MotionEvent.ACTION_DOWN -> {
isScrolling = true
downY = event.y.toInt()
downTime = System.currentTimeMillis()
}
MotionEvent.ACTION_MOVE -> {
actionMove(y - downY)
onSelectListener()
}
MotionEvent.ACTION_UP -> {
val move = Math.abs(y - downY)
// 判断这段时间移动的距离
if (System.currentTimeMillis() - downTime < goonTime && move > goonDistance) {
goonMove(y - downY)
} else {
actionUp(y - downY)
noEmpty()
isScrolling = false
}
}
else -> {}
}
return true
}
代码解析:
isEnable是控制是否能滑动的,不用过多关注
在手势为ACTION_DOWN 的时候,记录开始滑动的y坐标和时间,在手势为**ACTION_MOVE **的时候开始移动,并调用actionMove方法设置移动的坐标,然后调用invalidate方法进行重绘。onSelectListener是一个滑动时候的选中监听
/**
* 移动的时候
*
* @param move
*/
private fun actionMove(move: Int) {
for (item in itemList!!) {
item.move(move)
}
invalidate()
}
最后在手势为ACTION_UP 的时候,判断在ACTION_DOWN这段时间移动的距离,如果当前移动的时间小于短促移动的时间,当前移动的距离却大于短促移动的距离,那么我们就调用goonMove方法多移动一点距离以达到一个惯性移动的体验。
/**
* 继续移动一定距离
*/
@Synchronized
private fun goonMove(move: Int) {
Thread {
var distance = 0
while (distance < unitHeight * MOVE_NUMBER) {
try {
Thread.sleep(5)
} catch (e: InterruptedException) {
e.printStackTrace()
}
actionThreadMove(if (move > 0) distance else distance * -1)
distance += 10
}
actionUp(if (move > 0) distance - 10 else distance * -1 + 10)
noEmpty()
}.start()
}
/**
* 移动,线程中调用
*
* @param move
*/
private fun actionThreadMove(move: Int) {
for (item in itemList!!) {
item.move(move)
}
val rMessage = Message()
rMessage.what = REFRESH_VIEW
mHandler.sendMessage(rMessage)
}
否则就直接用actionUp和noEmpty直接移动
/**
* 松开的时候
*
* @param move
*/
private fun actionUp(move: Int) {
var newMove = 0
if (move > 0) {
for (i in itemList!!.indices) {
if (itemList[i].isSelected) {
newMove = itemList[i].moveToSelected().toInt()
if (onSelectListener != null) onSelectListener!!.endSelect(
itemList[i].id,
itemList[i].itemText
)
break
}
}
} else {
for (i in itemList!!.indices.reversed()) {
if (itemList[i].isSelected) {
newMove = itemList[i].moveToSelected().toInt()
if (onSelectListener != null) onSelectListener!!.endSelect(
itemList[i].id,
itemList[i].itemText
)
break
}
}
}
for (item in itemList) {
item.newY(move + 0)
}
slowMove(newMove)
val rMessage = Message()
rMessage.what = REFRESH_VIEW
mHandler.sendMessage(rMessage)
}
/**
* 不能为空,必须有选项
*/
private fun noEmpty() {
if (!noEmpty) return
for (item in itemList!!) {
if (item.isSelected) return
}
val move = itemList[0].moveToSelected().toInt()
if (move < 0) {
defaultMove(move)
} else {
defaultMove(
itemList[itemList.size - 1]
.moveToSelected().toInt()
)
}
for (item in itemList) {
if (item.isSelected) {
if (onSelectListener != null) onSelectListener!!.endSelect(item.id, item.itemText)
break
}
}
}
/**
* 缓慢移动
*
* @param move
*/
@Synchronized
private fun slowMove(move: Int) {
Thread { // 判断正负
var m = if (move > 0) move else move * -1
val i = if (move > 0) 1 else -1
// 移动速度
val speed = 1
while (true) {
m -= speed
if (m <= 0) {
for (item in itemList!!) {
item.newY(m * i)
}
val rMessage = Message()
rMessage.what = REFRESH_VIEW
mHandler.sendMessage(rMessage)
try {
Thread.sleep(2)
} catch (e: InterruptedException) {
e.printStackTrace()
}
break
}
for (item in itemList!!) {
item.newY(speed * i)
}
val rMessage = Message()
rMessage.what = REFRESH_VIEW
mHandler.sendMessage(rMessage)
try {
Thread.sleep(2)
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
if (itemList != null) {
for (item in itemList) {
if (item.isSelected) {
if (onSelectListener != null) onSelectListener!!.endSelect(
item.id,
item.itemText
)
break
}
}
}
}.start()
}
/**
* 移动到默认位置
*
* @param move
*/
private fun defaultMove(move: Int) {
for (item in itemList!!) {
item.newY(move)
}
val rMessage = Message()
rMessage.what = REFRESH_VIEW
mHandler.sendMessage(rMessage)
}
一些移动相关的方法。
单个绘制里面值得说的就是drawSelf方法了,注释都写的比较清晰了。
/**
* 绘制自身
*
* @param canvas 画板
* @param containerWidth 容器宽度
*/
fun drawSelf(canvas: Canvas, containerWidth: Int) {
if (textPaint == null) {
textPaint = TextPaint()
textPaint!!.isAntiAlias = true
}
if (textRect == null) textRect = Rect()
// 判断是否被选择
if (isSelected) {
textPaint!!.color = selectedColor
// 获取距离标准位置的距离
var moveToSelect = moveToSelected()
moveToSelect = if (moveToSelect > 0) moveToSelect else moveToSelect * -1
// 计算当前字体大小
val textSize =
normalFont + (selectedFont - normalFont) * (1.0f - moveToSelect / unitHeight.toFloat())
textPaint!!.textSize = textSize
} else {
textPaint!!.color = normalColor
textPaint!!.textSize = normalFont
}
// 判断是否可视
if (!isInView) return
//判断是一行还是两行,两行数据用,分割
if (itemText.indexOf(",") != -1) {
var (text1, text2) = itemText.split(",")
// 返回包围整个字符串的最小的一个Rect区域
text1 = TextUtils.ellipsize(
text1,
textPaint,
containerWidth.toFloat(),
TextUtils.TruncateAt.END
) as String
textPaint!!.getTextBounds(text1, 0, text1.length, textRect)
//双排文字一
canvas.drawText(
text1,
x + controlWidth / 2 - textRect!!.width() / 2,
(y + move + unitHeight / 5 * 2 + textRect!!.height() / 5 * 2).toFloat(),
textPaint!!
)
// 返回包围整个字符串的最小的一个Rect区域
text2 = TextUtils.ellipsize(
text2,
textPaint,
containerWidth.toFloat(),
TextUtils.TruncateAt.END
) as String
textPaint!!.getTextBounds(text2, 0, text2.length, textRect)
//双排文字2
canvas.drawText(
text2,
x + controlWidth / 2 - textRect!!.width() / 2,
(y + move + unitHeight / 5 * 3 + textRect!!.height() / 5 * 3).toFloat(),
textPaint!!
)
} else {
// 返回包围整个字符串的最小的一个Rect区域
itemText = TextUtils.ellipsize(
itemText,
textPaint,
containerWidth.toFloat(),
TextUtils.TruncateAt.END
) as String
textPaint!!.getTextBounds(itemText, 0, itemText.length, textRect)
// 绘制内容
canvas.drawText(
itemText, x + controlWidth / 2 - textRect!!.width() / 2,
(y + move + unitHeight / 2 + textRect!!.height() / 2).toFloat(), textPaint!!
)
}
}
import android.content.Context
import android.graphics.Canvas
import android.graphics.LinearGradient
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.Shader.TileMode
import android.os.Handler
import android.os.Message
import android.text.TextPaint
import android.text.TextUtils
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View
class WheelView : View {
/**
* 控件宽度
*/
private var controlWidth = 0f
/**
* 控件高度
*/
private var controlHeight = 0f
/**
* 是否正在滑动
*
* @return
*/
/**
* 是否滑动中
*/
var isScrolling = false
/**
* 选择的内容
*/
private val itemList: MutableList<ItemObject>? = mutableListOf()
/**
* 设置数据
*/
private var dataList = mutableListOf<String>()
/**
* 按下的坐标
*/
private var downY = 0
/**
* 按下的时间
*/
private var downTime: Long = 0
/**
* 短促移动
*/
private val goonTime: Long = 200
/**
* 短促移动距离
*/
private val goonDistance = 100
/**
* 画线画笔
*/
private var linePaint: Paint? = null
/**
* 线的默认颜色
*/
private var lineColor = -0x1000000
/**
* 线的默认宽度
*/
private var lineHeight = 2f
/**
* 默认字体
*/
private var normalFont = 14.0f
/**
* 选中的时候字体
*/
private var selectedFont = 22.0f
/**
* 单元格高度
*/
private var unitHeight = 50
/**
* 显示多少个内容
*/
private var itemNumber = 7
/**
* 默认字体颜色
*/
private var normalColor = -0x1000000
/**
* 选中时候的字体颜色
*/
private var selectedColor = -0x10000
/**
* 蒙板高度
*/
private var maskHeight = 48.0f
/**
* 选择监听
*/
private var onSelectListener: OnSelectListener? = null
/**
* 设置是否可用
*
* @param isEnable
*/
var isEnable = true
/**
* 是否允许选空
*/
private var noEmpty = true
/**
* 正在修改数据,避免ConcurrentModificationException异常
*/
private var isClearing = false
constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(
context,
attrs,
defStyle
) {
init(context, attrs)
initData()
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
init(context, attrs)
initData()
}
constructor(context: Context?) : super(context) {
initData()
}
/**
* 初始化,获取设置的属性
*
* @param context
* @param attrs
*/
private fun init(context: Context, attrs: AttributeSet?) {
val attribute = context.obtainStyledAttributes(attrs, R.styleable.WheelView)
unitHeight =
attribute.getDimension(R.styleable.WheelView_unitHeight, unitHeight.toFloat()).toInt()
itemNumber = attribute.getInt(R.styleable.WheelView_itemNumber, itemNumber)
normalFont = attribute.getDimension(R.styleable.WheelView_normalTextSize, normalFont)
selectedFont = attribute.getDimension(R.styleable.WheelView_selectedTextSize, selectedFont)
normalColor = attribute.getColor(R.styleable.WheelView_normalTextColor, normalColor)
selectedColor = attribute.getColor(R.styleable.WheelView_selectedTextColor, selectedColor)
lineColor = attribute.getColor(R.styleable.WheelView_lineColor, lineColor)
lineHeight = attribute.getDimension(R.styleable.WheelView_lineHeight, lineHeight)
maskHeight = attribute.getDimension(R.styleable.WheelView_maskHeight, maskHeight)
noEmpty = attribute.getBoolean(R.styleable.WheelView_noEmpty, true)
isEnable = attribute.getBoolean(R.styleable.WheelView_isEnable, true)
attribute.recycle()
controlHeight = (itemNumber * unitHeight).toFloat()
}
/**
* 初始化数据
*/
private fun initData() {
isClearing = true
itemList!!.clear()
for (i in dataList.indices) {
val itemObject = ItemObject()
itemObject.id = i
itemObject.itemText = dataList[i]
itemObject.x = 0
itemObject.y = i * unitHeight
itemList.add(itemObject)
}
isClearing = false
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
controlWidth = measuredWidth.toFloat()
if (controlWidth != 0f) {
setMeasuredDimension(measuredWidth, itemNumber * unitHeight)
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
drawLine(canvas)
drawList(canvas)
drawMask(canvas)
}
/**
* 绘制线条
*
* @param canvas
*/
private fun drawLine(canvas: Canvas) {
if (linePaint == null) {
linePaint = Paint()
linePaint!!.color = lineColor
linePaint!!.isAntiAlias = true
linePaint!!.strokeWidth = lineHeight
}
canvas.drawLine(
0f, controlHeight / 2 - unitHeight / 2 + lineHeight,
controlWidth, controlHeight / 2 - unitHeight / 2 + lineHeight, linePaint!!
)
canvas.drawLine(
0f, controlHeight / 2 + unitHeight / 2 - lineHeight,
controlWidth, controlHeight / 2 + unitHeight / 2 - lineHeight, linePaint!!
)
}
@Synchronized
private fun drawList(canvas: Canvas) {
if (isClearing) return
try {
for (itemObject in itemList!!) {
itemObject.drawSelf(canvas, measuredWidth)
}
} catch (e: Exception) {
Log.e("WheelView", "drawList: $e")
}
}
/**
* 绘制遮盖板
*
* @param canvas
*/
private fun drawMask(canvas: Canvas) {
val colorArray = intArrayOf(0x0042C8FF, 0x3D42C8FF, 0x0042C8FF)
val positionArray = floatArrayOf(0f, 0.5f, 1f)
val lg3 = LinearGradient(
0f, 0f,
controlWidth, 0f, colorArray, positionArray, TileMode.MIRROR
)
val paint3 = Paint()
paint3.shader = lg3
canvas.drawRect(
0f, controlHeight / 2 - unitHeight / 2 + lineHeight, controlWidth,
controlHeight / 2 + unitHeight / 2 - lineHeight, paint3
)
}
override fun onTouchEvent(event: MotionEvent): Boolean {
if (!isEnable) return true
val y = event.y.toInt()
when (event.action) {
MotionEvent.ACTION_DOWN -> {
isScrolling = true
downY = event.y.toInt()
downTime = System.currentTimeMillis()
}
MotionEvent.ACTION_MOVE -> {
actionMove(y - downY)
onSelectListener()
}
MotionEvent.ACTION_UP -> {
val move = Math.abs(y - downY)
// 判断这段时间移动的距离
if (System.currentTimeMillis() - downTime < goonTime && move > goonDistance) {
goonMove(y - downY)
} else {
actionUp(y - downY)
noEmpty()
isScrolling = false
}
}
else -> {}
}
return true
}
/**
* 继续移动一定距离
*/
@Synchronized
private fun goonMove(move: Int) {
Thread {
var distance = 0
while (distance < unitHeight * MOVE_NUMBER) {
try {
Thread.sleep(5)
} catch (e: InterruptedException) {
e.printStackTrace()
}
actionThreadMove(if (move > 0) distance else distance * -1)
distance += 10
}
actionUp(if (move > 0) distance - 10 else distance * -1 + 10)
noEmpty()
}.start()
}
/**
* 不能为空,必须有选项
*/
private fun noEmpty() {
if (!noEmpty) return
for (item in itemList!!) {
if (item.isSelected) return
}
val move = itemList[0].moveToSelected().toInt()
if (move < 0) {
defaultMove(move)
} else {
defaultMove(
itemList[itemList.size - 1]
.moveToSelected().toInt()
)
}
for (item in itemList) {
if (item.isSelected) {
if (onSelectListener != null) onSelectListener!!.endSelect(item.id, item.itemText)
break
}
}
}
/**
* 移动的时候
*
* @param move
*/
private fun actionMove(move: Int) {
for (item in itemList!!) {
item.move(move)
}
invalidate()
}
/**
* 移动,线程中调用
*
* @param move
*/
private fun actionThreadMove(move: Int) {
for (item in itemList!!) {
item.move(move)
}
val rMessage = Message()
rMessage.what = REFRESH_VIEW
mHandler.sendMessage(rMessage)
}
/**
* 松开的时候
*
* @param move
*/
private fun actionUp(move: Int) {
var newMove = 0
if (move > 0) {
for (i in itemList!!.indices) {
if (itemList[i].isSelected) {
newMove = itemList[i].moveToSelected().toInt()
if (onSelectListener != null) onSelectListener!!.endSelect(
itemList[i].id,
itemList[i].itemText
)
break
}
}
} else {
for (i in itemList!!.indices.reversed()) {
if (itemList[i].isSelected) {
newMove = itemList[i].moveToSelected().toInt()
if (onSelectListener != null) onSelectListener!!.endSelect(
itemList[i].id,
itemList[i].itemText
)
break
}
}
}
for (item in itemList) {
item.newY(move + 0)
}
slowMove(newMove)
val rMessage = Message()
rMessage.what = REFRESH_VIEW
mHandler.sendMessage(rMessage)
}
/**
* 缓慢移动
*
* @param move
*/
@Synchronized
private fun slowMove(move: Int) {
Thread { // 判断正负
var m = if (move > 0) move else move * -1
val i = if (move > 0) 1 else -1
// 移动速度
val speed = 1
while (true) {
m -= speed
if (m <= 0) {
for (item in itemList!!) {
item.newY(m * i)
}
val rMessage = Message()
rMessage.what = REFRESH_VIEW
mHandler.sendMessage(rMessage)
try {
Thread.sleep(2)
} catch (e: InterruptedException) {
e.printStackTrace()
}
break
}
for (item in itemList!!) {
item.newY(speed * i)
}
val rMessage = Message()
rMessage.what = REFRESH_VIEW
mHandler.sendMessage(rMessage)
try {
Thread.sleep(2)
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
if (itemList != null) {
for (item in itemList) {
if (item.isSelected) {
if (onSelectListener != null) onSelectListener!!.endSelect(
item.id,
item.itemText
)
break
}
}
}
}.start()
}
/**
* 移动到默认位置
*
* @param move
*/
private fun defaultMove(move: Int) {
for (item in itemList!!) {
item.newY(move)
}
val rMessage = Message()
rMessage.what = REFRESH_VIEW
mHandler.sendMessage(rMessage)
}
/**
* 滑动监听
*/
private fun onSelectListener() {
if (onSelectListener == null) return
for (item in itemList!!) {
if (item.isSelected) {
onSelectListener!!.selecting(item.id, item.itemText)
}
}
}
/**
* 设置数据 (第一次)
*
* @param data
*/
fun setData(data: MutableList<String>) {
dataList = data
initData()
}
/**
* 重置数据
*
* @param data
*/
fun refreshData(data: MutableList<String>) {
setData(data)
invalidate()
}
/**
* 获取返回项 id
*
* @return
*/
val selected: Int
get() {
for (item in itemList!!) {
if (item.isSelected) return item.id
}
return -1
}
/**
* 获取返回的内容
*
* @return
*/
val selectedText: String
get() {
for (item in itemList!!) {
if (item.isSelected) return item.itemText
}
return ""
}
/**
* 设置默认选项
*
* @param index
*/
fun setDefault(index: Int) {
if (index > itemList!!.size - 1) return
val move = itemList[index].moveToSelected()
defaultMove(move.toInt())
}
/**
* 获取列表大小
*
* @return
*/
val listSize: Int
get() = itemList?.size ?: 0
/**
* 获取某项的内容
*
* @param index
* @return
*/
fun getItemText(index: Int): String {
return itemList?.get(index)?.itemText ?: ""
}
/**
* 监听
*
* @param onSelectListener
*/
fun setOnSelectListener(onSelectListener: OnSelectListener?) {
this.onSelectListener = onSelectListener
}
var mHandler: Handler =
object : Handler() {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when (msg.what) {
REFRESH_VIEW -> invalidate()
else -> {}
}
}
}
/**
* 单条内容
*/
private inner class ItemObject {
/**
* id
*/
var id = 0
/**
* 内容
*/
var itemText = ""
/**
* x坐标
*/
var x = 0
/**
* y坐标
*/
var y = 0
/**
* 移动距离
*/
var move = 0
/**
* 字体画笔
*/
private var textPaint: TextPaint? = null
/**
* 字体范围矩形
*/
private var textRect: Rect? = null
/**
* 绘制自身
*
* @param canvas 画板
* @param containerWidth 容器宽度
*/
fun drawSelf(canvas: Canvas, containerWidth: Int) {
if (textPaint == null) {
textPaint = TextPaint()
textPaint!!.isAntiAlias = true
}
if (textRect == null) textRect = Rect()
// 判断是否被选择
if (isSelected) {
textPaint!!.color = selectedColor
// 获取距离标准位置的距离
var moveToSelect = moveToSelected()
moveToSelect = if (moveToSelect > 0) moveToSelect else moveToSelect * -1
// 计算当前字体大小
val textSize =
normalFont + (selectedFont - normalFont) * (1.0f - moveToSelect / unitHeight.toFloat())
textPaint!!.textSize = textSize
} else {
textPaint!!.color = normalColor
textPaint!!.textSize = normalFont
}
// 判断是否可视
if (!isInView) return
//判断是一行还是两行,两行数据用,分割
if (itemText.indexOf(",") != -1) {
var (text1, text2) = itemText.split(",")
// 返回包围整个字符串的最小的一个Rect区域
text1 = TextUtils.ellipsize(
text1,
textPaint,
containerWidth.toFloat(),
TextUtils.TruncateAt.END
) as String
textPaint!!.getTextBounds(text1, 0, text1.length, textRect)
//双排文字一
canvas.drawText(
text1,
x + controlWidth / 2 - textRect!!.width() / 2,
(y + move + unitHeight / 5 * 2 + textRect!!.height() / 5 * 2).toFloat(),
textPaint!!
)
// 返回包围整个字符串的最小的一个Rect区域
text2 = TextUtils.ellipsize(
text2,
textPaint,
containerWidth.toFloat(),
TextUtils.TruncateAt.END
) as String
textPaint!!.getTextBounds(text2, 0, text2.length, textRect)
//双排文字2
canvas.drawText(
text2,
x + controlWidth / 2 - textRect!!.width() / 2,
(y + move + unitHeight / 5 * 3 + textRect!!.height() / 5 * 3).toFloat(),
textPaint!!
)
} else {
// 返回包围整个字符串的最小的一个Rect区域
itemText = TextUtils.ellipsize(
itemText,
textPaint,
containerWidth.toFloat(),
TextUtils.TruncateAt.END
) as String
textPaint!!.getTextBounds(itemText, 0, itemText.length, textRect)
// 绘制内容
canvas.drawText(
itemText, x + controlWidth / 2 - textRect!!.width() / 2,
(y + move + unitHeight / 2 + textRect!!.height() / 2).toFloat(), textPaint!!
)
}
}
/**
* 是否在可视界面内
*
* @return
*/
val isInView: Boolean
get() = if (y + move > controlHeight || y + move + unitHeight / 2 + textRect!!.height() / 2 < 0) false else true
/**
* 移动距离
*
* @param _move
*/
fun move(_move: Int) {
move = _move
}
/**
* 设置新的坐标
*
* @param _move
*/
fun newY(_move: Int) {
move = 0
y = y + _move
}
/**
* 判断是否在选择区域内
*
* @return
*/
val isSelected: Boolean
get() {
if (y + move + unitHeight >= controlHeight / 2 - unitHeight / 2 + lineHeight
&& y + move + unitHeight <= controlHeight / 2 + unitHeight / 2 - lineHeight
) {
return true
}
return (y + move <= controlHeight / 2 - unitHeight / 2 + lineHeight
&& y + move + unitHeight >= controlHeight / 2 + unitHeight / 2 - lineHeight)
}
/**
* 获取移动到标准位置需要的距离
*/
fun moveToSelected(): Float {
return controlHeight / 2 - unitHeight / 2 - (y + move)
}
}
/**
* 选择监听
*
* @author JiangPing
*/
interface OnSelectListener {
/**
* 结束选择
*
* @param id
* @param text
*/
fun endSelect(id: Int, text: String?)
/**
* 选中的内容
*
* @param id
* @param text
*/
fun selecting(id: Int, text: String?)
}
companion object {
/**
* 刷新界面
*/
private const val REFRESH_VIEW = 0x001
/**
* 移动距离
*/
private const val MOVE_NUMBER = 5
}
}
主要还是考查自定义view相关能力。