Android显示框架:自定义View实践之绘制篇
文章目录
- 一 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);
}
}
它在屏幕上绘制了一个圆形,如图:
在处理绘制的时候有以下几个关键点:
- 处理绘制需要重写绘制方法,常用的是View的onDraw(),当然我们也可以使用其他的绘制方法来处理遮盖关系。
- 完成绘制的是Canvas类,该类提供了绘制系列方法drawXXX()。裁剪系列方法clipXXX()以及几何变换方法translate()方法,还有辅助绘制的Path与Matrix。
- 定制绘制的是Paint类,该类是绘制所用的画笔,可以实现特殊的绘制效果。
我们分别来看看这个关键的角色。
一 View
我们讨论的第一个问题就是View/ViewGroup的绘制顺序问题,绘制在View.draw()方法里调用的,具体的执行顺序是:
- drawBackground():绘制背景,不能重写。
- onDraw():绘制主体。
- dispatchDraw():绘制子View
- 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)后面。
我们来写个例子理解下。
举例
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
举例
//线性渐变
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:辐射范围之外的着色模式
举例
//辐射渐变
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是一个很有用的类,可以利用该类做各种各样的图片裁剪。
//位图着色
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 用来和目标像素相加。
举例
//颜色过滤器
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)
设置图像的抖动。
抖动是指把图像从较高色彩深度(即可用的颜色数)向较低色彩深度的区域绘制时,在图像中有意地插入噪点,通过有规律地扰乱图像来让图像对于肉眼更加真实的做法。
当然这个效果旨在低位色的时候比较有用,例如,ARGB_4444 或者 RGB_565,不过现在Android默认的色彩深度都是32位的ARGB_8888,这个方法的效果没有那么明显。
setFilterBitmap(boolean filter)
设置是否使用双线性过滤来绘制 Bitmap 。
图像在放大绘制的时候,默认使用的是最近邻插值过滤,这种算法简单,但会出现马赛克现象;而如果开启了双线性过滤,就可以让结果图像显得更加平滑。
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
举例
//图形轮廓效果
//绘制圆角
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:阴影颜色
举例
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
分别为:
//设置遮罩图层,处于目标上层图层
//关闭硬件加速
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:是否连接到圆心。如果不连接到圆心就是弧形,如果连接到圆心,就是扇形。
例如
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实践之绘制篇的更多相关文章
- Android中使用自定义View实现下载进度的显示
一般有下载功能的应用都会有这样一个场景,需要一个图标来标识不同的状态.之前在公司的项目中写过一个,今天抽空来整理一下. 一般下载都会有这么几种状态:未开始.等待.正在下载.下载结束,当然有时候会有下载 ...
- Android 初阶自定义 View 字符头像
自己很少做自定义 View ,只有最开始的时候跟着郭神写了一个小 Demo ,后来随着见识的越来越多,特别是在开源社区看到很多优秀的漂亮的控件,都是羡慕的要死,但是拉下来的代码还是看不明白,而且当时因 ...
- Android开发进阶——自定义View的使用及其原理探索
在Android开发中,系统提供给我们的UI控件是有限的,当我们需要使用一些特殊的控件的时候,只靠系统提供的控件,可能无法达到我们想要的效果,这时,就需要我们自定义一些控件,来完成我们想要的效果了.下 ...
- android开发之自定义View 详解 资料整理 小冰原创整理,原创作品。
2019独角兽企业重金招聘Python工程师标准>>> /** * 作者:David Zheng on 2015/11/7 15:38 * * 网站:http://www.93sec ...
- Android 自定义View修炼-Android开发之自定义View开发及实例详解
在开发Android应用的过程中,难免需要自定义View,其实自定义View不难,只要了解原理,实现起来就没有那么难. 其主要原理就是继承View,重写构造方法.onDraw,(onMeasure)等 ...
- android尺子的自定义view——RulerView
项目中用到自定义尺子的样式: 原代码在github上找的,地址:https://github.com/QQabby/HorizontalRuler 原效果为 因为跟自己要使用的view稍有不同 所以 ...
- Android中实现自定义View组件并使其能跟随鼠标移动
场景 实现效果如下 注: 博客: https://blog.csdn.net/badao_liumang_qizhi 关注公众号 霸道的程序猿 获取编程相关电子书.教程推送与免费下载. 实现 新建An ...
- android 缩放平移自定义View 显示图片
1.背景 现在app中,图片预览功能肯定是少不了的,用户基本已经形成条件反射,看到小图,点击看大图,看到大图两个手指开始进行放大,放大后,开始移动到指定部位~~~ 我相信看图的整个步骤,大家或者说用户 ...
- Android 路由框架ARouter最佳实践
转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/76165252 本文出自[赵彦军的博客] 一:什么是路由? 说简单点就是映射页面跳转 ...
随机推荐
- 七个可以提升python程序性能的好习惯,你知道吗?
掌握一些技巧,可尽量提高Python程序性能,也可以避免不必要的资源浪费.今天就为大家带来七个可以提升python程序性能的好习惯,赶快来学习吧:. 1.使用局部变量 尽量使用局部变量代替全局变量:便 ...
- mysql总结思维导图
mysql总结思维导图.脑图 先整理了一个思维导图出来,到时候再继续补充并且深入挖掘一下,再写博文. 另外,看了很多优秀的博文,在这里先mark一下. https://www.cnblogs.com/ ...
- (3.15)常用知识-sql server分页
推荐使用row_number over()方法,或2012以上使用offset PageSize = PageNumber = 方法一:(最常用的分页代码, top / not in) UserId ...
- python web框架 django 用pycharm 添加django项目
用pycharm 创建django项目 用pycharm 启动django 用项目名启动 点击蓝色连接的url 直接跳转到页面 修改 运行django 程序 设置 可以改端口 可以在创建djang ...
- lock,Monitor,Mutex的区别
lock和Monitor的区别 一.lock的底层本身是Monitor来实现的,所以Monitor可以实现lock的所有功能. 二.Monitor有TryEnter的功能,可以防止出现死锁的问题,lo ...
- MyBatis3用户指南
1. 范围和生命周期 SqlSessionFactoryBuilder -->SqlSessionFactory-->SqlSession-->Mapper 实例 SqlSe ...
- 20165324《Java程序设计》第七周
20165324<Java程序设计>第七周 教材学习内容总结 第11章 JDBC与MySOLz数据库 MySQL数据库管理系统,简称MySQL. 使用步骤: 启动MySQL数据库服务 器建 ...
- selenium 模块
介绍 selenium最初是一个自动化测试工具,而爬虫中使用它主要是为了解决requests无法直接执行JavaScript代码的问题 selenium本质是通过驱动浏览器,完全模拟浏览器的操作,比如 ...
- day6-面向对象
Python 面向对象 Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的.本章节我们将详细介绍Python的面向对象编程. 如果你以前没有接触过 ...
- 十八般武艺之 Runloop
嗯,runloop ,看过,用过.但是有时候突然被问到,总是不能很好的描述给他人,也许是程序员本来口拙的缘故吧.另外,也是对runloop还是理解的不够透彻. 于是乎,决定重新整理一下,加深一下印象. ...