教你用kotlin擼一個背景透明度聯動的banner
最近因為接到個需求:banner分成前景和背景2層,背景隨著前景的橫向切換而變換透明度,透明度的變換具體是,消失的上一張慢慢變透明,同時要出現的下一張慢慢變不透明,好了,話不多說,先上圖,看下效果:
image
看完之后我們來擼代碼。
(一):準備工作
因為我們這次要用kotlin來寫,所以先加好依賴
image
由于是demo,所以隨便找了個banner庫
image
準備基本完成,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<Any>
init {
initView()
}
fun initView() {
View.inflate(context, R.layout.layout_linkage_bg, this)
}
/**
* 初始化背景
*/
fun setBgRes(res: List<Any>) {
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<Any>, srcRes: List<Any>) {
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。