Android绘图基础之Shader
2017-06-30 17:57
120 查看
转载请注明出处:http://blog.csdn.net/crazy1235/article/details/74011243
Shader – 着色器!
Shader 共有5个子类!
![](https://img-blog.csdn.net/20170630223520279?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3JhenkxMjM1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
下面分别来说一下这五种着色器!
来看下BitmapShader的构造函数:
第一个参数就是要处理的图片!
第二个和第三个参数表示贴图模式 – TileMode, 是一个枚举类型。
举例说明:
CLAMP + CLAMP
![](https://img-blog.csdn.net/20170701105502283?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3JhenkxMjM1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
REPEAT + REPEAT
![](https://img-blog.csdn.net/20170701105838696?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3JhenkxMjM1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
REPEAT + CLAMP
![](https://img-blog.csdn.net/20170701110035070?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3JhenkxMjM1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
MIRROR + MIRROR
![](https://img-blog.csdn.net/20170701110200837?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3JhenkxMjM1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
上下左右都是镜像图片
MIRROR + REPEAT
![](https://img-blog.csdn.net/20170701110322552?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3JhenkxMjM1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
左右是镜像,上下是重复的 !
将绘制区域改成一个圆!
![](https://img-blog.csdn.net/20170701111153370?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3JhenkxMjM1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
哇擦!牛逼了,这不就是圆形图片嘛!!!
改一下圆的大小再看看!
![](https://img-blog.csdn.net/20170701111427271?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3JhenkxMjM1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
此时圆形宽高比bitmap要大,设置的shader风格又是REPEAT,所以就出现了上图展现的结果!
但是在实际开发中,显示ImageView的大小可不是根据图片的大小来定的。如果想要这个圆形图片宽度占满屏幕呢? 又不像出现上面的重复情况
此时就需要针对bitmap进行缩放操作!
然后在onDraw()函数中:
![](https://img-blog.csdn.net/20170701154216220?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3JhenkxMjM1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
此时将自定义图片在布局中的大小改变一下!
![](https://img-blog.csdn.net/20170701154359334?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3JhenkxMjM1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
此时圆形图片的大小也有变化!
至此一个最简单的自定义圆形图片的功能就能实现了!
这是除了用PorterDuff.Mode 方式之外 另一种实现自定义圆形图片的方式!
当然不只能实现圆形图片那么简单!!!!
各种图形都可以,因为Canvas 不仅有drawCircle()函数,还有各种drawRect(), drawOval() ,drawText(),甚至还有drawPath() !!!
来看一个drawText()的效果!
![](https://img-blog.csdn.net/20170701155136452?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3JhenkxMjM1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
效果是不是很神奇!!!
在用drawPath() 函数来画一个五角形的图案!
![](https://img-blog.csdn.net/20170701164231469?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3JhenkxMjM1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
看一下LinearGradient的构造函数!
![](https://img-blog.csdn.net/20170701171908748?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3JhenkxMjM1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
LinearGradient还有另外一个构造函数:
举例说明:
![](https://img-blog.csdn.net/20170701172636775?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3JhenkxMjM1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
一共四个渐变色,由于设置positions为null,所以就均分了整个宽度。
来段文字试试!
![](https://img-blog.csdn.net/20170701174058036?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3JhenkxMjM1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
来动起来!
![](http://forum.csdn.net/PointForum/ui/scripts/csdn/Plugin/003/monkey/16.gif)
将上面的代码再改一改,加上自定义属性,再加上动画开始和停止的函数,就可以作为一个闪动文字TextView 来使用了!
![](https://img-blog.csdn.net/20170701181928528?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3JhenkxMjM1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
第二个构造函数!!
![](https://img-blog.csdn.net/20170701182252189?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3JhenkxMjM1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
跟上面的LinearGradient用法很相似!
![](https://img-blog.csdn.net/20170701181024162?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3JhenkxMjM1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
而另外一个构造函数与 LinearGradient 的第二个构造函数也是相似的!
![](https://img-blog.csdn.net/20170701181359737?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3JhenkxMjM1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
为什么成为组合渲染器恩?
来看构造函数!
不仅可以设置两个Shader,也可以设置Xfermode。作用就是将两个Shader通过Xfermode来进行组合!
shaderA 就相当于 DST图像
shaderB 就相当于 SRC图像
关于Xfermode详见:http://blog.csdn.net/crazy1235/article/details/73835933
举例:
效果如下:
![](https://img-blog.csdn.net/20170702001051517?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvY3JhenkxMjM1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
over~~
Shader – 着色器!
Shader 共有5个子类!
public Shader setShader(Shader shader) { // If mShader changes, cached value of native shader aren't valid, since // old shader's pointer may be reused by another shader allocation later if (mShader != shader) { mNativeShader = -1; } // Defer setting the shader natively until getNativeInstance() is called mShader = shader; return shader; }
下面分别来说一下这五种着色器!
BitmapShader
图片着色器
来看下BitmapShader的构造函数:
public BitmapShader(@NonNull Bitmap bitmap, TileMode tileX, TileMode tileY) { mBitmap = bitmap; mTileX = tileX; mTileY = tileY; init(nativeCreate(bitmap, tileX.nativeInt, tileY.nativeInt)); }
第一个参数就是要处理的图片!
第二个和第三个参数表示贴图模式 – TileMode, 是一个枚举类型。
public enum TileMode { /** * 当图形尺寸大于bitmap尺寸时,用bitmap的边缘颜色进行填充剩余空间 */ CLAMP (0), /** * 当要绘制的图形尺寸大于Bitmap尺寸时,会用Bitmap重复平铺整个绘制的区域 */ REPEAT (1), /** * 当绘制的图形尺寸大于Bitmap尺寸时,MIRROR模式下也会用Bitmap重复平铺整个绘图区域,与REPEAT不同的是,两个相邻的Bitmap互为镜像。 */ MIRROR (2); TileMode(int nativeInt) { this.nativeInt = nativeInt; } final int nativeInt; }
举例说明:
CLAMP + CLAMP
private Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.child); private BitmapShader bitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); // CLAMP + CLAMP
paint.setShader(bitmapShader); canvas.drawRect(0, 0, bitmap.getWidth() * 2, bitmap.getHeight() * 2, paint);
REPEAT + REPEAT
private BitmapShader bitmapShader2 = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); // REPEAT + REPEAT paint.setShader(bitmapShader2); canvas.drawRect(0, 0, bitmap.getWidth() * 2.5F, bitmap.getHeight() * 2.5F, paint);
REPEAT + CLAMP
private BitmapShader bitmapShader2 = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.CLAMP); // REPEAT + CLAMP paint.setShader(bitmapShader2); canvas.drawRect(0, 0, bitmap.getWidth() * 2.5F, bitmap.getHeight() * 2.5F, paint);
MIRROR + MIRROR
private BitmapShader bitmapShader3 = new BitmapShader(bitmap, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR); // MIRROR + MIRROR paint.setShader(bitmapShader3); canvas.drawRect(0, 0, bitmap.getWidth() * 2.5F, bitmap.getHeight() * 2.5F, paint);
上下左右都是镜像图片
MIRROR + REPEAT
private BitmapShader bitmapShader3 = new BitmapShader(bitmap, Shader.TileMode.MIRROR, Shader.TileMode.REPEAT); // MIRROR + REPEAT paint.setShader(bitmapShader3); canvas.drawRect(0, 0, bitmap.getWidth() * 2.5F, bitmap.getHeight() * 2.5F, paint);
左右是镜像,上下是重复的 !
上面的demo都是基于绘制区域比原图(bitmap)要大的情况下。那么当绘制区域小于原图的时候会发生什么呢?
将绘制区域改成一个圆!
paint.setShader(bitmapShader2); int radius = bitmap.getWidth() / 2; if (bitmap.getHeight() < bitmap.getWidth()) { radius = bitmap.getHeight() / 2; } canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, radius, paint);
哇擦!牛逼了,这不就是圆形图片嘛!!!
改一下圆的大小再看看!
private BitmapShader bitmapShader2 = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); //REPEAT
paint.setShader(bitmapShader2); int radius = bitmap.getWidth() / 2; if (bitmap.getHeight() < bitmap.getWidth()) { radius = bitmap.getHeight() / 2; } canvas.drawCircle(bitmap.getWidth(), bitmap.getHeight(), radius * 2, paint);
此时圆形宽高比bitmap要大,设置的shader风格又是REPEAT,所以就出现了上图展现的结果!
但是在实际开发中,显示ImageView的大小可不是根据图片的大小来定的。如果想要这个圆形图片宽度占满屏幕呢? 又不像出现上面的重复情况
此时就需要针对bitmap进行缩放操作!
// 首先要设置测量宽高一直 @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int size = Math.min(getMeasuredWidth(), getMeasuredHeight()); radius = size / 2; setMeasuredDimension(size, size); }
然后在onDraw()函数中:
int w = getWidth(); int h = getHeight(); float mScale = (radius * 2.0f) / Math.min(bitmap.getHeight(), bitmap.getWidth()); Matrix matrix = new Matrix(); matrix.setScale(mScale, mScale); bitmapShader2 = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); // 什么模式无所谓了 bitmapShader2.setLocalMatrix(matrix); paint.setShader(bitmapShader2); canvas.drawCircle(radius, radius, radius, paint);
此时将自定义图片在布局中的大小改变一下!
<com.jacksen.demo.view.canvas.MyCanvasView android:id="@+id/my_canvas_view" android:layout_width="200dp" android:layout_height="200dp" />
此时圆形图片的大小也有变化!
至此一个最简单的自定义圆形图片的功能就能实现了!
这是除了用PorterDuff.Mode 方式之外 另一种实现自定义圆形图片的方式!
当然不只能实现圆形图片那么简单!!!!
各种图形都可以,因为Canvas 不仅有drawCircle()函数,还有各种drawRect(), drawOval() ,drawText(),甚至还有drawPath() !!!
来看一个drawText()的效果!
float mScale = (radius * 2.0f) / Math.min(bitmap.getHeight(), bitmap.getWidth()); Matrix matrix = new Matrix(); matrix.setScale(mScale, mScale); bitmapShader2 = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); // 模式无所谓了 bitmapShader2.setLocalMatrix(matrix); paint.setShader(bitmapShader2); paint.setTextSize(300.0f); paint.setColor(Color.BLACK); paint.setTextSkewX(-0.5f); // X轴错切 canvas.drawText("笨小孩", 0, radius, paint);
效果是不是很神奇!!!
在用drawPath() 函数来画一个五角形的图案!
public Path getPentagonPath() { Path path = new Path(); float radian = degree2Radian(36);// 36为五角星的角度 float radius_in = (float) (radius * Math.sin(radian / 2) / Math .cos(radian)); // 中间五边形的半径 (就是内接圆的半径) path.moveTo((float) (radius * Math.cos(radian / 2)), 0);// 此点为多边形的起点 path.lineTo((float) (radius * Math.cos(radian / 2) + radius_in * Math.sin(radian)), (float) (radius - radius * Math.sin(radian / 2))); path.lineTo((float) (radius * Math.cos(radian / 2) * 2), (float) (radius - radius * Math.sin(radian / 2))); path.lineTo((float) (radius * Math.cos(radian / 2) + radius_in * Math.cos(radian / 2)), (float) (radius + radius_in * Math.sin(radian / 2))); path.lineTo( (float) (radius * Math.cos(radian / 2) + radius * Math.sin(radian)), (float) (radius + radius * Math.cos(radian))); path.lineTo((float) (radius * Math.cos(radian / 2)), (float) (radius + radius_in)); path.lineTo( (float) (radius * Math.cos(radian / 2) - radius * Math.sin(radian)), (float) (radius + radius * Math.cos(radian))); path.lineTo((float) (radius * Math.cos(radian / 2) - radius_in * Math.cos(radian / 2)), (float) (radius + radius_in * Math.sin(radian / 2))); path.lineTo(0, (float) (radius - radius * Math.sin(radian / 2))); path.lineTo((float) (radius * Math.cos(radian / 2) - radius_in * Math.sin(radian)), (float) (radius - radius * Math.sin(radian / 2))); path.close();// 使这些点构成封闭的多边形 return path; } private float degree2Radian(int degree) { return (float) (Math.PI * degree / 180); }
canvas.drawPath(getPentagonPath(), paint);
LinearGradient
线性渐变! 就是类似于PS里面的渐变效果,给定两个色值,中间的颜色根据这两个色值来进行计算填充,形成一个渐变的效果!
看一下LinearGradient的构造函数!
// x0 和y0是颜色渐变的起点坐标。 // x1和y1是颜色渐变的终点坐标。 // color0是起点颜色值 // color0是终点颜色值。 // tile 就是TileMode类型参数 public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, TileMode tile) { // ... }
LinearGradient linearGradient = new LinearGradient(0, 0, w, h, Color.BLACK, Color.YELLOW, Shader.TileMode.CLAMP); paint.setShader(linearGradient); canvas.drawCircle(radius, radius, radius, paint);
LinearGradient还有另外一个构造函数:
// colors 表示颜色数组,渐变的过程中可以设置很多个颜色,各个颜色之间进行渐变效果 // positions 数组表示颜色的占比,范围是[0...1] public LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[], TileMode tile) { // ... }
举例说明:
LinearGradient linearGradient = new LinearGradient(0, 0, w, 0, new int[]{Color.RED, Color.BLUE, Color.BLACK, Color.YELLOW}, null, Shader.TileMode.CLAMP); paint.setShader(linearGradient); canvas.drawRect(0, 0, w, h, paint);
一共四个渐变色,由于设置positions为null,所以就均分了整个宽度。
来段文字试试!
paint.setTextSize(70.0f); canvas.drawText("世间安得双全法,不负如来不负卿", 0, radius, paint);
来动起来!
![](http://forum.csdn.net/PointForum/ui/scripts/csdn/Plugin/003/monkey/16.gif)
// 闪动文字 textview public class FlickerTextView extends android.support.v7.widget.AppCompatTextView { private Paint paint; private LinearGradient linearGradient; private int offset = 0; private Matrix matrix = new Matrix(); public FlickerTextView(Context context) { this(context, null); } public FlickerTextView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public FlickerTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { paint = getPaint(); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); ValueAnimator animator = ValueAnimator.ofInt(0, 2 * getMeasuredWidth()); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { offset = (int) animation.getAnimatedValue(); postInvalidate(); } }); animator.setRepeatMode(ValueAnimator.RESTART); animator.setRepeatCount(ValueAnimator.INFINITE); animator.setDuration(1200); animator.start(); // new linearGradient = new LinearGradient(-getMeasuredWidth(), 0, 0, 0, new int[]{getCurrentTextColor(), Color.RED, Color.BLUE, getCurrentTextColor()}, null, Shader.TileMode.CLAMP); paint.setShader(linearGradient); } @Override protected void onDraw(Canvas canvas) { matrix.setTranslate(offset, 0); linearGradient.setLocalMatrix(matrix); paint.setShader(linearGradient); super.onDraw(canvas); // 上面用的是getPanit(),这里讲super.onDraw()放到最后一行,就可以应用我们修改后的paint! } }
将上面的代码再改一改,加上自定义属性,再加上动画开始和停止的函数,就可以作为一个闪动文字TextView 来使用了!
SweepGradient
扫描渐变, 从X轴正方向开始,顺时针进行扫描渲染360°!
// cx, cy表示中心点坐标 // color0是起始颜色 // color1是终止颜色 public SweepGradient(float cx, float cy, int color0, int color1) { // ... }
SweepGradient sweepGradient = new SweepGradient(radius, radius, Color.YELLOW, Color.BLUE); paint.setShader(sweepGradient); canvas.drawCircle(radius, radius, radius, paint);
第二个构造函数!!
public SweepGradient(float cx, float cy, int colors[], float positions[]) { // ... }
SweepGradient sweepGradient = new SweepGradient(radius, radius, new int[]{Color.YELLOW, Color.BLUE, Color.GREEN, Color.BLACK}, null); paint.setShader(sweepGradient); canvas.drawCircle(radius, radius, radius, paint);
RadialGradient
径向渐变,由中心向四周辐射
//centerX 圆心的X坐标 //centerY 圆心的Y坐标 //radius 圆的半径 //centerColor 中心颜色 //edgeColor 边缘颜色 //tileMode public RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, @NonNull TileMode tileMode) { // ... }
跟上面的LinearGradient用法很相似!
RadialGradient radialGradient = new RadialGradient(radius, radius, radius, Color.YELLOW, Color.RED, Shader.TileMode.REPEAT); paint.setShader(radialGradient); canvas.drawCircle(radius, radius, radius, paint);
而另外一个构造函数与 LinearGradient 的第二个构造函数也是相似的!
public RadialGradient(float centerX, float centerY, float radius, @NonNull int colors[], @Nullable float stops[], @NonNull TileMode tileMode)
RadialGradient radialGradient = new RadialGradient(radius, radius, radius, new int[]{Color.YELLOW, Color.RED, Color.BLUE, Color.MAGENTA}, new float[]{0.2f, 0.2f, 0.5f, 1f}, Shader.TileMode.REPEAT); paint.setShader(radialGradient); canvas.drawCircle(radius, radius, radius, paint);
ComposeShader
组合渲染器!
为什么成为组合渲染器恩?
来看构造函数!
public ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode) public ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode)
不仅可以设置两个Shader,也可以设置Xfermode。作用就是将两个Shader通过Xfermode来进行组合!
shaderA 就相当于 DST图像
shaderB 就相当于 SRC图像
关于Xfermode详见:http://blog.csdn.net/crazy1235/article/details/73835933
举例:
// 创建圆形bitmap static Bitmap getCircleBitmap(int radius) { Bitmap bm = Bitmap.createBitmap(radius * 2, radius * 2, Bitmap.Config.ARGB_8888); Canvas c = new Canvas(bm); Paint p = new Paint(Paint.ANTI_ALIAS_FLAG); p.setColor(Color.WHITE); c.drawCircle(radius, radius, radius, p); return bm; }
BitmapShader bitmapShader = new BitmapShader(getCircleBitmap(radius / 4), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); // shaderA LinearGradient linearGradient = new LinearGradient(0, 0, w, h, new int[]{Color.RED, Color.BLUE, Color.BLACK, Color.YELLOW}, null, Shader.TileMode.CLAMP); // shaderB ComposeShader composeShader = new ComposeShader(bitmapShader, linearGradient, PorterDuff.Mode.SRC_IN); // SRC_IN paint.setShader(composeShader); canvas.drawRect(0, 0, w, h, paint);
效果如下:
over~~
相关文章推荐
- Android UI开发专题(二) 之绘图基础
- Android UI开发专题(二) 之绘图基础
- Android绘图基础Paint和Canvas介绍-android学习之旅(六十一)
- Android 绘图基础:Path(绘制三角形、贝塞尔曲线、正余弦)
- Android 绘图基础:Canvas画布——自定义View基础(绘制表盘、矩形、圆形、弧、渐变)
- Android UI开发专题(二) 之绘图基础
- Android中Canvas绘图基础详解(附源码下载)
- Android绘图基础Paint和Canvas介绍-android学习之旅(六十一)
- Android 绘图基础:Canvas,Paint,RectF,Paint类
- Android基础入门教程——8.3.1 三个绘图工具类详解
- Android_UI开发专题之绘图基础
- Android绘图基础:Canvas、Paint等的使用
- Android绘图基础:Canvas、Paint、Path的简单使用
- Android绘图基础Paint和Canvas介绍-android学习之旅(六十一)
- Android2D绘图基础
- Android UI开发专题(二) 之绘图基础
- Android UI 绘图基础
- Android绘图基础
- android Shader绘图探究
- Android基础入门教程——8.3.13 Paint API之—— Shader(图像渲染)