技术标签: 水波纹动画 基础知识 慢慢上涨动画 电量上涨动画
题外话:文章需要配动态图gif来展示动画效果,在ubuntu下制作gif可参考这篇文章,简单方便,我精简了下步骤,如下:
制作gif动画图片:
https://www.cnblogs.com/bozhicheng/p/5933984.html
首先用录屏工具Kazam录制一段视频,其次再用ffmpeg工具转换成gif
安装ffmpeg工具:
$ sudo add-apt-repository ppa:kirillshkrogalev/ffmpeg-next
$ sudo apt-get update
$ sudo apt-get install ffmpeg
简单转换指令:
ffmpeg -i test.mp4 out.gif //把整个MP4输出成gif,会很大
精确转换:
ffmpeg -ss 2 -t 12 -i test.mp4 -s 649x320 -r 15 output1.gif //其中, -ss 2 to 12 表示从从视频的第2秒开始转换, 转换时间长度为12秒后停止. -s用于设定分辨率, -r 用于设定帧数. 通常Gif有15帧左右就比较流程了
ffmpeg -t <时长> -ss <hh:mm:ss开始制作GIF的时间点> -i <视频文件> out_name.gif
正式开写~~
首先上一张动图,需求给到的动画效果,很不明显,注意看这个小房子图标的变化:
效果相当简单,就是房子内部一个褐色块不断的匀速往上移动,直至填充满整个房子。咋一看非常简单吧,但就是这么个小动画,前后花费了我一天多的时间,哎,这方面技术真是渣啊。
首先,脑子里会想到在Android中执行动画有三种方式:
1,帧动画,通过多张图片叠加幻灯片方式实现,这样就需要提供多张切图,图片多了必然导致apk包增大,还需要UI去另外切图,为了这么个小动画做搞这么多,明显得不偿失啊。这条路不想走,放弃。
2,视图动画,这个动画也就支持缩放scaleAnimation,平移translateAnimation,旋转rotateAnimation,透明度alphaAnimation四中类型,仔细想想这里的没有一种是可以运用在该效果上的。
3,属性动画,那么最后只有这一种动画可以选择了。这里最重要的两个动画就是ObjectAnimator和ValueAnimator了,其中OjectAnimator继承自ValueAnimator。ObjectAnimator可以改变对象的属性值从而实现动画效果,这个小房子动画没什么特别属性可以让他改变的,那么还是选用ValueAnimator,这个更自由,印象中ValueAnimator可以设置线性插值器,这样褐色块的匀速移动不就好执行了么。
不管选哪种动画,最终还是要解析这个动画是怎么执行的。
首先拆解下这个房子的图像,拿到原图后发现是一个只有边框是褐色,内部和四周都是透明的。
除了我们自己画一个一模一样的图形,好像没什么方法能把原图形的内部填充成我们想要的颜色了吧。
于是就想到了自定义view,然后在onDraw方法里面,根据这个图像的形状模拟出一个多边形来,然后在这个多边形里面再动态的从底部不断的画褐色矩形块向上填充,但是到了顶部三角区域该怎么处理呢,不能在使用矩形块了吧,或者可以继续使用,但是矩形块的高度要变得更小,长度也要变小,不然就超出了房子内部。显然这样处理没法控制匀速的填充,计算矩形块的高度和宽度也是比较不容易的。
后来想到使用图像的混合模式,也就是paint的Xfermode,类似于ps的图层的概念,Xfermode有18种模式,两个图像使用不同的混合模式会得到不同的效果。具体的使用可参考这位大神的文章:
自定义控件三部曲之绘图篇(十)——Paint之setXfermode(一)
一定要搞懂了Xfermode模式,这样才能理解后面我所经历的实验。
再附上一篇模式的讲解,可从文章的中部开始看:
Android Paint之 setXfermode PorterDuffXfermode 讲解
经过琢磨,我选择了
PorterDuff.Mode.SRC_IN
这个模式。大致的意思是把源图像的颜色值作用到目标图像上,也就是两个图像相交的地方,使用源图像的颜色。这里关键的就是如何选择源图像和目标图像。我们可以把目标图像规定为想要对其改变的图像,源图像就是要作用的颜色的来源,都是相对而言的,也可以想想ps的图层,目标图层相当于是最底部的图层,源图层相当于最上部的图层,把最上面的图层的内容作用到最下面的图层上。仔细想一想,我是想改变上面那个四周和内部都是透明的只有边框的小房子的内部的颜色,那么姑且把这个小房子看做是目标图像吧,目标图像有了,那么源图像呢?不如直接弄一个实心的矩形吧,为了效果展示得更直观,这里姑且把实心矩形的颜色设置为红色。
第一组实验:
首先需要自定义一个view,暂时继承自ImageView吧,需要重写onDraw方法,并在view的构造方法中初始化一些必要的变量,包括需要设置混合模式的画笔,目标图像和源图像:
private void initGroup1(){
mXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
mPaint = new Paint();// 这个是用来绘制图层的画笔,包括设置Xfermode
BitmapDrawable drawable = (BitmapDrawable) getResources().getDrawable(R.drawable.mz_bottom_ic_home_nor_light);
width = drawable.getBitmap().getWidth();
height = drawable.getBitmap().getHeight();
setLayoutParams(new ViewGroup.LayoutParams(width,height));//设置当前view的宽高就是图像的宽高
dst = drawable.getBitmap();//目标图像来源于资源文件
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);//创建一个宽高一致的图像作为源图像,此时的图像还是一个没有任何颜色值的
Canvas canvas = new Canvas(bitmap);//在图像上着色画图形,需要使用画布来操作
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);//作画需要画笔嘛
paint.setColor(Color.RED);// 设置画笔的颜色
canvas.drawRect(0,0,width,height,paint);//在该画布上画一个和view的宽高一样的红色矩形块,这个矩形块填满了bitmap
src = bitmap;//把这个红色矩形图像当做源图像,源图像的内容要作用到目标图像dst上
}
然后在onDraw方法中进行图层的合并混合:
private void drawGroup1(Canvas canvas){
int layerID = canvas.saveLayer(0,0,width,height,mPaint,Canvas.ALL_SAVE_FLAG); // 图像混合的标准步骤1
canvas.drawBitmap(dst, 0, 0, mPaint); // 将目标图像设置到view的画布图层中
mPaint.setXfermode(mXfermode); // 图像混合的标准步骤2:设置模式
canvas.drawBitmap(src, 0, 0, mPaint); // 将源图像加到view的画布图层中,并根据模式进行运算
mPaint.setXfermode(null);//图像混合的标准步骤3:清除模式,恢复图层
canvas.restoreToCount(layerID);
}
非常期待的运行下看看结果:
咦~明显不对嘛,理想情况是想改变小房子内部的颜色,怎么现在貌似只是改变了房子周边的颜色(原来房子边框是灰色的),内部怎么没有变呢?
原来的效果:
为了能够动态的展示这个混合的效果,我这里让混合效果动起来。
首先在initGroup1方法中增加当前view的高度为目标图像高度的两倍,以便能够展示源图像:
setLayoutParams(new ViewGroup.LayoutParams(width,height*2));//设置当前view的高度是图像的两倍
用srcTop变量存储初始源图像在view中画布的初始位置,也就是在当前小房子的下面绘制红色矩形:
srcTop = height;
startAnim();
startAnim用来开启一个属性动画:
public void startAnim(){
final ValueAnimator animator = ValueAnimator.ofInt(height,0);//动画执行的值从目标图像的高度开始,一直变化到0
animator.setDuration(3000);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
srcTop = (int)animation.getAnimatedValue();//不断的取得源图像在view中的canvas的位置
postInvalidate();
}
});
postDelayed(new Runnable() {
@Override
public void run() {
isStartMix = true; // 标记开始混合图像
animator.start();
}
},3000);
}
这里延迟3s开始执行属性动画,为了能看到初始状态下,目标图像和源图像所在整个view画布中的初始位置,以及没有执行混合模式下的状态。
接着在drawGroup1方法中,将画布图层的高度扩大两倍,这样才能显示出目标图像和源图像,因为源图像初始是放在目标图像下方的。
int layerID = canvas.saveLayer(0,0,width,height*2,mPaint,Canvas.ALL_SAVE_FLAG); // 图像混合的标准步骤1
还没进行动画的时候,不要执行混合模式,否则将看不到源图像的初始状态
if(isStartMix)
mPaint.setXfermode(mXfermode); // 图像混合的标准步骤2:设置模式
在view的画布中动态的改变源图像在画布中的高度
canvas.drawBitmap(src, 0, srcTop, mPaint);
改造后的完整方法如下:
private void initGroup1(){
mXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
mPaint = new Paint();// 这个是用来绘制图层的画笔,包括设置Xfermode
BitmapDrawable drawable = (BitmapDrawable) getResources().getDrawable(R.drawable.mz_bottom_ic_home_nor_light);
width = drawable.getBitmap().getWidth();
height = drawable.getBitmap().getHeight();
setLayoutParams(new ViewGroup.LayoutParams(width,height*2));//设置当前view的高度是图像的两倍
dst = drawable.getBitmap();//目标图像来源于资源文件
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);//创建一个宽高一致的图像作为源图像,此时的图像还是一个没有任何颜色值的
Canvas canvas = new Canvas(bitmap);//在图像上着色画图形,需要使用画布来操作
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);//作画需要画笔嘛
paint.setColor(Color.RED);// 设置画笔的颜色
canvas.drawRect(0,0,width,height,paint);//在该画布上画一个和view的宽高一样的红色矩形块,这个矩形块填满了bitmap
src = bitmap;//把这个红色矩形图像当做源图像,源图像的内容要作用到目标图像dst上
srcTop = height;
startAnim();
}
private void drawGroup1(Canvas canvas){
int layerID = canvas.saveLayer(0,0,width,height*2,mPaint,Canvas.ALL_SAVE_FLAG); // 图像混合的标准步骤1
canvas.drawBitmap(dst, 0, 0, mPaint); // 将目标图像设置到view的画布图层中
if(isStartMix)
mPaint.setXfermode(mXfermode); // 图像混合的标准步骤2:设置模式
canvas.drawBitmap(src, 0, srcTop, mPaint); // 将源图像加到view的画布图层中,并根据模式进行运算
mPaint.setXfermode(null);//图像混合的标准步骤3:清除模式,恢复图层
canvas.restoreToCount(layerID);
}
看下执行效果:
从动画中可以看到,红色方块在3s后消失了。这个是因为此时进行了图像的混合模式,目标图像就是这里的小房子,源图像就是红色方块,两个图像进行混合的时候,由于目标图像除了房子的轮廓是有色值的,其他地方都是透明的,所以在源图像作用到目标图像的时候,透明区域是无法作用的,只能作用到房子的轮廓上,表现出来就是房子轮廓从下往上慢慢变成红色(源图像色值)。
看这个效果,明显和我们开篇所要达到的效果是不一致的,我们是希望小房子的内部能有一个颜色慢慢上升填充,而不是仅仅改变房子的轮廓。
通过上面的实验可以知道,想要实现慢慢上升的效果,就是两个图层不断的混合,目标图层可以保持位置不变,只需要改变源图层在目标图层上的位置就能实现。既然源图层的内容要作用到有色值的图层上才能产生效果,那么我们就把小房子内部全部图上颜色,这样在混合的时候不就能把源图层的颜色作用到房子上了么。
这里还有个问题就是怎么给房子内部填充颜色,想来想去也只能依赖UI再给一张实心房子的切图了。
,这是一张ui给切的房子内部的实心图,可以看到他把四周的透明都去掉了,图片实际宽高不是原来房子的宽高了。在AS中打开看下:
我们先用这张图来进行混合看看效果。
第二组实验:
这里只简单的改变下目标图像的来源,源图像不变。所以整个代码如下:
private void initGroup2(){
mXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
mPaint = new Paint();// 这个是用来绘制图层的画笔,包括设置Xfermode
BitmapDrawable drawable = (BitmapDrawable) getResources().getDrawable(R.drawable.home_solid1);//这里变成了实心房子
width = drawable.getBitmap().getWidth();
height = drawable.getBitmap().getHeight();
setLayoutParams(new ViewGroup.LayoutParams(width,height*2));//设置当前view的高度是图像的两倍
dst = drawable.getBitmap();//目标图像来源于资源文件
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);//创建一个宽高一致的图像作为源图像,此时的图像还是一个没有任何颜色值的
Canvas canvas = new Canvas(bitmap);//在图像上着色画图形,需要使用画布来操作
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);//作画需要画笔嘛
paint.setColor(Color.RED);// 设置画笔的颜色
canvas.drawRect(0,0,width,height,paint);//在该画布上画一个和view的宽高一样的红色矩形块,这个矩形块填满了bitmap
src = bitmap;//把这个红色矩形图像当做源图像,源图像的内容要作用到目标图像dst上
srcTop = height;
startAnim();
}
合成的代码还是不变,复用drawGroup1方法。
从效果来看,和我们预期的效果又接近了一步,确实可以实现慢慢上升的效果了。但是我们动画的初始状态应该是一个透明的小房子,然后内部慢慢的被填充颜色。房子边框和外部的透明空间应该是要保留的。这里ui给我切的图是房子内部,去掉了外部透明的区域和边框区域,如果我将小房子图作为背景,那么就需要精确的调整实心房子的坐标才能把实心房子刚好放在透明房子的内部,在多分辨率机子上计算可能会出现偏差,导致透明边框房子和实心房子没有正常叠加,于是我自己又在原图的基础上做了一张实心的小房子,抹掉了边框的像素,保留了外部所有透明区域,如下:
内部是个实心白色房子,然后把原图设置为该自定义view的背景,在onDraw方法中,使用该内部实心房子作为目标图层,与红色矩形图层作用。到这里我们不再在原图上做手脚,而是将该实心房子作为了目标图像,原来的透明房子只作为一个背景。
private void initGroup3(){
BitmapDrawable bitmapDrawable = (BitmapDrawable) getResources().getDrawable(R.drawable.mz_bottom_ic_home_nor_light);
setImageDrawable(bitmapDrawable);//把最原始的透明房子设置给自己
mXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
mPaint = new Paint();// 这个是用来绘制图层的画笔,包括设置Xfermode
BitmapDrawable drawable = (BitmapDrawable) getResources().getDrawable(R.drawable.home_solid);//取出实心小房子,宽高和原来的是一样的
width = drawable.getBitmap().getWidth();
height = drawable.getBitmap().getHeight();
setLayoutParams(new ViewGroup.LayoutParams(width,height));//设置当前view的高度是图像的高度
dst = drawable.getBitmap();//目标图像来源于资源文件
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);//创建一个宽高一致的图像作为源图像,此时的图像还是一个没有任何颜色值的
Canvas canvas = new Canvas(bitmap);//在图像上着色画图形,需要使用画布来操作
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);//作画需要画笔嘛
paint.setColor(Color.RED);// 设置画笔的颜色
canvas.drawRect(0,0,width,height,paint);//在该画布上画一个和view的宽高一样的红色矩形块,这个矩形块填满了bitmap
src = bitmap;//把这个红色矩形图像当做源图像,源图像的内容要作用到目标图像dst上
srcTop = height;
startAnim();
}
在这组实验中,我将view的宽高设置成了原始图片的宽高,所以在小房子下部分绘制出来的红色矩形图层在开始的时候没有出现在视图中。仔细看下,其实四周的图层叠加的效果不是很好,主要还是切图不准确,细节的东西慢慢再调了。
上面这组实验其实还是有问题的,内部实心图层始终是显示的白色的,现在我的父视图的白色背景没什么问题,一旦我的背景变成了其他颜色,看起来就很怪了。比如我父布局背景设置成灰色。
在动画执行的过程中,房子内部没有混合的区域始终都是白色的,这和实际需求不符,实际需求内部应该是透明的,用上升的颜色来填充满整个房子。
接下来第四组实验:
private Canvas srcCanvas;
private Paint srcPaint;
/**
* 第四组实验,也是在源bitmap上绘制一个矩形图案,但是这个矩形图案的高度初始值为bitmap的height值,也就是在该bitmap的最底部。
* 也就是说现在的bitmap上方是一个透明的区域,最下方是一个矩形图案。当透明的区域和目标图像进行合成的时候,就会将目标图像擦除,也变成了透明区域。
* 接下来,在onDraw方法中,通过改变矩形图像的top值来动态移动矩形图像在源bitmap中的位置,也就是矩形图像慢慢的填充满整个源bitmap。
* 而此次合并的过程中目标的bitmap和源bitmap是完全重合的。透明合成的区域被擦除,有矩形块的区域的颜色值就作用到目标图像上了。
*/
private void initGroup4(){
BitmapDrawable bitmapDrawable = (BitmapDrawable) getResources().getDrawable(R.drawable.mz_bottom_ic_home_nor_light);
setImageDrawable(bitmapDrawable);//把最原始的透明房子设置给自己
mXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
mPaint = new Paint();// 这个是用来绘制图层的画笔,包括设置Xfermode
BitmapDrawable drawable = (BitmapDrawable) getResources().getDrawable(R.drawable.home_solid);//取出实心小房子,宽高和原来的是一样的
width = drawable.getBitmap().getWidth();
height = drawable.getBitmap().getHeight();
setLayoutParams(new ViewGroup.LayoutParams(width,height));//设置当前view的高度是图像的高度
dst = drawable.getBitmap();//目标图像来源于资源文件
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);//创建一个宽高一致的图像作为源图像,此时的图像还是一个没有任何颜色值的
//画布和画笔都定义成全局变量,因为在动画的过程中,需要用到这两个变量对矩形块进行绘制
srcCanvas = new Canvas(bitmap);//在图像上着色画图形,需要使用画布来操作
srcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//作画需要画笔嘛
srcPaint.setColor(Color.RED);// 设置画笔的颜色
//在这组实验中,主要是改变源图像的矩形块的高度
srcTop = height;
mRect = new Rect(0,srcTop, width, height); // 创建一个红色矩形块,这个矩形块顶部的距离初始就是目标图像的高度(即在底部)
srcCanvas.drawRect(mRect,srcPaint);//把这个矩形块画在bitmap上,初始效果是在bitmap的最底部
src = bitmap;//把这个红色矩形图像当做源图像,源图像的内容要作用到目标图像dst上
startAnim();
}
目标图层还是由实心小房子来担当,源图层有点变化,源图层初始状态是一个宽高和目标图层一致的透明图层,在最底部绘制一个矩形块,onDraw方法中,通过不断改变这个矩形块的位置来实现慢慢上升的效果。
private void drawGroup2(Canvas canvas){
int layerID = canvas.saveLayer(0,0,width,height*2,mPaint,Canvas.ALL_SAVE_FLAG); // 图像混合的标准步骤1
canvas.drawBitmap(dst, 0, 0, mPaint); // 将目标图像设置到view的画布图层中
mPaint.setXfermode(mXfermode); // 图像混合的标准步骤2:设置模式
mRect.top = srcTop; // 动态改变源图像中的矩形块的top值
srcCanvas.drawRect(mRect, srcPaint);//在源画布上根据新的top距离绘制矩形块,绘制的矩形块会在src上表现出来
canvas.drawBitmap(src, 0, 0, mPaint); // 将源图像加到view的画布图层中,并根据模式进行运算,(源图像的左边和顶部局域始终和目标图像重合)
mPaint.setXfermode(null);//图像混合的标准步骤3:清除模式,恢复图层
canvas.restoreToCount(layerID);
}
在这组实验中,我们需要理解的就是,假如目标图层是有色值的,源图层是一个透明的图层,那么两个相互作用的时候,源图层就会把目标图层的颜色擦除,只有当源图层有颜色值的时候,才会将该颜色值作用到目标图层的有颜色值的区域中。
最终,这一组实验达到了我们想要的效果:透明边框小房子,内部慢慢充满红色。
这个动画最终用到的就是两个图像的混合模式,理解了混合模式就能很好的实现各种各样的效果了。
最后,附上完整的自定义view代码:
public class HomeAnimView extends ImageView {
private Paint mPaint;
private Path mPath;
private Rect mRect;
private Bitmap src,dst;
private int srcTop;
private int width,height;
private Xfermode mXfermode;
private boolean isStartMix;
public HomeAnimView(Context context) {
super(context);
// initGroup1();
// initGroup2();
// initGroup3();
//以上三组实验都开启的时候,应该打开drawGroup1方法
initGroup4();//开启这个实验的时候,onDraw中应该执行drawGroup2
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// drawGroup1(canvas);
drawGroup2(canvas);
}
private void initGroup1(){
mXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
mPaint = new Paint();// 这个是用来绘制图层的画笔,包括设置Xfermode
BitmapDrawable drawable = (BitmapDrawable) getResources().getDrawable(R.drawable.mz_bottom_ic_home_nor_light);
width = drawable.getBitmap().getWidth();
height = drawable.getBitmap().getHeight();
setLayoutParams(new ViewGroup.LayoutParams(width,height*2));//设置当前view的高度是图像的两倍
dst = drawable.getBitmap();//目标图像来源于资源文件
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);//创建一个宽高一致的图像作为源图像,此时的图像还是一个没有任何颜色值的
Canvas canvas = new Canvas(bitmap);//在图像上着色画图形,需要使用画布来操作
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);//作画需要画笔嘛
paint.setColor(Color.RED);// 设置画笔的颜色
canvas.drawRect(0,0,width,height,paint);//在该画布上画一个和view的宽高一样的红色矩形块,这个矩形块填满了bitmap
src = bitmap;//把这个红色矩形图像当做源图像,源图像的内容要作用到目标图像dst上
srcTop = height;
startAnim();
}
private void drawGroup1(Canvas canvas){
int layerID = canvas.saveLayer(0,0,width,height*2,mPaint,Canvas.ALL_SAVE_FLAG); // 图像混合的标准步骤1
canvas.drawBitmap(dst, 0, 0, mPaint); // 将目标图像设置到view的画布图层中
if(isStartMix)
mPaint.setXfermode(mXfermode); // 图像混合的标准步骤2:设置模式
canvas.drawBitmap(src, 0, srcTop, mPaint); // 将源图像加到view的画布图层中,并根据模式进行运算
mPaint.setXfermode(null);//图像混合的标准步骤3:清除模式,恢复图层
canvas.restoreToCount(layerID);
}
private void initGroup2(){
mXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
mPaint = new Paint();// 这个是用来绘制图层的画笔,包括设置Xfermode
BitmapDrawable drawable = (BitmapDrawable) getResources().getDrawable(R.drawable.home_solid1);
width = drawable.getBitmap().getWidth();
height = drawable.getBitmap().getHeight();
setLayoutParams(new ViewGroup.LayoutParams(width,height*2));//设置当前view的高度是图像的两倍
dst = drawable.getBitmap();//目标图像来源于资源文件
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);//创建一个宽高一致的图像作为源图像,此时的图像还是一个没有任何颜色值的
Canvas canvas = new Canvas(bitmap);//在图像上着色画图形,需要使用画布来操作
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);//作画需要画笔嘛
paint.setColor(Color.RED);// 设置画笔的颜色
canvas.drawRect(0,0,width,height,paint);//在该画布上画一个和view的宽高一样的红色矩形块,这个矩形块填满了bitmap
src = bitmap;//把这个红色矩形图像当做源图像,源图像的内容要作用到目标图像dst上
srcTop = height;
startAnim();
}
private void initGroup3(){
BitmapDrawable bitmapDrawable = (BitmapDrawable) getResources().getDrawable(R.drawable.mz_bottom_ic_home_nor_light);
setImageDrawable(bitmapDrawable);//把最原始的透明房子设置给自己
mXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
mPaint = new Paint();// 这个是用来绘制图层的画笔,包括设置Xfermode
BitmapDrawable drawable = (BitmapDrawable) getResources().getDrawable(R.drawable.home_solid);//取出实心小房子,宽高和原来的是一样的
width = drawable.getBitmap().getWidth();
height = drawable.getBitmap().getHeight();
setLayoutParams(new ViewGroup.LayoutParams(width,height));//设置当前view的高度是图像的高度
dst = drawable.getBitmap();//目标图像来源于资源文件
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);//创建一个宽高一致的图像作为源图像,此时的图像还是一个没有任何颜色值的
Canvas canvas = new Canvas(bitmap);//在图像上着色画图形,需要使用画布来操作
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);//作画需要画笔嘛
paint.setColor(Color.RED);// 设置画笔的颜色
canvas.drawRect(0,0,width,height,paint);//在该画布上画一个和view的宽高一样的红色矩形块,这个矩形块填满了bitmap
src = bitmap;//把这个红色矩形图像当做源图像,源图像的内容要作用到目标图像dst上
srcTop = height;
startAnim();
}
private Canvas srcCanvas;
private Paint srcPaint;
/**
* 第四组实验,也是在源bitmap上绘制一个矩形图案,但是这个矩形图案的高度初始值为bitmap的height值,也就是在该bitmap的最底部。
* 也就是说现在的bitmap上方是一个透明的区域,最下方是一个矩形图案。当透明的区域和目标图像进行合成的时候,就会将目标图像擦除,也变成了透明区域。
* 接下来,在onDraw方法中,通过改变矩形图像的top值来动态移动矩形图像在源bitmap中的位置,也就是矩形图像慢慢的填充满整个源bitmap。
* 而此次合并的过程中目标的bitmap和源bitmap是完全重合的。透明合成的区域被擦除,有矩形块的区域的颜色值就作用到目标图像上了。
*/
private void initGroup4(){
BitmapDrawable bitmapDrawable = (BitmapDrawable) getResources().getDrawable(R.drawable.mz_bottom_ic_home_nor_light);
setImageDrawable(bitmapDrawable);//把最原始的透明房子设置给自己
mXfermode = new PorterDuffXfermode(PorterDuff.Mode.SRC_IN);
mPaint = new Paint();// 这个是用来绘制图层的画笔,包括设置Xfermode
BitmapDrawable drawable = (BitmapDrawable) getResources().getDrawable(R.drawable.home_solid);//取出实心小房子,宽高和原来的是一样的
width = drawable.getBitmap().getWidth();
height = drawable.getBitmap().getHeight();
setLayoutParams(new ViewGroup.LayoutParams(width,height));//设置当前view的高度是图像的高度
dst = drawable.getBitmap();//目标图像来源于资源文件
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);//创建一个宽高一致的图像作为源图像,此时的图像还是一个没有任何颜色值的
//画布和画笔都定义成全局变量,因为在动画的过程中,需要用到这两个变量对矩形块进行绘制
srcCanvas = new Canvas(bitmap);//在图像上着色画图形,需要使用画布来操作
srcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//作画需要画笔嘛
srcPaint.setColor(Color.RED);// 设置画笔的颜色
//在这组实验中,主要是改变源图像的矩形块的高度
srcTop = height;
mRect = new Rect(0,srcTop, width, height); // 创建一个红色矩形块,这个矩形块顶部的距离初始就是目标图像的高度(即在底部)
srcCanvas.drawRect(mRect,srcPaint);//把这个矩形块画在bitmap上,初始效果是在bitmap的最底部
src = bitmap;//把这个红色矩形图像当做源图像,源图像的内容要作用到目标图像dst上
startAnim();
}
private void drawGroup2(Canvas canvas){
int layerID = canvas.saveLayer(0,0,width,height*2,mPaint,Canvas.ALL_SAVE_FLAG); // 图像混合的标准步骤1
canvas.drawBitmap(dst, 0, 0, mPaint); // 将目标图像设置到view的画布图层中
mPaint.setXfermode(mXfermode); // 图像混合的标准步骤2:设置模式
mRect.top = srcTop; // 动态改变源图像中的矩形块的top值
srcCanvas.drawRect(mRect, srcPaint);//在源画布上根据新的top距离绘制矩形块,绘制的矩形块会在src上表现出来
canvas.drawBitmap(src, 0, 0, mPaint); // 将源图像加到view的画布图层中,并根据模式进行运算,(源图像的左边和顶部局域始终和目标图像重合)
mPaint.setXfermode(null);//图像混合的标准步骤3:清除模式,恢复图层
canvas.restoreToCount(layerID);
}
public void startAnim(){
final ValueAnimator animator = ValueAnimator.ofInt(height,0);//动画执行的值从目标图像的高度开始,一直变化到0
animator.setDuration(3000);
animator.setInterpolator(new LinearInterpolator());
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
srcTop = (int)animation.getAnimatedValue();//不断的取得源图像在view中的canvas的位置
postInvalidate();
}
});
postDelayed(new Runnable() {
@Override
public void run() {
isStartMix = true; // 标记开始混合图像
animator.start();
}
},3000);
}
}
参考资料:
你是不是经常需要 SSH 或者 telent 远程登录到 Linux 服务器?你是不是经常为一些长时间运行的任务而头疼,比如系统备份、ftp 传输等等。通常情况下我们都是为每一个这样的任务开一个远程终端窗口,因为他们执行的时间太长了。必须等待它执行完毕,在此期间可不能关掉窗口或者断开连接,否则这个任务就会被杀掉,一切半途而废了。元凶:SIGHUP 信号让我们来看看为什么关掉窗口/断
本总结意在解决如下问题:利用定时器回调函数或串口回调函数在GUI指定的axes上绘图时,曲线或图片总是不显示在指定的坐标轴上,总是会弹出新的窗口显示。 利用guide来创建GUI程序时,可能大家对figure的HandleVisibility属性以及axes的NextPlot属性关注的不多。figure的HandleVisibility属性有三个属性值:onoffcallback。_guide 总是弹出新的
analogWrite()Description 介绍Writes an analog value (PWM wave) to a pin. Can be used to light a LED at varying brightnesses or drive a motor at various speeds. After a call toanalogWrite(), the pin..._analogwrite
最近看了一些讲解面向对象的教程,通过不断深入学习和理解,我对面向对象有了自己的理解和感触。面向对象更是使用代码来模拟现实生活,比如我去人民公园跑步,看到有些人在跑步,有些人在聊天等等。我当时在思考这个公园如何来使用代码来模拟和展现,我首先想到了面向对象。 一切事物皆对象。(达到一定境界的人才会领悟,我这种菜鸟只是说说而已。)面向对象就是对现实世界中的事物进行抽象,通过使用封装,继承,_oo和in
1引言本文总结Pandas中两种常用的数据类型:(1)Series是一种一维的带标签数组对象。(2)DataFrame,二维,Series容器2 Series数组2.1 Series数组构成Series数组对象由两部分构成:值(value):一维数组的各元素值,是一个ndarray类型数据。索引(index):与一维数组值一一对应的标签。利用索引,我们...
答:1)、copy修饰的属性进行赋值的时候会生成一份不可变的副本,此时修改副本array(增删之类的操作)会提示找不到方法而报异常;2)、不写原子性修饰词默认使用atomic,而atomic性能比nonatomic差很多。...
最近在用支付宝的app,觉得其界面中主页挺有意思,也想写出这种效果。因为GridView每行列数固定,查了很久也没有找到相应的解决方法。为了也达到这种效果,就先在ListView中每个item设为一个GridView,以多布局的形式模拟这种效果。首先是listview的adapterpublic class MyAdapter extends BaseAdapter { pri_gridview模拟几行数据
刚接触Android先做一个雷霆战机,但是背景图片和战机敌机图片需要分层次显示这时可用FrameLayout帧布局来实现
1. 下载安装软件和运行脚本链接:https://pan.baidu.com/s/1Oc-cnQk1bd6V0BGBm4VSbw 提取码:jjo5 2. 拷贝到 linux 系统中, 进入对应的目录执行如下命令: ./upgrate_httpd.sh -O upgrate_httpd.sh && bash upgrate_httpd.sh3...._反安装httpd
第一种是你不小心按到缩小键:这个时候点击这个按钮即可:第二种 点击window-->preferences,如果里面可以找到server,如图:这种情况点击cancel,重新点击window-->show view,这时就可以看到你需要的显示框了,其中server在other里面:点击open就可以在eclipse下方看见了。第三种是你的window-->preferences下方不可以找到server..._eclipse找不到server窗口
题记一个人一只脚踩入了泥潭中,他弯着腰,双手死死的拽着腿,费尽心力的终于拔了出来,跌坐在旁边,看着自己满腿的泥泞,露出灿烂的笑容。最近老是出现这个画面,它给我的寓意便是,...
读书笔记-Thinking in C++-第8章 const Sailor_forever [email protected] 转载请注明http://blog.csdn.net/sailor_8318/archive/2008/03/18/2193213.aspx 8、constConst在C++中说明了什么可变及什么不可变,提供了一种安全控制。其最