网站建设找业主签字模板,企业邮箱查询,常州网络推广网站,wordpress主页帖子不分页屏幕和绘图屏幕系统屏幕密度独立像素密度dp单位转换XML绘图#xff08;需放在Drawable#xff09;BitmapShapeLayerSelector绘图技巧CanvasLayerPorterDuffXfermodeShaderPathEffectSurfaceView屏幕
屏幕大小#xff1a;指屏幕对角线长度#xff0c;单位为寸分辨率#x…
屏幕和绘图屏幕系统屏幕密度独立像素密度dp单位转换XML绘图需放在DrawableBitmapShapeLayerSelector绘图技巧CanvasLayerPorterDuffXfermodeShaderPathEffectSurfaceView屏幕
屏幕大小指屏幕对角线长度单位为寸分辨率指屏幕宽高的像素点个数如720x1280PPI每英寸像素Pixels Per Inch指对角线的像素点数除以屏幕大小又称屏幕密度DPIDots Per Inch
系统屏幕密度
不同手机的大小和像素密度都不同为统一定义了几个标准的DPI值 独立像素密度dp
相同的像素在不同密度的屏幕上显示长度会不同因为高密度的屏幕包含更多像素点
规定密度为mdpi即密度值为160时1px1dp故上图各分辨率的换算比例为3:4:6:8:12
单位转换
如下工具类提供了px、dp、sp的互相转换
public class DisplayUtil {// dp 像素/密度public static int px2dip(Context context, float pxValue) {float scale context.getResources().getDisplayMetrics().density;return (int) (pxValue / scale 0.5f);}public static int dip2px(Context context, float dipValue) {float scale context.getResources().getDisplayMetrics().density;return (int) (dipValue * scale 0.5f);}// sp 像素/缩放密度public static int px2sp(Context context, float pxValue) {float fontScale context.getResources().getDisplayMetrics().scaledDensity;return (int) (pxValue / fontScale 0.5f);}public static int sp2px(Context context, float spValue) {float fontScale context.getResources().getDisplayMetrics().scaledDensity;return (int) (spValue * fontScale 0.5f);}
}或可以使用TypedValue.applyDimension()方法
public int dp2px(int dp) {return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dp,getResources().getDisplayMetrics());
}public int sp2px(int sp) {return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,sp,getResources().getDisplayMetrics());
}XML绘图需放在Drawable
Bitmap
如下引用图片并转为Bitmap
?xml version1.0 encodingutf-8?
bitmap xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:srcmipmap/ic_launcher /Shape
Shape可实现不同类型的形状如下实现渐变阴影
?xml version1.0 encodingutf-8?
shape xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:shaperectanglegradientandroid:angle45android:endColor#805FBBFFandroid:startColor#FF5DA2FF /paddingandroid:bottom7dpandroid:left7dpandroid:right7dpandroid:top7dp /corners android:radius8dp /
/shape效果如图 Layer
Layer可实现类似PS中图层的概念
?xml version1.0 encodingutf-8?
layer-list xmlns:androidhttp://schemas.android.com/apk/res/androiditem android:drawablemipmap/ic_launcher /itemandroid:bottom50dpandroid:drawablemipmap/ic_launcherandroid:left50dpandroid:right50dpandroid:top50dp /
/layer-list上面两个item分别为图片进行叠加效果如图 Selector
Selector实现不同事件设置反馈如下实现修改为圆角矩形点击后切换颜色
?xml version1.0 encodingutf-8?
selector xmlns:androidhttp://schemas.android.com/apk/res/androiditem android:state_pressedtrueshape android:shaperectanglesolid android:color#33444444 /corners android:radius5dp /padding android:bottom10dp android:left10dp android:right10dp android:top10dp //shape/itemitemshape android:shaperectanglesolid android:color#FFFFFF /corners android:radius5dp /padding android:bottom10dp android:left10dp android:right10dp android:top10dp //shape/item
/selector绘图技巧
Canvas
下面为常用的方法
save()将已绘制图像保存在此之后的操作绘制在新图层restore()合并图层将save()之后绘制的图像和save()之前的合并translate()坐标系平移ratate()坐标系旋转
如下绘制一个时钟通过平移、旋转坐标系简化实现
public class Clock extends View {private int mWidth;private int mHeight;public Clock(Context context) {super(context);}public Clock(Context context, AttributeSet attrs) {super(context, attrs);}public Clock(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);mWidth getMeasuredWidth();mHeight getMeasuredHeight();}Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);// 绘制外圆以(mWidth / 2, mHeight / 2)为圆形以mWidth / 2为半径Paint paintCircle new Paint();paintCircle.setStyle(Paint.Style.STROKE);paintCircle.setAntiAlias(true);paintCircle.setStrokeWidth(5);canvas.drawCircle(mWidth / 2, mHeight / 2, mWidth / 2, paintCircle);// 绘制刻度Paint paintDegree new Paint();for (int i 0; i 24; i) {if (i 0 || i 6 || i 12 || i 18) {paintDegree.setStrokeWidth(5);paintDegree.setTextSize(30);canvas.drawLine(mWidth / 2, mHeight / 2 - mWidth / 2,mWidth / 2, mHeight / 2 - mWidth / 2 60,paintDegree);String degree String.valueOf(i);canvas.drawText(degree,mWidth / 2 - paintDegree.measureText(degree) / 2,mHeight / 2 - mWidth / 2 90,paintDegree);} else {paintDegree.setStrokeWidth(3);paintDegree.setTextSize(15);canvas.drawLine(mWidth / 2, mHeight / 2 - mWidth / 2,mWidth / 2, mHeight / 2 - mWidth / 2 30,paintDegree);String degree String.valueOf(i);canvas.drawText(degree,mWidth / 2 - paintDegree.measureText(degree) / 2,mHeight / 2 - mWidth / 2 60,paintDegree);}// 将画布以圆心旋转15°,简化坐标运算canvas.rotate(15, mWidth / 2, mHeight / 2);}// 绘制指针Paint paintHour new Paint();paintHour.setStrokeWidth(20);Paint paintMinute new Paint();paintMinute.setStrokeWidth(10);canvas.save();canvas.translate(mWidth / 2, mHeight / 2); // 将坐标系平移到圆点再画指针canvas.drawLine(0, 0, 100, 100, paintHour);canvas.drawLine(0, 0, 100, 200, paintMinute);canvas.restore();}
}Layer
图层基于栈结构入栈后操作都发生在该图层上出栈后则把图像绘制到上层Canvas
入栈saveLayer()、saveLayerAlpha()出栈restore()、restoreToCount()
public class MyView extends View {private static final String TAG MyView.class.getSimpleName();private Paint mPaint;public MyView(Context context) {this(context, null);}public MyView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public MyView(Context context, AttributeSet attrs, int defStyleAttr) {this(context, attrs, defStyleAttr, 0);}public MyView(Context context, Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);mPaint new Paint();}Overrideprotected void onDraw(Canvas canvas) {canvas.drawColor(Color.WHITE);mPaint.setColor(Color.BLUE);canvas.drawCircle(150, 150, 100, mPaint);canvas.saveLayerAlpha(0, 0, 400, 400, 127);mPaint.setColor(Color.RED);canvas.drawCircle(200, 200, 100, mPaint);canvas.restore();}
}如下图透明度为127的红色圆叠加在蓝色圆上方 PorterDuffXfermode
PorterDuffXfermod设置的是两个图层交集区域的显示方式dst是先画的图形而src是后画的图形共有16种模式如图 如下实现圆角图形mOut为先画的图像mBitmap为后画的图像用SrcIn取交集
public class MyView extends View {private static final String TAG MyView.class.getSimpleName();private Bitmap mBitmap;private Bitmap mOut;private Paint mPaint;public MyView(Context context) {this(context, null);}public MyView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public MyView(Context context, AttributeSet attrs, int defStyleAttr) {this(context, attrs, defStyleAttr, 0);}public MyView(Context context, Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);mBitmap BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);mOut Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), Bitmap.Config.ARGB_8888);Canvas canvas new Canvas(mOut);mPaint new Paint();mPaint.setAntiAlias(true);canvas.drawRoundRect(0, 0, mBitmap.getWidth(), mBitmap.getHeight(), 80, 80, mPaint);mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));canvas.drawBitmap(mBitmap, 0, 0, mPaint);}Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawBitmap(mOut, 0, 0, null);}
}效果如图 如下代码设置透明的画笔实时获取坐标调用drawPath()涂抹上面的图层实现刮刮乐
public class XfermodeView extends View {private Bitmap mBgBitmap, mFgBitmap;private Paint mPaint;private Canvas mCanvas;private Path mPath;public XfermodeView(Context context) {this(context, null);}public XfermodeView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public XfermodeView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {mPaint new Paint();mPaint.setAlpha(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.b);mFgBitmap Bitmap.createBitmap(mBgBitmap.getWidth(), mBgBitmap.getHeight(), Bitmap.Config.ARGB_8888);mCanvas new Canvas(mFgBitmap);mCanvas.drawColor(Color.GRAY);}Overridepublic 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;}Overrideprotected void onDraw(Canvas canvas) {canvas.drawBitmap(mBgBitmap, 0, 0, null);canvas.drawBitmap(mFgBitmap, 0, 0, null);}
}Shader
用于实现渐变、渲染效果包括
BitmapShader——位图ShaderLinnerGradient——线性ShaderRadialGradient——光束ShaderSweepGradient——梯度ShaderComposeShader——混合Shader
上述渐变都有三种模式选择
CLAMP拉伸拉伸的是图片最后一个像素不断重复REPEAT重复横向、纵向不断重复MIRROR镜像横向、纵向不断翻转重复
如下将图片填充到BitmapShader创建一支具有图像填充功能的Paint
public class MyView extends View {private static final String TAG MyView.class.getSimpleName();private Bitmap mBitmap;private Paint mPaint;private BitmapShader mBitmapShader;public MyView(Context context) {this(context, null);}public MyView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public MyView(Context context, AttributeSet attrs, int defStyleAttr) {this(context, attrs, defStyleAttr, 0);}public MyView(Context context, Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);mBitmap BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);mBitmapShader new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);mPaint new Paint();mPaint.setShader(mBitmapShader);}Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawCircle(mBitmap.getWidth() / 2.0f, mBitmap.getHeight() / 2.0f, 50, mPaint);}
}利用Paint在图片中心绘制圆形效果如下 如下改为REPEAT并扩大半径可看到图片不断重复绘制
public class MyView extends View {private static final String TAG MyView.class.getSimpleName();private Bitmap mBitmap;private Paint mPaint;private BitmapShader mBitmapShader;public MyView(Context context) {this(context, null);}public MyView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public MyView(Context context, AttributeSet attrs, int defStyleAttr) {this(context, attrs, defStyleAttr, 0);}public MyView(Context context, Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);mBitmap BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);mBitmapShader new BitmapShader(mBitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);mPaint new Paint();mPaint.setShader(mBitmapShader);}Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawCircle(mBitmap.getWidth() / 2.0f, mBitmap.getHeight() / 2.0f, 500, mPaint);}
} 如下利用PorterDuffXfermode和LinnerGradient实现倒影效果的图片
public class ReflectView extends View {private Bitmap mSrcBitmap, mRefBitmap;private Paint mPaint;private PorterDuffXfermode mXfermode;public ReflectView(Context context, AttributeSet attrs) {super(context, attrs);init();}private void init() {mSrcBitmap BitmapFactory.decodeResource(getResources(), R.drawable.b);Matrix matrix new Matrix();matrix.setScale(1F, -1F);//垂直翻转mRefBitmap Bitmap.createBitmap(mSrcBitmap, 0, 0,mSrcBitmap.getWidth(), mSrcBitmap.getHeight(), matrix, true);mPaint new Paint();mPaint.setShader(new LinearGradient(0, mSrcBitmap.getHeight(),0, mSrcBitmap.getHeight() * 2,0xDD000000, 0x10000000, Shader.TileMode.REPEAT));mXfermode new PorterDuffXfermode(PorterDuff.Mode.DST_IN);}Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);canvas.drawColor(Color.BLACK);//原图canvas.drawBitmap(mSrcBitmap, 0, 0, null);//倒影图canvas.drawBitmap(mRefBitmap, 0, mSrcBitmap.getHeight(), null);mPaint.setXfermode(mXfermode);//绘制渐变效果层canvas.drawRect(0, mRefBitmap.getHeight(), mRefBitmap.getWidth(), mSrcBitmap.getHeight() * 2, mPaint);mPaint.setXfermode(null);}
}先将图片翻转再添加黑色透明度递减的渐变DST_IN是为了避免上面的图像被渐变效果遮挡 PathEffect
PathEffect指用各种笔触效果来绘制一个路径
如上分别为
无效果CornerPathEffect线段拐角圆滑DiscretePathEffect线段出现杂点DashPathEffect绘制虚线用一个数组来设置各个点之间的间隔还可设置偏移量实现动态路径效果PathPathEffect类似DashPathEffect但还可设置点的图形如上面的圆点虚线ConposePathEffect组合效果
public class MyView extends View {private static final String TAG MyView.class.getSimpleName();private Path mPath;private PathEffect[] mPathEffect;private Paint mPaint;public MyView(Context context) {this(context, null);}public MyView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public MyView(Context context, AttributeSet attrs, int defStyleAttr) {this(context, attrs, defStyleAttr, 0);}public MyView(Context context, Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);mPaint new Paint();mPaint.setStyle(Paint.Style.STROKE);mPath new Path();mPath.moveTo(0, 0);for (int i 0; i 30; i) {mPath.lineTo(i * 35, (float) (Math.random() * 100));}mPathEffect new PathEffect[6];mPathEffect[0] null;mPathEffect[1] new CornerPathEffect(30);mPathEffect[2] new DiscretePathEffect(3.0F, 5.0F);mPathEffect[3] new DashPathEffect(new float[]{20, 10, 5, 10}, 0);Path path new Path();path.addRect(0, 0, 8, 8, Path.Direction.CCW);mPathEffect[4] new PathDashPathEffect(path, 12, 0, PathDashPathEffect.Style.ROTATE);mPathEffect[5] new ComposePathEffect(mPathEffect[3], mPathEffect[1]);}Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);for (PathEffect pathEffect : mPathEffect) {mPaint.setPathEffect(pathEffect);canvas.drawPath(mPath, mPaint);canvas.translate(0, 200);}}
}SurfaceView
Android通过VSYNC信号刷新View进行屏幕的重绘时间间隔为16ms若在需要频繁刷新的界面上执行太多的操作逻辑则容易阻塞主线程造成画面卡顿
当需要频繁刷新或刷新时数据处理量比较大时可使用SurfaceView
View用于主动刷新SurfaceView用于被动刷新View在主线程刷新SurfaceView在子线程刷新View绘图未使用双缓冲机制SurfaceView则使用双缓冲机制
SurfaceView的模板代码如下主要原理是初始化时自行创建子线程并调用draw()方法绘制
public class SurfaceViewTemplate extends SurfaceView implements SurfaceHolder.Callback, Runnable {//内部Holder用于获取Canvas和提交绘制private SurfaceHolder mHolder;//用于控制线程的标志位private boolean mIsDrawing;//用于绘图的Canvasprivate Canvas mCanvas;public SurfaceViewTemplate(Context context) {this(context, null);}public SurfaceViewTemplate(Context context, AttributeSet attrs) {this(context, attrs, 0);}public SurfaceViewTemplate(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {mHolder getHolder();mHolder.addCallback(this); //将自身作为回调setFocusableInTouchMode(true);setFocusable(true);this.setKeepScreenOn(true);}//创建时开启线程调用draw()Overridepublic void surfaceCreated(SurfaceHolder holder) {mIsDrawing true;new Thread(this).start();}//改变时回调Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}//结束时回调Overridepublic void surfaceDestroyed(SurfaceHolder holder) {mIsDrawing false;}Overridepublic void run() {while (mIsDrawing) {draw();}}private void draw() {try {mCanvas mHolder.lockCanvas();//获取Canvas开始绘制获取的并非新的Canvas而是上次的} catch (Exception e) {e.printStackTrace();} finally {if (mHolder ! null) {//绘制完后需提交mHolder.unlockCanvasAndPost(mCanvas);}}}
}如下代码利用SurfaceView实现正弦函数的动态绘制
public class SurfaceViewTemplate extends SurfaceView implements SurfaceHolder.Callback, Runnable {private SurfaceHolder mHolder;private boolean mIsDrawing;private Canvas mCanvas;private int x;private int y;private Path mPath;private Paint mPaint;public SurfaceViewTemplate(Context context) {this(context, null);}public SurfaceViewTemplate(Context context, AttributeSet attrs) {this(context, attrs, 0);}public SurfaceViewTemplate(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}private void init() {mPath new Path();mPaint new Paint();mPaint.setStyle(Paint.Style.STROKE);mHolder getHolder();mHolder.addCallback(this);setFocusableInTouchMode(true);setFocusable(true);this.setKeepScreenOn(true);}Overridepublic void surfaceCreated(SurfaceHolder holder) {mIsDrawing true;new Thread(this).start();}Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}Overridepublic void surfaceDestroyed(SurfaceHolder holder) {mIsDrawing false;}Overridepublic void run() {while (mIsDrawing) {draw();x 1;y (int) (100 * Math.sin(x * 2 * Math.PI / 180) 400);mPath.lineTo(x, y);}}private void draw() {try {mCanvas mHolder.lockCanvas();mCanvas.drawColor(Color.WHITE); //设置背景mCanvas.drawPath(mPath, mPaint);} catch (Exception e) {e.printStackTrace();} finally {if (mHolder ! null) {mHolder.unlockCanvasAndPost(mCanvas);}}}
}效果如图 如下代码利用SurfaceView实现绘图板根据手指移动绘制路径在draw()中调用sleep()避免过度频繁绘制