教你用kotlin擼一個背景透明度聯動的banner

教你用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。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,820評論 25 708
  • 為齊心協力打好創城“攻堅戰”,營造“創城”良好交通秩序,自全面打響城市道路交通環境整治提升行動“攻堅戰”以來,市中...
    8ed683847d47閱讀 871評論 0 1
  • 舟山——海天佛國,漁都港城 東方和園公寓位于舟山群島新區朱家尖鎮中心大洞岙,金沙大道和慶豐路口,與普陀山景區隔海相...
    jenny不是杰呢閱讀 751評論 0 1
  • 富人的理財習慣 首先,要努力賺錢,累積“第一桶金”對于鮮有意外“橫財”的職場中人來說,一定要把提高收入列為最重要的...
    院子2554閱讀 161評論 0 0
  • 總是無休止的壓抑,無聲的疼痛,無盡的掙扎。 生而為人學會的第一件事大概就是討好,心底是無盡的不安全感。 找到真愛的...
    啦啦啦hh閱讀 155評論 0 0