首先对自定义view拆分一下流程,要实现两张图片的分割线滑动对比视图,首先肯定需要两个摆放图片 的imageview,再通过分割线的位置来对图片控件的大小进行绘制,绘制分割线,以及其他控件上的内容。
众所周知,先绘制的控件在底部,类似xml文件里如果不设置elevation,也是默认后面的view盖住前面的view,所以先绘制右边的图片,名字叫imageviewB,后通过分割线的定位再绘制imageviewA的位置,从左盖住B的部分内容。
接下来在绘制分割线,以及其他元素。下面是全部代码
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.text.TextPaint
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.animation.LinearInterpolator
import android.widget.ImageView
import androidx.core.content.ContextCompat
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.SizeUtils
class ComparisonImageView : View {
private val mImageViewA by lazy {
ImageView(context).apply {
setImageResource(R.mipmap.img_take_photo_default)
scaleType = ImageView.ScaleType.CENTER_CROP
}
}
private val mImageViewB by lazy {
ImageView(context).apply {
setImageResource(R.mipmap.img_take_photo_default)
scaleType = ImageView.ScaleType.CENTER_CROP
}
}
private val imgViewBefore by lazy {
ContextCompat.getDrawable(
context,
R.mipmap.img_before
)
}
private val imgViewAfter by lazy {
ContextCompat.getDrawable(
context,
R.mipmap.img_after
)
}
private val mDrawableBar by lazy { ContextCompat.getDrawable(context, R.mipmap.img_score_bar) }
private val mDrawableLine by lazy {
ContextCompat.getDrawable(
context,
R.mipmap.img_score_line
)
}
private val lineHalfWidth by lazy { SizeUtils.dp2px(1f).toFloat() }
private val dp12 by lazy { SizeUtils.dp2px(12f).toFloat() }
private val dp34 by lazy { SizeUtils.dp2px(26f).toFloat() }
private val dp64 by lazy { SizeUtils.dp2px(45f).toFloat() }
private val dp74 by lazy { SizeUtils.dp2px(114f).toFloat() }
var drawMode = 0
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
)
constructor(
context: Context?,
attrs: AttributeSet?,
defStyleAttr: Int,
defStyleRes: Int
) : super(context, attrs, defStyleAttr, defStyleRes)
fun setImage(imgNormal: Bitmap?, imgChange: Bitmap?) {
mImageViewA.setImageBitmap(imgChange)
mImageViewB.setImageBitmap(imgNormal)
}
private val mTextPain by lazy {
TextPaint().apply {
color = Color.parseColor("#FFFFFF")
textSize = SizeUtils.dp2px(14f).toFloat()
strokeWidth = 0.9F
style = Paint.Style.FILL_AND_STROKE
isAntiAlias = true
setShadowLayer(3f, 0f, SizeUtils.dp2px(1f).toFloat(), Color.parseColor("#80000000"))
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (drawMode == 1) {
mImageViewB.draw(canvas)
return
}
mImageViewB.draw(canvas)
canvas.save()
canvas.clipRect(getLineValue(), 0, width, height)
mImageViewA.draw(canvas)
canvas.restore()
canvas.save()
canvas.clipRect(
getLineValue() - lineHalfWidth,
0f,
getLineValue() + lineHalfWidth,
height.toFloat()
)
mDrawableLine?.setBounds(0, 0, width, height)
mDrawableLine?.draw(canvas)
canvas.restore()
canvas.save()
canvas.translate(dp34, height-dp64)
imgViewBefore?.setBounds(0, 0, SizeUtils.dp2px(68f), SizeUtils.dp2px(32f))
imgViewBefore?.draw(canvas)
canvas.restore()
canvas.save()
canvas.translate(width - SizeUtils.dp2px(68f) - dp34, height-dp64)
imgViewAfter?.setBounds(0, 0, SizeUtils.dp2px(68f), SizeUtils.dp2px(32f))
imgViewAfter?.draw(canvas)
canvas.restore()
canvas.save()
canvas.translate(getLineValue() - dp64 / 2, height - dp64 - dp34 - dp64)
mDrawableBar?.setBounds(0, 0, dp64.toInt(), dp34.toInt())
mDrawableBar?.draw(canvas)
canvas.restore()
}
fun setCompareMode(mode: Int) {
if (drawMode == mode) return
drawMode = mode
postInvalidate()
}
var linePer = 0.5f
private fun getLineValue(): Int {
return (width * linePer).toInt()
}
fun autoPlayDrawImage(start: Float, end: Float, duration: Long = 1000) {
postDelayed({
val va = ValueAnimator.ofFloat(start, end)
va.interpolator = LinearInterpolator()
va.addUpdateListener {
linePer = it.animatedValue as Float
postInvalidate()
}
va.duration = duration
va.start()
}, 150)
}
var startX = 0f
override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
parent.requestDisallowInterceptTouchEvent(true)
return super.dispatchTouchEvent(event)
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
event ?: return false
when (event.action) {
MotionEvent.ACTION_DOWN -> {
startX = event.rawX
val lx = getLineValue()
val boo = (startX > (lx - dp74)) && (startX < (lx + dp74))
LogUtils.e("滑动开始,$boo")
return boo
}
MotionEvent.ACTION_MOVE -> {
val movx = event.rawX
val dir = movx - startX
val lx = getLineValue()
linePer = (lx + dir) / width
if (linePer < 0) {
linePer = 0f
} else if (linePer >= 1) {
linePer = 1f
}
startX = movx
invalidate()
LogUtils.e("滑动中,true")
return true
}
MotionEvent.ACTION_UP -> {
LogUtils.e("滑动抬起,false")
return false
}
}
LogUtils.e("滑动五判断,true${event.action}")
return true
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
mImageViewA.measure(widthMeasureSpec, heightMeasureSpec)
mImageViewB.measure(widthMeasureSpec, heightMeasureSpec)
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
mImageViewA.layout(left, top, right, bottom)
mImageViewB.layout(left, top, right, bottom)
}
internal inline fun String.debugLog() {
LogUtils.d(ComparisonImageView::class.java.simpleName, this)
}
}