Android clipping

Clipping

在Android中如果多个view嵌套的会引起overdraw,很多时候一些view被覆盖了,对用户是不可见的,但是依然会进行绘制,这个时候使用clipping来进行对不可见区域进行裁剪,可以减少overdraw提高gpu的效率。
如下图所示:使用clipping rectangle就可以实现对view的裁剪。

使用clipping能达到的效果

clipping的使用也很简单:
1、裁剪出想要渲染的部分

canvas.clipRect(clipRectLeft,clipRectTop, clipRectRight,clipRectBottom)

如下只想渲染出300*300大小的区域

val rect2 = Rect(100, 100, 400, 400)canvas.clipRect(rect2)canvas.drawBitmap(bgBitmap, 0f, 0f, null)

2、裁剪出不需要渲染的部分

canvas.clipOutRect(clipRectLeft,clipRectTop, clipRectRight,clipRectBottom)

如下抠掉中间300*300的区域

        val rect2 = Rect(100, 100, 400, 400)if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {canvas.clipRect(rect2,Region.Op.DIFFERENCE)} else {canvas.clipOutRect(rect2)}canvas.drawBitmap(bgBitmap, 0f, 0f, null)

可以定义一个剪辑区域,并保存该状态。 然后平移画布,添加剪辑区域并旋转。 做一些绘图后,可以恢复原来的裁剪状态,可以继续做不同的平移和倾斜变换,如图所示。

clip方法

clip和clipOut方法的区别:通过clip的区域是显示的区域,通过clipOut方法是把该区域不显示。

实例

其实是两张图片叠加在一起形成的,后面是一个全屏的背景图,加上一个椭圆柱。

两张图片叠加的部分对用户不可见,属于过度绘制区,为了减少过度绘制对性能的影响可以用clipping方法对重叠的部分进行裁剪。
裁剪
可以先渲染出底部圆柱,然后再从背景中抠出底部区域的大小不用渲染,最后渲染背景:

上图中黑色区域代表clipOut区域
通过计算出底部圆柱的高度和宽度来对背景图进行一次裁剪这里自定义一个ClipImageDrawable继承了Drawable

class ClippedImageDrawable(context: Context) : Drawable() {companion object {private const val TAG = "ClippedImageDrawable"}private var bgBitmap: Bitmap =BitmapFactory.decodeResource(context.resources, R.drawable.book_detail_bg)private val shelfBitmap = BitmapFactory.decodeResource(context.resources, R.drawable.bookshelf)override fun draw(canvas: Canvas) {Log.d(TAG, "width ${bounds.width()}  height ${bounds.height()}")bounds.width()val top = 0.71 * bounds.height()val bottom = bounds.height()val rect = Rect(0, top.toInt(), shelfBitmap.width, bottom)canvas.drawBitmap(shelfBitmap, 0f, top.toFloat(), null)if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {canvas.clipRect(rect, Region.Op.DIFFERENCE)} else {canvas.clipOutRect(rect)}canvas.drawBitmap(bgBitmap, 0f, 0f, null)}override fun setAlpha(alpha: Int) {}override fun setColorFilter(colorFilter: ColorFilter?) {}override fun getOpacity(): Int {return PixelFormat.TRANSLUCENT}
}

上面代码中先拿到背景和圆柱的bitmap对象,然后再背景图中裁剪出一个跟圆柱大小的rect,再执行

       if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {canvas.clipRect(rect, Region.Op.DIFFERENCE)} else {canvas.clipOutRect(rect)}

上面的canvas.clipRect(rect, Region.Op.DIFFERENCE)就是把rect和背景相交的部分裁剪,在Build.VERSION_CODES.O以上的版本可以使用canvas.clipOutRect(rect)方法。
注意上面代码的顺序:
1、先用canvas.drawBitmap底部的圆柱。
2、使用clip裁剪出圆柱图片的rect。
3、最后再绘制背景bitmp
在activity中用一下方式使用

val drawable = ClippedImageDrawable(this)
val imageView = ImageView(this).apply {val param = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT)scaleType = ImageView.ScaleType.FIT_XYlayoutParams = paramsetImageDrawable(drawable)
}
setContentView(imageView)

给imageView设置drawable,效果如下:

看上面效果图中,圆柱和背景的相交处有两个空白,这是因为圆柱图片是一个长方形并且顶部有两处空白,这样绘制出来就达不到设计给的效果。这里的原因是在使用clip的时候用的是一个rect形状,要达到背景和圆柱融合的效果,使用rect形状是不行的。
可以使用clipoutPath方法来裁剪出想要的区域。
上面圆柱顶部的弧度可以用二次赛贝尔曲线来实现https://www.tweenmax.com.cn/tool/bezier/。

绘制二阶Bezier曲线
/** * 从上一个点开始,绘制二阶Bezier曲线 * (x1,y1)为控制点, (x2,y2)为终点 * 如果之前没有调用过 moveTo(),则默认从 (0,0)作为起点绘制。 /
public void quadTo(float x1, float y1, float x2, float y2) ;
/
* * 和quadTo相同,只不过这里是使用的是相对坐标。 */
public void rQuadTo(float dx1, float dy1, float dx2, float dy2)

结合path和贝塞尔曲线绘制出底部的区域
path.moveTo(0f, top.toFloat()+40)
path.quadTo(
(bounds.width() / 2).toFloat(), top.toFloat()-50, bounds.width().toFloat(),
(top + 60).toFloat()
)
path.lineTo(bounds.width().toFloat(), bounds.height().toFloat())
path.lineTo(0f, bounds.height().toFloat())

如下图对底部区域执行clipOut之后

最终实现代码

package com.example.android.clippingexampleimport android.content.Context
import android.graphics.*
import android.graphics.drawable.Drawable
import android.os.Build
import android.util.Log/*** create by 胡汉君* date 2021/8/21 17:39*/
class ClippedImageDrawable(context: Context) : Drawable() {companion object {private const val TAG = "ClippedImageDrawable"}private var bgBitmap: Bitmap =BitmapFactory.decodeResource(context.resources, R.drawable.book_detail_bg)private val shelfBitmap = BitmapFactory.decodeResource(context.resources, R.drawable.bookshelf)private val path = Path()override fun draw(canvas: Canvas) {Log.d(TAG, "width ${bounds.width()}  height ${bounds.height()}")path.reset()bounds.width()val top = 0.71 * bounds.height()path.moveTo(0f, top.toFloat()+40)path.quadTo((bounds.width() / 2).toFloat(), top.toFloat()-50, bounds.width().toFloat(),(top + 60).toFloat())path.lineTo(bounds.width().toFloat(), bounds.height().toFloat())path.lineTo(0f, bounds.height().toFloat())path.close()canvas.drawBitmap(shelfBitmap, 0f, top.toFloat(), null)if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {canvas.clipPath(path, Region.Op.DIFFERENCE)} else {canvas.clipOutPath(path)}canvas.drawBitmap(bgBitmap, 0f, 0f, null)}override fun setAlpha(alpha: Int) {}override fun setColorFilter(colorFilter: ColorFilter?) {}override fun getOpacity(): Int {return PixelFormat.TRANSLUCENT}
}

效果

参考
1、https://medium.com/android-news/simplifying-layouts-with-layer-list-drawables-2f750ea1504e
2、https://blog.zen.ly/implementing-custom-drawables-part-1-5530a98cefc9
3、https://developer.android.google.cn/codelabs/advanced-android-kotlin-training-clipping-canvas-objects?hl=vi#0

Android clippling使用相关推荐

  1. Unity5.6+ 导出Android 应用程序apk的环境配置及导出过程

    首先下载并安装安卓SDK和java的JDK 安卓sdk下载: http://www.android-studio.org/ 也可以在这下载: 链接:http://pan.baidu.com/s/1bp ...

  2. Android 的NDK的Makefile编写

    Android.mk 是google根据Linux GNU Makefile精简编译脚本.具体来说:这就是GNU Makefile的一小部分. 举一个简单例子: LOCAL_PATH := $(cal ...

  3. Android Animation (安卓动画)概念简介

    Android Animation Android 四种动画分别为逐帧动画和补间动画.属性动画.过渡动画: Frame Animation (逐帧动画) 实现方式:xml 和 Java代码 图片跳转的 ...

  4. 基于Android和SpringBoot的购物App

    (Shopping)购物应用商城 本软件使用Android和SpringBoot.JavaWeb技术实现:并结合百度LBS平台的SDK.支付宝App支付客户端SDK.MobTech的ShareSDK: ...

  5. Android数据持久化:SharePreference

    SharePreference:作为Android数据持久化的一种,具有一定的便捷性,适合存储一些体积小的数据. 存储数据方式:键值对的方式,类似于Map: 利用SharePreference.Edi ...

  6. Android数据持久化:文件存储

    数据持久化: 数据可分为瞬时数据和关键数据.保存在内存之中的数据是瞬时数据,而对于一些关键性数据,后期需要持续使用的,应当保存在存储设备中: 持久化保存方式: 文件存储.SharePreference ...

  7. Android Studio中RecycerView依赖库加载问题

    依赖包导入思考: 参考资料:recycleview导包问题 打开修改本项目中的build.gradle; 切勿着急添加包,应当提前查看其中的版本号(因为加载的v7包要和其版本保持一致性): 例如: 因 ...

  8. Android布局优化之include、merge、ViewStub

    include:引入重复使用的相同布局 merge:减少include布局的层级,将子元素直接添加到merge标签的parent中 ViewStub:其实就是一个宽高都为0的一个View,它默认是不可 ...

  9. Android动画之帧动画和补间动画

    Android系统提供三种动画:帧动画.补间动画和属性动画.这里先分析总结帧动画和补间动画. FrameAnimation 帧动画,通俗来说就是按照图片动作顺序依次播放来形成动画,创建帧动画可以用 x ...

最新文章

  1. [2018-08-03] python开发个人资源共享网--第一天
  2. 用python绘制柱状图标题-零基础用 Python 画图表,让你的论文高一个逼格
  3. php 多只能上传20个文件解决办法,修改php.ini 的max_file_uploads
  4. 为什么谐振时电抗为0_220KV/110KVCVT互感器串联谐振试验装置技术方案
  5. 【junit】junit4单元测试eclipse
  6. 【C++ grammar】数据类型转换、列表初始化
  7. Hadoop--克隆3x虚拟机
  8. 【LeetCode】7. Reverse Integer
  9. 动软代码生成器-模板修改,模型修改
  10. 小程序加入人脸识别_微信小程序怎么实现人脸识别
  11. php weka,使用Weka进行数据挖掘
  12. Leetcode|DFS|130. 被围绕的区域
  13. Yandex-好用的以图搜图网站
  14. 无线传感器网络技术原理及应用 许毅编著(转载请注明出处---https://www.cnblogs.com/qingl)...
  15. Vue引入静态图片的两种方式
  16. 小心肝队-冲刺日志(第二天)
  17. vue常用的时间、手机号等的格式化方法
  18. NYT assail military militant
  19. iOS开发:获取设备的MAC地址
  20. 树莓派控制WS2812屏幕(python)

热门文章

  1. Possibly consider using a shorter maxLifetime value.问题处理
  2. linux centos7 iso镜像下载,CentOS Linux 7.9 (2009) iso镜像下载
  3. S/4 HANA标准表MARC增强字段
  4. 阿伟的学习计划5.28-6.29
  5. 如何将图片压缩到15k以下?教你一键压缩图片的大小
  6. lombok插件的使用
  7. 看盘做短线的10种方法(转)
  8. CSS实现背景图片高斯模糊效果
  9. wcp mysql 密码_[wcp部署]Linux(Ubuntu)安装部署WCP
  10. 深入理解Java虚拟机(周志明第三版)- 第十二章:Java内存模型与线程