本文介绍Android的绘图机制,包括屏幕的尺寸信息,2D绘图中Canvas和paint的使用,XML绘图中bitmap\shape\selector\layer,如何用色彩矩阵处理图片的色彩(色调、饱和度、亮度),用变换矩阵进行图片处理以及画笔的特效处理
问题汇总:
带着问题学习、复习,效果更好,加油(ง •_•)ง!
1. 4.7寸手机的4.7寸是什么?
2. dp是什么?dp与px的区别。
3. Canvas和Paint的作用。
4. XML中Bitmap、shape、layer、selector作用
5. shape有哪几种重要属性,各自的作用?
6. Canvas的save()、restore()、tanslate()、rotate()的作用
7. 位图Bitmap包含哪两部分,ARGB是什么?
8. 色彩的三个要素和作用
9. ColorMatrix的作用,用ColorMatrix的作用改变色彩三要素的思路
10. 如何通过改变像素点的ARGB(以此来处理图片)
11. 如何用图形变换矩阵Matrix处理图片
12. 如何用像素块处理图片(drawBitmapMesh)
13. PorterDuffXfermode作用和应用。
14. shader作用和应用。
15. PathEffect的作用和应用
学习中看到有博客记录的很详细,有图:
小仙女-android群英传之绘图机制和处理技巧
1-屏幕尺寸信息
1-屏幕参数
- 屏幕大小指的是屏幕对角线的长度,使用“寸”度量,4.7寸手机、5.5寸手机
- PPI:每英寸像素(Pixels Per Inch)又被称为DPI,对角线的像素点数除以屏幕的大小得到的。
3-独立像素密度dp
Android用mdpi即密度值为160的屏幕作为标准,在这个屏幕上1px = 1dp。例如100dp的长度,在mdpi中为100px,而在hdpi中为150px。
因此dp用于屏幕适配最好。
比例换算表 | mdpi | hdpi | xhdpi | xxhdpi |
---|---|---|---|---|
dp | 1 | 1 | 1 | 1 |
px | 1 | 1.5 | 2 | 3 |
2-2D绘图基础
系统提供Canvas提供绘图方法。还提供了各种绘制图像的API。如drawPoint
点,drawLine
线,矩形,多边形,弧,圆等。paint是重要的元素,能提供setAntiAlias()
设置锯齿效果,setStyle()
设置画笔的风格。通过画笔功能不同,组合各种API就能实现想要的效果。
实例:简单测试Canvas
public class TwoDActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/*将自定义控件设为ContentView-用于测试Canvas效果*/
setContentView(new CustomeView(this));
}
/**-----------------------------------------
* 自定义控件,重写draw方法,用于测试canvas
* -----------------------------------------*/
class CustomeView extends View{
public CustomeView(Context context){
super(context);
}
@Override
public void draw(Canvas canvas) {
/*---------------------------------
* 绘制实心矩形
* --------------------------------*/
Paint paint = new Paint();
paint.setStyle(Paint.Style.FILL); //画笔风格
paint.setColor(Color.BLACK); //画笔颜色
canvas.drawRect(0, 0, 100, 100, paint);//绘制矩形
/*---------------------------------
* 绘制空心矩形
* --------------------------------*/
paint.setStyle(Paint.Style.STROKE); //画笔风格
paint.setColor(Color.BLACK); //画笔颜色
canvas.drawRect(150, 0, 250, 100, paint);//绘制矩形
/*-----------------------------------------------------------------
* 在指定位置绘制文字-以(x,y)起点的水平直线为基准线,绘制直线Text
* -----------------------------------------------------------------*/
paint.setStyle(Paint.Style.FILL); //画笔风格
paint.setTextSize(80);
paint.setColor(Color.RED); //画笔颜色
canvas.drawText("Hello World!", 300, 100, paint);
super.draw(canvas);
}
}
}
3-XML绘图基础
XML不仅仅可以作为Java中的一个布局文件、配置列表,还可以绘制图画等内容。
1-Bitmap
可以将图片直接转为bitmap,用于程序中。
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/blank"/>
2-Shape
XML绘图精华所在,无鸾是拟物化、扁平化还是渐变都可以。
shape属性
主要是6部分属性,省略部分具体细节,实际内容写写代码通过效果就可以掌握。(下列代码可以直接复制,可以看到效果)
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<!--shape可以为rectangle(默认),oval,line,ring-->
<!--shape为rectangle使用, 默认为1dp-->
<corners android:radius="5dp"/>
<!--填充颜色-->
<solid android:color="@color/colorAccent"/>
<!--渐变色-->
<gradient
android:centerX="1"
android:centerY="20"
android:centerColor="@color/colorPrimary"
android:endColor="@color/colorPrimaryDark"
android:startColor="@color/colorAccent"
android:type="linear"/>
<padding />
<!--制定边框, dashwidth虚线宽度,dashGap虚线间隔-->
<stroke
android:color="#334444"
android:width="20dp"
android:dashWidth="6dp"
android:dashGap="2dp"/>
<!--指定大小,一般在ImageView中配合scaleType属性使用-->
<size />
</shape>
3-Layer
类似于Photoshop图层,可以实现图层。
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="@color/colorAccent"/>
<item
android:drawable="@color/colorPrimary"
android:left="50dp"
android:top="50dp"
android:right="50dp"
android:bottom="50dp"/>
</layer-list>
4-Selector
Selector用于帮助开发者实现静态绘图中的事件反馈,通过给不同事件设置图像,在获取相应输入后,返回相应结果。
如同Button点击时候的效果,可以用selector自定义View的触摸反馈。
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:drawable="@color/colorPrimaryDark">
<shape />
</item>
<item android:state_pressed="false"
android:drawable="@color/colorAccent">
<shape />
</item>
</selector>
这里就是点击和非点击时,显示相应图片,显示效果可以通过shape来实现。
CSDN关于selector的博客:http://blog.csdn.net/pcaxb/article/details/47317251
4-Android绘图技巧
1-Canvas
Canvas中有四个重要的方法:
1. Canvas.save():将之前绘制内容都保存起来,接下来的绘制就好像在新的图层进行绘制一样。
2. Canvas.restore():合并图层操作,将save之后的图像和save之前的图像合并起来。
3. Canvas.tanslate():平移,将坐标系进行平移,绘制都以之后的坐标系为参照。
4. Canvas.rotate():旋转,将坐标系进行旋转。避免了复杂运算,化繁为简。
绘制时钟
自定义时钟View
private int mCenterX = 550; //自定义圆心X
private int mCenterY = 800; //自定义圆心Y
private int mRadius = 500; //圆的半径
/**-----------------------------------------
* 自定义钟表
* -----------------------------------------*/
class ClockView extends View{
public ClockView(Context context){
super(context);
}
@Override
protected void onDraw(Canvas canvas) {
//绘制圆形
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(5);
canvas.drawCircle(mCenterX, mCenterY, mRadius, paint);
/*--------------------------------------------
* 绘制刻度线: 1. 先在0点位置绘制竖线
* 2. 将坐标系旋转30度
* 3. 继续原样绘制竖线
* 4. 循环绘制,最终绘制出所有刻度线
* -------------------------------------------*/
paint = new Paint();
paint.setStrokeWidth(3);
for(int i = 0; i < 12; i++){
//大刻度线
if(i == 0 || i == 3 || i == 6 || i == 9){
paint.setColor(Color.RED);
paint.setStrokeWidth(5);
canvas.drawLine(mCenterX, mCenterY-mRadius, mCenterX, mCenterY-mRadius+60, paint);
String strTime = i+"";
int textSize = 40;
paint.setTextSize(textSize);
canvas.drawText(strTime, mCenterX-textSize/3, mCenterY-mRadius+60+textSize, paint);
}else{
paint.setColor(Color.BLACK);
paint.setStrokeWidth(3);
canvas.drawLine(mCenterX, mCenterY-mRadius, mCenterX, mCenterY-mRadius+30, paint);
int textSize = 20;
String strTime = i+"";
paint.setTextSize(textSize);
canvas.drawText(strTime, mCenterX-textSize/3, mCenterY-mRadius+30+textSize, paint);
}
//旋转
canvas.rotate(30, mCenterX, mCenterY);
}
super.onDraw(canvas);
}
}
2-Layer
类似于Photoshop图层概念。Android使用saveLayer(),saveLayerAlpha()
将一个图层入栈。restore(), restoreToCount()
将一个图层出栈。入栈后,所有操作发生在该图层之上。出栈后,就会将图像绘制到上层Canvas上。
CSDN layer详细讲解:http://blog.csdn.net/cquwentao/article/details/51423371
5-图像处理:色彩特效处理
Android中最常用的就是位图Bitmap,Bitmap包含了点阵和颜色值。点阵就是包含像素的矩阵。颜色值就是ARGB-对应透明度、红色、绿色、蓝色。
这里只记录要点,详细内容请看原著或者网络博客。
1-色彩矩阵分析
图像描述的三个角度:
1. 色调-物体显示的颜色
2. 饱和度-颜色的纯度。0(灰度)到100%(饱和)来进行描述。
3. 亮度-颜色相对的明暗程度。
Android使用颜色矩阵ColorMatrix,4X5的矩阵。
初始矩阵:不会对颜色产生任何变化
- | - | - | - | - | 偏移量 |
---|---|---|---|---|---|
决定R | 1 | 0 | 0 | 0 | 0 |
决定G | 0 | 1 | 0 | 0 | 0 |
决定B | 0 | 0 | 1 | 0 | 0 |
透明度 | 0 | 0 | 0 | 1 | 0 |
改变颜色
1-改变偏移量
1 | 0 | 0 | 0 |
0 | 1 | 0 | 0 |
0 | 0 | 1 | 0 |
0 | 0 | 0 | 1 |
红色和绿色分量都增加100,红绿结合,导致颜色偏黄。
2-改变颜色系数
1 | 0 | 0 | 0 |
0 | 2 | 0 | 0 |
0 | 0 | 1 | 0 |
0 | 0 | 0 | 1 |
会导致
3-改变色光属性
色调、饱和度、亮度,Android都封装了一些API来快速调整这些参数,不用计算矩阵值。
实例:通过seekbar选择设置图片的色调、饱和度和亮度。
布局文件:包含各属性的seekbar
<?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="com.example.administrator.featherdemos.BitmapActivity"
android:orientation="vertical">
<ImageView
android:id="@+id/imageview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="4" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="色调:"
android:textColor="#000"
android:textSize="23dp" />
<SeekBar
android:id="@+id/hue_seekbar"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="4" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="饱和度:"
android:textColor="#000"
android:textSize="23dp" />
<SeekBar
android:id="@+id/saturation_seekbar"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="4" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="horizontal">
<TextView
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="亮度:"
android:textColor="#000"
android:textSize="23dp" />
<SeekBar
android:id="@+id/lum_seekbar"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="4" />
</LinearLayout>
</LinearLayout>
Activity使用
- 初始化控件
- handleImageEffect:利用ColorMatrix设置色调、饱和度、亮度
- 给seekbar设置监听器—获取数值,并调用handleImageEffect处理图片,将最终结果设置到ImageView中显示。
public class BitmapActivity extends Activity implements SeekBar.OnSeekBarChangeListener{
ImageView imageView;
SeekBar hueSeekBar;
SeekBar saturationSeekBar;
SeekBar lumSeekBar;
Bitmap bitmap;
float mHue = 0; //色调初始值为0, 原图色调
float mSaturation = 1; //饱和度1,原图饱和度
float mLum = 1; //亮度1,原图亮度
//SeekBar的中间值
int MID_VALUE = 127;
//SeekBar的最大值
int MAX_VALUE = 255;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bitmap);
imageView = (ImageView) findViewById(R.id.imageview);
//从本地文件获取drawble
Drawable drawable = ContextCompat.getDrawable(this, R.drawable.jide);
bitmap = ((BitmapDrawable)drawable).getBitmap();
imageView.setImageBitmap(bitmap);
hueSeekBar = (SeekBar) findViewById(R.id.hue_seekbar);
hueSeekBar.setMax(MAX_VALUE);
hueSeekBar.setProgress(MID_VALUE);
saturationSeekBar = (SeekBar) findViewById(R.id.saturation_seekbar);
saturationSeekBar.setMax(MAX_VALUE);
saturationSeekBar.setProgress(MID_VALUE);
lumSeekBar = (SeekBar) findViewById(R.id.lum_seekbar);
lumSeekBar.setMax(MAX_VALUE);
lumSeekBar.setProgress(MID_VALUE);
hueSeekBar.setOnSeekBarChangeListener(this);
saturationSeekBar.setOnSeekBarChangeListener(this);
lumSeekBar.setOnSeekBarChangeListener(this);
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
switch(seekBar.getId()){
case R.id.hue_seekbar:
mHue = (progress - MID_VALUE) * 1.0F / MID_VALUE * 180;
break;
case R.id.saturation_seekbar:
mSaturation = progress * 1.0F / MID_VALUE;
break;
case R.id.lum_seekbar:
mLum = progress * 1.0F / MID_VALUE;
break;
default:break;
}
imageView.setImageBitmap(handleImageEffect(mHue, mSaturation, mLum));
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
/**----------------------------------------------------
* 处理图像的色调,饱和度,亮度。
* 因为Android中不能处理原Bitmap,需要处理Bitmap的副本,
* 因此使用原Bitmap创建副本,进行处理。
* ---------------------------------------------------*/
public Bitmap handleImageEffect(float hue, float saturation, float lum){
//创建副本Bitmap
Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.jide);
Bitmap bitmap = bmp.copy(Bitmap.Config.ARGB_8888,true);
//创建画板、画笔
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
/*-----------------------------------
* 处理色调: setRotate中0,1,2代表R,G,B
* -----------------------------------*/
ColorMatrix hueColorMatrix = new ColorMatrix();
hueColorMatrix.setRotate(0, hue);
hueColorMatrix.setRotate(1, hue);
hueColorMatrix.setRotate(2, hue);
/*-----------------------------------
* 设置饱和度
* -----------------------------------*/
ColorMatrix saColorMatrix = new ColorMatrix();
saColorMatrix.setSaturation(saturation);
/*-------------------------------------------
* 设置亮度:将三原色相同比例混合显示出白色,以此提高亮度
* ------------------------------------------*/
ColorMatrix lumColorMatrix = new ColorMatrix();
lumColorMatrix.setScale(lum, lum, lum, 1);
/*---------------------------------
* 将矩阵作用效果混合, 叠加效果。
* --------------------------------*/
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.postConcat(hueColorMatrix);
colorMatrix.postConcat(saColorMatrix);
colorMatrix.postConcat(lumColorMatrix);
/*-------------------------
* 设置矩阵, 进行绘制
* ------------------------*/
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
canvas.drawBitmap(bitmap, 0, 0, paint);
return bitmap;
}
}
drawble图片
我这里是ji.jpg
,可以将该图片换为任何图片,切记图片不能太大,代码中没有进行OOM处理。
2-Android颜色矩阵-ColorMatrix
使用4X5颜色矩阵进行色彩处理。通过设置具体矩阵某元素来处理色彩。这里使用GridLayout动态创建4X5的EditText。
布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="3" />
<GridLayout
android:id="@+id/mGroup"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="3"
android:columnCount="5"
android:rowCount="4">
</GridLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1.5"
android:orientation="vertical">
<Button
android:id="@+id/btn_change"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:padding="10dp"
android:text="Change"
android:onClick="btnChange"
android:textSize="20sp" />
<Button
android:id="@+id/btn_reset"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:padding="10dp"
android:onClick="btnReset"
android:text="Reset"
android:textSize="20sp" />
</LinearLayout>
</LinearLayout>
Activity
获取矩阵中数据,并通过ColorMatrix作用到图像上。
public class ColorMatrixActivity extends Activity {
private Bitmap bitmap;
private ImageView mImageView;
private GridLayout mGroup;
private EditText [] mEts = new EditText[20];
private int mEtWidth,mEtHeight;
private float[] mColorMatrix = new float[20];
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_color_matrix);
bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.jide);
mImageView = (ImageView) findViewById(R.id.imageView);
mGroup = (GridLayout) findViewById(R.id.mGroup);
mImageView.setImageBitmap(bitmap);
//无法在OnCreate中获取到View的宽高,通过post在视图创建后测量
mGroup.post(new Runnable() {
@Override
public void run() {
//获取宽高信息
mEtWidth = mGroup.getWidth()/5;
mEtHeight = mGroup.getHeight()/4;
addEts();
initMatrix();
}
});
}
/*--------------------------------------
* 将矩阵数组(一维)通过ColorMatrix作用到图像上
* 以此对图片进行色彩处理
* ------------------------------------*/
private void setImageMatrix(){
Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(),
Bitmap.Config.ARGB_8888);
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.set(mColorMatrix);
Canvas canvas = new Canvas(bmp);
Paint paint = new Paint();
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
canvas.drawBitmap(bitmap,0, 0, paint);
mImageView.setImageBitmap(bmp);
}
//添加EditText
private void addEts(){
for(int i = 0;i < 20;i++){
EditText editText = new EditText(ColorMatrixActivity.this);
mEts[i] = editText;
mGroup.addView(editText,mEtWidth,mEtHeight);
}
}
//初始化颜色矩阵为初始状态
private void initMatrix(){
for(int i= 0;i<20;i++){
if(i % 6 ==0 ) {
mEts[i].setText(String.valueOf(1));
}else{
mEts[i].setText(String.valueOf(0));
}
}
}
//获取矩阵值
private void getMatrix(){
for(int i = 0 ;i < 20;i++){
mColorMatrix[i] = Float.valueOf(mEts[i].getText().toString());
}
}
//作用点击事件
public void btnChange(View view) {
getMatrix();
setImageMatrix();
}
//重置矩阵效果
public void btnReset(View view) {
initMatrix();
getMatrix();
setImageMatrix();
}
}
3-常用色彩处理效果
有灰度、反转、怀旧等。
反转矩阵
-1 | 0 | 0 | 1 |
0 | -1 | 0 | 1 |
0 | 0 | -1 | 1 |
0 | 0 | 0 | 1 |
其余不赘述,请查询资料。
4-像素点处理
可以通过改变每个像素点的ARGB值,进行色彩处理。Android提供Bitmap.getPixels(colorArray,省略...)
来提取整个图片的像素点,并保存到数组中。
r = Color.red(colorArray);
g = Color.green(colorArray);
b = Color.blue(colorArray);
a = Color.alpha(colorArray);
获取到原ARGB后,通过计算得到新的ARGB。
r1 = i* r + j* g + k* b;
newPix[i] = Color.argb(a, r1, g1, b1);
bitmap.setPixels(newPix, ...);
使用不同参数,就能达到老照片、浮雕等效果。
6-图形特效处理
图形变换类似于颜色变换。使用3X3变换矩阵进行变换。可以进行平移变换、缩放变换、旋转变换、错切变换。
1-矩阵Matrix
使用矩阵Matrix对图片进行平移、缩放、旋转、错切变换。
public class PictureEditActivity extends Activity {
ImageView imageView;
static float data = 10f; //自定义的数据
Bitmap bitmap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_picture_edit);
imageView = (ImageView) findViewById(R.id.pic_edit_imageview);
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.jide);
//imageView.setImageBitmap(bitmap);
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
editPic();
}
}, 1000);
}
public void editPic(){
Canvas canvas = new Canvas();
/*------------------------------------
* 使用矩阵处理图片
* -----------------------------------*/
float[] mImageMatrix = new float[9];
Matrix matrix = new Matrix();
matrix.setValues(mImageMatrix);//转为矩阵
canvas.drawBitmap(bitmap, matrix, null);
//set会覆盖整个矩阵
matrix.setRotate(data); //旋转
matrix.setTranslate(data, data);//平移
matrix.setScale(data, data); // 缩放
matrix.setSkew(data, data); //错切
/*------------------------------
* 矩阵混合
* pre()前乘: 当前矩阵 X 该次使用的矩阵
* post()后乘: 该次使用的矩阵 X当前矩阵
* ------------------------------*/
matrix.preRotate(data); //会用 当前矩阵 X 该次的旋转矩阵
matrix.postScale(data, data); // 本次缩放矩阵 X 当前矩阵
/*
* 1.先旋转45度
* 2.再平移(200, 200)
* */
//后乘: 旋转后平移
matrix.setRotate(45f);
matrix.postTranslate(200, 200);
//先乘: 平移前旋转
matrix.setTranslate(200, 200);
matrix.preRotate(45f);
/*---------------------
* 测试:
* --------------------*/
Bitmap bmp = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(),
Bitmap.Config.ARGB_8888); //创造bitmp的复制品bmp
canvas = new Canvas(bmp); //用bmp创造画布
canvas.drawBitmap(bitmap, matrix, null); //根据bitmap以matrix变化后的图片, 在bmp上进行绘制
imageView.setImageBitmap(bmp);
}
}
2-像素块
例如色彩处理中像素点的处理,将图片分为像素块,改变像素块来处理图片。
drawBitmapMesh()
canvas.drawBitmapMesh(bitmap,WIDTH,HEIGHT,verts,0,null,0,null);
,将第五个参数的坐标数组传入,bitmap会根据像素块数组,将WIDTH X HEIGHT网格中,每个焦点的坐标进行相应的修改。
public class FlagImage extends View {
Bitmap bitmap;
//定义两个常量,这两个常量指定该图片横向20格,纵向上都被划分为10格
private final int WIDTH = 30;
private final int HEIGHT = 30;
//记录该图像上包含的231个顶点
private final int COUNT = (WIDTH +1) * (HEIGHT + 1);
//定义一个数组,记录Bitmap上的21*11个点的坐标
private final float[] verts = new float[COUNT * 2];
//定义一个数组,记录Bitmap上的21*11个点经过扭曲后的坐标
//对图片扭曲的关键就是修改该数组里元素的值
private final float[] orig = new float[COUNT * 2];
//振幅大小
private final float A = 10;
private float k = 0; //旗帜飞扬
public FlagImage(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.jide);
float bitmapWidth = bitmap.getWidth();
float bitmapHeight = bitmap.getHeight();
int index = 0;
for(int y = 0; y <= HEIGHT; y++){
float fy = bitmapHeight * y / HEIGHT; //图像高度 乘以 y/总格数 可以获得 纵向y的坐标
for(int x = 0;x<= WIDTH;x ++){
float fx = bitmapWidth * x/WIDTH; //x的坐标值
orig [index * 2 + 0] = verts [index * 2 + 0] = fx; //[(x1,y1), (x2,y2), (...)...]一维数组依次存储
orig [index * 2 + 1] = verts [index * 2 + 1] = fy+100; //这里人为将坐标+100是为了让图像下移,避免扭曲后被屏幕遮挡。
index += 1;
}
}
}
@Override
protected void onDraw(Canvas canvas) {
flagWave();//旗帜飞扬
k += 0.1F;
//对bitmap按verts数组进行扭曲
//从第一个点(由第5个参数0控制)开始扭曲
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; //x值不变
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;
}
}
}
private void flagWave(float cx, float cy){
for(int i = 0; i < COUNT * 2; i += 2)
{
float dx = cx - orig[i + 0];
float dy = cy - orig[i + 1];
float dd = dx * dx + dy * dy;
//计算每个坐标点与当前点(cx,cy)之间的距离
float d = (float)Math.sqrt(dd);
//计算扭曲度,距离当前点(cx,cy)越远,扭曲度越小
float pull = 80000 / ((float)(dd * d));
//对verts数组(保存bitmap 上21 * 21个点经过扭曲后的坐标)重新赋值
if(pull >= 1)
{
verts[i + 0] = cx;
verts[i + 1] = cy;
}
else
{
//控制各顶点向触摸事件发生点偏移
verts[i + 0] = orig[i + 0] + dx * pull;
verts[i + 1] = orig[i + 1] + dx * pull;
}
}
//通知View组件重绘
invalidate();
}
public boolean onTouchEvent(MotionEvent event)
{
// //调用warp方法根据触摸屏事件的坐标点来扭曲verts数组
// flagWave(event.getX() , event.getY());
return true;
}
}
7-画笔特效处理
1-PorterDuffXfermode
用于设置两个图像交际区域的显示方式。以此可以制作圆形图片等效果。
圆形图片
先将画布变成圆角矩形。再将图片绘制到画布上。
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.jide);
Bitmap outBmp = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(outBmp); //用outbmp创建空白画布
Paint paint = new Paint();
paint.setAntiAlias(true);
canvas.drawRoundRect(0, 0, bitmap.getWidth(), bitmap.getHeight(), 80, 80, paint);//先画出圆角长方形
/*
* 将bitmap绘制到圆角矩形的画布上(outBmp)
* */
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, 0, 0, paint);
roundImageView.setImageBitmap(outBmp);
刮刮卡效果
- 准备背景bitmap
- 准备灰色的bitmap
- 在外层灰色bitmap上用透明画笔绘制出路径(PorterDuffXfermode会将透明度与灰色bitmap融合,使得灰色bitmap在路径上变成透明)
- 在onDraw(Canvas canvas)整个View的canvas上先绘制背景bitmap,再绘制外层灰色bitmap
public class CardImageView extends View {
Bitmap mBgBitmap,mFgBitmap;
Canvas mCanvas;
Paint mPaint;
Path mPath;
public CardImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initCardImage();
}
private void initCardImage() {
mPaint = new Paint();
mPaint.setAlpha(0); //将画笔的透明度设为0
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); //目标的透明路径 覆盖在 外层图层之上, 让外层图层也透明
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND); //让画的线圆滑
mPaint.setStrokeWidth(50);
mPaint.setStrokeCap(Paint.Cap.ROUND); //圆滑
mPath = new Path();
mBgBitmap = BitmapFactory.decodeResource(getResources(),R.drawable.jide); //内层图层:图片
mFgBitmap = Bitmap.createBitmap(mBgBitmap.getWidth(),mBgBitmap.getHeight(),Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mFgBitmap);
mCanvas.drawColor(Color.GRAY); //外层图层为灰色
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
mPath.reset();
mPath.moveTo(event.getX(),event.getY());
break;
case MotionEvent.ACTION_MOVE:
mPath.lineTo(event.getX(),event.getY());
break;
}
mCanvas.drawPath(mPath,mPaint); //给外层图层 画路径(路径画笔的透明色会让外层图层透明)
invalidate();
return true;
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBgBitmap,0,0,null);
canvas.drawBitmap(mFgBitmap,0,0,null);
}
}
2-Shader
着色器、渐变器:用于渐变和渲染效果。主要有BitmapShader(位图)、LinearGradient(线性)、RadialGradient(光束)、SweepGradient(梯度)、ComposeShader(混合)
BitmapShader shader = new BitmapShader(bitmap, Shader.TitleMode.CLAMP, Shader.TitleMode.CLAMP); //初始化
Paint paint = new Paint();
paint.setShader(shader); //设置shader
canvas.drawCircle(500, 250, 200, paint); //绘制
其余的使用和例程就省略了,有兴趣的参考开头的博客或者原著。
主要就是选择需要的Shader,并且在Paint里面setShader,就可以去绘制了。
3-PathEffect
用想要的效果去绘制路径,比如让路径圆滑,出现噪点等。主要使用方法如下:
Path path = new Path(); //创造路径
...
PathEffect pe = new ComposePathEffect(...);
path.setPathEffect(pe); //设置路径
canvas.drawPath(path, paint); //绘制路径