Android为TV端助力 转载:Android绘图Canvas十八般武器之Shader详解及实战篇(下)
LinearGradient 线性渐变渲染器
LinearGradient中文翻译过来就是线性渐变的意思。线性渐变通俗来讲就是给起点设置一个颜色值如#faf84d,终点设置一个颜色值如#CC423C,然后在一个区域内绘图,这个图像的颜色将呈现非常美妙的效果,颜色会从起点颜色到终点颜色过渡。给一张图,大家直观感受一下
我们看LinearGradient的API,发现它只有两个构造方法,非常简单。
LinearGradient (float x0,
float y0,
float x1,
float y1,
int color0,
int color1,
Shader.TileMode tile)
//x0 和y0是颜色渐变的起点坐标。
//x1和y1是颜色渐变的终点坐标。
//color0是起点颜色值
//color0是终点颜色值。
//tile 就是TileMode类型参数,这个我们上一篇已经讲过了。
LinearGradient的用法。
//1 创建LinearGradient对象,并设置它的起点坐标,终点坐标,起点颜色值,终点颜色值,然后设置TileMode
mShader = new LinearGradient(0,0,w,0,Color.parseColor("#faf84d"),
Color.parseColor("#CC423C"), Shader.TileMode.CLAMP);
//2 将Shader赋值给Paint对象。
mPaint.setShader(mShader);
//3 绘制图形
canvas.drawRect(0,0,w,h/2,mPaint);
用法非常简单。
LinearGradient还有一个构造方法。
LinearGradient (float x0,
float y0,
float x1,
float y1,
int[] colors,
float[] positions,
Shader.TileMode tile)
需要注意的是,这里有一个int[] colors和float[] positions它们代表什么意思呢?
实际上LinearGradient除了可以指定起点颜色值和终点颜色值外,还有可以指定许多中间颜色值。就如彩虹一般。而colors[]数组存放的就是这样的颜色值组合。大家看看代码和图片效果就可能直观感受到。
//渐变的是一个颜色序列(#faf84d,#003449,#808080,#cc423c)
mShader = new LinearGradient(0,0,w,0,new int[]{Color.parseColor("#faf84d"),Color.parseColor("#003449"),
Color.parseColor("#808080"),
Color.parseColor("#CC423C")},null,Shader.TileMode.CLAMP);
mPaint.setShader(mShader);
canvas.drawRect(0,0,w,h/2,mPaint);
颜色很丰富是不是?颜色从一个颜色过渡到另外一个颜色直到过渡到终点颜色。
大家有没有注意到,我将上面代码中的float[] positon置为null,而它代表了什么呢?它其实与colors数组对应,代表了各个颜色值在位置,positions数组中的值大小范围从0.0到1.0,0.0代表起点位置,1.0代表终点位置。如果这个数组被置为空的话,颜色就会平均分配。 ,如果这个数组不为空呢?我们结合代码效果来讲解。
mShader = new LinearGradient(0,0,w,0,new int[]{Color.parseColor("#faf84d"),Color.parseColor("#003449"),
Color.parseColor("#808080"),
Color.parseColor("#CC423C")},new float[]{0.0f,0.6f,0.8f,1.0f},Shader.TileMode.CLAMP);
mPaint.setShader(mShader);
canvas.drawRect(0,0,w,h/2,mPaint);
代码中colors[]并没有改变,只是多了positon[],效果却不一样了。
new int[]{Color.parseColor("#faf84d"),Color.parseColor("#003449"),
Color.parseColor("#808080"),
Color.parseColor("#CC423C")}
new float[]{0.0f,0.6f,0.8f,1.0f}
// #faf84d对应的position值是0.0 所以为起点位置。
// #003449对应0.6 所以这个颜色位置起点到终点中间0.6比率的地方。
// #808080对应0.8 这个颜色在0.8比率的地方
// #cc423c对应1.0 这个颜色为终点处的颜色
需要注意的是,position[]数组中的数组最好是由小到大,这是为什么呢?它不支持0.8 然后再到0.6之类。大家看代码。
mShader = new LinearGradient(0,0,w,0,new int[]{Color.parseColor("#faf84d"),Color.parseColor("#003449"),
Color.parseColor("#808080"),
Color.parseColor("#CC423C")},new float[]{0.6f,0.8f,0.2f,0.0f},Shader.TileMode.CLAMP);
可以看到颜色可以从0.6的位置过渡到0.8,后面的就不起作用了。
RadialGradient 环行渲染器
我喜欢称它为径向渐变,因为PHOTOSHOP中就对应有径向渐变的概念。
径向渐变,所谓径向就是辐射状,由中心向四周辐射。
径向渐变也只有两个构造方法,基本用法跟线性渐变差不多。
RadialGradient (float centerX,
float centerY,
float radius,
int centerColor,
int edgeColor,
Shader.TileMode tileMode)
//centerX 圆心的X坐标
//centerY 圆心的Y坐标
//radius 圆的半径
//centerColor 中心颜色
//edgeColor 边缘颜色
//tileMode 这个不用介绍了吧?
上代码。
mShader = new RadialGradient(w/2,h/2,w/2,Color.parseColor("#faf84d"),
Color.parseColor("#CC423C"), Shader.TileMode.CLAMP);
mPaint.setShader(mShader);
canvas.drawRect(0,0,w,h,mPaint);
效果:
RadialGradient (float centerX,
float centerY,
float radius,
int[] colors,
float[] stops,
Shader.TileMode tileMode)
同LinearGradient一样,这里也有一个颜色数组和位置数组,意义也是一样的,stop[]也可以为null,如果为null的话,color[]数组的颜色就会平均分配在区域之中。否则,它对应的颜色就会按照比例填充。
mShader = new RadialGradient(w/2,h/2,w/2,new int[]{Color.parseColor("#00aa00"),Color.parseColor("#880033"),
Color.parseColor("#F8795A"),
Color.parseColor("#CC423C")},new float[]{0.0f,0.2f,0.8f,1.0f}, Shader.TileMode.CLAMP);
mPaint.setShader(mShader);
canvas.drawRect(0,0,w,h,mPaint);
SweepGradient 梯度渐变渲染器
梯度渐变,或者叫做扫描渐变。我觉得扫描更适合吧,它是指从x轴出发,以逆时钟为方向,以扫描360度形成的区域进行颜色的变换。
SweepGradient (float cx,
float cy,
int color0,
int color1)
//color0是起始颜色
//color1是终止颜色
代码示例:
mShader = new SweepGradient(w/2,h/2,Color.RED,Color.BLUE);
mPaint.setShader(mShader);
canvas.drawRect(0,0,w,h,mPaint);
效果:
SweepGradient (float cx,
float cy,
int[] colors,
float[] positions)
大家应该也明白这个方法中每个参数的含义。
mShader = new SweepGradient(w/2,h/2,new int[]{Color.RED,Color.CYAN,Color.YELLOW,
Color.GREEN,Color.MAGENTA,Color.BLUE},new float[]{0.0f,0.2f,0.3f,0.4f,0.8f,1.0f});
mPaint.setShader(mShader);
canvas.drawRect(0,0,w,h,mPaint);
我们把颜色丰富点,本来想弄成赤橙黄绿青蓝紫,结果因为懒,就随便弄了点,效果如下:
ComposeShader 组合渲染器
混合渲染,在这里我又开始称Shader为渲染了,因为ComposeShader不仅仅用于颜色,它能将两个Shader对象参考Xfermode规则进行颜色混合。
网上的一张图:
这张图详细的解释了混合模式的组合效果,我机会我也写一篇相关博文。
再看ComposeShader的两个构造方法。
ComposeShader (Shader shaderA,
Shader shaderB,
Xfermode mode)
ComposeShader (Shader shaderA,
Shader shaderB,
PorterDuff.Mode mode)
接下来,我们写代码验证一下。
实战1
- 编写1个BitmapShader.
- 编写1个RadiasGradient。
- 将它们进行混合产生新的Shader.
- 以新的Shader绘制一个圆。
public class CircleView extends View {
private Paint mPaint;
private Shader mShader;
public CircleView(Context context) {
this(context,null);
}
public CircleView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mPaint.setAntiAlias(true);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//这里为了方便演示,将尺寸固定为400*400
setMeasuredDimension(400,400);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int w = getWidth();
int h = getHeight();
int radius = w <= h ? w/2 : h/2;
Bitmap bmp = BitmapFactory.decodeResource(getResources(),R.drawable.repeat);
Bitmap result = Bitmap.createScaledBitmap(bmp,w,h,false);
//1. 编写1个BitmapShader.
BitmapShader bitmapShader = new BitmapShader(result, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
//2. 编写1个RadiasGradient。
RadialGradient radialGradient = new RadialGradient(radius,radius,radius,Color.BLACK,Color.TRANSPARENT, Shader.TileMode.CLAMP);
//3. 将它们进行混合产生新的Shader.
ComposeShader composeShader = new ComposeShader(bitmapShader,radialGradient,new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mPaint.setShader(composeShader);
//4. 以新的Shader绘制一个圆。
canvas.drawCircle(w/2,h/2,radius,mPaint);
}
}
我们来看看混合后的效果是怎么样的。
哇,好梦幻的狗狗。
实战2 倒影功能
以前刚开始学Android的时候,项目里面要用到倒影,当时的自己是写不出来的,好在网上有现成的代码可以copy。现在我们可以运用ComposeShader来实现这么一个View。
需求分析
- 倒影与原图比例为1:4。
- 倒影与原图之间有5px的间隙。
- 倒影的下边缘不能太平整了,要尽量跟真实的一致。
好了为了节省篇幅,我只粘贴onDraw()中的代码。
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//定义各种宽高
int bmpWidth = 200;
int bmpHeight = 200;
int gap = 5;
int reflectionHeight = bmpHeight / 4;
//绘制原图
Bitmap bmp = BitmapFactory.decodeResource(getResources(),R.drawable.repeat);
Bitmap result = Bitmap.createScaledBitmap(bmp,bmpWidth,bmpHeight,false);
canvas.drawBitmap(result,0,0,null);
canvas.save();
//向下移动准备在原图下方绘制倒影
canvas.translate(0,bmpHeight+gap);
Matrix m = new Matrix();
m.postScale(-1f,1f);
m.postRotate(-180);
//将原图水平翻转
Bitmap texture = Bitmap.createBitmap(result,0,0,result.getWidth(),result.getHeight(),m,false);
//创建BitmapShader和LinearShader。
BitmapShader bitmapShader = new BitmapShader(texture, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
LinearGradient linearGradient = new LinearGradient(0,0,0,reflectionHeight,Color.BLACK,Color.TRANSPARENT, Shader.TileMode.CLAMP);
ComposeShader composeShader = new ComposeShader(bitmapShader,linearGradient,new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
mPaint.setShader(composeShader);
//以混合模式绘制矩形区域,可以获得倒影效果。
canvas.drawRect(0,0,bmpWidth,reflectionHeight,mPaint);
canvas.restore();
}
效果:
倒影出来了。
Android绘图Canvas十八般武器之Shader详解及实战篇(上)
Android为TV端助力 转载:Android绘图Canvas十八般武器之Shader详解及实战篇(下)的更多相关文章
- Android为TV端助力 转载:Android绘图Canvas十八般武器之Shader详解及实战篇(上)
前言 Android中绘图离不开的就是Canvas了,Canvas是一个庞大的知识体系,有Java层的,也有jni层深入到Framework.Canvas有许多的知识内容,构建了一个武器库一般,所谓十 ...
- Android为TV端助力 转载:RecyclerView分页加载
package com.android.ryane.pulltoloaddata_recyclerview; import android.os.Handler;import android.os.L ...
- Android为TV端助力 转载:android MVC设计模式
Controller控制器 import android.app.Dialog; import android.app.ProgressDialog; import android.os.Bundle ...
- Android为TV端助力 转载自jguangyou的博客,XML基本属性大全
android:layout_width 指定组件布局宽度 android:layout_height 指定组件布局高度 android:alpha 设置组件透明度 android:backgroun ...
- Android为TV端助力 转载弩的博客
Android.mk简介:Android.mk文件用来告知NDK Build 系统关于Source的信息. Android.mk将是GNU Makefile的一部分,且将被Build System解析 ...
- Android为TV端助力:(转载)修改TextView字体样式
一.开篇 因为 Android 字体相关的内容还比较多的.有时候其实我们只需要调整一下属性就可以满足设计师的需求,或者是一个退后的方案(毕竟有发版的时间卡住了),有一些效果可以大概满足需求. 那么本文 ...
- Android为TV端助力转载:码农小阿飞(SpannableString)
用SpannableString打造绚丽多彩的文本显示效果 引语 TeXtView大家应该都不陌生,文本展示控件嘛! 就用TextView显示普普通通的文本,OK,很简单,Android入门的都会,没 ...
- Android为TV端助力 转载:android自定义view实战(温度控制表)!
效果图 package cn.ljuns.temperature.view; import com.example.mvp.R; import android.content.Context;impo ...
- Android为TV端助力 转载:Java 泛型
一. 泛型概念的提出(为什么需要泛型)? 首先,我们看下下面这段简短的代码: 1 public class GenericTest { 2 3 public static void main(Stri ...
随机推荐
- ModelState 错误信息输出
在MVC的项目中,我们通常情况下,为了方便(偷懒),会直接使用 !ModelState.IsValid 来判断实体的验证是否正确,但是这样对于用户的体验是不好的,当填写的内容比较多的时候,用户需要自己 ...
- vue android低版本 白屏问题 你是不是用了Object.assign ??
问题描述 在部分比较低版本的手机中,发现apk安装后白屏,但是大部分手机都能安装. 本人在使用android4.4时候,也是安装后打开白屏. 原因: 代码写法不兼容 this.user = Objec ...
- shell脚本命令(记录)
1.重命名文件 将D盘下的A.txt 重命名为B.txt mv D:\\A.txt D:\\B.txt 2.删除文件 删除D盘下的A.txt文件 rm D:\\A.txt 3.修改文件内容并保存 // ...
- deque源码4(deque元素操作:pop_back、pop_front、clear、erase、insert)
deque源码1(deque概述.deque中的控制器) deque源码2(deque迭代器.deque的数据结构) deque源码3(deque的构造与内存.ctor.push_back.push_ ...
- 通过Calendar简单解析Date日期,获取年、月、日、星期的数值
有时候项目中需要用到Date的年.月.日.星期的数值.那么解析方法如下: /**解析日期,获取年月日星期*/ private void parseDateToYearMonthDayWeek(Date ...
- 微信支付之手机H5支付实践
最近项目中支付部分涉及到微信支付,使用的是h5支付,官方文档中是没有demo的,所以摸着石头过河,将踩过的坑记录如下. 一 应用场景 H5支付是指商户在微信客户端外的移动端网页展示商品或服务,用户在前 ...
- 为什么使用SLF4J比使用log4j或者java.util.logging更好
1.SLF4j是什么? SLF4J 并没有真正地实现日志记录,它只是一个允许你使用任何java日志记录库的抽象适配层. 如果你正在编写内部或者外部使用的API或者应用库的话,如果使用了slf4j,那么 ...
- B树与B+详解
承接上篇SQLite采用B树结构使得SQLite内存占用资源较少,本篇将讲述B树的具体操作(建树,插入,删除等操作).在看博客时,建议拿支笔和纸,一点一点操作,毕竟知识是自己的,自己也要消化的.本篇通 ...
- Entity Framework Core 中文入门文档
点击链接查看文档: Entity Framework Core 中文入门文档
- 浅谈select for update 和select lock in share mode的区别
有些情况下为了保证数据逻辑的一致性,需要对SELECT的操作加锁.InnoDB存储引擎对于SELECT语句支持两种一致性的锁定读(locking read)操作. . SELECT …… FOR UP ...