Android中级

您所在的位置:网站首页 surfaceview的使用 Android中级

Android中级

#Android中级| 来源: 网络整理| 查看: 265

屏幕和绘图 屏幕系统屏幕密度独立像素密度dp单位转换 XML绘图(需放在Drawable)BitmapShapeLayerSelector 绘图技巧CanvasLayerPorterDuffXfermodeShaderPathEffectSurfaceView

屏幕 屏幕大小:指屏幕对角线长度,单位为寸分辨率:指屏幕宽高的像素点个数,如720x1280PPI:每英寸像素(Pixels Per Inch),指对角线的像素点数除以屏幕大小,又称屏幕密度DPI(Dots Per Inch) 系统屏幕密度

不同手机的大小和像素密度都不同,为统一,定义了几个标准的DPI值

在这里插入图片描述

独立像素密度dp

相同的像素,在不同密度的屏幕上显示,长度会不同,因为高密度的屏幕包含更多像素点

规定密度为mdpi(即密度值为160)时1px=1dp故上图各分辨率的换算比例为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

Shape

Shape可实现不同类型的形状,如下实现渐变阴影

效果如图

在这里插入图片描述

Layer

Layer可实现类似PS中图层的概念

上面两个item分别为图片进行叠加,效果如图

在这里插入图片描述

Selector

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); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mWidth = getMeasuredWidth(); mHeight = getMeasuredHeight(); } @Override protected 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(); } @Override protected 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); } @Override protected 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); } @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); } }

在这里插入图片描述

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); } @Override protected 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); } @Override protected 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); } @Override protected 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


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3