文章目录

  • 1、简介
  • 2、Paint 类常用方法介绍
    • 1)setColor() 设置颜色
    • 2) setStrokeWidth() 设置宽度
    • 3)setAntiAlias(true) 抗齿距
    • 4) setAlpha() 设置画笔透明度
    • 5)setARGB() 设置透明度 和 颜色
    • 6) setStyle() 设置画笔样式
  • 3、线条样式相关的类
    • 1) setStrokeJoin() 线条连接处样式
    • 2)setStrokeMiter() 斜接模式长度限制
    • 3)、setStrokeCap() 设置线头的模式
    • 4)、setPathEffect()设置所有拐角变成圆角。
    • 5) DashPathEffect()设置线条为虚线
    • 6)、PathDashPathEffect() 使用path 绘制虚线
    • 7) DiscretePathEffect() 设置线条随机偏移
    • 8) SumPathEffect () 两种线条模式都执行
    • 9) 、ComposePathEffect()线条组合模式
  • 4、setShader()Gradient 着色渐变 渲染相关的类
    • 1)LinearGradient 线性渐变
    • 2) 、径向渐变 RadialGradient()
    • 3) 、扫描渐变 SweepGradient()
    • 4)位图渐变 BitmapShader
    • 5)混合 渐变( ComposeShader )
  • 5、颜色效果处理
    • 1) LightingColorFilter 设定 基本色素 黄绿黑
    • 2)、PorterDuffColorFilter 设置颜色 模式运算
    • 3)、ColorMatrixColorFilter 色彩锐度等
    • 4)setXfermode 图片转换模式
      • Xfermode 注意事项
  • 6、色彩优化
    • 1)、setDither(boolean dither) 设置图像抖动
    • 2)、setFilterBitmap(boolean filter) 线性过滤
  • 7、设置阴影或者上层效果
    • 1)、setShadowLayer () 设置阴影
    • 2)setMaskFilter(MaskFilter maskfilter) 绘制层上附件效果
  • 8、获取实际路径
    • 1) getFillPath(Path src, Path dst)
    • 2) getTextPath 获取文本路径

1、简介

在Android 中,2D 的图形绘制一般使用的是 Graphics2D 这个类。
Graphics 类,提供了对几何形状、坐标转换、颜色管理和文本布局更为复杂的控制。它是用于在 Java™ 平台上呈现二维形状、文本和图像的基础类。

自定义View 一般我们需要自己绘制一些图形,这就像画家一样,我们需要画笔和画布。

在Android 中整好有两个这样的类,来方便我们自己创作View.
Paint 类 我们称之为 画笔,为画图的过程中,定义各种参数,比如:颜色、线条样式、图案样式等。
Canvas 类我们定义为画布,主要提供若干方法用于绘制各种颜色图案:点、线、路径等。

我们绘制自定义View的时候,一般先定义Paint ,指定绘图参数,再通过Canvas 进行绘制图形。

自定义 MyView 继承 View

package myapplication.lum.com.myapplication;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;public class MyView extends View {String TAG = "lum_ : ";Paint paint;public MyView(Context context) {super(context);Log.i(TAG,"1");}public MyView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);Log.i(TAG,"2");}public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);Log.i(TAG,"3");}public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);Log.i(TAG,"4");}protected void onDraw(Canvas canvas) {//图形绘制}}

布局文件 引用:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><myapplication.lum.com.myapplication.MyViewandroid:layout_width="wrap_content"android:layout_height="wrap_content" /></LinearLayout>

2、Paint 类常用方法介绍

Paint 类 主要可以实现 画笔的 颜色、透明度、文本 样式等

1)setColor() 设置颜色

我们可以很简单的知道这是设置 画笔的颜色

paint.setColor(Color.RED);  //设置画笔为红色
2) setStrokeWidth() 设置宽度

设置画笔的宽度

        paint.setStrokeWidth(10);//设置画笔宽度 ,单位px

在画笔宽度为 0 的情况下,使用 drawLine 或者使用描边模式(STROKE)也可以绘制出内容。只是绘制出的内容始终是 1 像素,不受画布缩放的影响。该模式被称为hairline mode (发际线模式)。

如果你设置了画笔宽度为 1 像素,那么如果画布放大到 2 倍,1 像素会变成 2 像素。但如果是 0 像素,那么不论画布如何缩放,绘制出来的宽度依旧为 1 像素。

        paint.setStrokeWidth(0);//设置画笔宽度0  ,单位px  默认一个像素paint.setColor(Color.BLUE);paint.setStyle(Paint.Style.STROKE);canvas.scale(10,10); //放大 画布 10倍canvas.drawCircle(50,50,20,paint);

并没有改变画笔像素大小

更改宽度参数:

        paint.setStrokeWidth(1);//设置画笔宽度1  ,单位pxpaint.setColor(Color.BLUE);paint.setStyle(Paint.Style.STROKE);canvas.scale(10,10); //放大 画布 10倍canvas.drawCircle(50,50,20,paint);

画笔像素被跟随放大:

3)setAntiAlias(true) 抗齿距

设置是否开启抗齿距: true 开启;false ,不开启

        paint.setAntiAlias(true);//抗锯齿功能

抗锯齿是指在图像中,物体边缘总会或多或少的呈现三角形的锯齿,而抗锯齿就是指对图像边缘进行柔化处理,使图像边缘看起来更平滑,更接近实物的物体。说白了,就是画面你看上去,有一小格一小格的东西。抗拒齿就是画面更加精细。

我们画一条直线 对双数三个方法做一次对比:
自定义MyView 继承View 主要代码列出:

    public MyView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);Log.i(TAG,"2");//就是通过修改画笔的一些参数设置paint = new Paint(); //新建一个画笔对象paint.setAntiAlias(true);//抗锯齿功能 打开paint.setColor(Color.RED);  //设置画笔颜色 红色paint.setStrokeWidth(10);//设置画笔宽度 10,单位px}protected void onDraw(Canvas canvas) {//画一条直线canvas.drawLine(0,0,200,200,paint);}

修改 paint 参数:

   public MyView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);Log.i(TAG,"2");//就是通过修改画笔的一些参数设置paint = new Paint(); //新建一个画笔对象paint.setAntiAlias(false);//抗锯齿功能 关闭paint.setColor(Color.BLUE);  //设置画笔颜色  蓝色paint.setStrokeWidth(40);//设置画笔宽度 40  ,单位px}protected void onDraw(Canvas canvas) {//画一条直线canvas.drawLine(0,0,200,200,paint);}

截个图给你看看:

4) setAlpha() 设置画笔透明度
public void setAlpha(int a) {      nSetAlpha(mNativePaint, a);
}

取值范围 0~ 255
0是完全透明,255是完全不透明,数值越大 越不透明

  public MyView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);Log.i(TAG,"2");//就是通过修改画笔的一些参数设置paint = new Paint(); //新建一个画笔对象paint.setAntiAlias(true);//抗锯齿功能paint.setStrokeWidth(100);//设置画笔宽度 ,单位pxpaint.setColor(Color.BLACK); //设置 黑色paint.setAlpha(255); **//设置255  完全不透明**}protected void onDraw(Canvas canvas) {//画一个圆canvas.drawCircle(500, 500,400, paint);//画一个点}

修改参数:

  paint.setAlpha(10); //设置255  完全不透明

5)setARGB() 设置透明度 和 颜色
    public void setARGB(int a, int r, int g, int b) {setColor((a << 24) | (r << 16) | (g << 8) | b);}

setARGB 设置有四个参数,取值范围都是 0~255,
argb形式alpha,red,green,blue,
第一个参数 是 透明度的 设置
第二个、第三个、第四个 参数分别的 红绿黄 三个基本色素值,来控制画笔颜色

使用方法基本同上。

paint.setARGB(100,0,0,0);

就是意思 设置 透明度为 100, 000 Android 色度代码 表示黑色 的画笔。

6) setStyle() 设置画笔样式

设置绘制的图形是空心样式还是实心样式,默认为实心样式。

    public void setStyle(Style style) {nSetStyle(mNativePaint, style.nativeInt);}

画笔样式有三种,
Paint.Style.FILL:填充内部
Paint.Style.STROKE :描边
Paint.Style.FILL_AND_STROKE :填充内部和描边

我们画一个圆来给大家展示一下 这几个的区别

    public MyView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);Log.i(TAG,"2");//就是通过修改画笔的一些参数设置paint = new Paint(); paint.setAntiAlias(true);//抗锯齿功能paint.setStrokeWidth(100);//设置画笔宽度 ,单位px}protected void onDraw(Canvas canvas) {paint.setColor(Color.BLUE);paint.setStyle(Paint.Style.FILL); //设置填充模式canvas.drawCircle(500, 500,400, paint);//画一个圆}

修改一下样式的参数:

        paint.setStyle(Paint.Style.STROKE); //描边模式


修改一下样式:

paint.setStyle(Paint.Style.FILL_AND_STROKE);  //填充 与 描边模式

很多人看来
paint.setStyle(Paint.Style.FILL); //设置填充模式
paint.setStyle(Paint.Style.FILL_AND_STROKE); //填充 与 描边模式

看起来没有什么差别。
下面我们把这两种模式放到一起比较:
FILL_AND_STROKE 模式画一个 红色的圆
FILL 模式画一个 蓝色的 透明圆

    public MyView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);Log.i(TAG,"2");//就是通过修改画笔的一些参数设置paint = new Paint(); //新建一个画笔对象paint.setAntiAlias(true);//抗锯齿功能paint.setStrokeWidth(100);//设置画笔宽度 ,单位px}protected void onDraw(Canvas canvas) {paint.setColor(Color.RED); //设置红色paint.setStyle(Paint.Style.FILL_AND_STROKE);  //设置 填充 与 描边 样式canvas.drawCircle(500, 500,400, paint);//画圆paint.reset(); //重置画笔paint.setColor(Color.BLUE);  // 蓝色paint.setStyle(Paint.Style.FILL); //填充模式paint.setAlpha(100); canvas.drawCircle(500, 500,400, paint); //画圆}

我们看到 虽然 他们的 半径,坐标 都是一样的 ,但是最终显示的大小却不是一样的。

我们 再把 填充模式 和 扫描 模式 放到一起比较就清楚了:
FILL 模式画一个 红色的圆
STROKE 模式画一个 蓝色的 透明圆

  public MyView(Context context, @Nullable AttributeSet attrs) {super(context, attrs);Log.i(TAG,"2");//就是通过修改画笔的一些参数设置paint = new Paint(); //新建一个画笔对象paint.setAntiAlias(true);//抗锯齿功能paint.setStrokeWidth(100);//设置画笔宽度 ,单位px}protected void onDraw(Canvas canvas) {paint.setColor(Color.RED); //设置红色paint.setStyle(Paint.Style.FILL);  //设置 填充canvas.drawCircle(500, 500,400, paint);//画圆paint.setColor(Color.BLUE);  // 蓝色paint.setAlpha(100);paint.setStyle(Paint.Style.STROKE); //描边 模式canvas.drawCircle(500, 500,400, paint); //画圆}

因此我们可以看到 ,描边模式 显示出来的 半径 是比填充模式大的,
描边模式/填充与描边模式 实际显示半径 r = 圆半径 + (画笔的宽度 / 2)

3、线条样式相关的类

1) setStrokeJoin() 线条连接处样式

样条连接处的样式有三种:

public static enum Join {
BEVEL,
MITER,
ROUND
}

**当绘图样式为 STROKRE / FILL_AND_STROKE 的时候,**这个方法用于指定线条连接拐角样式:

我们代码示例:
Paint 图形样式 设置为 STROKRE,连接处样式 设置为 BEVEL:

    protected void onDraw(Canvas canvas) {paint.setStrokeWidth(50);//设置画笔宽度 ,单位pxpaint.setColor(Color.BLUE);paint.setStyle(Paint.Style.STROKE); //设置绘图模式 扫描paint.setStrokeJoin(Paint.Join.BEVEL); //设置线条闭合模式 BEVELcanvas.drawRect(100,100,500,500,paint); //画矩形Path path = new Path();  //绘制路径 画一个闭合三角形path.moveTo(100,700);path.lineTo(300,900);path.lineTo(100,1200);path.close();canvas.drawPath(path,paint);}

我们可以看到:
连接处 的样式

当我们改变参数:

          paint.setStrokeJoin(Paint.Join.MITER); //设置线条闭合模式 MITER

当我们改变参数:

    paint.setStyle(Paint.Style.FILL_AND_STROKE); //设置绘图模式 扫描 FILL_AND_STROKEpaint.setStrokeJoin(Paint.Join.ROUND); //设置线条闭合模式 MITER

2)setStrokeMiter() 斜接模式长度限制

Android 中线段连接方式默认是 MITER,即在拐角处延长外边缘,直到相交位置。

根据数学原理我们可知,如果夹角足够小,接近于零,那么交点位置就会在延长线上无限远的位置。 为了避免这种情况,如果连接模式为 MITER(尖角),当连接角度小于一定程度时会自动将连接模式转换为 BEVEL(平角)。

那么多大的角度算是比较小呢?根据资料显示,这个角度大约是 28.96°,即 MITER(尖角) 模式下小于该角度的线段连接方式会自动转换为 BEVEL(平角) 模式。

我们代码测试一下:
我们设置 连接模式为 MITER ,但是绘制一个连接角度很小的路径

  protected void onDraw(Canvas canvas) {paint.setStrokeWidth(50);//设置画笔宽度 ,单位pxpaint.setColor(Color.BLUE);paint.setStyle(Paint.Style.STROKE);paint.setStrokeJoin(Paint.Join.MITER); //设置线条闭合模式 MITERPath path = new Path();  //绘制路径 画一个闭合三角形path.moveTo(100,700);path.lineTo(600,700);path.lineTo(100,800);path.close();canvas.drawPath(path,paint);}

我们可以看到 小角度 下,确实是 MITER(尖角) 连接模式 自动转换成了 BEVEL (平角)
这个角度 我们计算可得 角度为 11.31 度

这个方法是对于 setStrokeJoin() 的一个补充,它用于设置 MITER 型拐角的延长线的最大值。所谓「延长线的最大值」,是这么一回事:

当线条拐角为 MITER 时,拐角处的外缘需要使用延长线来补偿

而这种补偿方案会有一个问题:如果拐角的角度太小,就有可能由于出现连接点过长的情况。比如这样:
所以为了避免意料之外的过长的尖角出现, MITER 型连接点有一个额外的规则:当尖角过长时,自动改用 BEVEL 的方式来渲染连接点。例如上图的这个尖角,在默认情况下是不会出现的,而是会由于延长线过长而被转为 BEVEL 型连接点:


用几何知识很容易得出这个比值的计算公式:如果拐角的大小为 θ ,那么这个比值就等于 1 / sin ( θ / 2 ) 。

这个 miter limit 的默认值是 4,对应的是一个大约 29° 的锐角:

默认情况下,大于这个角的尖角会被保留,而小于这个夹角的就会被「削成平头」

所以设置 这个值 越大,则可以保证越小的角度显示出来尖角。
一般设置的参数值 ,和角度值参照表

3)、setStrokeCap() 设置线头的模式

该方法用于设置落笔时的样式,控制我们的画笔在离开画板时留下的最后一点图形,
可选值如下:

public static enum Cap {
BUTT,
ROUND,
SQUARE
}

我们代码展示一下:

  protected void onDraw(Canvas canvas) {paint.setStrokeWidth(80);//设置画笔宽度 ,单位pxpaint.setColor(Color.BLUE);paint.setStyle(Paint.Style.STROKE);paint.setStrokeCap(Paint.Cap.BUTT);   //设置平头canvas.drawLine(300,300,700,300,paint);paint.setStrokeCap(Paint.Cap.ROUND); //设置圆头canvas.drawLine(300,500,700,500,paint);paint.setStrokeCap(Paint.Cap.SQUARE); //设置方头canvas.drawLine(300,700,700,700,paint);}

4)、setPathEffect()设置所有拐角变成圆角。
PathEffect pathEffect = new CornerPathEffect(20);
paint.setPathEffect(pathEffect);...canvas.drawPath(path, paint);

它的构造方法 CornerPathEffect(float radius) 的参数 radius 是圆角的半径。

CornerPathEffect 也可以让手绘效果更加圆润。

5) DashPathEffect()设置线条为虚线

DashPathEffect 用于实现虚线效果(适用于 STROKE 或 FILL_AND_STROKE 样式)。

// intervals:必须为偶数,用于控制显示和隐藏的长度。
// phase:相位。
DashPathEffect(float intervals[], float phase)

我们代码测试:

        PathEffect pathEffectOne = new DashPathEffect(new float[]{100, 50}, 0);paint.setPathEffect(pathEffectOne);Path pathOne = new Path();pathOne.moveTo(100,50);pathOne.lineTo(100,1000);canvas.drawPath(pathOne,paint);//  canvas.drawLine(100,50,100,1000,paint);Path pathTwo = new Path();PathEffect pathEffectTwo = new DashPathEffect(new float[]{100, 50},   50);  //偏移量 添加 50 paint.setPathEffect(pathEffectTwo);pathTwo.moveTo(200,50);pathTwo.lineTo(200,1000);canvas.drawPath(pathTwo,paint);

6)、PathDashPathEffect() 使用path 绘制虚线

它的构造方法 PathDashPathEffect(Path shape, float advance, float phase, PathDashPathEffect.Style style) 中,
shape 参数是用来绘制的 Path ;
advance 是两个相邻的 shape 段之间的间隔,不过注意,这个间隔是两个 shape 段的起点的间隔,而不是前一个的终点和后一个的起点的距离;
phase 和 DashPathEffect 中一样,是虚线的偏移;
最后一个参数 style,是用来指定拐弯改变的时候 shape 的转换方式。
style 的类型为 PathDashPathEffect.Style ,是一个 enum ,具体有三个值:
TRANSLATE:位移
ROTATE:旋转
MORPH:变体

Path dashPath = ...; // 使用一个三角形来做 dash
PathEffect pathEffect = new PathDashPathEffect(dashPath, 40, 0,  PathDashPathEffectStyle.TRANSLATE);
paint.setPathEffect(pathEffect);...canvas.drawPath(path, paint);

更改参数 显示效果:

PathDashPathEffectStyle.TRANSLATE

7) DiscretePathEffect() 设置线条随机偏移

把线条进行随机的偏离,让轮廓变得乱七八糟。乱七八糟的方式和程度由参数决定。

// segmentLength: 分段长度
// deviation: 偏移距离
DiscretePathEffect(float segmentLength, float deviation);

代码示例:

    protected void onDraw(Canvas canvas) {paint.setStrokeWidth(10);//设置画笔宽度 ,单位pxpaint.setColor(Color.BLUE);paint.setStyle(Paint.Style.STROKE);canvas.drawLine(100,100,100,1000,paint);paint.setPathEffect(new DiscretePathEffect(50,10));Path path = new Path();path.moveTo(500,100);path.lineTo(500,1000);canvas.drawPath(path,paint);}

8) SumPathEffect () 两种线条模式都执行

这是一个组合效果类的 PathEffect 。它的行为特别简单,就是分别按照两种 PathEffect 分别对目标进行绘制。

PathEffect dashEffect = new DashPathEffect(new float[]{20, 10}, 0);
PathEffect discreteEffect = new DiscretePathEffect(20, 5);
PathEffect  pathEffect = new SumPathEffect(dashEffect, discreteEffect);
paint.setPathEffect(pathEffect );...canvas.drawPath(path, paint);

同时对直线执行两种模式:

9) 、ComposePathEffect()线条组合模式

这也是一个组合效果类的 PathEffect 。不过它是先对目标 Path 使用一个 PathEffect,然后再对这个改变后的 Path 使用另一个 PathEffect。

PathEffect dashEffect = new DashPathEffect(new float[]{20, 10}, 0);
PathEffect discreteEffect = new DiscretePathEffect(20, 5);
PathEffect  pathEffect = new ComposePathEffect(dashEffect, discreteEffect);
paint.setPathEffect(pathEffect );
...canvas.drawPath(path, paint);

效果展示:


它的构造方法 ComposePathEffect(PathEffect outerpe, PathEffect innerpe) 中的两个 PathEffect 参数, innerpe 是先应用的, outerpe 是后应用的。所以上面的代码就是「先偏离,再变虚线」。而如果把两个参数调换,就成了「先变虚线,再偏离」。

注意: PathEffect 在有些情况下不支持硬件加速,需要关闭硬件加速才能正常使用:
Canvas.drawLine() 和 Canvas.drawLines() 方法画直线时,setPathEffect()
是不支持硬件加速的; PathDashPathEffect 对硬件加速的支持也有问题,所以当使用 PathDashPathEffect 的时候,最好也把硬件加速关了。

4、setShader()Gradient 着色渐变 渲染相关的类

Graphics2D 渐变种类有:

线性渐变:LinearGradient
径向渐变:RadialGradient
扫描渐变:SweepGradient
位图渐变:BitmapShader
混合渐变:ComposeShader

线性渐变、径向渐变和扫描渐变属于颜色渐变,指定 2 种或 2 种以上的颜色,根据颜色过渡算法自动计算出中间的过渡颜色,从而形成渐变效果,对于开发人员来说,无需关注中间的渐变颜色。
位图渐变则不再是简单的颜色渐变,而是以图片做为贴片有规律的变化,类似于壁纸平铺。混合渐变则能将多种渐变进行组合,实现更加复杂的渐变效果。

渐变种类 分为三种,我们 以A,B 代编两种颜色:

ABAB 型:A、B 两种颜色重复变化,通过 TileMode 类的 REPEAT 常量来表示; ABBA 型:A、B
两种颜色镜像变化,通过 TileMode 类的 MIRROR 常量来表示; AABB 型:A、B 两种颜色只出现一次,通过 TileMode
类的 CLAMP 常量来表示

如下图展示:

ABAB 渐变模式

1)LinearGradient 线性渐变

线性渐变是有两个点,不同颜色在渐变的方向与这两个点的连线垂直。

public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, TileMode tile)

参数意思:
x0、y0:用于决定线性方向的第一个点的坐标(x0,y0);
x1、y1:用于决定线性方向的第二个点的坐标(x1,y1);
color0:第一种颜色;
color1:第二种颜色;
tile:渐变模式

代码示例:

    protected void onDraw(Canvas canvas) {paint.setStrokeWidth(50);//设置画笔宽度 ,单位pxpaint.setColor(Color.BLACK);paint.setStyle(Paint.Style.FILL);LinearGradient linearGradient = new LinearGradient(100,100,200,100,Color.RED,Color.BLUE,Shader.TileMode.MIRROR) ;  //设置渐变区域 属性 paint.setShader(linearGradient); //设置线性渐变canvas.drawLine(100,100,800,100,paint); //划线linearGradient = new LinearGradient(100,200,800,800,Color.RED,Color.BLUE,Shader.TileMode.CLAMP);paint.setShader(linearGradient); //设置线性渐变canvas.drawRect(100,200,800,800,paint); //画矩形}


如果两种颜色无法满足绘图需求,LinearGradient 支持三种或者三种以上颜色的渐变,对应
的构造方法如下:

public LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[], TileMode
tile)

这是一个功能更加强大的构造方法,我们来看看该构造方法参数的作用:
x0、y0:起始点的坐标
x1、y1:终止点的坐标
colors:多种颜色
positions:颜色的位置(比例)
TileMode:渐变模式

我们代码测试一下:

        LinearGradient linearGradient = new LinearGradient(100,100,800,100,new int [] {Color.RED,Color.YELLOW,Color.BLUE},new float[]{0,0.5f,1},Shader.TileMode.CLAMP) ;  //设置渐变区域 属性paint.setShader(linearGradient); //设置线性渐变canvas.drawLine(100,100,800,100,paint); //划线

效果展示:

参数 colors 和 positions 都是数组,前者用于指定多种颜色,后者用于指定每种颜色的起始比例位置。positions 数组中的元素个数与 colors 要相同

2) 、径向渐变 RadialGradient()

径向渐变是以指定的点为中心,向四周以渐变颜色进行圆周扩散,和线性渐变一样,支持两种或多种颜色。

public RadialGradient(float x, float y, float radius, int color0, int color1, TileMode tile)

该构造方法支持两种颜色,下面是参数的作用:
x、y:中心点坐标
radius:渐变半径
color0:起始颜色
color1:结束颜色
TileMode:渐变模式

public RadialGradient(float x, float y, float radius, int colors[], float positions[], TileModetile)

该构造方法支持 3 种或 3 种以上颜色的渐变,各参数的作用如下:
x、y:中心点坐标
radius:渐变半径
colors:多种颜色
positions:颜色的位置(比例)
TileMode:渐变模式

代码示例:

    protected void onDraw(Canvas canvas) {paint.setStrokeWidth(50);//设置画笔宽度 ,单位pxpaint.setColor(Color.BLACK);paint.setStyle(Paint.Style.FILL);//两个颜色RadialGradient radialGradient = new RadialGradient(400,400,300,Color.RED,Color.BLUE,Shader.TileMode.CLAMP) ;//设置渐变区域 属性paint.setShader(radialGradient); //设置线性渐变canvas.drawCircle(400,400,300,paint); //划线//多种颜色radialGradient = new RadialGradient(400,1200,300,new int[]{Color.RED,Color.YELLOW,Color.GRAY,Color.BLUE},new float[]{0,0.4f,0.8f,1},Shader.TileMode.CLAMP) ;//设置渐变区域 属性paint.setShader(radialGradient); //设置线性渐变canvas.drawCircle(400,1200,300,paint); //划线}

结果显示:

3) 、扫描渐变 SweepGradient()

扫面渐变 ,类似于军事雷达一样,不断围绕圆心扫描。

public SweepGradient(float cx, float cy, int color0, int color1)

支持两种颜色的扫描渐变,参数的作用如下:
cx、cy:圆点坐标;
color0:起始颜色;
color1:结束颜色。

public SweepGradient(float cx, float cy, int colors[], float positions[])

支持多种颜色的扫描渐变,参数的作用如下:
cx、cy:圆点坐标;
colors:多种颜色;
positions:颜色的位置(比例)。

代码示例:

protected void onDraw(Canvas canvas) {paint.setStrokeWidth(50);//设置画笔宽度 ,单位pxpaint.setColor(Color.BLACK);paint.setStyle(Paint.Style.FILL);//两个颜色SweepGradient sweepGradient = new SweepGradient(400,400,Color.RED,Color.BLUE) ;//设置渐变区域 属性paint.setShader(sweepGradient); //设置线性渐变canvas.drawCircle(400,400,300,paint); //划线//多种颜色sweepGradient = new SweepGradient(400,1200,new int[]{Color.RED,Color.YELLOW,Color.GRAY,Color.BLUE},null) ;//设置渐变区域 属性 position  设置为 null 则均分paint.setShader(sweepGradient); //设置线性渐变canvas.drawCircle(400,1200,300,paint); //划线}

4)位图渐变 BitmapShader

位图渐变其实就是绘制的图形中将指定的位图作为背景。如果图形比位图小,则通过渐变模式进行平铺。
BitmapShader 只有一个构造方法。

public BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY)

TileMode.CLAMP 模式不平铺,TileMode.REPEAT 模式表示平铺,TileMode.MIRROR 模式也表示平铺,但是交错的位图是彼此的镜像,方向相反。可以同时指定水平和垂直两个方向
的渐变模式。

Shader.TileMode.CLAMP:如果着色器超出原始边界范围,会复制边缘颜色。
Shader.TileMode.MIRROR:横向和纵向的重复着色器的图像,交替镜像图像是相邻的图像总是接合。这个官方的说明可能不太好理解,说白了,就是图像不停翻转来平铺,直到平铺完毕。
Shader.TileMode.REPEAT: 横向和纵向的重复着色器的图像。
一般来说,当Canvas的宽度(高度)小于等于BitmapShader中Bitmap的宽度(高度),我们会使用Shader.TileMode.CLAMP模式,否则我们会使用Shader.TileMode.MIRROR或者Shader.TileMode.REPEAT模式。

代码测试:

    protected void onDraw(Canvas canvas) {paint.setStrokeWidth(50);//设置画笔宽度 ,单位pxpaint.setColor(Color.BLACK);paint.setStyle(Paint.Style.FILL);Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.agns); //获取图片资源 转换成bitmap 对象BitmapShader bitmapShader = new BitmapShader(bitmap,Shader.TileMode.REPEAT,Shader.TileMode.CLAMP);//设置 x,y  方向格式paint.setShader(bitmapShader);canvas.drawRect(10,10,800,800,paint);bitmapShader = new BitmapShader(bitmap,Shader.TileMode.REPEAT,Shader.TileMode.MIRROR);paint.setShader(bitmapShader);canvas.drawRect(10,850,800,1600,paint);}}

效果展示:
我们可以看到不同模式显示不同效果

5)混合 渐变( ComposeShader )

混合渐变是将两种不同的渐变通过位图运算后得到

ComposeShader 有两个构造方法:public ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode)public ComposeShader(Shader shaderA, Shader shaderB, Mode mode)

其中位图运算有16中之多,我们可以先大概看一下

    protected void onDraw(Canvas canvas) {paint.setStrokeWidth(50);//设置画笔宽度 ,单位pxpaint.setColor(Color.BLACK);paint.setStyle(Paint.Style.FILL);Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_2); //获取图片资源 转换成bitmap 对象Shader  shader1 = new BitmapShader(bitmap,Shader.TileMode.REPEAT,Shader.TileMode.MIRROR);//设置 x,y  方向格式LinearGradient shader2 = new LinearGradient(   //线性渐变0,0,500,0,Color.RED,Color.BLUE,Shader.TileMode.MIRROR);// ComposeShader:结合两个 ShaderShader shader = new ComposeShader(shader1, shader2, PorterDuff.Mode.XOR); //XOR 表示 裁剪shader1 shader2 交集paint.setShader(shader);canvas.drawRect(0,0,getRight(),getBottom(),paint);}

如图所示:

我们可以看一下各个模式的现象:


5、颜色效果处理

1) LightingColorFilter 设定 基本色素 黄绿黑

这个 LightingColorFilter 是用来模拟简单的光照效果的。
LightingColorFilter 的构造方法是 LightingColorFilter(int mul, int add),
参数里的 mul 和 add 都是和颜色值格式相同的 int 值,其中 mul 用来和目标像素相乘,add 用来和目标像素相加:

R' = R * mul.R / 0xff + add.R
G' = G * mul.G / 0xff + add.G
B' = B * mul.B / 0xff + add.B

对于一个颜色我们让其保持原,不改变颜色显示的 参数传递:
mul 为 0xffffff,add 为 0x000000(也就是0),那么对于一个像素,它的计算过程就是:
六位 分别代表基本色素 红绿黑

R' = R * 0xff / 0xff + 0x0 = R // R' = R
G' = G * 0xff / 0xff + 0x0 = G // G' = G
B' = B * 0xff / 0xff + 0x0 = B // B' = B

基于这个「基本 LightingColorFilter 」,你就可以修改一下做出其他的 filter。比如,如果你想去掉原像素中的红色,可以把它的 mul 改为 0x00ffff (红色部分为 0 ) ,那么它的计算过程就是:

R' = R * 0x0 / 0xff + 0x0 = 0 // 红色被移除
G' = G * 0xff / 0xff + 0x0 = G
B' = B * 0xff / 0xff + 0x0 = B

我们代码展示:

protected void onDraw(Canvas canvas) {
paint.setStrokeWidth(50);//设置画笔宽度 ,单位px
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.FILL);

Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.agns); //获取图片资源 转换成bitmap 对象
canvas.drawBitmap(bitmap,200,200,paint);ColorFilter lightingColorFilter = new LightingColorFilter(0x00ffff, 0x000000); //去掉红色
paint.setColorFilter(lightingColorFilter);
canvas.drawBitmap(bitmap,200,600,paint);

}
效果展示:


当然我们可以随意的修改颜色的参数,比如让绿色更亮:

    protected void onDraw(Canvas canvas) {paint.setStrokeWidth(50);//设置画笔宽度 ,单位pxpaint.setColor(Color.BLACK);paint.setStyle(Paint.Style.FILL);Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.agns); //获取图片资源 转换成bitmap 对象canvas.drawBitmap(bitmap,200,200,paint);ColorFilter lightingColorFilter = new LightingColorFilter(0xffffff, 0x008800); //绿色更亮paint.setColorFilter(lightingColorFilter);canvas.drawBitmap(bitmap,200,600,paint);}

2)、PorterDuffColorFilter 设置颜色 模式运算

这个 PorterDuffColorFilter 的作用是使用一个指定的颜色 和 一种指定的 PorterDuff.Mode 来与绘制对象进行合成。
它的构造方法是 PorterDuffColorFilter(int color, PorterDuff.Mode mode)
其中的 color 参数是指定的颜色, mode 参数是指定的 Mode

我们代码测试:

    protected void onDraw(Canvas canvas) {paint.setStrokeWidth(50);//设置画笔宽度 ,单位pxpaint.setStyle(Paint.Style.FILL);Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_2); //获取图片资源 转换成bitmap 对象canvas.drawBitmap(bitmap,200,200,paint);ColorFilter colorFilter = new PorterDuffColorFilter(Color.GREEN, PorterDuff.Mode.XOR); //去掉 和 绿色结合的部分paint.setColorFilter(colorFilter);canvas.drawBitmap(bitmap,200,600,paint);}

现象展示:

3)、ColorMatrixColorFilter 色彩锐度等

ColorMatrixColorFilter 使用一个 ColorMatrix 来对颜色进行处理。 ColorMatrix 这个类,内部是一个 4x5 的矩阵:

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

它的构造函数:

通过计算, ColorMatrix 可以把要绘制的像素进行转换。对于颜色 [R, G, B, A] ,转换算法是这样的:

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;

代码测试:

    protected void onDraw(Canvas canvas) {paint.setStrokeWidth(50);//设置画笔宽度 ,单位pxpaint.setStyle(Paint.Style.FILL);Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_2); //获取图片资源 转换成bitmap 对象canvas.drawBitmap(bitmap,200,200,paint);ColorMatrix colorFilter = new ColorMatrix(new float[]{-1f, 0f, 0f, 0f, 255f,0f, -1f, 0f, 0f, 255f,0f, 0f, -1f, 0f, 255f,0f, 0f, 0f, 1f, 0f }); //去掉 和 绿色结合的部分paint.setColorFilter(new ColorMatrixColorFilter(colorFilter));canvas.drawBitmap(bitmap,200,600,paint);}

现象展示:

这个主要是对显示的色彩进一步的调节等,

4)setXfermode 图片转换模式

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

    protected void onDraw(Canvas canvas) {paint.setStrokeWidth(50);//设置画笔宽度 ,单位pxpaint.setStyle(Paint.Style.FILL);Bitmap bitmapone = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_2); //获取图片资源 转换成bitmap 对象Bitmap bitmapTwo = BitmapFactory.decodeResource(getResources(),R.mipmap.rect_2); //获取图片资源 转换成bitmap 对象Xfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN); //取交集,交集样式取决于下层,颜色取决于上层int saved = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);canvas.drawBitmap(bitmapTwo, 0, 0, paint); paint.setXfermode(xfermode); // 设置 Xfermodecanvas.drawBitmap(bitmapone, 0, 0, paint); paint.setXfermode(null); // 用完及时清除 Xfermodecanvas.restoreToCount(saved);}

效果显示:

Xfermode 注意事项

Xfermode 使用很简单,不过有两点需要注意
使用离屏缓冲(Off-screen Buffer)
我们尝试使用代码直接绘制:

    protected void onDraw(Canvas canvas) {paint.setStrokeWidth(50);//设置画笔宽度 ,单位pxpaint.setStyle(Paint.Style.FILL);Bitmap bitmapone = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_2); //获取图片资源 转换成bitmap 对象Bitmap bitmapTwo = BitmapFactory.decodeResource(getResources(),R.mipmap.rect_2); //获取图片资源 转换成bitmap 对象Xfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.XOR);//  int saved = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);canvas.drawBitmap(bitmapone, 0, 0, paint); // 画方paint.setXfermode(xfermode); // 设置 Xfermodecanvas.drawBitmap(bitmapTwo, 0, 0, paint); // 画圆paint.setXfermode(null); // 用完及时清除 Xfermode//  canvas.restoreToCount(saved);}

我们看到都是黑色:

这是为什么呢:
如下

按照逻辑我们会认为,在第二步画圆的时候,跟它共同计算的是第一步绘制的方形。但实际上,却是整个 View 的显示区域都在画圆的时候参与计算,并且 View 自身的底色并不是默认的透明色,而且是遵循一种迷之逻辑,导致不仅绘制的是整个圆的范围,而且在范围之外都变成了黑色。就像这样。

这……那可如何是好?

要想使用 setXfermode() 正常绘制,必须使用离屏缓存 (Off-screen Buffer) 把内容绘制在额外的层上,再把绘制好的内容贴回 View 中。也就是这样:

通过使用离屏缓冲,把要绘制的内容单独绘制在缓冲层, Xfermode 的使用就不会出现奇怪的结果了。使用离屏缓冲有两种方式:

Canvas.saveLayer()

saveLayer() 可以做短时的离屏缓冲。使用方法很简单,在绘制代码的前后各加一行代码,在绘制之前保存,绘制之后恢复:

int saved = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);canvas.drawBitmap(rectBitmap, 0, 0, paint); // 画方
paint.setXfermode(xfermode); // 设置 Xfermode
canvas.drawBitmap(circleBitmap, 0, 0, paint); // 画圆
paint.setXfermode(null); // 用完及时清除 Xfermodecanvas.restoreToCount(saved);

View.setLayerType()

View.setLayerType() 是直接把整个 View 都绘制在离屏缓冲中。 setLayerType(LAYER_TYPE_HARDWARE) 是使用 GPU 来缓冲, setLayerType(LAYER_TYPE_SOFTWARE) 是直接直接用一个 Bitmap 来缓冲。

    protected void onDraw(Canvas canvas) {paint.setStrokeWidth(50);//设置画笔宽度 ,单位pxpaint.setStyle(Paint.Style.FILL);Bitmap bitmapone = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_2); //获取图片资源 转换成bitmap 对象Bitmap bitmapTwo = BitmapFactory.decodeResource(getResources(),R.mipmap.rect_2); //获取图片资源 转换成bitmap 对象Xfermode xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);//   int saved = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);this.setLayerType(View.LAYER_TYPE_SOFTWARE, paint); //设置软件缓存区canvas.drawBitmap(bitmapTwo, 0, 0, paint); // 蓝色方形 底层paint.setXfermode(xfermode); // 设置 Xfermodecanvas.drawBitmap(bitmapone, 0, 0, paint); // 绿色机器人paint.setXfermode(null); // 用完及时清除 Xfermode// canvas.restoreToCount(saved);}

如果没有特殊需求,可以选用第一种方法 Canvas.saveLayer() 来设置离屏缓冲,以此来获得更高的性能。

6、色彩优化

Paint 的色彩优化有两个方法: setDither(boolean dither) 和 setFilterBitmap(boolean filter) 。它们的作用都是让画面颜色变得更加「顺眼」,但原理和使用场景是不同的。

1)、setDither(boolean dither) 设置图像抖动

在实际的应用场景中,抖动更多的作用是在图像降低色彩深度绘制时,避免出现大片的色带与色块。

而且在 Android 里使用起来也很简单,一行代码就搞定:

paint.setDither(true);

setDither(dither) 已经没有当年那么实用了,因为现在的 Android 版本的绘制,默认的色彩深度已经是 32 位的 ARGB_8888 ,效果已经足够清晰了。只有当你向自建的 Bitmap 中绘制,并且选择 16 位色的 ARGB_4444 或者 RGB_565 的时候,开启它才会有比较明显的效果。

2)、setFilterBitmap(boolean filter) 线性过滤

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

而且它的使用同样也很简单:

paint.setFilterBitmap(true);

加上这一行,在放大绘制 Bitmap 的时候就会使用双线性过滤了。

7、设置阴影或者上层效果

1)、setShadowLayer () 设置阴影

构造函数:

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

方法的参数里, radius 是阴影的模糊范围; dx dy 是阴影的偏移量; shadowColor 是阴影的颜色。

代码示例:

    protected void onDraw(Canvas canvas) {// paint.setStrokeWidth(120);//设置画笔宽度 ,单位pxpaint.setStyle(Paint.Style.FILL);paint.setTextSize(100); //设置字体大小paint.setShadowLayer(10, 0, 0, Color.BLUE);canvas.drawText("这是个测试",100,100,paint);}

效果展示:

清楚阴影:

        paint.clearShadowLayer(); //清楚阴影

在硬件加速开启的情况下, setShadowLayer() 只支持文字的绘制,文字之外的绘制必须关闭硬件加速才能正常绘制阴影。

如果 shadowColor 是半透明的,阴影的透明度就使用 shadowColor 自己的透明度;而如果 shadowColor
是不透明的,阴影的透明度就使用 paint 的透明度。

2)setMaskFilter(MaskFilter maskfilter) 绘制层上附件效果

为之后的绘制设置 MaskFilter。上一个方法 setShadowLayer() 是设置的在绘制层下方的附加效果;而这个 MaskFilter 和它相反,设置的是在绘制层上方的附加效果。

到现在已经有两个 setXxxFilter(filter) 了。前面有一个 setColorFilter(filter) ,是对每个像素的颜色进行过滤;而这里的 setMaskFilter(filter) 则是基于整个画面来进行过滤。

MaskFilter 有两种: BlurMaskFilter 和 EmbossMaskFilter。

(1)模糊效果的 MaskFilter。
构造方法:
BlurMaskFilter(float radius, BlurMaskFilter.Blur style)

它有四种选择模式:

NORMAL: 内外都模糊绘制
SOLID: 内部正常绘制,外部模糊
INNER: 内部模糊,外部不绘制
OUTER: 内部不绘制,外部模糊

 protected void onDraw(Canvas canvas) {paint.setStrokeWidth(10);//设置画笔宽度 ,单位pxpaint.setStyle(Paint.Style.FILL);paint.setTextSize(100);this.setLayerType(LAYER_TYPE_SOFTWARE, null);Bitmap bitmapone = BitmapFactory.decodeResource(getResources(),R.mipmap.agns); //获取图片资源 转换成bitmap 对象BlurMaskFilter blurMaskFilterNORMAL = new BlurMaskFilter(50,BlurMaskFilter.Blur.NORMAL);paint.setMaskFilter(blurMaskFilterNORMAL);canvas.drawBitmap(bitmapone,50,200,paint);BlurMaskFilter blurMaskFilterOUTER = new BlurMaskFilter(50,BlurMaskFilter.Blur.OUTER);paint.setMaskFilter(blurMaskFilterOUTER);canvas.drawBitmap(bitmapone,550,200,paint);BlurMaskFilter blurMaskFilterINNER = new BlurMaskFilter(50,BlurMaskFilter.Blur.INNER);paint.setMaskFilter(blurMaskFilterINNER);canvas.drawBitmap(bitmapone,50,800,paint);BlurMaskFilter blurMaskFilterSOLID = new BlurMaskFilter(50,BlurMaskFilter.Blur.SOLID);paint.setMaskFilter(blurMaskFilterSOLID);canvas.drawBitmap(bitmapone,550,800,paint);}

效果展示:

(2)EmbossMaskFilter

EmbossMaskFilter(float[] direction, float ambient, float specular, float blurRadius) 的参数里, direction 是一个 3 个元素的数组,指定了光源的方向; ambient 是环境光的强度,数值范围是 0 到 1; specular 是炫光的系数; blurRadius 是应用光线的范围。

我们代码测试一下:

    protected void onDraw(Canvas canvas) {paint.setStrokeWidth(10);//设置画笔宽度 ,单位pxpaint.setStyle(Paint.Style.FILL);paint.setTextSize(100);this.setLayerType(LAYER_TYPE_SOFTWARE, null);//direction 是一个 3 个元素的数组,指定了光源的方向; ambient 是环境光的强度,数值范围是 0 到 1; specular 是炫光的系数; blurRadius 是应用光线的范围。EmbossMaskFilter embossMaskFilter =  new EmbossMaskFilter(new float[]{10, 10, 10}, 0.1f, 5, 5);paint.setMaskFilter(embossMaskFilter);canvas.drawText("Test",100,700,paint);

类似浮雕 效果:

8、获取实际路径

1) getFillPath(Path src, Path dst)

通过 getFillPath(src, dst) 方法就能获取这个实际 Path。方法的参数里,src 是原 Path ,而 dst 就是实际 Path 的保存位置。 getFillPath(src, dst) 会计算出实际 Path,然后把结果保存在 dst 里。

2) getTextPath 获取文本路径
getTextPath(String text, int start, int end, float x, float y, Path path)

分别是 文字/ 开始获取位置,结束获取的位置,文字所在的X坐标,文字所在的Y坐标,保存的路径

   protected void onDraw(Canvas canvas) {String text = "测试文本 Test";Paint srcPaint = new Paint();srcPaint.setTextSize(100);srcPaint.setColor(Color.BLACK);srcPaint.setStyle(Paint.Style.STROKE);canvas.drawText(text,50,100,srcPaint);//获取文本路径canvas.translate(0,150);Path desPath = new Path();Paint desPaint = new Paint();desPaint.setTextSize(100);desPaint.setColor(Color.BLACK);desPaint.setStyle(Paint.Style.STROKE);srcPaint.getTextPath(text,0,text.length(),50,100,desPath);canvas.drawPath(desPath,desPaint);}

待续···

文件参考:
Android Paint的使用详解

安卓自定义View进阶-画笔基础(Paint)

Android关于Paint你所知道的和不知道的一切

HenCoder Android 开发进阶: 自定义 View 1-2 Paint 详解

Android 自定义组件 开发详解 (李赞红)

Android Paint 详细讲解相关推荐

  1. Android架构详细讲解与C/C++开发支持原理

    Android架构详细讲解与C/C++开发支持原理 在Android 在NDK r5使用C/C++进行开发.(以前,Android 对C/C++开发的支持仅限于用C/C++开发动态链接库,然后在Jav ...

  2. [Android Studio]详细讲解Android6.0以上请求应用权限(解决请求权限窗口一闪而过的问题)

    现在的主流手机的Android版本都是8.0/9.0(笔者的华为手机为9.0),所以在开发Android的时候尽量使用Android8.0的版本(SDK 26).随着Android版本的提升,系统的安 ...

  3. Android webservice的用法详细讲解

    Android webservice的用法详细讲解 看到有很多朋友对WebService还不是很了解,在此就详细的讲讲WebService,争取说得明白吧.此文章采用的项目是我毕业设计的webserv ...

  4. 详细讲解Android的网络通信(HttpUrlConnection和HttpClient)

    前言,Android的网络通信的方式有两种:使用Socket或者HTTP,今天这一篇我们详细讲解使用HTTP实现的网络通信,HTTP又包括两种方式编程方式: (1)HttpUrlConnection: ...

  5. 炉石传说android手机版本,炉石传说安卓版下载gpu型号选择详细讲解介绍

    炉石传说安卓版下载gpu型号选择详细讲解介绍.炉石传说手机版已经正式上线,炉石传说IOS版以及安卓版都已经正式开放下载.好多小伙伴对于怎么下载以及下载gpu型号不知道该怎么选择,兔玩网小编就为大家带来 ...

  6. 详细讲解如何安装原生安卓电子市场(android market)

    最近刷了android4.0版本.由于rom没有自带电子市场(android market),国内厂商考虑到电子市场(android market)在大陆地区使用难度大,所以干脆直接阉割掉了,但是有些 ...

  7. linux安装 Android Studio详细教程,支持性较差,需要安装最新底层库内核的linux

    安装 Android Studio详细教程 libc6-i386 lib32stdc++6 lib32gcc1 lib32ncurses5 lib32z1 jdk1.8.0_25 android-st ...

  8. 第八节:详细讲解Java中的异常处理情况与I/O流的介绍以及类集合框架

    前言 大家好,给大家带来详细讲解Java中的异常处理情况与I/O流的介绍以及类集合框架的概述,希望你们喜欢 JAVA 异常 try...catch...finally结构的使用方法 class Tes ...

  9. Android 系统(207)---Android各种Drawable讲解和demo实例

    Android各种Drawable讲解和demo实例 转载自  : https://blog.csdn.net/linghu_java/article/details/42119969 PS:文字内容 ...

  10. Android UI RecyclerView讲解

    前言 RecyclerView是Android 5.0之后推出的列表类控件,具有高度的解耦性和灵活性.通过使用合适的LayoutManager,可以实现ListView.横向ListView.Grid ...

最新文章

  1. ubuntu18安装32位库
  2. Linux 磁盘分区、格式化、目录挂载
  3. [Python爬虫] scrapy爬虫系列 一.安装及入门介绍
  4. es对已有的索引给主键_ES中对索引的相关操作
  5. python 接收邮件服务器地址_python实现的接收邮件功能示例【基于网易POP3服务器】...
  6. JDBC之数据库的连接步骤(六步)
  7. 叫板英特尔,英伟达发布首个 CPU,集齐“三芯”!
  8. Docker部署Ghost
  9. Python爬虫之模拟CSDN网站登录
  10. 学习NA,Dynamips实验环境(工大瑞普)下载地址
  11. 三大国产操作系统,到底哪个最好用
  12. Android自定义WebView实现Youtube网络视频播放控件
  13. font setup -- dejavu 安装字体
  14. MFS(一)---mfs详解与部署
  15. cad直线和圆弧倒角不相切_CAD倒角技巧
  16. 华为 应用隐藏大师 计算机,【分享】应用隐藏大师v6.3.1~一键隐藏不想让别人看到的软件...
  17. 我的世界服务器无限刷凋零,《我的世界》高效刷凋零骷髅的方法
  18. 记一次Linux文件系统引发的项目启动错误(war包没有问题只有指定目录启动报错)
  19. JSP连接数据库实现注册登录(附带上传头像)
  20. 淘宝买家谈阿里质量意识

热门文章

  1. Kinetics-400数据集分类名中英文对照汇总
  2. 初探三维计算机视觉(三维重建) —— 相机模型 + 双目系统 + 点云模型
  3. 应用添加分享至微信、QQ和微博
  4. MIT数字经济研究:建平台要以“网络效应”为目标
  5. HTML网页设计结课作业 榆林子州 HTML5响应式旅游景区网站模板
  6. c语言结构体数组怎么初始化,c语言结构体数组初始化
  7. 学3D建模需要什么基础?
  8. thinkphp6 循环 视图_ThinkPHP6 视图
  9. PS(Photo Shop Cs6)批量调整图片大小
  10. matlab | imcrop手动截图演示