android 简单实现沉浸式通知栏(透明通知栏)

 

一.今天来给大家分享下一种取巧的方式浸式通知栏,先实现布局图片从通知栏底部开始展示,如图:

android 简单实现沉浸式通知栏(透明通知栏)_第1张图片

1.首先在当前的activity的布局中最外层LinearLayout或RelativeLayout中设置android:fitsSystemWindows="false",

fitsSystemWindows设为false表示布局从通知栏底部开始画;如果为true,表示从通知栏下面开始画;

2.通过上面的方法后呢,但是通知栏不是透明的,通知栏会遮挡住上面的图片,此时要实现一个方法让通知栏保持透明,

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    //使通知栏透明
    this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
}

3.现在通知栏不会遮挡住图片了,但是此时又有个问题了,返回箭头有一半是在通知栏的下面,一般正常手机的通知栏高度是20dp,也可以用另一种通过代码获得通知栏高度,

fun getStateBarHeight(context: Context): Int {
    var result = 0
    val resourceId = context.resources.getIdentifier("status_bar_height", "dimen", "android")
    if (resourceId > 0) {
        result = context.resources.getDimensionPixelSize(resourceId)
    }
    return result
}

现在可以在activity初始化返回箭头后,给返回箭头加上一个marginTop,

home_back_linear = findViewById(R.id.home_back_linear)
var backParams = home_back_linear?.layoutParams
var params1 = FrameLayout.LayoutParams(backParams)
params1.topMargin = titleHeight
home_back_linear?.layoutParams = params1

这样箭头放在通知栏之下正常位置了。

二.现在来通知栏和标题栏的背景颜色值根据scrollview滚动的位置实现颜色渐变,如图:

android 简单实现沉浸式通知栏(透明通知栏)_第2张图片android 简单实现沉浸式通知栏(透明通知栏)_第3张图片




    

        

        

        

        

        
            
            
        

        
    


上面有个关键的组件View,因为布局是从屏幕的最顶端开始画起的,所以设置一个view来当做通知栏的View,主要是用来控制状态的背景颜色,然后在代码中初始化status_bar_black_alpha时设置高度:

status_bar_black_alpha = findViewById(R.id.status_bar_black_alpha) as View
var statusParams = status_bar_black_alpha?.layoutParams
statusParams?.height = titleHeight
status_bar_black_alpha?.layoutParams = statusParams

上面还有个head_linear要设置跟返回箭头一样的marginTop高度:

head_linear = findViewById(R.id.head_linear)
var headParams = head_linear?.layoutParams
var params = FrameLayout.LayoutParams(headParams)
params.topMargin = titleHeight
head_linear?.layoutParams = params

然后现在来看看通知栏的颜色背景值根据scrollview滚动渐变:

先看实现自定义的scrollview,这里有个下拉图片放大的功能,模仿以前的QQ qq空间的功能:

package com.ctrip.ibu.ddt.view

import android.animation.ObjectAnimator
import android.content.Context
import android.util.AttributeSet
import android.util.DisplayMetrics
import android.util.TypedValue
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.LinearLayout
import android.widget.RelativeLayout
import android.widget.ScrollView
import com.ctrip.ibu.ddt.utils.AndroidUtil

/**
 * 初始化获取高度值,并记录
 * @param context
 * @param attrs
 *   jgdeng  2018/06/17
 */
class PullToZoomScrollView(context: Context, attrs: AttributeSet) : ScrollView(context, attrs) {
    private var isonce: Boolean = false//加载该View的布局时是否是第一次加载,是第一次就让其实现OnMeasure里的代码

    private var mParentView: LinearLayout? = null//布局的父布局,ScrollView内部只能有一个根ViewGroup,就是此View
    private var mTopView: ViewGroup? = null//这个是带背景的上半部分的View,下半部分的View用不到的
    private var mTopView1: ViewGroup? = null

    private val mScreenHeight: Int//整个手机屏幕的高度,这是为了初始化该View时设置mTopView用的
    private val mTopViewHeight: Int//这个就是mTopView的高度
    private val mTopViewWidth: Int

    private var mCurrentOffset = 0//当前右侧滚条顶点的偏移量。ScrollView右侧是有滚动条的,当下拉时,
    //滚动条向上滑,当向下滑动时,滚动条向下滑动。

    private var oa: ObjectAnimator? = null//这个是对象动画,这个在本View里很简单,也很独立,就在这里申明一下,后面有两个方法

    private var startY = 0f//向下拉动要放大,手指向下滑时,点击的第一个点的Y坐标
    private var isBig: Boolean = false//是否正在向下拉放大上半部分View
    private var isTouchOne: Boolean = false//是否是一次连续的MOVE,默认为false,
    //在MoVe时,如果发现滑动标签位移量为0,则获取此时的Y坐标,作为起始坐标,然后置为true,为了在连续的Move中只获取一次起始坐标
    //当Up弹起时,一次触摸移动完成,将isTouchOne置为false
    private var distance = 0f//向下滑动到释放的高度差
    private var isScrolledToTop: Boolean = true
    private var marginBottom: Int

    init {
        this.overScrollMode = View.OVER_SCROLL_NEVER
        val wm = getContext().getSystemService(Context.WINDOW_SERVICE) as WindowManager
        val metrics = DisplayMetrics()
        wm.defaultDisplay.getMetrics(metrics)
        mScreenHeight = metrics.heightPixels
        mTopViewWidth = metrics.widthPixels
//        mTopViewHeight = mScreenHeight / 2 - TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 90f, context.resources.displayMetrics).toInt()
        mTopViewHeight = AndroidUtil.dp2px(context, 250f)
        marginBottom = AndroidUtil.dp2px(context, 70f)
    }

    /**
     * 将记录的值设置到控件上,并只让控件设置一次
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        if (!isonce) {
            mParentView = this.getChildAt(0) as LinearLayout
            mTopView = mParentView?.getChildAt(0) as ViewGroup
            mTopView1 = mTopView?.getChildAt(0) as ViewGroup
//            mTopView?.layoutParams?.height = mTopViewHeight
            isonce = true
        }
    }

    override fun onTouchEvent(ev: MotionEvent): Boolean {
        val action = ev.action
        when (action) {
            MotionEvent.ACTION_DOWN -> {
            }
            MotionEvent.ACTION_MOVE -> {
                if (mCurrentOffset <= 0) {
                    if (!isTouchOne) {
                        startY = ev.y
                        isTouchOne = true
                    }
                    distance = ev.y - startY
                    if (distance > 0) {
                        isBig = true
                        setT((-distance).toInt() / 4)
                    }
                }
            }
            MotionEvent.ACTION_UP -> {
                if (isBig) {
                    reset()
                    isBig = false
                }
                isTouchOne = false
            }
        }
        return super.onTouchEvent(ev)
    }

    /**
     * 对象动画要有的设置方法
     * @param t
     */
    fun setT(t: Int) {
        scrollTo(0, 0)
        if (t < 0) {
            var params = RelativeLayout.LayoutParams(mTopView1?.layoutParams)
            params?.leftMargin = t / 2
            params?.rightMargin = t / 2
            mTopView1?.layoutParams = params

            mTopView?.layoutParams?.height = mTopViewHeight - t
            mTopView?.requestFocus()
        }
    }

    /**
     * 主要用于释放手指后的回弹效果
     */
    private fun reset() {
        if (oa != null && oa?.isRunning == true) {
            return
        }
        oa = ObjectAnimator.ofInt(this, "t", (-distance).toInt() / 4, 0)
        oa?.duration = 150
        oa?.start()
    }

    /**
     * 这个是设置向上滑动时,上半部分View滑动速度让其小于下半部分
     * @param l
     * @param t
     * @param oldl
     * @param oldt
     */
    override fun onScrollChanged(l: Int, t: Int, oldl: Int, oldt: Int) {
        super.onScrollChanged(l, t, oldl, oldt)
        mCurrentOffset = t//右边滑动标签相对于顶端的偏移量
        //当手势上滑,则右侧滚动条下滑,下滑的高度小于TopView的高度,则让TopView的上滑速度小于DownView的上滑速度
        //DownView的上滑速度是滚动条的速度,也就是滚动的距离是右侧滚动条的距离
        //则TopView的速度要小,只需要将右侧滚动条的偏移量也就是t缩小一定倍数就行了。我这里除以2速度减小1倍
        if (t <= mTopViewHeight && t >= 0 && !isBig) {
//            mTopView?.translationY = (t / 2).toFloat()//使得TopView滑动的速度小于滚轮滚动的速度
        }
        if (isBig) {
            scrollTo(0, 0)
        }
    }

    var onScrolledToTop: (isScrolledToTop: Boolean?, scrollY: Int) -> Unit = { _: Boolean?, _: Int -> }
    override fun onOverScrolled(scrollX: Int, scrollY: Int, clampedX: Boolean, clampedY: Boolean) {
        super.onOverScrolled(scrollX, scrollY, clampedX, clampedY)
        isScrolledToTop = if (scrollY == 0) true else false

        onScrolledToTop(isScrolledToTop, scrollY)
    }
}

主要是通过上面的onScrolledToTop()方法来获取滚动的高度的:

        var drawable = head_linear?.background?.mutate()
        var status_bar = status_bar_black_alpha?.background?.mutate()
        scrollView?.onScrolledToTop = { isScrolledToTop, scrollY ->
            println("scrollY--------->$scrollY")
            val top = banner_layout?.getChildAt(1)?.top
                    ?: 0
            if (scrollY > top) {
                val topTxt = top + (play_city_name_layout?.height ?: 0)
                home_back?.setTextColor(this.resources.getColor(R.color.ddt_black))
                if (topTxt >= scrollY) {
                    var height = scrollY - topTxt
                    drawable?.alpha = height * 255 / (topTxt - top)
                    status_bar?.alpha = height * 255 / (topTxt - top)
                } else {
                    drawable?.alpha = 255
                    status_bar?.alpha = 255
                }
                if (flagBackground) {
                    flagBack = false
                    head_linear?.visibility = View.VISIBLE
                    status_bar_black_alpha?.visibility = View.VISIBLE
                }
            } else {
                if (!flagBackground) {
                    flagBack = true
                    head_linear?.visibility = View.GONE
                    status_bar_black_alpha?.visibility = View.GONE
                    home_back?.setTextColor(this.resources.getColor(R.color.white))
                }
            }
        }

主要是上面drawable?.alpha和status_bar?.alpha 来实现背景透明渐变效果。

三.通过以上的activity布局中status_bar_black_alpha,可以变相的随意设置通知栏的颜色背景,可以实现通知栏背景和标题栏背景一样,还有从通知栏顶部到标题栏颜色值的渐变,

 




    

        

            

            
        
    

从上面的布局只需要设置head_status_linear的背景颜色就ok的。

总结:以上的方法主要是3个关键点android:fitsSystemWindows="false",this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

然后就是替代通知栏View的status_bar_black_alpha。理解了这三个关键点,就非常的简单可以实现了。

你可能感兴趣的:(android 简单实现沉浸式通知栏(透明通知栏))