(七)Paint 高级渲染
来源:互联网 发布:小米笔记本 显卡知乎 编辑:程序博客网 时间:2024/06/13 08:41
版权声明:本文为博主原创文章,未经博主允许不得转载。
本文纯个人学习笔记,由于水平有限,难免有所出错,有发现的可以交流一下。
这边继续 Paint 的一些高级用法 Shader,Canvas 的 drawXXXX 这个方法是画具体的形状,画笔的 shader 定义的就是图形的着色和外观。
一、 BitmapShader 位图图像渲染
BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY);
第一个参数为要渲染的图片,后面两个分别是 X 方向和 Y 方向的拉伸处理。
BitmapShader 可以设置一张图片,当我们调用 Canvas 的 drawXXXX 这个方法来进行形状的绘制的时候,会把这张图片填充到我们绘画的形状中。当图片过大的时候,从左上角对齐开始绘制,当形状过大,会对图片进行设置的 TileMode 拉伸处理,这里 TileMode 有三种拉伸模式:
TileMode 拉伸形式 CLAMP ---是拉伸最后一个像素铺满 MIRROR ---在横向纵向不足处不断翻转镜像平铺 REPEAT ---类似电脑壁纸,横向纵向不足的重复放置
上下两个矩形,横向都是采用 CLAMP 填充方式,在竖直方向上,上面一个矩形采用的是 MIRROR 填充方式,下一个矩形采用的是 REPEAT 填充方式。
1.圆形头像
先来看下效果:
public class MyGradientView extends View{ //加载的图片 private Bitmap bitmap; private Paint mPaint; //图片的宽高 private int mWidth; private int mHeight; //测量的宽高 float measureWidth; float measureHeight; //缩放比例 float scale; public MyGradientView(Context context, AttributeSet attrs) { super(context, attrs); mPaint = new Paint(); //加载图片,并获取图片的宽高 bitmap = ((BitmapDrawable)getResources().getDrawable(R.drawable.timg)).getBitmap(); mWidth = bitmap.getWidth(); mHeight = bitmap.getHeight(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub super.onMeasure(widthMeasureSpec, heightMeasureSpec); measureWidth = MeasureSpec.getSize(widthMeasureSpec); measureHeight = MeasureSpec.getSize(heightMeasureSpec); //考虑到宽高可能不一致,我们取宽高比的最小值 scale = Math.min(measureWidth/mWidth, measureHeight/mHeight); } @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub super.onDraw(canvas); //new一个 Shader ,宽高都采用 CLAMP 填充方式 BitmapShader shader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP); mPaint.setShader(shader); mPaint.setAntiAlias(true); //这边用矩阵进行设置图片的缩放,这个不在这边细讲 Matrix matrix = new Matrix(); //scale = 缩放大小 / 原大小 matrix.setScale(scale,scale); shader.setLocalMatrix(matrix); //以 View 的中心为圆心,宽高较小值的一般为半径画圆 canvas.drawCircle(measureWidth/2, measureHeight/2, Math.min(measureWidth/2, measureHeight/2), mPaint); }}
布局文件 xml 中直接引用即可。
2.放大镜
这边就在上方的 demo 中添加放大效果。
public class MyGradientView extends View{ //加载的图片 private Bitmap bitmap; private Paint mPaint; //图片的宽高 private int mWidth; private int mHeight; //测量的宽高 float measureWidth; float measureHeight; //缩放比例 float scale; //放大镜的半径 private static final int RADIUS = 100; // 放大后的图 private Bitmap mBitmapScale; //制作的圆形的图片(放大的局部),盖在Canvas上面 private ShapeDrawable mShapeDrawable; //记录背景图片移动的矩阵 private Matrix matrix; public MyGradientView(Context context, AttributeSet attrs) { super(context, attrs); mPaint = new Paint(); //加载图片,并获取图片的宽高 bitmap = ((BitmapDrawable)getResources().getDrawable(R.drawable.timg)).getBitmap(); mWidth = bitmap.getWidth(); mHeight = bitmap.getHeight(); matrix = new Matrix(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub super.onMeasure(widthMeasureSpec, heightMeasureSpec); measureWidth = MeasureSpec.getSize(widthMeasureSpec); measureHeight = MeasureSpec.getSize(heightMeasureSpec); //考虑到宽高可能不一致,我们取宽高比的最小值 scale = Math.min(measureWidth/mWidth, measureHeight/mHeight); } @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub super.onDraw(canvas); //new一个 Shader ,宽高都采用 CLAMP 填充方式 BitmapShader shader = new BitmapShader(bitmap, TileMode.CLAMP, TileMode.CLAMP); mPaint.setShader(shader); mPaint.setAntiAlias(true); //这边用矩阵进行设置图片的缩放,这个不在这边细讲 Matrix matrix = new Matrix(); //scale = 缩放大小 / 原大小 matrix.setScale(scale,scale); shader.setLocalMatrix(matrix); //以 View 的中心为圆心,宽高较小值的一般为半径画圆 canvas.drawCircle(measureWidth/2, measureHeight/2, Math.min(measureWidth/2, measureHeight/2), mPaint); //加上一个判断,避免每次都要去加载放大的图片 if(mBitmapScale == null){ //获取放大的图形(显示的背景图片缩小了,这里直接用原图) mBitmapScale = Bitmap.createScaledBitmap(bitmap, bitmap.getWidth(), bitmap.getHeight(), true); BitmapShader bitmapShader = new BitmapShader(mBitmapScale, TileMode.CLAMP, TileMode.CLAMP); //设置放大后显示的区域 //ShapeDrawable用法这边也不具体讲, mShapeDrawable = new ShapeDrawable(new OvalShape()); mShapeDrawable.getPaint().setShader(bitmapShader); mShapeDrawable.setBounds(0, 0, RADIUS * 2, RADIUS * 2); } mShapeDrawable.draw(canvas); } @Override public boolean onTouchEvent(MotionEvent event) { // TODO Auto-generated method stub int x = (int) event.getX(); int y = (int) event.getY(); //对放大的图片进行平移,将放大的图片往相反的方向挪动 matrix.setTranslate(-x / scale, -y / scale); mShapeDrawable.getPaint().getShader().setLocalMatrix(matrix); // 切出手势区域点位置的圆 mShapeDrawable.setBounds(x - RADIUS, y - RADIUS, x + RADIUS, y + RADIUS); invalidate(); return true; }}
二、LinearGradient 线性渲染
LinearGradient(float x0, float y0, float x1, float y1, int[] colors, float[] positions, TileMode tile);
前四个参数分别是起始点的坐标和渐变结束点的坐标,第五个参数是渐变颜色数组,第六个参数是渐变发送变化的位置数组(值再0-1之间),为空的话表示均匀分布。
这是一个从左上角到右下角的渐变:
霓虹灯
这里重要的一点是在 onDraw 方法中重新设置 mLinearGradient 的平移,触发 onSizeChanged 方法,从而再次调用 onDraw 。
public class LinearGradientTextView extends TextView{ private TextPaint mPaint; private int measureWidth; private LinearGradient mLinearGradient ; //效果的宽度 private int width = 100; //效果平移的矩阵 private Matrix mMatrix; //平移的距离 private float mTranslate; //移动速度 private float DELTAX = 20; public LinearGradientTextView(Context context) { super(context); } public LinearGradientTextView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); mPaint = getPaint(); mMatrix = new Matrix(); //线性渲染初始化 mLinearGradient = new LinearGradient(-width, 0, 0, 0, new int[]{0x22ffffff, 0xffffffff, 0x22ffffff}, null, TileMode.CLAMP); //为画笔设置线性渲染 mPaint.setShader(mLinearGradient); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // TODO Auto-generated method stub super.onMeasure(widthMeasureSpec, heightMeasureSpec); measureWidth = MeasureSpec.getSize(widthMeasureSpec); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //平移距离的叠加 mTranslate += DELTAX; //超出边沿就反向 if (mTranslate < 0 || mTranslate > measureWidth){ DELTAX = -DELTAX; } mMatrix.setTranslate(mTranslate, 0); //重新设置 mLinearGradient 的平移,会触发 onSizeChanged 方法 //onSizeChanged 方法调用完毕之后又会调 onDraw,从而循环 mLinearGradient.setLocalMatrix(mMatrix); postInvalidateDelayed(50); }}
三、RadialGradient 环形渲染
RadialGradient(float centerX, float centerY, float radius, int[] colors, float[] stops, TileMode tileMode);
前两个参数是环形渐变的圆心坐标,第三个为渐变半径(绘制图形大于这个半径,就按设置的填充方式进行填充),后面几个参数就跟 LinearGradient 一样。
水波纹扩散
这里主要用到了安卓的属性动画来不停的改变 RadialGradient 渲染的半径,属性动画暂时不讲,先大概了解一点。
public class RippleView extends Button { // 点击位置 private int mX, mY; private ObjectAnimator mAnimator; // 默认半径 private int DEFAULT_RADIUS = 50; private int mCurRadius = 0; private RadialGradient mRadialGradient; private Paint mPaint; public RippleView(Context context) { super(context); init(); } public RippleView(Context context, AttributeSet attrs) { super(context, attrs); init(); } private void init() { mPaint = new Paint(); } @Override public boolean onTouchEvent(MotionEvent event) { if (mX != event.getX() || mY != mY) { mX = (int) event.getX(); mY = (int) event.getY(); setRadius(DEFAULT_RADIUS); } switch (event.getAction()){ case MotionEvent.ACTION_DOWN: return true; case MotionEvent.ACTION_UP: { if (mAnimator != null && mAnimator.isRunning()) { mAnimator.cancel(); } if (mAnimator == null) { mAnimator = ObjectAnimator.ofInt(this,"radius",DEFAULT_RADIUS, getWidth()); } mAnimator.setInterpolator(new AccelerateInterpolator()); mAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { setRadius(0); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); mAnimator.start(); } } return super.onTouchEvent(event); } public void setRadius(final int radius) { mCurRadius = radius; if (mCurRadius > 0) { mRadialGradient = new RadialGradient(mX, mY, mCurRadius, 0x00FFFFFF, 0xFF58FAAC, Shader.TileMode.CLAMP); mPaint.setShader(mRadialGradient); } postInvalidate(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawCircle(mX, mY, mCurRadius, mPaint); }}
四、SweepGradient 渐变渲染/梯度渲染
SweepGradient(float cx, float cy, int[] colors, float[] positions);
前两个参数为圆心的 坐标,后两个参数分别是渐变颜色数组和渐变位置数组。
注:颜色按顺时针渐变
雷达扫描
这个比较简单,直接为 SweepGradient 添加一个任务,不同的变化旋转角度即可。
public class MyGradientView extends View{ private Paint mPaint; private int start = 0; private Matrix matrix; private boolean isStart = false; private int[] mColors = {Color.BLUE, Color.RED}; public MyGradientView(Context context, AttributeSet attrs) { super(context, attrs); mPaint = new Paint(); matrix = new Matrix(); } @Override protected void onDraw(Canvas canvas) { // TODO Auto-generated method stub super.onDraw(canvas); SweepGradient mSweepGradient = new SweepGradient(100, 100, mColors, null); mPaint.setShader(mSweepGradient); mPaint.setAntiAlias(true); //设置变化矩阵 mSweepGradient.setLocalMatrix(matrix); canvas.drawCircle(100, 100, 100, mPaint); //添加判断,只有第一次绘制才会启动线程 if (!isStart) { isStart = true; thread.start(); } } private Thread thread = new Thread(new Runnable() { public void run() { while (true) { //超过一圈,重置旋转角度 if(start == 360){ start = 0; } start = start + 2; //setRotate 每次回重置矩阵,再重新赋值 matrix.setRotate(start, 100, 100); postInvalidate(); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } });}
五、ComposeShader 组合渲染
从名字就可以看出来,是把渲染效果组合起来。
ComposeShader(Shader shaderA,Shader shaderB, Xfermode mode)
Parameters;
ComposeShader(Shader shaderA,Shader shaderB, PorterDuff.Mode mode);
前两个参数是要组合的渲染器,最后一个参数是组合的模式。
- (七)Paint 高级渲染
- Paint的高级渲染
- Android 高级UI解密 (一) :实例详解Paint 与 高级渲染
- Paint详细解析(1)—Shader(图像渲染)
- Paint高级效果
- 光照与渲染(七)- 反射
- [转载]Android 高级绘图 paint
- Android Shader 颜色、图像渲染 paint.setXfermode
- Android Paint之Shader渲染详解
- 绘图不可或缺的画笔Paint-渲染篇
- 高级渲染配置
- JavaScript高级程序设计(读书笔记)(七)
- ASP 3.0高级编程(七)
- android-exploitme(七):高级加密
- AHUOJ 高级语言程序设计实验-综合(七)
- java并发(七、高级并发对象)
- 《Unix环境高级编程》 总结 (七)
- python高级编程(七)--HTTP协议
- 最大和
- RabbitMQ简述
- 关于SVM的一些思考
- 罗永浩跟罗振宇八个半小时都聊了些什么
- 排序算法之堆排序
- (七)Paint 高级渲染
- 【java-算法】连续数组最大和
- 手机拍照银行卡识别银行卡号的技术sdk
- New Era with AlphaGo
- android后台截屏实现(2)--screencap源码修改
- get类型接口测试
- 解决win10系统下软件图标显示异常的问题
- 软件设计的目标
- Mac下eclipse添加hadoop-eclipse-plugin插件