阅读 514

教你用kotlin撸一个背景透明度联动的banner

      最近因为接到个需求:banner分成前景和背景2层,背景随着前景的横向切换而变换透明度,透明度的变换具体是,消失的上一张慢慢变透明,同时要出现的下一张慢慢变不透明,好了,话不多说,先上图,看下效果:


看完之后我们来撸代码。

(一):准备工作
  • 因为我们这次要用kotlin来写,所以先加好依赖


由于是demo,所以随便找了个banner库


准备基本完成,let's go。

(二):先说下思路,我准备先把背景做出来,布局中分别有3个imageview,分别代表前一张、当前和下一张,然后监听前景的切换而变换背景的透明度

  1. 先做背景

    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)
    }
}复制代码

怎么样,是不是灰常简单?赶紧试试吧。

代码地址在这:传送门

原创,转载请标明来源,3Q。


文章分类
Android
文章标签