自定义阴影View

前言
公司要出新的版本,界面的风格有了一些改变,对于一些View要加一些阴影的风格,使用cardview以及elevation实现了阴影,但是达不到设计图的那种阴影效果,所以只有通过自定义了。
感谢lijiankun24,借鉴了对View自定义阴影的封装,对View设置阴影有了些认识
github地址:https://github.com/lijiankun24/ShadowLayout
实现
1.对View实现阴影效果是通过 mPaint.setShadowLayer(float radius, float dx, float dy, int shadowColor)
 radius : 模糊半径,radius越大越模糊,越小越清晰,当radius=0时,则阴影消失不见

  dx: 阴影在x轴的偏移距离,正值向右偏移,负值向左偏移

  dy:  阴影在y轴的偏移距离,正值向下偏移,负值向上偏移

  color:阴影的颜色
  • 代码

      /**
       * 初始化画笔
       */
      private fun initPaint() {
          //画笔重置
          mPaint.reset()
          //设置抗锯齿
          mPaint.isAntiAlias = true
          //设置透明度
          mPaint.color = Color.TRANSPARENT
          /**
           * 参数1  模糊半径  越大越模糊
           * 参数2  阴影在x轴的距离
           * 参数3  阴影在y轴的距离
           * 参数4  阴影颜色
           */
          mPaint.setShadowLayer(mShadowRadius, mShadowDx, mShadowDy, mShadowColor)
      
      
      }
    
2.setShadowLayer只对文字绘制阴影支持硬件加速,对其它的view不支持硬件加速,所以我们需要禁用硬件加速
 //关闭单个view的硬件加速
setLayerType(View.LAYER_TYPE_SOFTWARE, null)
this.setWillNotDraw(false)
3.自定义属性 包括阴影的颜色,模糊半径,偏移距离以及显示的位置
  • 自定义属性

     <declare-styleable name="ShadowLayout">
       <!--阴影颜色-->
      <attr name="shadowColor" format="color"/>
       <!--阴影距离-->
      <attr name="shadowRadius" format="dimension"/>
      <!--在x轴的阴影-->
      <attr name="shadowDx" format="dimension"/>
       <!--在y轴的阴影-->
      <attr name="shadowDy" format="dimension"/>
      <!--阴影的方向-->
      <attr name="shadowSide" format="integer"/>
      <!--阴影的形状-->
      <attr name="shadowShape" format="integer"/>
    
  • 读取用户自定义属性

       val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ShadowLayout)
       mShadowColor = typedArray.getColor(R.styleable.ShadowLayout_shadowColor, Color.BLACK)
       mShadowRadius = typedArray.getDimension(R.styleable.ShadowLayout_shadowRadius, 0f)
       mShadowDx = typedArray.getDimension(R.styleable.ShadowLayout_shadowDx, 0f)
       mShadowDy = typedArray.getDimension(R.styleable.ShadowLayout_shadowDy, 0f)
       mShadowSide = typedArray.getInt(R.styleable.ShadowLayout_shadowSide, 0)
       mShadowShape = typedArray.getInt(R.styleable.ShadowLayout_shadowShape, 0)
       typedArray.recycle()
    
4.当我们设置阴影的时候,是使用该View去包含某个子View,而阴影是有模糊半径的,所以View需要通过设置Padding来让阴影显示出来,让子View显示我们指定的一个区域内
	   /**
	     * 对View进行测量
	     */
	    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
	        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
	        val effect = mShadowRadius
	        var rectLeft = 0f
	        var rectTop = 0f
	        var rectRight = this.measuredWidth.toFloat()
	        var rectBottom = this.measuredHeight.toFloat()
	        var paddingLeft = 0f
	        var paddingRight = 0f
	        var paddingTop = 0f
	        var paddingBottom = 0f
	        when (mShadowSide) {
	            LEFT -> {
	                rectLeft = effect
	                paddingLeft = effect
	            }
	            RIGHT -> {
	                rectRight = this.measuredWidth - effect
	                paddingRight = effect
	            }
	            TOP -> {
	                rectTop = effect
	                paddingTop = effect
	            }
	            BOTTOM -> {
	                rectBottom = this.measuredHeight - effect
	                paddingBottom = effect
	            }
	            LEFT_RIGHT -> {
	                rectLeft = effect
	                paddingLeft = effect
	                rectRight = this.measuredWidth - effect
	                paddingRight = effect
	            }
	            LEFT_TOP -> {
	                rectLeft = effect
	                paddingLeft = effect
	                rectTop = effect
	                paddingTop = effect
	            }
	            LEFT_BOTTOM -> {
	                rectLeft = effect
	                paddingLeft = effect
	                rectBottom = this.measuredHeight - effect
	                paddingBottom = effect
	            }
	            RIGHT_TOP -> {
	                rectRight = this.measuredWidth - effect
	                paddingRight = effect
	                rectTop = effect
	                paddingTop = effect
	            }
	          RIGHT_BOTTOM ->{
	              rectRight = this.measuredWidth - effect
	              paddingRight = effect
	              rectBottom = this.measuredHeight - effect
	              paddingBottom = effect
	          }
	          TOP_BOTTOM ->{
	              rectTop = effect
	              paddingTop = effect
	              rectBottom = this.measuredHeight - effect
	              paddingBottom = effect
	          }
	         LEFT_TOP_BOTTOM ->{
	              rectLeft = effect
	              paddingLeft = effect
	              rectTop = effect
	              paddingTop = effect
	              rectBottom = this.measuredHeight - effect
	              paddingBottom = effect
	          }
	          LEFT_TOP_RIGHT ->{
	              rectLeft = effect
	              paddingLeft = effect
	              rectTop = effect
	              paddingTop = effect
	              rectRight = this.measuredWidth - effect
	              paddingRight = effect
	         }
	         RIGHT_LEFT_BOTTOM ->{
	             rectLeft = effect
	             paddingLeft = effect
	             rectRight = this.measuredWidth - effect
	             paddingRight = effect
	             rectBottom = this.measuredHeight - effect
	             paddingBottom = effect
	         }
	         else ->{
	             paddingLeft = effect
	             paddingRight = effect
	             paddingTop = effect
	             paddingBottom = effect
	             rectLeft = effect
	             rectRight = this.measuredWidth - effect
	             rectTop = effect
	             rectBottom = this.measuredHeight - effect
	        }
	
	      }
	        mRectF.left = rectLeft
	        mRectF.right = rectRight
	        mRectF.top = rectTop
	        mRectF.bottom = rectBottom
	        this.setPadding(paddingLeft.toInt(), paddingTop.toInt(), paddingRight.toInt(), paddingBottom.toInt())
	
	    }
5.在onDraw()中绘制子View显示的区域以及形状
    if (mShadowShape == SHAPE_RECENTAGE) {//绘制长方形
        canvas.drawRect(mRectF, mPaint)
     } else if (mShadowShape == SHAPE_OVAL) {
        canvas.drawCircle(mRectF.centerX(), mRectF.centerY(), Math.min(mRectF.width(), mRectF.height()) / 2, mPaint)
    }
6.完整代码
	import android.content.Context
	import android.graphics.Canvas
	import android.graphics.Color
	import android.graphics.Paint
	import android.graphics.RectF
	import android.util.AttributeSet
	import android.view.View
	import android.widget.RelativeLayout
	class ShadowLayout @JvmOverloads constructor(
	        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
	) : RelativeLayout(context, attrs, defStyleAttr) {
	
	    /**标识符 阴影在布局的显示位置*/
	    private val LEFT: Int = 0
	    private val RIGHT: Int = 1
	    private val TOP: Int = 2
	    private val BOTTOM: Int = 3
	    private val LEFT_RIGHT: Int = 4
	    private val LEFT_TOP: Int = 5
	    private val LEFT_BOTTOM: Int = 6
	    private val RIGHT_TOP: Int = 7
	    private val RIGHT_BOTTOM: Int = 8
	    private val TOP_BOTTOM: Int = 9
	    private val LEFT_TOP_BOTTOM: Int = 10
	    private val LEFT_TOP_RIGHT: Int = 11
	    private val RIGHT_LEFT_BOTTOM: Int = 12
	    private val ALL: Int = 13
	    /**标识符 阴影的形状*/
	    private val SHAPE_RECENTAGE = 0
	    private val SHAPE_OVAL = 1
	    /**阴影的颜色*/
	    private var mShadowColor: Int = Color.TRANSPARENT
	    /**阴影的距离*/
	    private var mShadowRadius: Float = 0.0f
	    /**阴影在x轴的距离*/
	    private var mShadowDx: Float = 0.0f
	    /**阴影在y轴的距离*/
	    private var mShadowDy: Float = 0.0f
	    /**阴影的方向*/
	    private var mShadowSide: Int = ALL
	    /**阴影的形状*/
	    private var mShadowShape: Int = SHAPE_RECENTAGE
	    /**画笔*/
	    private val mPaint by lazy {
	        Paint()
	    }
	    /***/
	    private val mRectF: RectF  by lazy {
	        RectF()
	    }
	
	    init {
	        initAttrs(attrs, context)
	        initPaint()
	    }
	
	    /**
	     * 初始化自定义属性
	     */
	    private fun initAttrs(attrs: AttributeSet?, context: Context) {
	        //关闭单个view的硬件加速
	        setLayerType(View.LAYER_TYPE_SOFTWARE, null)
	        this.setWillNotDraw(false)
	        val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ShadowLayout)
	        mShadowColor = typedArray.getColor(R.styleable.ShadowLayout_shadowColor, Color.BLACK)
	        mShadowRadius = typedArray.getDimension(R.styleable.ShadowLayout_shadowRadius, 0f)
	        mShadowDx = typedArray.getDimension(R.styleable.ShadowLayout_shadowDx, 0f)
	        mShadowDy = typedArray.getDimension(R.styleable.ShadowLayout_shadowDy, 0f)
	        mShadowSide = typedArray.getInt(R.styleable.ShadowLayout_shadowSide, 0)
	        mShadowShape = typedArray.getInt(R.styleable.ShadowLayout_shadowShape, 0)
	        typedArray.recycle()
	    }
	
	    /**
	     * 初始化画笔
	     */
	    private fun initPaint() {
	        //画笔重置
	        mPaint.reset()
	        //设置抗锯齿
	        mPaint.isAntiAlias = true
	        //设置透明度
	        mPaint.color = Color.TRANSPARENT
	        /**
	         * 参数1  模糊半径  越大越模糊
	         * 参数2  阴影在x轴的距离
	         * 参数3  阴影在y轴的距离
	         * 参数4  阴影颜色
	         */
	        mPaint.setShadowLayer(mShadowRadius, mShadowDx, mShadowDy, mShadowColor)
	
	
	    }
	
	
	    /**
	     * 对View进行测量
	     */
	    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
	        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
	        val effect = mShadowRadius
	        var rectLeft = 0f
	        var rectTop = 0f
	        var rectRight = this.measuredWidth.toFloat()
	        var rectBottom = this.measuredHeight.toFloat()
	        var paddingLeft = 0f
	        var paddingRight = 0f
	        var paddingTop = 0f
	        var paddingBottom = 0f
	        when (mShadowSide) {
	            LEFT -> {
	                rectLeft = effect
	                paddingLeft = effect
	            }
	            RIGHT -> {
	                rectRight = this.measuredWidth - effect
	                paddingRight = effect
	            }
	            TOP -> {
	                rectTop = effect
	                paddingTop = effect
	            }
	            BOTTOM -> {
	                rectBottom = this.measuredHeight - effect
	                paddingBottom = effect
	            }
	            LEFT_RIGHT -> {
	                rectLeft = effect
	                paddingLeft = effect
	                rectRight = this.measuredWidth - effect
	                paddingRight = effect
	            }
	            LEFT_TOP -> {
	                rectLeft = effect
	                paddingLeft = effect
	                rectTop = effect
	                paddingTop = effect
	            }
	            LEFT_BOTTOM -> {
	                rectLeft = effect
	                paddingLeft = effect
	                rectBottom = this.measuredHeight - effect
	                paddingBottom = effect
	            }
	            RIGHT_TOP -> {
	                rectRight = this.measuredWidth - effect
	                paddingRight = effect
	                rectTop = effect
	                paddingTop = effect
	            }
	          RIGHT_BOTTOM ->{
	              rectRight = this.measuredWidth - effect
	              paddingRight = effect
	              rectBottom = this.measuredHeight - effect
	              paddingBottom = effect
	          }
	          TOP_BOTTOM ->{
	              rectTop = effect
	              paddingTop = effect
	              rectBottom = this.measuredHeight - effect
	              paddingBottom = effect
	          }
	         LEFT_TOP_BOTTOM ->{
	              rectLeft = effect
	              paddingLeft = effect
	              rectTop = effect
	              paddingTop = effect
	              rectBottom = this.measuredHeight - effect
	              paddingBottom = effect
	          }
	          LEFT_TOP_RIGHT ->{
	              rectLeft = effect
	              paddingLeft = effect
	              rectTop = effect
	              paddingTop = effect
	              rectRight = this.measuredWidth - effect
	              paddingRight = effect
	         }
	         RIGHT_LEFT_BOTTOM ->{
	             rectLeft = effect
	             paddingLeft = effect
	             rectRight = this.measuredWidth - effect
	             paddingRight = effect
	             rectBottom = this.measuredHeight - effect
	             paddingBottom = effect
	         }
	         else ->{
	             paddingLeft = effect
	             paddingRight = effect
	             paddingTop = effect
	             paddingBottom = effect
	             rectLeft = effect
	             rectRight = this.measuredWidth - effect
	             rectTop = effect
	             rectBottom = this.measuredHeight - effect
	        }
	
	      }
	        mRectF.left = rectLeft
	        mRectF.right = rectRight
	        mRectF.top = rectTop
	        mRectF.bottom = rectBottom
	        this.setPadding(paddingLeft.toInt(), paddingTop.toInt(), paddingRight.toInt(), paddingBottom.toInt())
	
	    }
	
	    /**
	     * 对View进行绘制
	     */
	    override fun onDraw(canvas: Canvas) {
	        super.onDraw(canvas)
	        initPaint()
	        if (mShadowShape == SHAPE_RECENTAGE) {//绘制长方形
	            canvas.drawRect(mRectF, mPaint)
	        } else if (mShadowShape == SHAPE_OVAL) {
	            canvas.drawCircle(mRectF.centerX(), mRectF.centerY(), Math.min(mRectF.width(), mRectF.height()) / 2, mPaint)
	        }
	    }
	
	    public fun setShadowRadius(mShadeRadiu: Int) {
	        this.mShadowRadius = mShadowRadius
	        //重新对View进行布局
	        requestLayout()
	        //在主线程/非主线程进行刷新
	        postInvalidate()
	    }
	
	    public fun setShadowColor(mShadeColor: Int) {
	        this.mShadowColor = mShadeColor
	        //重新对View进行布局
	        requestLayout()
	        //在主线程/非主线程进行刷新
	        postInvalidate()
	    }
	
	
	}
7.使用
    <view.com.shadowlayoutall.ShadowLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="15dp"
        android:layout_marginRight="15dp"
        android:layout_marginTop="20dp"
        app:shadowColor="#12000000"
        app:shadowRadius="19dp"
        app:shadowShape="0"
        app:shadowSide="12">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="@android:color/white"/>
    </view.com.shadowlayoutall.ShadowLayout>
8.实现效果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值