高级UI-Canvas 使用,Canvas 实践 自定义Drawable
2018-02-10 14:39
344 查看
canvas 基本方法
Canvas 实践 ReavlView效果
阶段一 实现两张图片的拼接
阶段二 动态改变图片的裁剪距离
直面意思是画布,其实是分装的一个工具类
一个Canvas类对象有四大基本要素
1、一个是用来保存像素的bitmap —– 画板
2、一个Canvas在Bitmap上进行绘制操作 —- 画布或者画纸(Layer—saveLayer操作时,新建一个透明的画布图层)
3、绘制的东西
4、绘制的画笔Paint
把我们的Canvas比喻成一块画板,为什么?
学习目标:
1、了解Canvas可以用来画些什么东西
2、Canvas的变换技巧—-了解Canvas里面的坐标系
3、Canvas的状态保存—状态栈、Layer栈
save和restore方法来保存和还原变化操作
saveLayer的时候都会新建一个透明的图层
1、实现ReavlView效果 — 通过图片剪裁拼接(自定义Drawable实现)
2、自定义SearchView
实现以上效果
需要分阶段来
涉及到的知识以及类: 矩阵Rect ,Drawable,Canvas裁剪等
理论分析一波:
先分析,现在有两张图片彩色和灰色,那么裁剪出的左边图片Drawable,右边图片Drawable,组合到一起.
代码:
下面是自定义Drawable
图片资源
效果图
以上完成了基本的裁剪. 下面需要完成可以动态的改变裁剪的距离.
实现动态改变有两个重要的方法 getLevel 和setImageLevel 看下Drawable 的源码
分别是 获取当前Level 和设置图片的Level
有了这个就可以根据Level 的大小来改变裁剪的位置以及大小.
下面做下根据点击图片动态改变Level 来改变裁剪的大小. 上代码
下面是自定义view
笔记:
RevealView 效果—(来源 http://fragmentapp.com/)
1、自定义的Drawable来做
2、水平ScrollView来实现滑动
SearchView效果
状态分解:
1、默认状态:圆圈、手柄
2、展开动画状态:1)动画前半段,圆圈减小至消失,底部横线加长
3、可以动手实现下还原动画
Canvas 双缓冲
Canvas 实践 ReavlView效果
阶段一 实现两张图片的拼接
阶段二 动态改变图片的裁剪距离
canvas 基本方法
Canvas直面意思是画布,其实是分装的一个工具类
一个Canvas类对象有四大基本要素
1、一个是用来保存像素的bitmap —– 画板
2、一个Canvas在Bitmap上进行绘制操作 —- 画布或者画纸(Layer—saveLayer操作时,新建一个透明的画布图层)
3、绘制的东西
4、绘制的画笔Paint
把我们的Canvas比喻成一块画板,为什么?
学习目标:
1、了解Canvas可以用来画些什么东西
除了常用的形状之外 画Region --- 区域的意思,它表示的Canvas图层上的一块封闭的区域 有以下几种方式:----具体含义 看图 DIFFERENCE(0), INTERSECT(1), UNION(2), XOR(3), REVERSE_DIFFERENCE(4), REPLACE(5);
2、Canvas的变换技巧—-了解Canvas里面的坐标系
Canvas里面牵扯两种坐标系:Canvas自己的坐标系、绘图坐标系 Canvas自己的坐标系 实际上就是画布的坐标系. Canvas的坐标系, 它就在View的左上角,做坐标原点往右是X轴正半轴,往下是Y轴的正半轴,有且只有一个,唯一不变 绘图坐标系 它不是唯一不变的,它与Canvas的Matrix有关系,当Matrix发生改变的时候,绘图坐标系对应的进行改变, 同时这个过程是不可逆的(save和restore方法来保存和还原变化操作) Matrix又是通过我们设置translate、rotate、scale、skew来进行改变的
3、Canvas的状态保存—状态栈、Layer栈
状态栈--save、 restore方法来保存和还原变换操作Matrix以及Clip剪裁 也可以通过restoretoCount直接还原到对应栈的保存状态 Layer栈--- saveLayer的时候都会新建一个透明的图层(离屏Bitmap-离屏缓冲),并且会将saveLayer之前的一些Canvas操作延续过来 后续的绘图操作都在新建的layer上面进行 当我们调用restore 或者 restoreToCount 时 更新到对应的图层和画布上
save和restore方法来保存和还原变化操作
saveLayer的时候都会新建一个透明的图层
1、实现ReavlView效果 — 通过图片剪裁拼接(自定义Drawable实现)
2、自定义SearchView
Canvas 实践 ReavlView效果
http://fragmentapp.com/实现以上效果
需要分阶段来
阶段一: 实现两张图片的拼接
从简单到困难, 先实现左半部分彩色右半部分灰色,采用自定义Drawable实现涉及到的知识以及类: 矩阵Rect ,Drawable,Canvas裁剪等
理论分析一波:
先分析,现在有两张图片彩色和灰色,那么裁剪出的左边图片Drawable,右边图片Drawable,组合到一起.
代码:
package android.reavlviewdemo; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.widget.ImageView; public class MainActivity extends AppCompatActivity { private ImageView mIv; private int[] mImgIds = new int[]{ //7个 R.drawable.avft, R.drawable.box_stack, R.drawable.bubble_frame, R.drawable.bubbles, R.drawable.bullseye, R.drawable.circle_filled, R.drawable.circle_outline, R.drawable.avft, R.drawable.box_stack, R.drawable.bubble_frame, R.drawable.bubbles, R.drawable.bullseye, R.drawable.circle_filled, R.drawable.circle_outline }; private int[] mImgIds_active = new int[]{ R.drawable.avft_active, R.drawable.box_stack_active, R.drawable.bubble_frame_active, R.drawable.bubbles_active, R.drawable.bullseye_active, R.drawable.circle_filled_active, R.drawable.circle_outline_active, R.drawable.avft_active, R.drawable.box_stack_active, R.drawable.bubble_frame_active, R.drawable.bubbles_active, R.drawable.bullseye_active, R.drawable.circle_filled_active, R.drawable.circle_outline_active }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mIv = (ImageView) findViewById(R.id.iv); RevealDrawable revealDrawable = new RevealDrawable( getResources().getDrawable(R.drawable.avft), getResources().getDrawable(R.drawable.avft_active)); mIv.setImageDrawable(revealDrawable); } }
下面是自定义Drawable
package android.reavlviewdemo; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.view.Gravity; /** * @author liuml * @explain * @time 2018/2/3 15:44 */ public class RevealDrawable extends Drawable { //Drawable 需要实现setBounds //灰色部分 private Drawable mUnselectDrawable; //彩色部分 private Drawable mSelectDrawable; private Rect mTempRect = new Rect(); public RevealDrawable(Drawable mUnselectDrawable, Drawable mSelectDrawable) { this.mSelectDrawable = mSelectDrawable; this.mUnselectDrawable = mUnselectDrawable; } @Override public void draw(@NonNull Canvas canvas) { //将两张图片进行裁剪和拼接 //1 ===== 画左边的图片 //获取边界矩形 Rect bounds = getBounds(); //初始化Rect Rect r = mTempRect; //获取边界矩阵的宽高 int w = bounds.width(); int h = bounds.height(); //从一个矩形区域裁剪出目标矩形 Gravity.apply( Gravity.LEFT,// 从哪个方向开始剪,左边还是右边 w / 2,// 目标矩形的宽 h, // 目标矩形的高 bounds,// 被剪裁图片的rect r// 目标rect ); canvas.save();// 因为下面需要对canvas进行裁剪 先保存一次canvas,后面进行还原. canvas.clipRect(r);//裁剪目标rect //画出 mUnselectDrawable.draw(canvas); canvas.restore();//还原canvas 方便下一次进行裁剪. //2 =====画右边的图片 //同理右边 Gravity.apply( Gravity.RIGHT, w / 2, h, bounds, r ); canvas.save(); canvas.clipRect(r); mSelectDrawable.draw(canvas); canvas.restore(); } @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); //定义两张图片的宽高 mSelectDrawable.setBounds(bounds); mUnselectDrawable.setBounds(bounds); } @Override public int getIntrinsicHeight() {//返回可绘制的固有高度 return Math.max(mSelectDrawable.getIntrinsicHeight(), mUnselectDrawable.getIntrinsicHeight()); } @Override public int getIntrinsicWidth() {//返回可绘制的固有宽度 return Math.max(mSelectDrawable.getIntrinsicWidth(), mUnselectDrawable.getIntrinsicWidth()); } @Override public void setAlpha(int alpha) { } @Override public void setColorFilter(@Nullable ColorFilter colorFilter) { } @Override public int getOpacity() { return PixelFormat.UNKNOWN; } }
图片资源
效果图
以上完成了基本的裁剪. 下面需要完成可以动态的改变裁剪的距离.
阶段二 : 动态改变图片的裁剪距离
上面裁剪的时候只是从每张图片的中间裁剪,下面需要动态改变图片的裁剪大小.实现动态改变有两个重要的方法 getLevel 和setImageLevel 看下Drawable 的源码
/** * Retrieve the current level. 检索当前的水平 实际上就是检索当前的级别 可以看到参数 int当前级别,从0(最小值)到10000(最大值)。 * * @return int Current level, from 0 (minimum) to 10000 (maximum). */ public final @IntRange(from=0,to=10000) int getLevel() { return mLevel; } /** * Sets the image level, when it is constructed from a * {@link android.graphics.drawable.LevelListDrawable}. 设置图像级别,当它是由a构建的 * * @param level The new level for the image. */ @android.view.RemotableViewMethod public void setImageLevel(int level) { mLevel = level; if (mDrawable != null) { mDrawable.setLevel(level); resizeFromDrawable(); } }
分别是 获取当前Level 和设置图片的Level
有了这个就可以根据Level 的大小来改变裁剪的位置以及大小.
1.1 最复杂的是两张图片进行拼接,一边灰色,一边彩色的 Drawable 状态,可以根据level来切换不同的状态绘制,level值是从0 ~ 10000进行变化 1)全灰色 --- 0 或者 10000 2)全彩色 --- 5000 3)左边灰色,右边彩色 5000 ~ 0 4)左边彩色,右边灰色 10000 ~ 5000
下面做下根据点击图片动态改变Level 来改变裁剪的大小. 上代码
package android.reavlviewdemo; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.ImageView; public class MainActivity extends AppCompatActivity { private int level = 10000; private ImageView mIv; private int[] mImgIds = new int[]{ //7个 R.drawable.avft, R.drawable.box_stack, R.drawable.bubble_frame, R.drawable.bubbles, R.drawable.bullseye, R.drawable.circle_filled, R.drawable.circle_outline, R.drawable.avft, R.drawable.box_stack, R.drawable.bubble_frame, R.drawable.bubbles, R.drawable.bullseye, R.drawable.circle_filled, R.drawable.circle_outline }; private int[] mImgIds_active = new int[]{ R.drawable.avft_active, R.drawable.box_stack_active, R.drawable.bubble_frame_active, R.drawable.bubbles_active, R.drawable.bullseye_active, R.drawable.circle_filled_active, R.drawable.circle_outline_active, R.drawable.avft_active, R.drawable.box_stack_active, R.drawable.bubble_frame_active, R.drawable.bubbles_active, R.drawable.bullseye_active, R.drawable.circle_filled_active, R.drawable.circle_outline_active }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mIv = (ImageView) findViewById(R.id.iv); RevealDrawable revealDrawable = new RevealDrawable( getResources().getDrawable(R.drawable.avft), getResources().getDrawable(R.drawable.avft_active)); mIv.setImageDrawable(revealDrawable); mIv.setImageLevel(5000); mIv.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //if(level > 0){ level -= 500; //} mIv.setImageLevel(level); } }); } }
下面是自定义view
package android.reavlviewdemo; import android.graphics.Canvas; import android.graphics.ColorFilter; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.view.Gravity; /** * @author liuml * @explain * @time 2018/2/3 15:44 */ public class RevealDrawable extends Drawable { //Drawable 需要实现setBounds //灰色部分 private Drawable mUnselectDrawable; //彩色部分 private Drawable mSelectDrawable; private Rect mTempRect = new Rect(); public RevealDrawable(Drawable mUnselectDrawable, Drawable mSelectDrawable) { this.mSelectDrawable = mSelectDrawable; this.mUnselectDrawable = mUnselectDrawable; } @Override public void draw(@NonNull Canvas canvas) { //将两张图片进行裁剪和拼接 int level = getLevel(); if (level == 0 || level == 10000) { // 画整张灰色的图 mUnselectDrawable.draw(canvas); } else if (level == 5000) { // 画整张彩色的图 mSelectDrawable.draw(canvas); } else { //获取边界矩形 Rect bounds = getBounds(); //初始化Rect Rect r = mTempRect; // 比例 -1 ~ 1 之间进行变化 -1 ~0 表示左边灰色,右边彩色 // 0 ~1 表示左边是彩色,右边灰色 float ratio = (level / 5000f) - 1f; //1. 画出灰色区域 { //获取边界矩阵的宽高 int w = bounds.width(); int h = bounds.height(); //判断从哪个方向进行裁剪 可以看下图 当0-5000时左边是灰色右边是彩色, 当5000-10000时 左边是彩色右边是灰色 int gravity = ratio < 0 ? Gravity.LEFT : Gravity.RIGHT; //目标裁剪的宽度 动态计算 w = (int) (w * Math.abs(ratio)); //从一个矩形区域裁剪出目标矩形 Gravity.apply( gravity,// 从哪个方向开始剪,左边还是右边 w,// 目标矩形的宽 h, // 目标矩形的高 bounds,// 被剪裁图片的rect r// 目标rect ); canvas.save();// 因为下面需要对canvas进行裁剪 先保存一次canvas,后面进行还原. canvas.clipRect(r);//裁剪目标rect //画出 mUnselectDrawable.draw(canvas); canvas.restore();//还原canvas 方便下一次进行裁剪. } //2. 画出才彩色区域 { //获取边界矩阵的宽高 int w = bounds.width(); int h = bounds.height(); int gravity = ratio < 0 ? Gravity.RIGHT : Gravity.LEFT; //目标裁剪的宽度 动态计算 这里是剩下的距离 w -= (int) (w * Math.abs(ratio)); Gravity.apply( gravity, w, h, bounds, r ); canvas.save(); canvas.clipRect(r); mSelectDrawable.draw(canvas); canvas.restore(); } } } @Override protected boolean onLevelChange(int level) { // 当设置level时,来重绘Drawable invalidateSelf(); return super.onLevelChange(level); } @Override protected void onBoundsChange(Rect bounds) { super.onBoundsChange(bounds); //定义两张图片的宽高 mSelectDrawable.setBounds(bounds); mUnselectDrawable.setBounds(bounds); } @Override public int getIntrinsicHeight() {//返回可绘制的固有高度 return Math.max(mSelectDrawable.getIntrinsicHeight(), mUnselectDrawable.getIntrinsicHeight()); } @Override public int getIntrinsicWidth() {//返回可绘制的固有宽度 return Math.max(mSelectDrawable.getIntrinsicWidth(), mUnselectDrawable.getIntrinsicWidth()); } @Override public void setAlpha(int alpha) { } @Override public void setColorFilter(@Nullable ColorFilter colorFilter) { } @Override public int getOpacity() { return PixelFormat.UNKNOWN; } }
笔记:
RevealView 效果—(来源 http://fragmentapp.com/)
1、自定义的Drawable来做
1.1 最复杂的是两张图片进行拼接,一边灰色,一边彩色的 Drawable 状态,可以根据level来切换不同的状态绘制,level值是从0 ~ 10000进行变化 1)全灰色 --- 0 或者 10000 2)全彩色 --- 5000 3)左边灰色,右边彩色 5000 ~ 0 4)左边彩色,右边灰色 10000 ~ 5000
2、水平ScrollView来实现滑动
ScrollView里面一层布局,布局里面加载一组ImageView
SearchView效果
状态分解:
1、默认状态:圆圈、手柄
2、展开动画状态:1)动画前半段,圆圈减小至消失,底部横线加长
2)动画前半段,圆圈消失后,手柄减小,底部横线加长
3、可以动手实现下还原动画
Canvas 双缓冲
相关文章推荐
- ASP.NET 实践:使用 IConfigurationSectionHandler 创建自定义配置段
- Android高级UI之自定义“更多”界面
- 在ASP.NET MVC中使用Knockout实践09,自定义绑定
- android 自定义view学习笔记————Paint和Canvas的简单使用
- Flex企业应用开发实践学习笔记(六)——使用ActionScript创建自定义组件
- ElementUI使用问题记录:设置路由+iconfont图标+自定义表单验证
- 使用Axure RP原型设计实践02,自定义部件以及熟悉与部件相关面板
- iOS开发UI高级—22Quartz2D使用(矩阵操作)
- android 自定义view使用Canvas实现支付宝咻一咻功能
- Drawable、Bitmap、Canvas、Paint和 Matrix 的使用(二)
- Android自定义View,paint+canvas的使用
- Kendo UI开发教程:使用Kendo UI Web创建自定义组件(基础篇)
- Android开发之Canvas及高级使用
- 【转】Android UI美化之Shape Drawable的使用
- UIWindow使用 自定义弹出界面
- 理解和使用 Unity UI 系统(canvas和CanvasScaler )
- Android UI高级控件之自定义Adapter(继承BaseAdapter)
- GA教程:使用自定义变量来扩展高级细分
- 自定义View时, 使用Canvas、Bitmap时易犯的内存泄露问题
- iOS并发编程笔记,包含GCD,Operation Queues,Run Loops,如何在后台绘制UI,后台I/O处理,最佳安全实践避免互斥锁死锁优先级反转等,以及如何使用GCD监视进程文件文件夹,并发测试的方案等