自定义ViewGroup动画

今天要写的是一个发散效果的动画, 练习一下自定义viewgroup,先上效果图:


Step1:

依旧是先分析一下,直接开写通常容易翻车。需求是若干View以均匀的角度从中心的View散发出来,动画效果无非就是平移动画,正常情况我们如果直接去写XML布局来做这个效果是很麻烦的,这些View的位置不容易确定,所以自定义一个ViewGroup,在ViewGroup里面摆放子View。先不去考虑动画,我们直接把这些View都按散发后的效果摆放起来。那么先看看view的layout的方法:public void layout(int l, int t, int r, int b) {..},需要传四个参数分别是上下左右的位置,为了方便计算,在这里将每个view看成一个内切圆,先上个草图大概看看(与真正的计算无关)

这里L是我们自己定义的一个长度即中心的view离散发出去View的距离,r为中心 view的半径,散发的角度是平分360度的,那么三角函数来了(这个自行解决)。可以算出距离中心的X和Y长度,从而确定view的位置。

Step2:

首先自定义一个ViewGroup,添加子view(部分代码):


再来看看自定义viewgroup,需要重写onMeasure()和OnLayout()方法,viewgroup默认不回去测量子控件,我们计算式需要知道子控件的大小,所以在onMeaser()测量子控件大小。onLayout()摆放子控件位置,因为是360度发散,摆放计算时需要注意以中心view的圆心为原点,区分出四个象限(每个象限的view情况不一样),方便计算(计算比较麻烦,耐心点,为了效果好点这个length我给了个随机值),如下草图

主要代码如下:

private int mwidth; //viewgroup宽

private int mheight; //viewgroup高

private int radius; //中心view的半径

private int width0; //中心view的宽

private int height0; //中心view的高

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec,heightMeasureSpec);

if(getChildCount() >0) {

for(inti =0;i < getChildCount();i++) { //测量子控件

View child = getChildAt(i);

child.measure(0,0);

}

}

}

@Override

protected voidonSizeChanged(intw, inth, intoldw, intoldh) { //获取viewgroup宽高

super.onSizeChanged(w,h,oldw,oldh);

mwidth= w;

mheight= h;

}

@Override

protected void onLayout(booleanchanged, intl, intt, intr, intb) {

layoutChild0();//第一个view作为中心view摆放

double a =  (Math.toRadians(360) / (getChildCount() -1));//计算平均夹角大小

for(inti =0;i < getChildCount() -1;i++) {//摆放除中心view的其他子控件

double child_a=a*i;//每个view对应的夹角

int length =newRandom().nextInt(200) +200;//随机生成200-400的数

//child.setVisibility(INVISIBLE);//为了动画效果

View child = getChildAt(i +1);

int child_width = child.getMeasuredWidth();//子view的宽高

int child_height = child.getMeasuredHeight();

//区分出四个象限的child,它们的摆放计算方式不同

if(child_a==0){//0度和180度特殊处理一下

child.layout(mwidth/2-child_width/2,mheight/2-child_height-length,mwidth/2+child_width/2,mheight/2-length);

}else if(child_a==Math.toRadians(180)){

child.layout(mwidth/2-child_width/2,mheight/2+length,mwidth/2+child_width/2,mheight/2+child_height+length);

}else if(child_a>0&& child_a<= Math.toRadians(90)) {//第一象限

intx = (int) ((length +radius) * Math.sin(child_a));

inty = (int) ((length +radius) * Math.cos(child_a));

child.layout(mwidth/2+ x,mheight/2-y - child_height /2,mwidth/2+ x + child_width,mheight/2-y + child_height /2);

}else if(child_a > Math.toRadians(90) && child_a < Math.toRadians(180)) {//第二象限

intx = (int) ((length +radius) * Math.sin(Math.toRadians(180)-child_a));

inty = (int) ((length +radius) * Math.cos(Math.toRadians(180)-child_a));

child.layout(mwidth/2+ x,mheight/2+ y - child_height /2,mwidth/2+ x + child_width,mheight/2+ y + child_height /2);

}else if(child_a> Math.toRadians(180) && a*i <=Math.toRadians(270)) {//第三

intx = (int) ((length +radius) * Math.cos(Math.toRadians(270)-child_a));

inty = (int) ((length +radius) * Math.sin(Math.toRadians(270)-child_a));

child.layout(mwidth/2- x - child_width,mheight/2+ y - child_height /2,mwidth/2- x,mheight/2+ y + child_height /2);

}else{//第四

inty = (int) ((length +radius) * Math.cos(Math.toRadians(360)-child_a));

intx = (int) ((length +radius) * Math.sin(Math.toRadians(360)-child_a));

child.layout(mwidth/2- x - child_width,mheight/2- y - child_height /2,mwidth/2- x,mheight/2- y + child_height /2);

}

}

}

//最中间的child摆放

private void layoutChild0() {

View child0 = getChildAt(0);

child0.setOnClickListener(this);

width0= child0.getMeasuredWidth();

height0= child0.getMeasuredHeight();

radius= Math.max(width0,height0) /2;

child0.layout(mwidth/2-width0/2,mheight/2-height0/2,mwidth/2+width0/2,mheight/2+height0/2);

}


经过一系列计算将view摆放好了,其实这个自定义控件已经完成了一大半,主要是摆放复杂点,效果如下图

ok,剩下的就是动画效果了,这里用的是TranslateAnimation(int fromXType, float fromXValue, intto XType, float toXValue,int fromYType, float fromYValue, int toYType, float toYValue),需要的几个参数也很容易明白,关于这个type有三种ABSOLUTE(将自身作0点,往左则减往下则加),RELATIVE_TO_SELF(相对于自己),RELATIVE_TO_PARENT(相对于父容器)。不太了解具体含义也没关系,可以自己去试。分析动画效果,只需要从中心点到我们给它摆放好的位置即可,所以这里用ABSOLUTE代码如下:

private void childAnimation() {//这段代码我是在child0的onClick()里面调用的,此处忽略

for(int i=0;i<getChildCount-1;i++){

View child=getChildAt(i+1);

child.setVisibility(VISIBLE);//在

TranslateAnimation ta=newTranslateAnimation(Animation.ABSOLUTE,-child.getLeft() +mwidth/2-width0/2,Animation.ABSOLUTE,0,Animation.ABSOLUTE,-child.getTop() +mheight/2-height0/2,Animation.ABSOLUTE,0);//从中心点到自己的位置(0)

ta.setDuration(1200);

ta.setStartOffset(200*i);//设置开始偏移时间,每个view平移时间有一定间隔

ta.setFillAfter(true);

ta.setInterpolator(newOvershootInterpolator());

child.startAnimation(ta);

}

}


TheEnd

除了计算麻烦一点,其它的很容易,主要是要理解view和viewgroup的流程,安利一下郭霖大神的View绘制流程http://blog.csdn.net/guolin_blog/article/details/17045157。重要的事情说三遍:拿到效果图一定要一步步分析,耐心点!上一篇留下了一个invalidate()的问题,看完郭霖大神对于invalidate()的分析然后自己找找源码一步步走,你就明白了,invalidate()最终调用了performTraversals()performTraversals方法就是整个View树开始绘制的起始节点,所以,View调用invalidate方法的实质是:层层上传到父级,直到传递到ViewRootImpl后会触发scheduleTraversals方法,然后整个View树就开始重新按照View的绘制流程进行重绘任务。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容