Android 绘图机制处理
系统屏幕密度,密度值,分辨率对应关系
1
ldpi=120(240*320),mdpi=160(320*480),hdpi=240(480*800),xhdpi=320(720*1280),xxhdpi=480(1080*1980)
独立像素密度(密度无关像素 (dp)在定义 UI 布局时应使用的虚拟像素单位,用于以密度无关方式表示布局维度 或位置)
1
ldpi:mdpi:hdpi:xhdpi:xxhdpi=120:160:240:320:480=0.75px:1px:1.5px:2px:3px=3:4:6:8:12
单位转换
1
2
3
4
5
6
7
8
9
10scale=getResources().getDisplayMetrics().density;
px2dp: pxValue/scale+0.5f;
dp2px: dpValue*scale+0.5f;
fontScale=getResources().getDisplayMetrics().scaledDensity;
px2sp: pxValue/fontScale+0.5f;
sp2px: pxValue*fontScale+0.5f;
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dp,
getResource().getDisplayMetrics());
2D绘图
- setAntiAlias()//画笔锯齿
- setColor()//画笔颜色
- setARGB()
- setAlpha()
- setTextSize()
- setStyle()//空心,实心
- setStrokerWidth()//边框宽度
1
2
3
4
5
6
7
8
9
10
11drawPoint
drawLine
drawLines
drawRect
drawRoundRect
drawCircle
drawArc 绘制弧形,使用中心或实心,空心
drawOval 绘制椭圆
drawText 绘制文本
drawPosText 指定位置绘制文本
drawPath 绘制路径
XML
Bitmap
1
2<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@mipmap/car1" />Shape
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15<shape
android:shape="rectangle|oval|line|ring">
<!--配合ImageView scaleType-->
<size
android:width="12dp"
android:height="12dp" />
<!--虚线宽度,间隔-->
<stroke
android:width="20dp"
android:color="@color/colorAccent"
android:dashGap="2dp"
android:dashWidth="6dp" />
</shape>Layer
1
2
3
4<layer-list
<item android:drawable="@mipmap/car1"/>
<item android:drawable="@mipmap/car2"/>
</layer-list>Selector
1
2
3
4
5
6
7
8
9
10
11<item android:drawable="@android:drawable/btn_default" />
<!--没有焦点时-->
<item android:state_window_focused="false" />
<!--非触摸单击时-->
<item android:state_focused="true" android:state_pressed="true" />
<!--触摸模式下单击时-->
<item android:state_focused="false" android:state_pressed="true" />
<!--选中时-->
<item android:state_selected="true" />
<!--获得焦点时-->
<item android:state_focused="true" />
绘图技巧
Canvas
1
2
3
4
5
6
7
8
9//保存之前绘制图像,后续绘制在新的图层
canvas.save()
//save之前的图像和之后的图像合并
canvas.restore()
//坐标系平移,原点(0,0)移动到(x,y)
canvas.translate()
//坐标系旋转
canvas.rotate()Board
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48private void init() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(2);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = getWidth();
mHeight = getHeight();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(mWidth / 2, mHeight / 2,
mWidth / 2, mPaint);
for (int i = 0; i < 24; i++) {
if (i == 0 || i == 6 || i == 12 | i == 18) {
mPaint.setStrokeWidth(5);
mPaint.setTextSize(30);
canvas.drawLine(mWidth / 2, mHeight / 2 - mWidth / 2,
mWidth / 2, mHeight / 2 - mWidth / 2 + 60, mPaint);
canvas.drawText(String.valueOf(i), mWidth / 2, mHeight / 2 - mWidth / 2 + 90, mPaint);
} else {
mPaint.setStrokeWidth(3);
mPaint.setTextSize(16);
canvas.drawLine(mWidth / 2, mHeight / 2 - mWidth / 2,
mWidth / 2, mHeight / 2 - mWidth / 2 + 30, mPaint);
canvas.drawText(String.valueOf(i), mWidth / 2, mHeight / 2 - mWidth / 2 + 60, mPaint);
}
//旋转画布简化角度,坐标运算
canvas.rotate(15, mWidth / 2, mHeight / 2);
}
canvas.save();
mPaint.setColor(Color.CYAN);
mPaint.setStrokeWidth(10);
canvas.translate(mWidth / 2, mHeight / 2);
canvas.drawLine(0, 0, 100, 100, mPaint);
canvas.drawLine(0, 0, 100, 200, mPaint);
canvas.restore();
}
layer图层
1
2
3
4
5
6
7
8
9
10
11@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setColor(Color.BLUE);
canvas.drawCircle(mWidth / 2, mHeight / 2, 50, mPaint);
canvas.saveLayerAlpha(0, 0, mWidth, mHeight, 127, ALL_SAVE_FLAG);
mPaint.setColor(Color.RED);
canvas.drawCircle(mWidth / 2 + 50, mHeight / 2 + 50, 50, mPaint);
canvas.restore();
}色彩特效
- 图片的数据结构常使用位图Bitmap,由点阵(像素点矩阵)和颜色值(ARGB)组成
- 色彩处理中包含色调,饱和度,亮度,使用ColorMatrix(4*5颜色矩阵)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35[a, b, c, d, e,
f, g, h, i, j,
k, l, m, n, o,
p, q, r, s, t ]
在Android中以一维数组存储 float[] mArray = new float[20]
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;
初始矩阵不会对颜色改变
A=[
1,0,0,0,0
0,1,0,0,0
0,0,1,0,0
0,0,0,1,0]
色调
setRotate(int axis,floate degrees)
axis=0 the RED color
axis=1 the GREEN color
axis=2 the BLUE color
饱和度,为0时是灰度图像
setSaturation(float sat)
亮度
setScale(float rScale, float gScale, float bScale,float aScale)
矩阵乘法运算
postConcat(ColorMatrix postmatrix)
setConcat(postmatrix, this)
imageMatrix.postContact(hueMatrix)
imageMatrix.postContact(saturationMatrix)
imageMatrix.postContact(lumMatrix)滑动seekbar,改变颜色混合效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42public static Bitmap handleImageEffect(Bitmap bm, float hue, float saturation, float lum) {
Bitmap bmp = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);
Paint paint = new Paint();
ColorMatrix hueMatrix = new ColorMatrix();
hueMatrix.setRotate(0, hue);
hueMatrix.setRotate(1, hue);
hueMatrix.setRotate(2, hue);
ColorMatrix saturationMatrix = new ColorMatrix();
saturationMatrix.setSaturation(saturation);
ColorMatrix lumMatrix = new ColorMatrix();
lumMatrix.setScale(lum, lum, lum, 1);
ColorMatrix imageMatrix = new ColorMatrix();
imageMatrix.postConcat(hueMatrix);
imageMatrix.postConcat(saturationMatrix);
imageMatrix.postConcat(lumMatrix);
paint.setColorFilter(new ColorMatrixColorFilter(imageMatrix));
canvas.drawBitmap(bm, 0, 0, paint);
return bmp;
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
switch (seekBar.getId()) {
case R.id.hueSeekBar:
mHue = (progress - 50) * 1.0f / 50 * 180;
break;
case R.id.saturationSeekBar:
mSaturation = progress * 1.0f / 50;
break;
case R.id.lumSeekBar:
mLum = progress * 1.0f / 50;
break;
}
filterImage.setImageBitmap(ColorMatrixUtil.handleImageEffect(bm,
mHue, mSaturation, mLum));
}Android不允许直接修改原图,创建同大小的位图,并将原图绘制到该Bitmap
1
2
3
4Bitmap bmp=Bitmap.createBitmap(bm.getWidth(),bm.getHeight(),Bitmap.Config.ARGB_8888);
Canvas canvas=new Canvas(bmp);
paint.setColorFilter(new ColorMatrixColorFilter(matrix));
canvas.drawBitmap(bm,0,0,paint);
ColorMatrix
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44private void addEts() {
gridLayout.post(new Runnable() {
int mEtHeight;
int mEtWidth;
@Override
public void run() {
mEtHeight = gridLayout.getWidth() / 4;
mEtWidth = gridLayout.getWidth() / 5;
for (int i = 0; i < 20; i++) {
EditText editText = new EditText(getBaseContext());
gridLayout.addView(editText, mEtWidth, mEtHeight);
mEts[i] = editText;
}
initMatrix();
}
});
}
private void initMatrix() {
for (int i = 0; i < mEts.length; i++) {
if (i % 6 == 0) {
mEts[i].setText("1");
} else {
mEts[i].setText("0");
}
}
}
private void getMatrix() {
for (int i = 0; i < mEts.length; i++) {
mColorMatrix[i] = Float.valueOf(mEts[i].getText().toString());
}
}
private void setImageMatrix() {
Bitmap bmp = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bmp);
Paint paint = new Paint();
ColorMatrix colorMatrix = new ColorMatrix(mColorMatrix);
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
canvas.drawBitmap(bm, 0, 0, paint);
filterImage.setImageBitmap(bmp);
}- 图像反转
获取图像像素值,三种像素点处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85private void getPixels() {
ImageView pixelImage1 = findViewById(R.id.pixelImage1);
ImageView pixelImage2 = findViewById(R.id.pixelImage2);
ImageView pixelImage3 = findViewById(R.id.pixelImage3);
bm = BitmapFactory.decodeResource(getResources(), R.mipmap.car3);
int[] pixels = new int[bm.getWidth() * bm.getHeight()];
int[] newPixels1 = new int[bm.getWidth() * bm.getHeight()];
int[] newPixels2 = new int[bm.getWidth() * bm.getHeight()];
int[] newPixels3 = new int[bm.getWidth() * bm.getHeight()];
bm.getPixels(pixels, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight());
Bitmap bmp1 = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Bitmap.Config.ARGB_8888);
Bitmap bmp2 = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Bitmap.Config.ARGB_8888);
Bitmap bmp3 = Bitmap.createBitmap(bm.getWidth(), bm.getHeight(), Bitmap.Config.ARGB_8888);
for (int i = 0; i < pixels.length; i++) {
int color = pixels[i];
int r = Color.red(color);
int g = Color.green(color);
int b = Color.blue(color);
int a = Color.alpha(color);
//老照片
int r1 = (int) (0.393 * r + 0.769 * g + 0.189 * b);
int g1 = (int) (0.349 * r + 0.686 * g + 0.168 * b);
int b1 = (int) (0.272 * r + 0.534 * g + 0.131 * b);
newPixels1[i] = Color.rgb(r1, g1, b1);
//底片处理
int r2 = 255 - r;
int g2 = 255 - g;
int b2 = 255 - b;
if (r > 255) {
r = 255;
} else if (r < 0) {
r = 0;
}
if (g > 255) {
g = 255;
} else if (g < 0) {
g = 0;
}
if (b > 255) {
b = 255;
} else if (b < 0) {
b = 0;
}
newPixels2[i] = Color.argb(a, r2, g2, b2);
//浮雕
int rB = 0;
int gB = 0;
int bB = 0;
if (i + 1 < pixels.length) {
int colorB = pixels[i + 1];
rB = Color.red(colorB);
gB = Color.green(colorB);
bB = Color.blue(colorB);
rB = r - rB + 127;
gB = g - gB + 127;
bB = b - bB + 127;
if (rB > 255) {
rB = 255;
}
if (gB > 255) {
gB = 255;
}
if (bB > 255) {
bB = 255;
}
}
newPixels3[i] = Color.rgb(rB, gB, bB);
}
bmp1.setPixels(newPixels1, 0, bm.getWidth(),
0, 0, bm.getWidth(), bm.getHeight());
bmp2.setPixels(newPixels2, 0, bm.getWidth(),
0, 0, bm.getWidth(), bm.getHeight());
bmp3.setPixels(newPixels3, 0, bm.getWidth(),
0, 0, bm.getWidth(), bm.getHeight());
pixelImage1.setImageBitmap(bmp1);
pixelImage2.setImageBitmap(bmp2);
pixelImage3.setImageBitmap(bmp3);
}图像变换矩阵初始矩阵
1
2
3[1 0 0
0 1 0
0 0 1]图像处理基本变换
- Translate
1
2
3
4
5
6
7p(x0,y0)->p(x,y)
x=Δx+x0
y=Δy+y0
变换矩阵
[1 0 Δx
0 1 Δy
0 0 1 ]- Rotate 以坐标原点为中心旋转
- Scale 每个点的坐标等比例缩放
- Skew 保持所有点的x或y轴坐标不变,对应的y或x坐标等比例平移,有水平错切和垂直错切
1
2
3
4
5
6matrix.setRotate()
matrix.setTranslate()
matrix.setScale()
matrix.setSkew()
set()会重置
pre().post()前乘后乘对矩阵混合使用drawBitmapMesh()像素块
1
2改变图像坐标值重新定位每一个图像块
drawBitmapMesh(@NonNull Bitmap bitmap, int meshWidth, int meshHeight,@NonNull float[] verts, int vertOffset, @Nullable int[] colors, int colorOffset,@Nullable Paint paint)旗帜FlagView
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69public class FlagView extends View {
private float[] orig, verts;
int HEIGHT = 100, WIDTH = 100;
//振幅
private float A = 20;
private Bitmap bitmap;
private float k;
public FlagView(Context context) {
this(context, null);
}
public FlagView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public FlagView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.car3);
int bitmapWidth = bitmap.getWidth();
int bitmapHeight = bitmap.getHeight();
int index = 0;
orig = new float[bitmapHeight * bitmapWidth];
verts = new float[bitmapHeight * bitmapWidth];
for (int y = 0; y <= HEIGHT; y++) {
float fy = bitmapHeight * y / HEIGHT;
for (int x = 0; x <= WIDTH; x++) {
float fx = bitmapWidth * x / WIDTH;
orig[index * 2 + 0] = verts[index * 2 + 0] = fx;
//+100 避免被遮挡
orig[index * 2 + 1] = verts[index * 2 + 1] = fy + 100;
index += 1;
}
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
flagWave();
k += 0.1f;
canvas.drawBitmapMesh(bitmap, WIDTH, HEIGHT, verts,
0, null, 0, null);
invalidate();
}
private void flagWave() {
for (int j = 0; j <= HEIGHT; j++) {
for (int i = 0; i <= WIDTH; i++) {
verts[(j * (WIDTH + 1) + i) * 2 + 0] += 0;
//图像动起来,纵坐标周期性变化
float offsetY = (float) Math.sin((float) i / WIDTH * 2 * Math.PI + Math.PI * k);
verts[(j * (WIDTH + 1) + i) * 2 + 1] = orig[(j * WIDTH + i) * 2 + 1] + offsetY * A;
}
}
}
}