利用 2D 图形和 PorterDuffXferMode 等实现被遮罩的图片
来源:互联网 发布:ios8.4.1完美越狱mac 编辑:程序博客网 时间:2024/06/05 03:16
图片的遮罩就是将裁剪遮罩应用于图片或形状,定义应用中另一张图片的可见边界。
利用 2D 图形和 PorterDuffXferMode,可以将各种遮罩应用于某张位图。
第一张效果图:
其基本步骤:
1. 创建一个可变的空白 Bitmap 实例,以及在其中绘图的 Canvas。
2. 首先在 Canvas 上画好遮罩模式。
3. 将 PorterDuffXferMode 应用到 Paint 上。
4. 用传输模式将原图绘制到 Canvas 上。
其中的关键是 PorterDuffXferMode,它会考虑到 Canvas 中已有的数据的状态和应用到当前操作的图形数据的状态。
第一中方法实现遮罩,使用图片作为 BitmapShader 将内容绘制到另一个元素中。通过这种方式,就可以将图片像素视为用于绘制形状或者元素的“颜色”,这些形状或者元素将组成遮罩图片。
RoundedCornerImageView.java :
<span style="font-size:18px;">package com.scxh.imagecover;import android.content.Context;import android.graphics.Bitmap;import android.graphics.BitmapShader;import android.graphics.Canvas;import android.graphics.Matrix;import android.graphics.Paint;import android.graphics.RectF;import android.graphics.Shader;import android.util.AttributeSet;import android.view.View;public class RoundedCornerImageView extends View{ private Bitmap mImage; private Paint mBitmapPaint; private RectF mBounds; private float mRadius = 25.0f; public RoundedCornerImageView(Context context) { this(context, null); } public RoundedCornerImageView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public RoundedCornerImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { // 创建图片涂绘 mBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG); // 创建作为绘图边界的矩形 mBounds = new RectF(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int height = 0; int width = 0; // 所请求大小是图片内容的大小 int imageHeight, imageWidth; if (mImage == null) { imageHeight = imageWidth = 0; } else { imageHeight = mImage.getHeight(); imageWidth = mImage.getWidth(); } // 获得最佳测量值并在视图上设置该值 width = getMeasurement(widthMeasureSpec, imageWidth); height = getMeasurement(heightMeasureSpec, imageHeight); setMeasuredDimension(width, height); } private int getMeasurement(int measureSpec, int contentSize) { int specSize = MeasureSpec.getSize(measureSpec); switch (MeasureSpec.getMode(measureSpec)) { case MeasureSpec.AT_MOST: return Math.min(specSize, contentSize); case MeasureSpec.UNSPECIFIED: return contentSize; case MeasureSpec.EXACTLY: return specSize; default: return 0; } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { if (w != oldw || h != oldh) { // 我们要使图片居中,因此在视图改变大小时偏移值 int imageWidth, imageHeight; if (mImage == null) { imageWidth = imageHeight = 0; } else { imageWidth = mImage.getWidth(); imageHeight = mImage.getHeight(); } int left = (w - imageWidth) / 2; int top = (h - imageHeight) / 2; // 设置边界以偏移圆角矩形(整个图形居中) mBounds.set(left, top, left+imageWidth, top+imageHeight); // 偏移着色器以在矩形内部绘制位图 // 如果没有此步骤,位图将在视图中的(0, 0)处 if (mBitmapPaint.getShader() != null) { Matrix m = new Matrix(); m.setTranslate(left, top); mBitmapPaint.getShader().setLocalMatrix(m); } } } /** * 供使用者调用,并创建一个 BitmapShader 来封装图片像素,并在用于绘图 * 的画笔上进行相应的设置。 * @param bitmap 位图 */ public void setImage(Bitmap bitmap) { if (mImage != bitmap) { mImage = bitmap; if (mImage != null) { BitmapShader shader = new BitmapShader(mImage, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); mBitmapPaint.setShader(shader); } else { mBitmapPaint.setShader(null); } // 绘制成 bitmap requestLayout(); } } // 让视图绘制背景等对象 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 使用计算得出的值绘制图片 if (mBitmapPaint != null) { canvas.drawRoundRect(mBounds, mRadius, mRadius, mBitmapPaint); } }}</span>
该类中关于自定义 view 时,用到的测量等,可以参见我以前的博客《简单的完全自定义视图(同心圆)》:http://blog.csdn.net/antimage08/article/details/50103433点击打开链接
MainActivity.java :
<span style="font-size:18px;">package com.scxh.imagecover;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); RoundedCornerImageView imageView = new RoundedCornerImageView(this); Bitmap source = BitmapFactory.decodeResource(getResources(), R.drawable.image01); imageView.setImage(source); setContentView(imageView); }}</span>
第二中方法实现遮罩:此处采用两张图片,一张如上图的效果所示;另一张采用一个黑色的倒三角形(从 300 * 300 像素上扣取)。效果如下:
首先在 Canvas 中绘制三角形图片,这就是图片的遮罩。然后,在同一个 Canvas 上绘制原图时应用 PorterDuff.Mode.SRC_IN 转换,得到的就是带圆角的原图。
这是因为 SRC_IN 转换模式就是告诉 Paint 对象,只在 Canvas 上原图和目标图(已经画好的三角形)重叠视为地方绘制像素点,像素点则来自原图。
在运行 Android 5.0 及更高版本的设备上,Android 框架支持通过动态阴影表明视图的提高(通过 elevation 和 translationZ 属性)。
在简单的示例中,可以在内部进行处理,但如果应用任意遮罩,则还必须使用匹配的 ViewOutlineProvider 指示在何处产生阴影。
ViewOutlineProvider 有一个必须的方法 getOutline(),如果由于大小或配置发生变化而需要更新轮廓,就会调用该方法。
MaskActivity.java :
<span style="font-size:18px;">package com.scxh.imagecover;import android.app.Activity;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Outline;import android.graphics.Paint;import android.graphics.Path;import android.graphics.PorterDuff;import android.graphics.PorterDuffXfermode;import android.os.Build;import android.os.Bundle;import android.view.View;import android.view.ViewOutlineProvider;import android.widget.ImageView;public class MaskActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ImageView imageView = new ImageView(this); imageView.setScaleType(ImageView.ScaleType.CENTER); // 创建并加载图片(通常是不可修改的) Bitmap source = BitmapFactory.decodeResource(getResources(), R.drawable.image01); Bitmap mask = BitmapFactory.decodeResource(getResources(), R.drawable.dsjx); // 创建一个可修改的位置以及一个在其中绘制的 Canvas final Bitmap result = Bitmap.createBitmap(source.getWidth(), source.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(result); Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setColor(Color.BLACK); canvas.drawBitmap(mask, 0, 0, paint); // PorterDuff.Mode.SRC_IN 模式:会根据目标边界对原图进行裁剪 paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); canvas.drawBitmap(source, 0, 0, paint); paint.setXfermode(null); imageView.setImageBitmap(result); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // 提高视图以建立可见阴影(数值越大阴影扩散的范围就越大) imageView.setElevation(30f); // 绘制匹配遮罩的轮廓,从而提供适当的阴影 imageView.setOutlineProvider(new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { int x = (view.getWidth() - result.getWidth()) / 2; int y = (view.getHeight() - result.getHeight()) / 2; Path path = new Path(); // 路径的起始位置(倒三角形的左上角) path.moveTo(x, y); // 沿路径绘制直线 (倒三角形的右上角) path.lineTo(x + result.getWidth(), y); // 沿路径绘制直线 (倒三角形的下顶点) path.lineTo(x + result.getWidth() / 2, (float) (y + result.getHeight()/1.6)); // 沿路径绘制直线 (倒三角形的左上角) path.lineTo(x, y); // 绘制成封闭图形后,关闭路径 path.close(); outline.setConvexPath(path); } }); } setContentView(imageView); }}</span>
如果轮廓足够简单,Android 还可以将其作为视图的剪切遮罩。只需要调用 setClipToOutline(true),即可表明视图应使用其轮廓作为剪切遮罩。
到Android5.0为止,仅支持通过矩形,圆形和圆角矩形轮廓进行剪切。上图的三角形就不能用作剪切。
圆形轮廓剪切的效果图:
OutlineActivity.java :
package com.scxh.imagecover;import android.app.Activity;import android.graphics.Outline;import android.os.Bundle;import android.view.View;import android.view.ViewOutlineProvider;import android.widget.ImageView;public class OutlineActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final ImageView imageView = new ImageView(this); imageView.setScaleType(ImageView.ScaleType.CENTER); // 提高视图以建立可见阴影(数值越大阴影扩散的范围就越大) imageView.setElevation(30f); imageView.setImageResource(R.drawable.image02); // 告诉视图使用其轮廓作为剪切遮罩 imageView.setClipToOutline(true); // 为剪切和阴影提供圆形视图轮廓 imageView.setOutlineProvider(new ViewOutlineProvider() { @Override public void getOutline(View view, Outline outline) { ImageView mImageView = (ImageView)view; int radius = mImageView.getDrawable().getIntrinsicHeight()/2; int centerX = (view.getRight() - view.getLeft())/2; int centerY = (view.getBottom() - view.getTop())/2; outline.setOval(centerX - radius, centerY - radius, centerX + radius, centerY + radius); } }); setContentView(imageView); }}
0 0
- 利用 2D 图形和 PorterDuffXferMode 等实现被遮罩的图片
- 利用Quartz 2D实现图片的旋转、缩放、裁剪
- 实现仿微信聊天时发送图片的形状(NinePatch+PorterDuffXfermode)
- 使用PorterDuffXferMode实现自定义的圆角图片
- 利用PHP实现图片等比例放大和缩小
- 2D和3D图形引擎的混合
- PorterDuffXfermode自定义View实现圆角图片
- 利用PorterDuffXfermode 对Bitmap的特殊处理
- 图形绘制中的PorterDuffXfermode
- PorterDuffXfermode:图形混合模式
- Android入门——利用Canvas完成绘制点、圆、直线、路径、椭圆、多边形等2D图形
- wpf利用动画实现图形变化产生3d效果
- Android学习研究(四)通过PorterDuffXfermode形成简单的圆角和圆形图片
- Android自定义View---PorterDuffXfermode两个交叠图形的显示
- 2D图形的变换和变换矩阵
- 自定义View通过PorterDuffXfermode实现图片遮罩效果
- 利用 css3 的图形3d翻转效果应用demo
- Graphics 2D实现一些图形
- LeetCode 040 Combination Sum II
- 读Android学Java基础之按位操作符
- Android自助餐之文件存储
- How to add a new key on android
- 每天学习十分钟2之jQuery学习
- 利用 2D 图形和 PorterDuffXferMode 等实现被遮罩的图片
- iOS面试题5
- MFC中部分控件的使用
- Objective - C UITabbarController
- yii2项目前台页面开发中,用到的算法1 (例题解析)
- sessionStorage和localStorage区别
- ios面试算法题(5)——扑克发牌、最小和差问题、V字图形打印
- jquery 滚动特效
- Oracle开发环境搭建