Android
1.1 背景相关与系统架构分析 1.2 开发环境搭建 1.2.1 使用Eclipse + ADT + SDK开发Android APP 1.2.2 使用Android Studio开发Android APP 1.3 SDK更新不了问题解决 1.4 Genymotion模拟器安装 1.5.1 Git使用教程之本地仓库的基本操作 1.5.2 Git之使用GitHub搭建远程仓库 1.6 .9(九妹)图片怎么玩 1.7 界面原型设计 1.8 工程相关解析(各种文件,资源访问) 1.9 Android程序签名打包 1.11 反编译APK获取代码&资源 2.1 View与ViewGroup的概念 2.2.1 LinearLayout(线性布局) 2.2.2 RelativeLayout(相对布局) 2.2.3 TableLayout(表格布局) 2.2.4 FrameLayout(帧布局) 2.2.5 GridLayout(网格布局) 2.2.6 AbsoluteLayout(绝对布局) 2.3.1 TextView(文本框)详解 2.3.2 EditText(输入框)详解 2.3.3 Button(按钮)与ImageButton(图像按钮) 2.3.4 ImageView(图像视图) 2.3.5.RadioButton(单选按钮)&Checkbox(复选框) 2.3.6 开关按钮ToggleButton和开关Switch 2.3.7 ProgressBar(进度条) 2.3.8 SeekBar(拖动条) 2.3.9 RatingBar(星级评分条) 2.4.1 ScrollView(滚动条) 2.4.2 Date & Time组件(上) 2.4.3 Date & Time组件(下) 2.4.4 Adapter基础讲解 2.4.5 ListView简单实用 2.4.6 BaseAdapter优化 2.4.7ListView的焦点问题 2.4.8 ListView之checkbox错位问题解决 2.4.9 ListView的数据更新问题 2.5.0 构建一个可复用的自定义BaseAdapter 2.5.1 ListView Item多布局的实现 2.5.2 GridView(网格视图)的基本使用 2.5.3 Spinner(列表选项框)的基本使用 2.5.4 AutoCompleteTextView(自动完成文本框)的基本使用 2.5.5 ExpandableListView(可折叠列表)的基本使用 2.5.6 ViewFlipper(翻转视图)的基本使用 2.5.7 Toast(吐司)的基本使用 2.5.8 Notification(状态栏通知)详解 2.5.9 AlertDialog(对话框)详解 2.6.0 其他几种常用对话框基本使用 2.6.1 PopupWindow(悬浮框)的基本使用 2.6.2 菜单(Menu) 2.6.3 ViewPager的简单使用 2.6.4 DrawerLayout(官方侧滑菜单)的简单使用 3.1.1 基于监听的事件处理机制 3.2 基于回调的事件处理机制 3.3 Handler消息传递机制浅析 3.4 TouchListener PK OnTouchEvent + 多点触碰 3.5 监听EditText的内容变化 3.6 响应系统设置的事件(Configuration类) 3.7 AnsyncTask异步任务 3.8 Gestures(手势) 4.1.1 Activity初学乍练 4.1.2 Activity初窥门径 4.1.3 Activity登堂入室 4.2.1 Service初涉 4.2.2 Service进阶 4.2.3 Service精通 4.3.1 BroadcastReceiver牛刀小试 4.3.2 BroadcastReceiver庖丁解牛 4.4.1 ContentProvider初探 4.4.2 ContentProvider再探——Document Provider 4.5.1 Intent的基本使用 4.5.2 Intent之复杂数据的传递 5.1 Fragment基本概述 5.2.1 Fragment实例精讲——底部导航栏的实现(方法1) 5.2.2 Fragment实例精讲——底部导航栏的实现(方法2) 5.2.3 Fragment实例精讲——底部导航栏的实现(方法3) 5.2.4 Fragment实例精讲——底部导航栏+ViewPager滑动切换页面 5.2.5 Fragment实例精讲——新闻(购物)类App列表Fragment的简单实现 6.1 数据存储与访问之——文件存储读写 6.2 数据存储与访问之——SharedPreferences保存用户偏好参数 6.3.1 数据存储与访问之——初见SQLite数据库 6.3.2 数据存储与访问之——又见SQLite数据库 7.1.1 Android网络编程要学的东西与Http协议学习 7.1.2 Android Http请求头与响应头的学习 7.1.3 Android HTTP请求方式:HttpURLConnection 7.1.4 Android HTTP请求方式:HttpClient 7.2.1 Android XML数据解析 7.2.2 Android JSON数据解析 7.3.1 Android 文件上传 7.3.2 Android 文件下载(1) 7.3.3 Android 文件下载(2) 7.5.1 WebView(网页视图)基本用法 7.5.2 WebView和JavaScrip交互基础 7.5.3 Android 4.4后WebView的一些注意事项 7.5.4 WebView文件下载 7.5.5 WebView缓存问题 7.5.6 WebView处理网页返回的错误码信息 7.6.1 Socket学习网络基础准备 7.6.2 基于TCP协议的Socket通信(1) 7.6.3 基于TCP协议的Socket通信(2) 7.6.4 基于UDP协议的Socket通信 8.1.1 Android中的13种Drawable小结 Part 1 8.1.2 Android中的13种Drawable小结 Part 2 8.1.3 Android中的13种Drawable小结 Part 3 8.2.1 Bitmap(位图)全解析 Part 1 8.2.2 Bitmap引起的OOM问题 8.3.1 三个绘图工具类详解 8.3.2 绘图类实战示例 8.3.3 Paint API之—— MaskFilter(面具) 8.3.4 Paint API之—— Xfermode与PorterDuff详解(一) 8.3.5 Paint API之—— Xfermode与PorterDuff详解(二) 8.3.6 Paint API之—— Xfermode与PorterDuff详解(三) 8.3.7 Paint API之—— Xfermode与PorterDuff详解(四) 8.3.8 Paint API之—— Xfermode与PorterDuff详解(五) 8.3.9 Paint API之—— ColorFilter(颜色过滤器)(1/3) 8.3.10 Paint API之—— ColorFilter(颜色过滤器)(2-3) 8.3.11 Paint API之—— ColorFilter(颜色过滤器)(3-3) 8.3.12 Paint API之—— PathEffect(路径效果) 8.3.13 Paint API之—— Shader(图像渲染) 8.3.14 Paint几个枚举/常量值以及ShadowLayer阴影效果 8.3.15 Paint API之——Typeface(字型) 8.3.16 Canvas API详解(Part 1) 8.3.17 Canvas API详解(Part 2)剪切方法合集 8.3.18 Canvas API详解(Part 3)Matrix和drawBitmapMash 8.4.1 Android动画合集之帧动画 8.4.2 Android动画合集之补间动画 8.4.3 Android动画合集之属性动画-初见 8.4.4 Android动画合集之属性动画-又见 9.1 使用SoundPool播放音效(Duang~) 9.2 MediaPlayer播放音频与视频 10.1 TelephonyManager(电话管理器) 10.2 SmsManager(短信管理器) 10.3 AudioManager(音频管理器) 10.4 Vibrator(振动器) 10.5 AlarmManager(闹钟服务) 10.6 PowerManager(电源服务) 10.7 WindowManager(窗口管理服务) 10.8 LayoutInflater(布局服务) 10.9 WallpaperManager(壁纸管理器) 10.10 传感器专题(1)——相关介绍 10.11 传感器专题(2)——方向传感器 10.12 传感器专题(3)——加速度/陀螺仪传感器

8.3.16 Canvas API详解(Part 1)

本节引言:

前面我们花了13小节详细地讲解了Android中Paint类大部分常用的API,本节开始我们来讲解 Canvas(画板)的一些常用API,我们在

  • 8.3.1 三个绘图工具类详解
  • 中已经列出了我们可供调用的一些方法,我们分下类:


    • drawXxx方法族:以一定的坐标值在当前画图区域画图,另外图层会叠加, 即后面绘画的图层会覆盖前面绘画的图层。
    • clipXXX方法族:在当前的画图区域裁剪(clip)出一个新的画图区域,这个 画图区域就是canvas对象的当前画图区域了。比如:clipRect(new Rect()), 那么该矩形区域就是canvas的当前画图区域
    • getXxx方法族:获得与Canvas相关一些值,比如宽高,屏幕密度等。
    • save(),restore(),saveLayer(),restoreToCount()等保存恢复图层的方法
    • translate(平移),scale(缩放),rotate(旋转),skew(倾斜)

    当然还有其他一些零散的方法,嗯,从本节开始我会挑一些感觉有点意思的API来进行学习~

    而本节先给大家带来的是translate(平移),scale(缩放),rotate(旋转),skew(倾斜) 以及save(),restore()的详解!

    官方API文档:Canvas

    另外我们先要明确Canvas中X轴与Y轴的方向:


    1.translate(平移)

    方法translate(float dx, float dy)

    解析:平移,将画布的坐标原点向左右方向移动x,向上下方向移动y,canvas默认位置在(0,0)

    参数:dx为水平方向的移动距离,dy为垂直方向的移动距离

    使用示例

    for(int i=0; i < 5; i++) {
        canvas.drawCircle(50, 50, 50, mPaint);
        canvas.translate(100, 100);
    }
    
    运行效果



    2.rotate(旋转)

    方法rotate(float degrees) / rotate(float degrees, float px, float py)

    解析:围绕坐标原点旋转degrees度,值为正顺时针

    参数:degrees为旋转角度,px和py为指定旋转的中心点坐标(px,py)

    使用示例

    Rect rect = new Rect(50,0,150,50);
    canvas.translate(200, 200);
    for(int i = 0; i < 36;i++){
        canvas.rotate(10);
        canvas.drawRect(rect, mPaint);
    }
    

    运行效果

    代码分析

    这里我们先调用了translate(200,200)将canvas的坐标原点移向了(200,200),再进行绘制,所以我们 绘制的结果可以完整的在画布上显示出来,假如我们是为rotate设置了(10,200,200),会是这样一个 结果:

    有疑问是吧,这个涉及到Canvas多图层的概念,等等会讲~


    3.scale(缩放)

    方法scale(float sx, float sy) / scale(float sx, float sy, float px, float py)

    解析:对画布进行缩放

    参数:sx为水平方向缩放比例,sy为竖直方向的缩放比例,px和py我也不知道,小数为缩小整数为放大

    使用示例

    canvas.drawBitmap(bmp,0,0,mPaint);
    canvas.scale(0.8f, 0.8f);
    canvas.drawBitmap(bmp, 0, 0, mPaint);
    canvas.scale(0.8f, 0.8f);
    canvas.drawBitmap(bmp,0,0,mPaint);
    

    运行效果


    4.skew(倾斜)

    方法skew(float sx, float sy)

    解析:倾斜,也可以译作斜切,扭曲

    参数:sx为x轴方向上倾斜的对应角度,sy为y轴方向上倾斜的对应角度,两个值都是tan值哦! 都是tan值!都是tan值!比如要在x轴方向上倾斜60度,那么小数值对应:tan 60 = 根号3 = 1.732!

    使用示例

    canvas.drawBitmap(bmp,0,0,mPaint);
    canvas.translate(200, 200);
    canvas.skew(0.2f,-0.8f);
    canvas.drawBitmap(bmp,0,0,mPaint);
    

    运行效果


    5.Canvas图层的概念以及save()和restore()详解

    我们一般喜欢称呼Canvas为画布,童鞋们一直觉得Canvas就是一张简单的画纸,那么我想 问下多层的动画是怎么用canvas来完成的?上面那个translate平移的例子,为什么 drawCircle(50, 50, 50, mPaint); 参考坐标一直是(50,50)那为何会出现这样的效果? 有疑惑的童鞋可能是一直将屏幕的概念与Canvas的概念混淆了,下面我们来还原下 调用translate的案发现场:

    如图,是画布坐标原点的每次分别在x,y轴上移动100;那么假如我们要重新回到(0,0) 点处绘制新的图形呢?怎么破,translate(-100,-100)的慢慢地平移回去?不会真的这么 纠结吧...

    好吧,不卖关子了,我们可以在做平移变换之前将当前canvas的状态进行保存,其实Canvas为 我们提供了图层(Layer)的支持,而这些Layer(图层)是按"栈结构"来进行管理的

    当我们调用save()方法,会保存当前Canvas的状态然后作为一个Layer(图层),添加到Canvas栈中, 另外,这个Layer(图层)不是一个具体的类,就是一个概念性的东西而已!

    而当我们调用restore()方法的时候,会恢复之前Canvas的状态,而此时Canvas的图层栈 会弹出栈顶的那个Layer,后继的Layer来到栈顶,此时的Canvas回复到此栈顶时保存的Canvas状态!

    简单说就是:save()往栈压入一个Layer,restore()弹出栈顶的一个Layer,这个Layer代表Canvas的 状态!也就是说可以save()多次,也可以restore()多次,但是restore的调用次数不能大于save 否则会引发错误!这是网上大部分的说法,不过实际测试中并没有出现这样的问题,即使我restore的 次数多于save,也没有出现错误~目测是系统改了,等下测给大家看~ 来来来,写个例子验证下save和restore的作用!

    写个例子

    例子代码

    canvas.save();  //保存当前canvas的状态
    
    canvas.translate(100, 100);
    canvas.drawCircle(50, 50, 50, mPaint);
    
    canvas.restore();  //恢复保存的Canvas的状态
    canvas.drawCircle(50, 50, 50, mPaint);
    

    运行结果

    不用说什么了吧,代码和结果已经说明了一切,接着我们搞得复杂点,来一发 多个save()和restore()!

    例子代码:

    canvas.save();
    
    canvas.translate(300, 300);
    canvas.drawBitmap(bmp, 0, 0, mPaint);
    canvas.save();
    
    canvas.rotate(45);
    canvas.drawBitmap(bmp, 0, 0, mPaint);
    canvas.save();
    
    canvas.rotate(45);
    canvas.drawBitmap(bmp, 0, 0, mPaint);
    canvas.save();
    
    canvas.translate(0, 200);
    canvas.drawBitmap(bmp, 0, 0, mPaint);
    

    运行结果

    结果分析

    首先平移(300,300)画图,然后旋转45度画图,再接着旋转45度画图,接着平移(0,200), 期间每次画图前都save()一下,看到这里你可能有个疑问,最后这个平移不是y移动200 么,怎么变成向左了?嘿嘿,我会告诉你rotate()旋转的是整个坐标轴么?坐标轴的 变化:

    嗯,rotate()弄懂了是吧,那就行,接着我们来试试restore咯~我们在最后绘图的前面 加两个restore()!

    canvas.restore();
    canvas.restore();
    canvas.translate(0, 200);
    canvas.drawBitmap(bmp, 0, 0, mPaint);
    

    运行结果

    不说什么,自己体会,再加多个restore()

    有点意思,再来,继续加restore()

    嗯,好像不可以再写restore了是吧,因为我们只save了四次,按照网上的说法, 这会报错的,真的是这样吗?这里我们调用Canvas给我们提供的一个获得当前栈中 有多少个Layer的方法:getSaveCount();然后在save()和restore()的前后都 加一个Log将栈中Layer的层数打印出来:

    结果真是喜闻乐见,毕竟实践出真知,可能是Canvas改过吧,或者其他原因,这里 要看源码才知道了,时间关系,这里我们知道下restore的次数可以比save多就好了, 但是还是建议restore的次数还是少于save,以避免造成不必要的问题~ 至于进栈和出栈的流程我就不话了,笔者自己动笔画画,非常容易理解!


    6.saveLayer()与restoreToCount()讲解

    其实这两个方法和save以及restore大同小异,只是在后者的基础上多了一些东东而已, 比如saveLayer(),有下面多个重载方法:

    你可以理解为save()方法保存的是整个Canvas,而saveLayer()则可以选择性的保存某个区域的状态, 另外,我们看到餐宿和中有个:int saveFlags,这个是设置改保存那个对象的!可选值有:

    标记 说明
    ALL_SAVE_FLAG 保存全部的状态
    CLIP_SAVE_FLAG 保存裁剪的某个区域的状态
    CLIP_TO_LAYER_SAVE_FLAG 保存预先设置的范围里的状态
    FULL_COLOR_LAYER_SAVE_FLAG 保存彩色涂层
    HAS_ALPHA_LAYER_SAVE_FLAG 不透明图层保存
    MATRIX_SAVE_FLAG Matrix信息(translate,rotate,scale,skew)的状态保存

    PS:上述说明有点问题,笔者英语水平低,可能说错,如果有知道的,请务必指正提出,谢谢~

    这里我们写个例子来验证下:我们选用CLIP_TO_LAYER_SAVE_FLAG模式来写个例子

    实现代码

    RectF bounds = new RectF(0, 0, 400, 400);
    canvas.saveLayer(bounds, mPaint, Canvas.CLIP_TO_LAYER_SAVE_FLAG);
    canvas.drawColor(getResources().getColor(R.color.moss_tide));
    canvas.drawBitmap(bmp, 200, 200, mPaint);
    canvas.restoreToCount(1);
    canvas.drawBitmap(bmp, 300, 200, mPaint);
    

    运行结果

    关于saveLayer()后面用到再详解研究吧~这里先知道个大概~

    接着到这个restoreToCount(int),这个更简单,直接传入要恢复到的Layer层数, 直接就跳到对应的那一层,同时会将该层上面所有的Layer踢出栈,让该层 成为栈顶~!比起你写多个restore()方便快捷多了~


    本节小结:

    本节是纠结了几天才写出来的,因为笔者一开始对这个Canvas图层的概念也不是很清晰, 今天下午做完事捋了捋思路,晚上再加加班终于把这篇东西写出来了,相信应该能帮助 大家更清楚的理解Canvas,进阶自定义控件时也不会一头雾水~嘿嘿,本节就到这里, 如果有写错的地方欢迎提出,万分感谢~

    参考文献AndroidのCanvasを使いこなす! – 基本的な描画

    © 2021 jiaocheng.bubufx.com  联系我们
    ICP备案:鲁ICP备09046678号-3