上片文章初步讲述了自定义View的一些方法 还有一些小东西梳理下,不知道怎么了 突然的感到头疼,,真是一个悲伤的故事。
先来看一下DrawText方法
/**baseLine和FontMetrics
* 关于baseLine和FontMetrics
* x,y并不是文字左上角的坐标点,它比较特殊,y所代表的是基线坐标y的坐标。基线
* drawText(String text, float x, float y, Paint paint)
* 除了基线,还有另外的四条线,它们分别是 top,ascent,descent和bottom,它们的含义分别为:
*top:可绘制的最高高度所在线
*bottom:可绘制的最低高度所在线
*ascent :系统建议的,绘制单个字符时,字符应当的最高高度所在线
*descent:系统建议的,绘制单个字符时,字符应当的最低高度所在线
* Created by xuenan on 2016/7/18.
*/
public class MyView4 extends View{
Paint mPaint;
private Paint paint; //网格绘图
public MyView4(Context context) {
super(context);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
//mPaint.setColor(Color.parseColor("#23AC3B"));
mPaint.setStyle(Paint.Style.FILL);
mPaint.setTextSize(120);
paint = new Paint();
paint.setColor(Color.parseColor("#A8A8A8"));
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(1);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.parseColor("#F5FFFA"));//画布的背景
//网格线的绘制
final int width = getWidth();
final int height = getHeight();
final int space = 100; //长宽间隔
int vertz = 0;
int hortz = 0;
for(int i=0;i<100;i++){
canvas.drawLine(0, vertz, width, vertz, paint);
canvas.drawLine(hortz, 0, hortz, height, paint);
vertz+=space;
hortz+=space;
}
//1.1 BaseLine基线
//1、canvas.drawText()中参数y是基线y的坐标
//2、x坐标、基线位置、文字大小确定,文字的位置就是确定的了。
//定义在x坐标在所绘制矩形相对位置的函数是:setTextAlign(Paint.Align align)
//Paint.Align是枚举类型,值分别为 : Paint.Align.LEFT,Paint.Align.CENTER和Paint.Align.RIGHT。
//mPaint.setTextAlign(Paint.Align.CENTER);//主要是这里的取值不一样
//canvas.drawText("abcdefghijk", 200, 200, mPaint);
//canvas.drawLine(0, 200, getWidth(), 200, mPaint);
//canvas.drawLine(200, 0, 200, getHeight(), mPaint);
//1.2FontMetrics
//还有另外的四条线,它们分别是 top,ascent,descent和bottom,它们的含义分别为:
//top:可绘制的最高高度所在线
//bottom:可绘制的最低高度所在线
//ascent :系统建议的,绘制单个字符时,字符应当的最高高度所在线
//descent:系统建议的,绘制单个字符时,字符应当的最低高度所在线
//两个构造方法的区别是,得到对象的成员变量的值一个为float类型,一个为int类型。
//Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
//Paint.FontMetricsInt fm= mPaint.getFontMetricsInt();
//以float为例
//Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
//float ascent = fontMetrics.ascent;//当前绘制顶线
//float descent = fontMetrics.descent;//当前绘制底线
//float top = fontMetrics.top;//可绘制最顶线
//float bottom = fontMetrics.bottom;//可绘制最底线
//float leading = fontMetrics.leading;//top和ascent在y轴上的差值 为负数
//计算方法 FontMetrics的这几个变量的值都是以baseLine为基准的
//ascent = ascent线的y坐标 - baseline线的y坐标;//负数
//descent = descent线的y坐标 - baseline线的y坐标;//正数
//top = top线的y坐标 - baseline线的y坐标;//负数
//bottom = bottom线的y坐标 - baseline线的y坐标;//正数
//leading = top线的y坐标 - ascent线的y坐标;//负数
//ascent线Y坐标 = baseline线的y坐标 + fontMetric.ascent;
//descent线Y坐标 = baseline线的y坐标 + fontMetric.descent;
//top线Y坐标 = baseline线的y坐标 + fontMetric.top;
//bottom线Y坐标 = baseline线的y坐标 + fontMetric.bottom;
//1.3、绘制ascent,descent,top,bottom线
int baseLineY = 200;
mPaint.setTextAlign(Paint.Align.LEFT);
canvas.drawText("abcdefghijkl's", 200, baseLineY, mPaint);
Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
float top = fontMetrics.top + baseLineY;
float ascent = fontMetrics.ascent + baseLineY;
float descent = fontMetrics.descent + baseLineY;
float bottom = fontMetrics.bottom + baseLineY;
//绘制基线
mPaint.setColor(Color.parseColor("#FF1493"));
canvas.drawLine(0, baseLineY, getWidth(), baseLineY, mPaint);
//绘制top直线
mPaint.setColor(Color.parseColor("#FFB90F"));
canvas.drawLine(0, top, getWidth(), top, mPaint);
//绘制ascent直线
mPaint.setColor(Color.parseColor("#b03060"));
canvas.drawLine(0, ascent, getWidth(), ascent, mPaint);
//绘制descent直线
mPaint.setColor(Color.parseColor("#912cee"));
canvas.drawLine(0, descent, getWidth(), descent, mPaint);
//绘制bottom直线
mPaint.setColor(Color.parseColor("#1E90FF"));
canvas.drawLine(0, bottom, getWidth(), bottom, mPaint);
//绘制最小矩形
//String text="abcdefghijkl's";
//canvas.drawRect(200,ascent,mPaint.measureText(text)+200,descent,mPaint);
//文字高度
//float text_height= bottom - top; //注意top为负数
//文字中点y坐标
//float text_center = (bottom - top) / 2;
//float text_height=Math.abs(top-bottom);//同上
//文字宽度
//float text_width = mPaint.measureText(text);
}
}
再看一下图片混合
/**mPaint.setXfermode()图像混合
* Created by xuenan on 2016/7/18.
*/
public class MyView5 extends View{
private Paint mPaint;
private int width = 800;
private int height = 800;
private Bitmap dstBmp;
private Bitmap srcBmp;
public MyView5(Context context) {
super(context);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setColor(Color.parseColor("#23AC3B"));
mPaint.setStyle(Paint.Style.FILL);
mPaint.setTextSize(120);
//目标图
dstBmp = makeBitmap(width, height, 0);
//源图
srcBmp = makeBitmap(width, height, 1);
}
public Bitmap makeBitmap(int w, int h, int style) {
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint();
p.setAntiAlias(true);
switch (style) {
case 0:
p.setColor(Color.parseColor("#ff00ff"));
c.drawOval(new RectF(0, 0, w, h), p);
break;
case 1:
p.setColor(Color.parseColor("#00ffff"));
c.drawRect(new RectF(0, 0, w, h), p);
break;
default:
}
return bm;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.parseColor("#F5FFFA"));//画布的背景
//1、mPaint.setXfermode(Xfermode xfermode)
//关于Xfermode这个类 翻译如下
//Xfermode是对于被称为实现自定义的“转移模式”,在绘图管线对象的基类。
//静态函数创建(模式)可以调用返回的任何作为模式枚举指定的预定义子类的一个实例。
//当Xfermode被分配到一个画笔,然后对象与该画笔绘制应用了xfermode。
//改类有三个子类AvoidXfermode PixelXorXfermode PorterDuffXfermode
//前两个已经过时了 不再介绍 重点介绍第三个
//只有一个参数PorterDuff.Mode是枚举类型,值有18个
// Mode.CLEAR
// Mode.SRC
// Mode.DST
// Mode.SRC_OVER
// Mode.DST_OVER
// Mode.SRC_IN
// Mode.DST_IN
// Mode.SRC_OUT
// Mode.DST_OUT
// Mode.SRC_ATOP
// Mode.DST_ATOP
// Mode.XOR
// Mode.DARKEN
// Mode.LIGHTEN
// Mode.MULTIPLY
// Mode.SCREEN
// Mode.OVERLAY
// Mode.ADD
// mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
//xml文件中设置math_parent
//新建图层
int layerID = canvas.saveLayer(0,0,getWidth(),getHeight(),mPaint,Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(dstBmp, 0, 0, mPaint);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(srcBmp, width/2, height/2, mPaint);
mPaint.setXfermode(null);
canvas.restoreToCount(layerID);
}
/**图像混合实现圆角图片
* @param bitmap 原图
* @param pixels 圆角大小
* @return
*/
public Bitmap getRoundCornerBitmap(Bitmap bitmap, float pixels) {
//获取bitmap的宽高
int width = bitmap.getWidth();
int height = bitmap.getHeight();
Bitmap cornerBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Paint paint = new Paint();
Canvas canvas = new Canvas(cornerBitmap);
paint.setAntiAlias(true);
canvas.drawRoundRect(new RectF(0, 0, width, height), pixels, pixels, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, width, height), paint);
//绘制边框
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(6);
paint.setColor(Color.GREEN);
canvas.drawRoundRect(new RectF(0, 0, width, height), pixels, pixels, paint);
return cornerBitmap;
}
}
一个利用图片混合实现波浪纹的效果
/**
* mPath.rQuadTo()贝塞尔曲线的绘制原理
* 二阶贝赛尔
* public void quadTo(float x1, float y1, float x2, float y2)
* public void rQuadTo(float dx1, float dy1, float dx2, float dy2)
* 三阶贝赛尔
* public void cubicTo(float x1, float y1, float x2, float y2,float x3, float y3)
* public void rCubicTo(float x1, float y1, float x2, float y2,float x3, float y3)
* 一阶贝塞尔公式
* B(t) = (1-t)*P0+t*P1; t=>[0,1]
* 二阶贝塞尔公式
* B(t) = (1-t)^2*P0+2*t*(1-t)*P1+t^2*P2; t=>[0,1]
* 三阶贝塞尔曲线
* <p/>
* Created by xuenan on 2016/7/18.
*/
public class MyView6 extends View {
private Context context;
private Bitmap mBitmap;
private Canvas mCanvas;
private Path mWavePath;
private Paint mCirclePaint;
private Paint mWavePaint;
private int mCanvasSize;
private int mCircleRadius;
private int mCircleCenterX;
private int mCircleCenterY;
private int mWaveOriginX;
private int mWaveOriginY;
private int mWaveMoveX;
private int mWaveAmplitude = DEFAULT_WAVE_AMPLITUDE;
private int mWaveLength = DEFAULT_WAVE_LENGTH;
private static final int DEFAULT_WAVE_AMPLITUDE = 100;
private static final int DEFAULT_WAVE_LENGTH = 800;
public MyView6(Context context) {
super(context);
this.context = context;
init();
}
private void init() {
setLayerType(LAYER_TYPE_SOFTWARE, null);//关闭硬件加速
mCirclePaint = new Paint();
mCirclePaint.setColor(Color.parseColor("#abc123"));
mCirclePaint.setAntiAlias(true);
mWavePaint = new Paint();
mWavePaint.setStyle(Paint.Style.FILL);
mWavePaint.setColor(Color.parseColor("#D22D2E"));
mWavePaint.setAntiAlias(true);
mWavePath = new Path();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mCircleCenterX = w / 2;
mCircleCenterY = h / 2;
mCircleRadius = (int) (Math.min(mCircleCenterX, mCircleCenterY) * 0.615f);
mWaveOriginX = -mWaveLength;
mWaveOriginY = mCircleCenterX;
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = measureSize(widthMeasureSpec);
int height = measureSize(heightMeasureSpec);
int imageSize = (width < height) ? width : height;
setMeasuredDimension(imageSize, imageSize);
}
private int measureSize(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else if (specMode == MeasureSpec.AT_MOST) {
result = specSize;
} else {
result = mCanvasSize;
}
return result;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mCanvasSize = canvas.getWidth();
if (canvas.getHeight() < mCanvasSize) {
mCanvasSize = canvas.getHeight();
}
int layerID = canvas.saveLayer(0, 0, getWidth(), getHeight(), mWavePaint, Canvas.ALL_SAVE_FLAG);
mCanvas.drawCircle(mCircleCenterX, mCircleCenterY, mCircleRadius, mCirclePaint);
mWavePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
mWavePath.reset();
mWavePaint.setColor(Color.parseColor("#ff00ff"));
mWavePath.moveTo(mWaveOriginX + mWaveMoveX, mWaveOriginY);
for (int i = mWaveOriginX; i <= getWidth() + mWaveLength; i += mWaveLength) {
mWavePath.rQuadTo(mWaveLength / 4f, -mWaveAmplitude, mWaveLength / 2f, 0);
mWavePath.rQuadTo(mWaveLength / 4f, mWaveAmplitude, mWaveLength / 2f, 0);
}
mWavePath.lineTo(getWidth(), getHeight());
mWavePath.lineTo(0, getHeight());
mWavePath.close();
mCanvas.drawPath(mWavePath, mWavePaint);
canvas.drawBitmap(mBitmap, 0, 0, null);
mWavePaint.setXfermode(null);
canvas.restoreToCount(layerID);
}
public void startAnim() {
ValueAnimator animator = ValueAnimator.ofInt(0, Math.abs(mWaveLength));
animator.setDuration(1000);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mWaveMoveX = (int) animation.getAnimatedValue();
postInvalidate();
}
});
animator.start();
}
}
最后看一下Canvas画布的一些属性
/**Canvas 的变换与操作
有时候我们还需要对 Canvas 做一些操作,比如旋转,裁剪,平移等等。
canvas.translate 平移
canvas.rotate 旋转
canvas.scale 缩放
canvas.skew 错切
canvas.clipRect 裁剪
canvas.save和canvas.restore 保存和恢复
PorterDuffXfermode 图像混合 (paint相关方法)
* Created by xuenan on 2016/7/18.
*/
public class MyView7 extends View{
Paint mPaint;
private Paint paint; //网格绘图
public MyView7(Context context) {
super(context);
mPaint = new Paint();
mPaint.setStrokeWidth(3);
mPaint.setStyle(Paint.Style.STROKE);
paint = new Paint();
paint.setColor(Color.parseColor("#A8A8A8"));
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(1);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.parseColor("#F5FFFA"));//画布的背景
//网格线的绘制
final int width = getWidth();
final int height = getHeight();
final int space = 100; //长宽间隔
int vertz = 0;
int hortz = 0;
for(int i=0;i<100;i++){
canvas.drawLine(0, vertz, width, vertz, paint);
canvas.drawLine(hortz, 0, hortz, height, paint);
vertz+=space;
hortz+=space;
}
//1.1
//mPaint.setColor(Color.parseColor("#008B00"));
//canvas.drawCircle(200,200,200,mPaint);
//translate平移 translate(float dx, float dy)
//float dx:水平方向平移的距离,正数指向正方向(向右)平移的量,负数指向负方向(向左)平移的量
//flaot dy:垂直方向平移的距离,正数指向正方向(向下)平移的量,负数指向负方向(向上)平移的量
//mPaint.setColor(Color.parseColor("#68228B"));
//canvas.translate(200,200);
//canvas.drawCircle(200,200,200,mPaint);
//屏幕显示与Canvas不是一个概念!Canvas是一个很虚幻的概念,相当于一个透明图层
//(用过PS的同学应该都知道),每次Canvas画图时(即调用Draw系列函数),
// 都会产生一个透明图层,然后在这个图层上画图,画完之后覆盖在屏幕上显示。
//translate 函数其实实际相当于平移坐标系,即平移坐标系的原点的位置。
//1.2Rotate
//旋转画布,看起来和图片旋转效果有点类似。默认是围绕坐标系原点旋转,同理也可以设定中心点旋转。
//rotate(float degrees)
//rotate(float degrees, float px, float py)
//第一个构造函数 degrees 参数表示旋转的度数。正数表示顺时针旋转,负数表示逆时针旋转,
// 0 度表示水平X轴方向。默认以坐标原点作为中心点。
//第二个构造函数 px , py 表示中心点坐标。
//mPaint.setColor(Color.parseColor("#008B00"));
//未旋转的直线
//canvas.drawLine(200, 200, 600, 200,mPaint);
//顺时针旋转30度
//canvas.rotate(30,400,200);
//mPaint.setColor(Color.parseColor("#68228B"));
//canvas.drawLine(200, 200, 600, 200,mPaint);
//1.3缩3、放(scale )有两个方法
//scale(float sx, float sy)//缩放
//scale(float sx, float sy, float px, float py)//先平移后缩放
//canvas.save();
//mPaint.setColor(Color.RED);
//未缩放的圆形
//canvas.drawCircle(200, 200, 100, mPaint);
//画布缩放
//canvas.scale(1.5f, 1.5f);
//mPaint.setColor(Color.GREEN);
//canvas.drawCircle(200, 200, 100, mPaint);
//canvas.restore();
//先将画布平移px,py,然后scale,scale结束之后再将画布平移回原基准点。
//canvas.scale(1.5f, 1.5f, 200, 200);
//mPaint.setColor(Color.YELLOW);
//canvas.drawCircle(200, 200, 100, mPaint);
//1.4、错切(skew)skew(float sx, float sy)
//float sx:将画布在x方向上倾斜相应的角度,sx倾斜角度的tan值
//float sy:将画布在y轴方向上倾斜相应的角度,sy为倾斜角度的tan值
//mPaint.setColor(Color.RED);
//未旋转的圆形
//canvas.drawRect(100, 100, 500, 400, mPaint);
//canvas.skew(0.56f, 0f);
//mPaint.setColor(Color.GREEN);
//canvas.drawRect(100, 100, 500, 400, mPaint);
//1.5、保存和恢复save()、restore()
//对画布的平移,旋转,缩放,错切,裁剪都是不可逆的操作,如果我们还需要返回到原始状态
//对画布进行操作怎么办呢?细心的同学肯定知道 save(),restore() 方法在上文已经出现过了,
//对的,它就是用来对画布状态的保存与恢复,这样我们就可以愉快的进行可逆操作了。方法预览:
//save():每次调用 save() 函数,都会把当前的画布的状态进行保存,然后放入特定的栈中;
//restore():每当调用 restore() 函数,就会把栈中最顶层的画布状态取出来,并按照这个状态
//恢复当前的画布,并在这个画布上做画。
canvas.drawColor(Color.GREEN);
//保存当前画布
canvas.save();
canvas.clipRect(new Rect(200, 200, 500, 400));
canvas.drawColor(Color.YELLOW);
//恢复画布
canvas.restore();
canvas.drawColor(Color.RED);
//在使用canvas.save和canvas.restore时最好配对使用,
//若restore( )的调用次数比save( )多可能会造成异常。
}
好困啊我 就写到这