文章目录

  • 一 View
  • 二 Paint
    • 2.1 颜色处理
    • 2.2 文字处理
    • 2.3 特殊处理
  • 三 Canvas
    • 3.1 界面绘制
    • 3.2 范围裁切
    • 3.3 集合变换
  • 四 Path
    • 4.1 添加图形
    • 4.3 画线(直线或曲线)
    • 4.3 辅助设置和计算

文章源码

本文还提供了三个综合性的完整实例来辅助理解。

  • View绘制 - 图片标签效果实现
  • Canvas绘图 - 水面涟漪效果实现
  • 二阶贝塞尔曲线的应用 - 杯中倒水效果实现

第一次阅览本系列文章,请参见导读,更多文章请参见文章目录

本篇文章我们来分析View绘制方面的实践。

一个简单的自定义View

public class DrawView extends View {

    Paint paint = new Paint();

    public DrawView(Context context) {
super(context);
} public DrawView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
} public DrawView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
paint.setColor(Color.BLACK);
canvas.drawCircle(150, 150, 150, paint);
}
}

它在屏幕上绘制了一个圆形,如图:

image.png

在处理绘制的时候有以下几个关键点:

  • 处理绘制需要重写绘制方法,常用的是View的onDraw(),当然我们也可以使用其他的绘制方法来处理遮盖关系。
  • 完成绘制的是Canvas类,该类提供了绘制系列方法drawXXX()。裁剪系列方法clipXXX()以及几何变换方法translate()方法,还有辅助绘制的Path与Matrix。
  • 定制绘制的是Paint类,该类是绘制所用的画笔,可以实现特殊的绘制效果。

我们分别来看看这个关键的角色。

一 View

我们讨论的第一个问题就是View/ViewGroup的绘制顺序问题,绘制在View.draw()方法里调用的,具体的执行顺序是:

  1. drawBackground():绘制背景,不能重写。
  2. onDraw():绘制主体。
  3. dispatchDraw():绘制子View
  4. onDrawForeground():绘制滑动边缘渐变、滚动条和前景。

我们先从个小例子开始。

我们如果继承View来实现自定义View。View类的onDraw()是空实现,所以我们的绘制代码写在super.onDraw(canvas)的前面或者后面都没有关系,如下所示:

public class DrawView extends View {
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制代码,写在super.onDraw(canvas)前后均可
}
}

但是如果我们继承特定的控件,例如TextView。我们就需要去考虑TextView的绘制逻辑。

public class DrawView extends TextView {
@Override
protected void onDraw(Canvas canvas) { //写在前面,DrawView的绘制会先于TextView的绘制,TextView绘制的内容可以会覆盖DrawView
super.onDraw(canvas);
//写在后面,DrawView的绘制会晚于TextView的绘制,DrawView绘制的内容可以会覆盖TextView
}
}
  • 写在前面,DrawView的绘制会先于TextView的绘制,TextView绘制的内容可以会覆盖DrawView
  • 写在后面,DrawView的绘制会晚于TextView的绘制,DrawView绘制的内容可以会覆盖TextView

具体怎么做取决于你实际的需求,例如你如果想给TextView加个背景,就写在super.onDraw(canvas)前面,想给TextView前面加些点缀,就
写在super.onDraw(canvas)后面。

我们来写个例子理解下。

举例

image.png
public class LabelImageView extends AppCompatImageView {

    /**
* 梯形距离左上角的长度
*/
private static final int LABEL_LENGTH = 100;
/**
* 梯形斜边的长度
*/
private static final int LABEL_HYPOTENUSE_LENGTH = 100; private Paint textPaint;
private Paint backgroundPaint;
private Path pathText;
private Path pathBackground; public LabelImageView(Context context) {
super(context);
init();
} public LabelImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
} public LabelImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//计算路径
calculatePath(getMeasuredWidth(), getMeasuredHeight());
canvas.drawPath(pathBackground, backgroundPaint);
canvas.drawTextOnPath("Hot", pathText, 100, -20, textPaint);
} @Override
public void onDrawForeground(Canvas canvas) {
super.onDrawForeground(canvas);
} /**
* 计算路径 x1 x2
* ................................ distance(标签离右上角的垂直距离)
* . . . .
* . . .. y1
* . . .
* . . .
* . . y2 height(标签垂直高度)
* . .
* ................................
*/
private void calculatePath(int measuredWidth, int measuredHeight) { int top = 185;
int right = measuredWidth; float x1 = right - LABEL_LENGTH - LABEL_HYPOTENUSE_LENGTH;
float x2 = right - LABEL_HYPOTENUSE_LENGTH;
float y1 = top + LABEL_LENGTH;
float y2 = top + LABEL_LENGTH + LABEL_HYPOTENUSE_LENGTH; pathText.reset();
pathText.moveTo(x1, top);
pathText.lineTo(right, y2);
pathText.close(); pathBackground.reset();
pathBackground.moveTo(x1, top);
pathBackground.lineTo(x2, top);
pathBackground.lineTo(right, y1);
pathBackground.lineTo(right, y2);
pathBackground.close();
} private void init() {
pathText = new Path();
pathBackground = new Path(); textPaint = new Paint();
textPaint.setTextSize(50);
textPaint.setFakeBoldText(true);
textPaint.setColor(Color.WHITE); backgroundPaint = new Paint();
backgroundPaint.setColor(Color.RED);
backgroundPaint.setStyle(Paint.Style.FILL);
}
}

所以你可以看到,当我们继承了一个View,根据需求的不同可以选择性重写我们需要的方法,在super前插入代码和在super后插入代码,效果是不一样的。

  • draw():super.draw()之前,被背景盖住;super.draw()后,盖住前景;
  • onDraw():super.onDraw()之前,背景与主体内容之前;super.onDraw()之后,主体内容和子View之间;
  • dispatchDraw():super.dispatchDraw()之前,主体内容和子View之间;super.dispatchDraw()之后,子View和前景之间;
  • onDrawForeground():super.onDrawForeground()之前,子View和前景之间;super.onDrawForeground()之后,盖住前景;

二 Paint

Paint:顾名思义,画笔,通过Paint可以对绘制行为进行控制。

Paint有三种构造方法

public class Paint {
//空的构造方法
public Paint() {
this(0);
} //传入flags来构造Paint,flags用来控制Paint的行为,例如:抗锯齿等
public Paint(int flags) {
mNativePaint = nInit();
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativePaint);
setFlags(flags | HIDDEN_DEFAULT_PAINT_FLAGS);
// TODO: Turning off hinting has undesirable side effects, we need to
// revisit hinting once we add support for subpixel positioning
// setHinting(DisplayMetrics.DENSITY_DEVICE >= DisplayMetrics.DENSITY_TV
// ? HINTING_OFF : HINTING_ON);
mCompatScaling = mInvCompatScaling = 1;
setTextLocales(LocaleList.getAdjustedDefault());
} //传入另外一个Paint来构造新的Paint
public Paint(Paint paint) {
mNativePaint = nInitWithPaint(paint.getNativeInstance());
NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mNativePaint);
setClassVariablesFrom(paint);
}
}

2.1 颜色处理类

在Paint类中,处理颜色主要有三个方法。

  • setShader(Shader shader):用来处理颜色渐变
  • setColorFilter(ColorFilter filter):用来基于颜色进行过滤处理;
  • setXfermode(Xfermode xfermode) 用来处理源图像和 View 已有内容的关系

setShader(Shader shader)

着色器是图像领域的一个通用概念,它提供的是一套着色规则。

public Shader setShader(Shader shader)

着色器具体由Shader的子类实现:

LinearGradient - 线性渐变

public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, TileMode tile)
  • x0 y0 x1 y1:渐变的两个端点的位置
  • color0 color1 是端点的颜色
  • tile:端点范围之外的着色规则,类型是 TileMode。TileMode 一共有 3 个值可选: CLAMP, MIRROR 和 REPEAT。CLAMP

举例

image.png
//线性渐变
Shader shader1 = new LinearGradient(0, 100, 200, 100, Color.RED, Color.BLUE, Shader.TileMode.CLAMP);
paint1.setShader(shader1); Shader shader2 = new LinearGradient(0, 600, 200, 600, Color.RED, Color.BLUE, Shader.TileMode.MIRROR);
paint2.setShader(shader2); Shader shader3 = new LinearGradient(0, 1100, 200, 1100, Color.RED, Color.BLUE, Shader.TileMode.REPEAT);
paint3.setShader(shader3); canvas.drawRect(0, 100, 1000, 500, paint1);
canvas.drawRect(0, 600, 1000, 1000, paint2);
canvas.drawRect(0, 1100, 1000, 1500, paint3);

SweepGradient - 辐射渐变

public RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, @NonNull TileMode tileMode)
  • centerX centerY:辐射中心的坐标
  • radius:辐射半径
  • centerColor:辐射中心的颜色
  • edgeColor:辐射边缘的颜色
  • tileMode:辐射范围之外的着色模式

举例

image.png
//辐射渐变
Shader shader1 = new RadialGradient(0, 100, 200, Color.RED, Color.BLUE, Shader.TileMode.CLAMP);
paint1.setShader(shader1); Shader shader2 = new RadialGradient(0, 600, 200, Color.RED, Color.BLUE, Shader.TileMode.MIRROR);
paint2.setShader(shader2); Shader shader3 = new RadialGradient(0, 1100, 200, Color.RED, Color.BLUE, Shader.TileMode.REPEAT);
paint3.setShader(shader3); canvas.drawRect(0, 100, 1000, 500, paint1);
canvas.drawRect(0, 600, 1000, 1000, paint2);

BitmapShader - 位图着色

使用位图的像素来填充图形或者文字。

 public BitmapShader(@NonNull Bitmap bitmap, TileMode tileX, TileMode tileY)
  • bitmap:用来做模板的 Bitmap 对象
  • tileX:横向的 TileMode
  • tileY:纵向的 TileMode。

举例

BitmapShader是一个很有用的类,可以利用该类做各种各样的图片裁剪。

image.png
//位图着色
Shader shader1 = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
paint1.setShader(shader1); //绘制圆形
canvas.drawCircle(500, 500, 300, paint1);

ComposeShader - 组合Shader

ComposeShader可以将连个Shader组合在一起。

public ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)
  • shaderA, shaderB:两个相继使用的 Shader
  • mode: 两个 Shader 的叠加模式,即 shaderA 和 shaderB 应该怎样共同绘制。它的类型是PorterDuff.Mode。

PorterDuff.Mode用来指定两个Shader叠加时颜色的绘制策略,它有很多种策略,也就是以一种怎样的模式来与原图像进行合成,具体如下:

蓝色矩形为原图像,红色圆形为目标图像。

更多细节可以参见PorterDuff.Mode官方文档

setColorFilter(ColorFilter filter)

颜色过滤器可以将颜色按照一定的规则输出,常见于各种滤镜效果。

public ColorFilter setColorFilter(ColorFilter filter)

我们通常使用的是ColorFilter的三个子类:

LightingColorFilter - 模拟光照效果

public LightingColorFilter(int mul, int add)

mul 和 add 都是和颜色值格式相同的 int 值,其中 mul 用来和目标像素相乘,add 用来和目标像素相加。

举例

image.png
//颜色过滤器
ColorFilter colorFilter1 = new LightingColorFilter(Color.RED, Color.BLUE);
paint2.setColorFilter(colorFilter1); canvas.drawBitmap(bitmapTimo, null, rect1, paint1);
canvas.drawBitmap(bitmapTimo, null, rect2, paint2);

PorterDuffColorFilter - 模拟颜色混合效果

public PorterDuffColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode)

PorterDuffColorFilter指定一种颜色和PorterDuff.Mode来与源图像就行合成,也就是以一种怎样的模式来与原图像进行合成,我们在上面已经讲过这个内容。

举例

//我们在使用Xfermode的时候也是使用它的子类PorterDuffXfermode
Xfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);
canvas.drawBitmap(rectBitmap, 0, 0, paint); // 画方
paint.setXfermode(xfermode); // 设置 Xfermode
canvas.drawBitmap(circleBitmap, 0, 0, paint); // 画圆
paint.setXfermode(null); // 用完及时清除 Xfermode

ColorMatrixColorFilter - 颜色矩阵过滤

ColorMatrixColorFilter使用一个颜色矩阵ColorMatrix来对象图像进行处理。

public ColorMatrixColorFilter(ColorMatrix matrix)

ColorMatrix是一个4x5的矩阵

[ a, b, c, d, e,
f, g, h, i, j,
k, l, m, n, o,
p, q, r, s, t ]

通过计算,ColorMatrix可以对要绘制的像素进行转换,如下:

R’ = a*R + b*G + c*B + d*A + e;
G’ = f*R + g*G + h*B + i*A + j;
B’ = k*R + l*G + m*B + n*A + o;
A’ = p*R + q*G + r*B + s*A + t;

利用ColorMatrixColorFilter(可以实现很多炫酷的滤镜效果。

setXfermode(Xfermode xfermode)

Paint.setXfermode(Xfermode xfermode)方法,它也是一种混合图像的方法。

Xfermode 指的是你要绘制的内容和 Canvas 的目标位置的内容应该怎样结合计算出最终的颜色。但通俗地说,其实就是要你以绘制的内容作为源图像,以View中已有的内
容作为目标图像,选取一个PorterDuff.Mode作为绘制内容的颜色处理方案。

小结

关于PorterDuff.Mode,我们已经提到

  • ComposeShader:混合两个Shader
  • PorterDuffColorFilter:增加一个单色的ColorFilter
  • Xfermode:指定原图像与目标图像的混合模式

这三种以不同的方式来使用PorterDuff.Mode,但是原理都是一样的。

2.2 文字处理类

Paint里有大量方法来设置文字的绘制属性,事实上文字在Android底层是被当做图片来处理的。

  • setTextSize(float textSize):设置文字大小
  • setTypeface(Typeface typeface):设置文字字体
  • setFakeBoldText(boolean fakeBoldText):是否使用伪粗体(并不是提到size,而是在运行时描粗的)
  • setStrikeThruText(boolean strikeThruText):是否添加删除线
  • setUnderlineText(boolean underlineText):是否添加下划线
  • setTextSkewX(float skewX):设置文字倾斜度
  • setTextScaleX(float scaleX):设置文字横向缩放
  • setLetterSpacing(float letterSpacing):设置文字间距
  • setFontFeatureSettings(String settings):使用CSS的font-feature-settings的方式来设置文字。
  • setTextAlign(Paint.Align align):设置文字对齐方式
  • setTextLocale(Locale locale):设置文字Local
  • setHinting(int mode):设置字体Hinting(微调),过向字体中加入 hinting 信息,让矢量字体在尺寸过小的时候得到针对性的修正,从而提高显示效果。
  • setSubpixelText(boolean subpixelText):设置次像素级抗锯齿,根据程序所运行的设备的屏幕类型,来进行针对性的次像素级的抗锯齿计算,从而达到更好的抗锯齿效果。

2.3 特殊效果类

setAntiAlias (boolean aa)

设置抗锯齿,默认关闭,用来是图像的绘制更加圆润。我们还可以在初始化的时候设置Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);。

setStyle(Paint.Style style)

设置填充风格,

  • FILL 模式,填充
  • STROKE 模式,画线
  • FILL_AND_STROKE 模式,填充 + 画线

如果是划线模式,我们针对线条还可以有多种设置。

setStrokeWidth(float width) - 设置线条粗细

setStrokeCap(Paint.Cap cap) - 设置线头的形状,默认为 BUTT

  • UTT 平头
  • ROUND 圆头
  • SQUARE 方头

setStrokeJoin(Paint.Join join) - 设置拐角的形状。默认为 MITER

  • MITER 尖角
  • BEVEL 平角
  • ROUND 圆角

setStrokeMiter(float miter)- 设置 MITER 型拐角的延长线的最大值

setDither(boolean dither)

设置图像的抖动。

image.png

抖动是指把图像从较高色彩深度(即可用的颜色数)向较低色彩深度的区域绘制时,在图像中有意地插入噪点,通过有规律地扰乱图像来让图像对于肉眼更加真实的做法。

当然这个效果旨在低位色的时候比较有用,例如,ARGB_4444 或者 RGB_565,不过现在Android默认的色彩深度都是32位的ARGB_8888,这个方法的效果没有那么明显。

setFilterBitmap(boolean filter)

设置是否使用双线性过滤来绘制 Bitmap 。

image.png

图像在放大绘制的时候,默认使用的是最近邻插值过滤,这种算法简单,但会出现马赛克现象;而如果开启了双线性过滤,就可以让结果图像显得更加平滑。

etPathEffect(PathEffect effect)

设置图形的轮廓效果。Android有六种PathEffect:

  • CornerPathEffect:将拐角绘制成圆角
  • DiscretePathEffect:将线条进行随机偏离
  • DashPathEffect:绘制虚线
  • PathDashPathEffect:使用指定的Path来绘制虚线
  • SumPathEffect:组合两个PathEffect,叠加应用。
  • ComposePathEffect:组合两个PathEffect,叠加应用。

CornerPathEffect(float radius)

  • float radius圆角半径

DiscretePathEffect(float segmentLength, float deviation)

  • float segmentLength:用来拼接每个线段的长度,
  • float deviation:偏离量

DashPathEffect(float[] intervals, float phase)

  • float[] intervals:指定了虚线的格式,数组中元素必须为偶数(最少是 2 个),按照「画线长度、空白长度、画线长度、空白长度」……的顺序排列
  • float phase:虚线的偏移量

PathDashPathEffect(Path shape, float advance, float phase, PathDashPathEffect.Style style)

  • Path shape:用来绘制的Path
  • float advance:两个相邻Path段起点间的间隔
  • float phase:虚线的偏移量
  • PathDashPathEffect.Style style:指定拐弯改变的时候 shape 的转换方式:TRANSLATE:位移、ROTATE:旋转、MORPH:变体

SumPathEffect(PathEffect first, PathEffect second)

  • PathEffect first:同时应用的PathEffect
  • PathEffect second:同时应用的PathEffect

ComposePathEffect(PathEffect outerpe, PathEffect innerpe)

  • PathEffect outerpe:后应用的PathEffect
  • PathEffect innerpe:先应用的PathEffect

举例

image.png
//图形轮廓效果
//绘制圆角
PathEffect cornerPathEffect = new CornerPathEffect(20);
paint1.setStyle(Paint.Style.STROKE);
paint1.setStrokeWidth(5);
paint1.setPathEffect(cornerPathEffect); //绘制尖角
PathEffect discretePathEffect = new DiscretePathEffect(20, 5);
paint2.setStyle(Paint.Style.STROKE);
paint2.setStrokeWidth(5);
paint2.setPathEffect(discretePathEffect); //绘制虚线
PathEffect dashPathEffect = new DashPathEffect(new float[]{20,10, 5, 10}, 0);
paint3.setStyle(Paint.Style.STROKE);
paint3.setStrokeWidth(5);
paint3.setPathEffect(dashPathEffect); //使用path来绘制虚线
Path path = new Path();//画一个三角来填充虚线
path.lineTo(40, 40);
path.lineTo(0, 40);
path.close();
PathEffect pathDashPathEffect = new PathDashPathEffect(path, 40, 0, PathDashPathEffect.Style.TRANSLATE);
paint4.setStyle(Paint.Style.STROKE);
paint4.setStrokeWidth(5);
paint4.setPathEffect(pathDashPathEffect);

setShadowLayer(float radius, float dx, float dy, int shadowColor)

设置阴影图层,处于目标下层图层。

  • float radius:阴影半径
  • float dx:阴影偏移量
  • float dy:阴影偏移量
  • int shadowColor:阴影颜色

举例

image.png
paint1.setTextSize(200);
paint1.setShadowLayer(10, 0, 0, Color.RED);
canvas.drawText("Android", 80, 300 ,paint1);

注:在硬件加速开启的情况下, setShadowLayer() 只支持文字的绘制,文字之外的绘制必须关闭硬件加速才能正常绘制阴影。如果 shadowColor 是半透明的,阴影的透明度就使用 shadowColor 自己
的透明度;而如果 shadowColor 是不透明的,阴影的透明度就使用 paint 的透明度。

setMaskFilter(MaskFilter maskfilter)

设置图层遮罩层,处于目标上层图层。

MaskFilter有两个子类:

  • BlurMaskFilter:模糊效果
  • BlurMaskFilter:浮雕效果

举例

模糊效果

  • BlurMaskFilter.Blur.NORMAL
  • BlurMaskFilter.Blur.SOLD
  • BlurMaskFilter.Blur.INNER
  • BlurMaskFilter.Blur.OUTTER

分别为:

image.png
image.png
image.png
image.png
//设置遮罩图层,处于目标上层图层
//关闭硬件加速
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
MaskFilter blurMaskFilter = new BlurMaskFilter(200, BlurMaskFilter.Blur.NORMAL);
paint2.setMaskFilter(blurMaskFilter); canvas.drawBitmap(bitmapTimo, null, rect1, paint1);
canvas.drawBitmap(bitmapTimo, null, rect2, paint2);

注:在硬件加速开启的情况下, setMaskFilter(MaskFilter maskfilter)只支持文字的绘制,文字之外的绘制必须关闭硬件加速才能正常绘制阴影。关闭硬件加速可以调用
View.setLayerType(View.LAYER_TYPE_SOFTWARE, null)或者在Activity标签里设置android:hardwareAccelerated="false"。

三 Canvas

Canvas实现了Android 2D图形的绘制,底层基于Skia实现。

3.1 界面绘制

Canvas提供了丰富的对象绘制方法,一般都以drawXXX()打头,绘制的对象包括:

  • 弧线(Arcs)
  • 颜色(Argb、Color)
  • 位图(Bitmap)
  • 圆(Circle)
  • 点(Point)
  • 线(Line)
  • 矩形(Rect)
  • 图片(Picture)
  • 圆角矩形(RoundRect)
  • 文本(Text)
  • 顶点(Vertices)
  • 路径(Path)

这里的方法大都很简单,我们来描述下期中比较复杂的方法。

弧线

public void drawArc(float left, float top, float right, float bottom, float startAngle,
float sweepAngle, boolean useCenter, @NonNull Paint paint) {
native_drawArc(mNativeCanvasWrapper, left, top, right, bottom, startAngle, sweepAngle,
useCenter, paint.getNativeInstance());
}
  • float left, float top, float right, float bottom:左、上、右、下的坐标。
  • float startAngle:弧形起始角度,Android坐标系x轴正右的方向是0度的位置,顺时针为正角度,逆时针为负角度。
  • float sweepAngle:弧形划过的角度。
  • boolean useCenter:是否连接到圆心。如果不连接到圆心就是弧形,如果连接到圆心,就是扇形。

例如

image.png
paint.setStyle(Paint.Style.FILL);//填充模式
canvas.drawArc(200, 100, 800, 500, -110, 100, true, paint);
canvas.drawArc(200, 100, 800, 500, 20, 140, false, paint);
paint.setStyle(Paint.Style.STROKE);//画线模式
paint.setStrokeWidth(5);
canvas.drawArc(200, 100, 800, 500, 180, 60, false, paint);

位图

  • public void drawBitmap(@NonNull Bitmap bitmap, float left, float top, @Nullable Paint paint) - 绘制位图
  • public void drawBitmapMesh(@NonNull Bitmap bitmap, int meshWidth, int meshHeight,
    @NonNull float[] verts, int vertOffset, @Nullable int[] colors, int colorOffset,
    @Nullable Paint paint) - 绘制拉伸位图

第一个方法很简单,就是在指定的坐标处开始绘制位图。我们着重来看看第二个方法,这个方法不是很常用(可能是计算比较复杂的锅

Android显示框架:自定义View实践之绘制篇的更多相关文章

  1. Android中使用自定义View实现下载进度的显示

    一般有下载功能的应用都会有这样一个场景,需要一个图标来标识不同的状态.之前在公司的项目中写过一个,今天抽空来整理一下. 一般下载都会有这么几种状态:未开始.等待.正在下载.下载结束,当然有时候会有下载 ...

  2. Android 初阶自定义 View 字符头像

    自己很少做自定义 View ,只有最开始的时候跟着郭神写了一个小 Demo ,后来随着见识的越来越多,特别是在开源社区看到很多优秀的漂亮的控件,都是羡慕的要死,但是拉下来的代码还是看不明白,而且当时因 ...

  3. Android开发进阶——自定义View的使用及其原理探索

    在Android开发中,系统提供给我们的UI控件是有限的,当我们需要使用一些特殊的控件的时候,只靠系统提供的控件,可能无法达到我们想要的效果,这时,就需要我们自定义一些控件,来完成我们想要的效果了.下 ...

  4. android开发之自定义View 详解 资料整理 小冰原创整理,原创作品。

    2019独角兽企业重金招聘Python工程师标准>>> /** * 作者:David Zheng on 2015/11/7 15:38 * * 网站:http://www.93sec ...

  5. Android 自定义View修炼-Android开发之自定义View开发及实例详解

    在开发Android应用的过程中,难免需要自定义View,其实自定义View不难,只要了解原理,实现起来就没有那么难. 其主要原理就是继承View,重写构造方法.onDraw,(onMeasure)等 ...

  6. android尺子的自定义view——RulerView

    项目中用到自定义尺子的样式: 原代码在github上找的,地址:https://github.com/QQabby/HorizontalRuler 原效果为 因为跟自己要使用的view稍有不同  所以 ...

  7. Android中实现自定义View组件并使其能跟随鼠标移动

    场景 实现效果如下 注: 博客: https://blog.csdn.net/badao_liumang_qizhi 关注公众号 霸道的程序猿 获取编程相关电子书.教程推送与免费下载. 实现 新建An ...

  8. android 缩放平移自定义View 显示图片

    1.背景 现在app中,图片预览功能肯定是少不了的,用户基本已经形成条件反射,看到小图,点击看大图,看到大图两个手指开始进行放大,放大后,开始移动到指定部位~~~ 我相信看图的整个步骤,大家或者说用户 ...

  9. Android 路由框架ARouter最佳实践

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/76165252 本文出自[赵彦军的博客] 一:什么是路由? 说简单点就是映射页面跳转 ...

随机推荐

  1. 七个可以提升python程序性能的好习惯,你知道吗?

    掌握一些技巧,可尽量提高Python程序性能,也可以避免不必要的资源浪费.今天就为大家带来七个可以提升python程序性能的好习惯,赶快来学习吧:. 1.使用局部变量 尽量使用局部变量代替全局变量:便 ...

  2. mysql总结思维导图

    mysql总结思维导图.脑图 先整理了一个思维导图出来,到时候再继续补充并且深入挖掘一下,再写博文. 另外,看了很多优秀的博文,在这里先mark一下. https://www.cnblogs.com/ ...

  3. (3.15)常用知识-sql server分页

    推荐使用row_number over()方法,或2012以上使用offset PageSize = PageNumber = 方法一:(最常用的分页代码, top / not in) UserId ...

  4. python web框架 django 用pycharm 添加django项目

    用pycharm 创建django项目 用pycharm 启动django  用项目名启动 点击蓝色连接的url 直接跳转到页面 修改 运行django 程序 设置  可以改端口 可以在创建djang ...

  5. lock,Monitor,Mutex的区别

    lock和Monitor的区别 一.lock的底层本身是Monitor来实现的,所以Monitor可以实现lock的所有功能. 二.Monitor有TryEnter的功能,可以防止出现死锁的问题,lo ...

  6. MyBatis3用户指南

    1. 范围和生命周期     SqlSessionFactoryBuilder -->SqlSessionFactory-->SqlSession-->Mapper 实例 SqlSe ...

  7. 20165324《Java程序设计》第七周

    20165324<Java程序设计>第七周 教材学习内容总结 第11章 JDBC与MySOLz数据库 MySQL数据库管理系统,简称MySQL. 使用步骤: 启动MySQL数据库服务 器建 ...

  8. selenium 模块

    介绍 selenium最初是一个自动化测试工具,而爬虫中使用它主要是为了解决requests无法直接执行JavaScript代码的问题 selenium本质是通过驱动浏览器,完全模拟浏览器的操作,比如 ...

  9. day6-面向对象

    Python 面向对象 Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的.本章节我们将详细介绍Python的面向对象编程. 如果你以前没有接触过 ...

  10. 十八般武艺之 Runloop

    嗯,runloop ,看过,用过.但是有时候突然被问到,总是不能很好的描述给他人,也许是程序员本来口拙的缘故吧.另外,也是对runloop还是理解的不够透彻. 于是乎,决定重新整理一下,加深一下印象. ...