Mask MaskView

本文深入解析CALayer中的mask与View中的maskView的使用与区别,通过实例展示如何创建遮罩层与被遮罩层,并实现特定视觉效果。重点介绍了mask属性的应用场景,以及如何在不同视图组件间建立遮罩关系。
关于CALayer中的mask(遮罩) 和 View中的maskView(遮罩view)的理解;

1.首先说一下关于我对遮罩的理解吧

  第一:遮罩我感觉就是当遮罩层的alpha通道为0的时候就是透明的,反之则显示,只是背景色变为被遮罩层的背景色,(感觉跟PS中的蒙版差不多原理);

  第二:例子,假设,我这边有A,B2个View,其中A的背景色为纯绿色,B为label,text=@“我是遮罩层”;

      然后就可以A.maskView = B;那么就把B看做是A的遮罩层(MaskView),最终的显示结果是,显示的字为绿色的“我是遮罩层”,

  额,大概的效果就是这样子,具体的话,代码来实现以下吧

  maskView:

 //遮罩背景层,相当于A层
    UIView *maskView = [[UIView  alloc]initWithFrame:self.gradientLabel.frame];
    maskView.backgroundColor = [UIColor redColor];
    [self.view addSubview:maskView];
    [self.view addConstraints:[self getLayoutConstraintWithView:maskView SupperView:self.gradientLabel Inset:UIEdgeInsetsMake(0, 30, 0, 120)]];
    
    //遮罩层 相当于B层
    UILabel *newLabel = [[UILabel alloc]init];
    newLabel.numberOfLines = 0;
    newLabel.text = @"LabelLabelLabelLabel";
    //关于这个,遮罩层要不要ADD进父视图,额,我这边的实验结果是可以不ADD进去,当然ADD进父视图貌似也没有影响
//    [self.view addSubview:newLabel];
//    [self.view addConstraints:[self getLayoutConstraintWithView:newLabel SupperView:self.gradientLabel Inset:UIEdgeInsetsZero]];
    maskView.maskView = newLabel;
    
    //设置了mask之后,一定要记住,重新设置B层的frame,此时可以看成B层是A层的subView来定位
    CGRect frame = self.gradientLabel.bounds;
    frame.origin.x = -30;
    frame.size.width += 30;
    newLabel.frame = frame;

显示结果:

 

关于layer的Mask

 //可以定义layer
//    CALayer *layer = [CALayer layer];
//    layer.frame = CGRectMake(50, 50, 100, 200);
//    layer.backgroundColor = [UIColor greenColor].CGColor;
//    [self.view.layer insertSublayer:layer atIndex:0];
    
    //也可以定义imageView
    UIImageView *image = [[UIImageView alloc]initWithFrame:CGRectMake(50, 50, 100, 200)
    ];
    image.image = [UIImage imageNamed:@"app1.png"];
    [self.view addSubview:image];

    UILabel *label = [[UILabel alloc]init];
    label.frame = CGRectMake(0, 0, 100, 0);
    label.font = [UIFont systemFontOfSize:25];
    label.text = @"我是遮罩层";
    label.numberOfLines = 0;
    [label sizeToFit];
    //记住,如果用的是layer的mask,那么遮罩层一定要ADD到父视图中
    [self.view addSubview:label];
    
    image.layer.mask = label.layer;
    label.frame = image.layer.bounds;

显示结果:

 

这个关于Mask(遮罩层)大致需要注意的就是这些了,

那么说一说我为什么会用到这个属性吧,因为我这边需要用到一个颜色渐变的Label,(额,网上别人也有,只是当时没理解,现在自己做一遍的话,理解透彻点)

那么这里就需要用到一个渐变的类CAGradientLayer

 CAGradientLayer *layer = [[CAGradientLayer alloc]init];
    layer.frame = CGRectMake(50, 50, 100, 200);
    layer.colors = @[(id)[UIColor greenColor].CGColor,(id)[UIColor blueColor].CGColor,(id)[UIColor yellowColor].CGColor];
    layer.startPoint = CGPointMake(0, 1);
    layer.endPoint = CGPointMake(1,1);
    [self.view.layer insertSublayer:layer atIndex:0];
    
    //也可以定义imageView
//    UIImageView *image = [[UIImageView alloc]initWithFrame:CGRectMake(50, 50, 100, 200)
//    ];
//    image.image = [UIImage imageNamed:@"app1.png"];
//    [self.view addSubview:image];

    UILabel *label = [[UILabel alloc]init];
    label.frame = CGRectMake(0, 0, 100, 0);
    label.font = [UIFont systemFontOfSize:25];
    label.text = @"我是遮罩层我是遮罩层";
    label.numberOfLines = 0;
    [label sizeToFit];
    //记住,如果用的是layer的mask,那么遮罩层一定要ADD到父视图中
    [self.view addSubview:label];
    
    layer.mask = label.layer;
    label.frame = layer.bounds;

效果:

额 好了 到此结束了 

package com.faltenreich.skeletonlayout import android.content.Context import android.graphics.Canvas import android.util.AttributeSet import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.FrameLayout import com.faltenreich.skeletonlayout.mask.SkeletonMask import com.faltenreich.skeletonlayout.mask.SkeletonMaskFactory import com.faltenreich.skeletonlayout.mask.SkeletonShimmerDirection open class SkeletonLayout @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0, originView: View? = null, private val config: SkeletonConfig = SkeletonConfig.default(context) ) : FrameLayout(context, attrs, defStyleAttr), Skeleton, SkeletonStyle by config { internal constructor( originView: View, config: SkeletonConfig ) : this(originView.context, null, 0, originView, config) private var mask: SkeletonMask? = null private var isSkeleton: Boolean = false private var isRendered: Boolean = false private var maskView: ViewGroup? = null init { attrs?.let { val typedArray = context.obtainStyledAttributes(it, R.styleable.SkeletonLayout, 0, 0) this.maskColor = typedArray.getColor(R.styleable.SkeletonLayout_maskColor, maskColor) this.maskCornerRadius = typedArray.getDimensionPixelSize(R.styleable.SkeletonLayout_maskCornerRadius, maskCornerRadius.toInt()).toFloat() this.showShimmer = typedArray.getBoolean(R.styleable.SkeletonLayout_showShimmer, showShimmer) this.shimmerColor = typedArray.getColor(R.styleable.SkeletonLayout_shimmerColor, shimmerColor) this.shimmerDurationInMillis = typedArray.getInt(R.styleable.SkeletonLayout_shimmerDurationInMillis, shimmerDurationInMillis.toInt()).toLong() this.shimmerDirection = SkeletonShimmerDirection.valueOf(typedArray.getInt(R.styleable.SkeletonLayout_shimmerDirection, shimmerDirection.ordinal)) ?: DEFAULT_SHIMMER_DIRECTION this.shimmerAngle = typedArray.getInt(R.styleable.SkeletonLayout_shimmerAngle, shimmerAngle) this.showPulse = typedArray.getBoolean(R.styleable.SkeletonLayout_showPulse, showPulse) this.pulseDurationInMillis = typedArray.getInt(R.styleable.SkeletonLayout_pulseDurationInMillis, pulseDurationInMillis.toInt()).toLong() this.pulseMinAlpha = typedArray.getFloat(R.styleable.SkeletonLayout_pulseMinAlpha, pulseMinAlpha) this.pulseMaxAlpha = typedArray.getFloat(R.styleable.SkeletonLayout_pulseMaxAlpha, pulseMaxAlpha) val maskLayout = typedArray.getResourceId(R.styleable.SkeletonLayout_maskLayout, 0) if (maskLayout != 0) { this.maskLayout = maskLayout } typedArray.recycle() } config.addValueObserver(::invalidateMask) originView?.let { view -> addView(view) } maskLayout?.let { maskLayout -> val maskView = runCatching { LayoutInflater.from(this.context).inflate(maskLayout, null, false) as ViewGroup }.getOrElse { Log.e(tag(), "Failed to create mask template") return@let } maskView.visibility = GONE addView(maskView) this.maskView = maskView } } override fun showOriginal() { isSkeleton = false if (childCount > 0) { views().forEach { it.visibility = VISIBLE } maskView?.visibility = GONE mask?.stop() mask = null } } override fun showSkeleton() { isSkeleton = true if (isRendered) { if (childCount > 0) { hideAllViews() setWillNotDraw(false) invalidateMask() mask?.invalidate() } else { Log.i(tag(), "No views to mask") } } } override fun isSkeleton(): Boolean = isSkeleton override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { super.onLayout(changed, left, top, right, bottom) isRendered = true if (isSkeleton) { showSkeleton() } } override fun onVisibilityChanged(changedView: View, visibility: Int) { super.onVisibilityChanged(changedView, visibility) mask?.invalidate() } override fun onWindowFocusChanged(hasWindowFocus: Boolean) { super.onWindowFocusChanged(hasWindowFocus) when (hasWindowFocus) { true -> mask?.start() false -> mask?.stop() } } override fun onAttachedToWindow() { super.onAttachedToWindow() if (isRendered) { invalidateMask() mask?.start() } } override fun onDetachedFromWindow() { super.onDetachedFromWindow() mask?.stop() } override fun onDraw(canvas: Canvas) { mask?.draw(canvas) } private fun invalidateMask() { if (isRendered) { mask?.stop() mask = null if (isSkeleton) { if (width > 0 && height > 0) { mask = SkeletonMaskFactory .createMask(this, config) .also { mask -> mask.mask( viewGroup = maskView ?: this, maskCornerRadius = maskCornerRadius ) } } else { Log.e(tag(), "Failed to mask view with invalid width and height") } } } else { Log.e(tag(), "Skipping invalidation until view is rendered") } } private fun hideAllViews() { if (maskView == null) { views().forEach { it.visibility = INVISIBLE } } else { maskView?.visibility = INVISIBLE views().forEach { if (it != maskView) it.visibility = GONE } } } companion object { val DEFAULT_MASK_COLOR = R.color.skeleton_mask const val DEFAULT_MASK_CORNER_RADIUS = 8f const val DEFAULT_SHIMMER_SHOW = true val DEFAULT_SHIMMER_COLOR = R.color.skeleton_shimmer const val DEFAULT_SHIMMER_DURATION_IN_MILLIS = 2000L val DEFAULT_SHIMMER_DIRECTION = SkeletonShimmerDirection.LEFT_TO_RIGHT const val DEFAULT_SHIMMER_ANGLE = 0 const val DEFAULT_PULSE_SHOW = false const val DEFAULT_PULSE_DURATION_IN_MILLIS = 1000L const val DEFAULT_PULSE_MIN_ALPHA = 0.3f const val DEFAULT_PULSE_MAX_ALPHA = 1.0f } } 解释这个代码
09-11
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值