教你用kotlin撸一个背景透明度联动的banner
最近因为接到个需求:banner分成前景和背景2层,背景随着前景的横向切换而变换透明度,透明度的变换具体是,消失的上一张慢慢变透明,同时要出现的下一张慢慢变不透明,好了,话不多说,先上图,看下效果:
看完之后我们来撸代码。
(一):准备工作
因为我们这次要用kotlin来写,所以先加好依赖
由于是demo,所以随便找了个banner库
准备基本完成,let's go。
(二):先说下思路,我准备先把背景做出来,布局中分别有3个imageview,分别代表前一张、当前和下一张,然后监听前景的切换而变换背景的透明度
先做背景
class LinkageBackground @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
private val backgroundCur: ImageView? by bindOptionalView(R.id.backgroundCur)
private val backgroundPre: ImageView? by bindOptionalView(R.id.backgroundPre)
private val backgroundNext: ImageView? by bindOptionalView(R.id.backgroundNext)
private lateinit var bgRes: MutableList
init {
initView()
}
fun initView() {
View.inflate(context, R.layout.layout_linkage_bg, this)
}
/**
* 初始化背景
*/
fun setBgRes(res: List) {
bgRes = res.toMutableList()
resetImg(0)
}
/**
* 向左滑动,不需要操作pre
*/
fun scroll2Left(offset: Float) {
// Log.d("LinkageBanner :", "<<<<<<<<<<$offset")
backgroundNext?.visibility = View.VISIBLE
backgroundNext?.alpha = offset
backgroundCur?.alpha = 1 - offset
}
/**
* 向右滑,让next隐藏,对pre和cur操作透明度
*/
fun scroll2Right(offset: Float) {
// Log.d("LinkageBanner :", ">>>>>>>>>>$offset")
backgroundNext?.visibility = View.GONE
backgroundNext?.alpha = 0f
backgroundPre?.visibility = View.VISIBLE
backgroundPre?.alpha = 1 - offset
backgroundCur?.alpha = offset
}
/**
* 重置所有背景,pre和next隐藏
*/
fun resetImg(cur: Int) {
backgroundPre?.visibility = View.GONE
backgroundNext?.visibility = View.GONE
val pre = if (cur - 1 < 0) bgRes.size - 1 else cur - 1
backgroundPre?.loadImg(bgRes[pre])
backgroundPre?.alpha = 0f
backgroundCur?.loadImg(bgRes[cur])
backgroundCur?.alpha = 1f
val next = if (cur + 1 > bgRes.size - 1) 0 else cur + 1
backgroundNext?.loadImg(bgRes[next])
backgroundNext?.alpha = 0f
}
}
上面的@JvmOverloads注解的作用:在有默认参数值的方法中使用,Kotlin会暴露多个重载方法。其余的在代码中都标注清楚了,应该不难懂
2.接下来做个容器,用来放背景和前景,在其中监听banner切换
class LinkageBanner @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
: FrameLayout(context, attrs, defStyleAttr) {
private val bgBanner: LinkageBackground? by bindOptionalView(R.id.bgBanner)
private val srcBanner: Banner? by bindOptionalView(R.id.srcBanner)
private var isScrolling = false
private var currentPosition = 0
private var beforeX: Float = 0f//手指刚触摸时的x坐标
var isScroll2Left = false
var isScroll2Right = false
init {
initView()
}
fun initView() {
View.inflate(context, R.layout.layout_linkage_banner, this)
}
fun setRes(bgRes: List, srcRes: List) {
bgBanner?.setBgRes(bgRes)
srcBanner?.setImageLoader(BannerImgLoader())
srcBanner?.setImages(srcRes)
srcBanner?.start()
setListener()
}
fun getBanner(): Banner {
return srcBanner!!
}
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
when (ev?.action) {
MotionEvent.ACTION_DOWN ->
//获取起始坐标值
beforeX = ev.x
MotionEvent.ACTION_MOVE ->
if (ev.x < beforeX) { // 向左侧滑动
isScroll2Left = true
isScroll2Right = false
} else if (ev.x > beforeX) {// 向右侧滑动
isScroll2Right = true
isScroll2Left = false
}
else -> {
}
}
return super.dispatchTouchEvent(ev)
}
private fun setListener() {
srcBanner?.setOnPageChangeListener(object : ViewPager.OnPageChangeListener {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
if (isScrolling) {
//根据滑动方向改变背景透明度
if (isScroll2Left && positionOffset > 0) {
bgBanner?.scroll2Left(positionOffset)
} else if (isScroll2Right && positionOffset > 0) {
bgBanner?.scroll2Right(positionOffset)
}
}
}
override fun onPageSelected(position: Int) {
currentPosition = position
bgBanner?.resetImg(currentPosition)
}
override fun onPageScrollStateChanged(state: Int) {
isScrolling = state == 1
}
})
}
class BannerImgLoader : ImageLoader() {
/**
* 注意:
* 1.图片加载器由自己选择,这里不限制,只是提供几种使用方法
* 2.返回的图片路径为Object类型,由于不能确定到底使用的那种图片加载器,
* 传输的到的是什么格式,那么这种就使用Object接收和返回,只需要强转成你传输的类型就行,
*/
override fun displayImage(context: Context, path: Any, imageView: ImageView) {
imageView.loadImg(path)
}
}
}
在实践过程中,发现viewpager的setOnPageChangeListener监听对于滑动方向的判断有些不太准确,所以我自己拦截了touch事件,对滑动方向做了判断。
3.最后就是在你代码中使用了,很简单,只需要初始化你背景和前景的图片资源就好:
class MainActivity : AppCompatActivity() {
private val linkageBanner: LinkageBanner? by bindOptionalView(R.id.linkageBanner)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//初始化
val bgRes = listOf(R.drawable.bg1, R.drawable.bg2, R.drawable.bg3)
val srcRes = listOf(R.drawable.src1, R.drawable.src2, R.drawable.src3)
linkageBanner?.setRes(bgRes, srcRes)
}
override fun onStart() {
super.onStart()
linkageBanner?.getBanner()?.startAutoPlay()
}
override fun onStop() {
super.onStop()
linkageBanner?.getBanner()?.stopAutoPlay()
}
}
怎么样,是不是灰常简单?赶紧试试吧。
代码地址在这:传送门
原创,转载请标明来源,3Q。