- 再次更新
使用中发现因为每个矩形都是左右两个小的拼成的,有时候中间会出现重叠的现象,也就是1px宽,考虑是使用RectF
的原因,把RectF
修改为Rect
- 更新
圆角矩形边框效果不好,像是被切了一般,原因是画笔有一定宽度,画到外面去了,就还剩一半,修改了一下
//画圆角矩形边框
mPaint.reset()
mPaint.color = textColor
mPaint.style = Paint.Style.STROKE
mPaint.strokeWidth = Utils.dp2px(context, 1f)
mPaint.isAntiAlias = true
rectF.left = rectF.left + mPaint.strokeWidth / 2
rectF.top = rectF.top + mPaint.strokeWidth / 2
rectF.right = rectF.right - mPaint.strokeWidth / 2
rectF.bottom = rectF.bottom - mPaint.strokeWidth / 2
canvas.drawRoundRect(rectF, corner, corner, mPaint)
每次都设计成ios原生控件的效果,android开发真是难。之前写过一次这个效果,在布局文件里写的,真的很麻烦。这次又有这个需求,写一个自定义view,以后再用就方便多了,记录一下。
自定义view代码
class IOSTab(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : View(context, attrs, defStyleAttr) {
private var mTabs: Array<String> = arrayOf("")
private var selectedPosition = 0
private var mWidth = 0
private var mHeight = 0
private var corner = Utils.dp2px(context, 5f)
private val mPaint = Paint()
//字号
private var textSize = Utils.dp2px(context,15)
//主色调
private var mainColor = context.resources.getColor(R.color.colorPrimary)
//字的颜色
private var textColor = context.resources.getColor(R.color.white)
//选中的颜色
private var selectColor = Color.parseColor("#ccffffff")
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context) : this(context, null)
init {
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.IOSTab)
mainColor = typedArray.getColor(R.styleable.IOSTab_tabMainColor,context.resources.getColor(R.color.colorPrimary))
textColor = typedArray.getColor(R.styleable.IOSTab_tabTextColor,context.resources.getColor(R.color.white))
selectColor = typedArray.getColor(R.styleable.IOSTab_tabSelectColor,Color.parseColor("#ccffffff"))
textSize = typedArray.getDimension(R.styleable.IOSTab_tabTextSize,Utils.dp2px(context,15))
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
mWidth = MeasureSpec.getSize(widthMeasureSpec)
mHeight = MeasureSpec.getSize(heightMeasureSpec)
}
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
val rectF = RectF(0f, 0f, mWidth.toFloat(), mHeight.toFloat())
//画背景
mPaint.isAntiAlias = true
mPaint.color = mainColor
mPaint.style = Paint.Style.FILL
canvas!!.drawRoundRect(rectF, corner, corner, mPaint)
//画圆角矩形边框
mPaint.reset()
mPaint.color = textColor
mPaint.style = Paint.Style.STROKE
mPaint.strokeWidth = Utils.dp2px(context, 1f)
mPaint.isAntiAlias = true
canvas!!.drawRoundRect(rectF, corner, corner, mPaint)
//画每一个字
mPaint.reset()
mPaint.color = textColor
mPaint.isAntiAlias = true
mPaint.textSize = textSize
val count = mTabs.size
if (count == 0) {
return
} else {
//画字
for (i in mTabs.indices){
drawText(i,canvas,mPaint)
}
}
//每个item的宽度
val itemWidth = mWidth / mTabs.size .toFloat()
//画分隔线
mPaint.strokeWidth = Utils.dp2px(context,0.5f)
mTabs.indices
.filter { it != 0 }
.forEach {
canvas.drawLine(it * itemWidth,0f, it * itemWidth, mHeight.toFloat(),mPaint)
}
mPaint.reset()
mPaint.color = selectColor
// mPaint.alpha = 0xcc
mPaint.style = Paint.Style.FILL
//画选中状态
if (count == 0 || count == 1){
return
}else{
if (selectedPosition == 0){
//第一个被选中,把左半边画上圆角
canvas.save()
//只画左半边
canvas.clipRect(RectF(0f,0f,itemWidth /2,mHeight.toFloat()))
//圆角矩形
val rectF = RectF(0f,0f,itemWidth,mHeight.toFloat())
canvas.drawRoundRect(rectF, corner, corner, mPaint)
canvas.restore()
canvas.drawRect(RectF(itemWidth /2,0f,itemWidth, mHeight.toFloat()),mPaint)
}else if (selectedPosition == count -1){
//最后一个被选中,右半边画上圆角
canvas.save()
//只画右半边
canvas.clipRect(RectF((selectedPosition + 0.5f)* itemWidth,0f,count * itemWidth,mHeight.toFloat()))
//圆角矩形
val rectF = RectF(selectedPosition* itemWidth,0f,count* itemWidth,mHeight.toFloat())
canvas.drawRoundRect(rectF, corner, corner, mPaint)
canvas.restore()
canvas.drawRect(RectF(selectedPosition* itemWidth,0f,(selectedPosition + 0.5f)* itemWidth, mHeight.toFloat()),mPaint)
}else{
//选中中间的
val rect = Rect((itemWidth * selectedPosition).toInt()
, 0
, (itemWidth*(selectedPosition+1)).toInt()
, mHeight)
canvas.drawRect(rect,mPaint)
}
}
//画选中的条目的文字
mPaint.reset()
mPaint.color = mainColor
mPaint.isAntiAlias = true
mPaint.textSize = textSize
drawText(selectedPosition,canvas,mPaint)
}
private fun drawText(position: Int, canvas: Canvas, paint: Paint) {
val text = mTabs[position]
val textWidth = paint.measureText(text)
val itemWidth = mWidth / mTabs.size
//X起始坐标,X轴居中
val startX = (itemWidth - textWidth) / 2 + itemWidth * position
//Y起始坐标,Y轴居中
val startY = mHeight / 2 + (Math.abs(mPaint.fontMetrics.ascent) - mPaint.fontMetrics.descent) / 2
canvas.drawText(text, startX, startY, paint)
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
if (event!!.action == MotionEvent.ACTION_DOWN){
if (mTabs.isNotEmpty()){
selectedPosition = (event.x / (mWidth / mTabs.size)).toInt()
invalidate()
if (listener != null)
listener!!.onChange(selectedPosition)
}
}
return super.onTouchEvent(event)
}
private var listener: IOSTab.OnSelectedItemChange? = null
//设置切换监听
fun setOnSelectedItemChange(listener: OnSelectedItemChange){
this.listener = listener
}
fun setTabs(tabs: Array<String>) {
mTabs = tabs
invalidate()
}
interface OnSelectedItemChange {
fun onChange(select: Int)
}
}
attrs文件中添加
<declare-styleable name="IOSTab">
<!-- 主色调,未被选中的条目背景色,选中条目的文字颜色-->
<attr name="tabMainColor" format="color"/>
<!-- 未被选中的条目的文字颜色,外围圆角边框颜色-->
<attr name="tabTextColor" format="color"/>
<!-- 被选中条目的背景色 -->
<attr name="tabSelectColor" format="color"/>
<!-- 文字大小 -->
<attr name="tabTextSize" format="dimension"/>
</declare-styleable>
使用
在布局文件中
<com.test.www.test.view.IOSTab
android:id="@+id/ios_tab"
android:layout_width="200dp"
android:layout_height="29dp"
android:layout_gravity="center"
app:tabSelectColor="#ccffffff"
/>
在activity或者fragment中
val tabs = arrayOf("标题一","标题二","标题三")
ios_tab.setTabs(tabs)
ios_tab.setOnSelectedItemChange(object : IOSTab.OnSelectedItemChange{
override fun onChange(select: Int) {
//在这里处理切换
}
})
效果