Android开发仿映客送礼物效果
这里写链接内容仿映客送小礼物的特效,顺便复习一下属性动画,话不多说先看效果图。
需求分析
可以看到整个动画有几部分组成,那我们就把每个部分拆分出来各个击破。
1.要显示那些内容以及内容间的位置关系?
可以看到我们要显示用户头像,昵称,礼物图标以及数量。所以这里我选择用framelayout来作为根布局。
2.需要哪些动画以及动画的执行顺序?
a.首先是整体从左到右飞入并有一个回弹(translationx + overshootinterpolator)
b.然后是礼物从左到右飞入而且是一个带减速效果的(translationx + decelerateinterpolator)
c.礼物数量依次累加同时伴随着缩放(scale+repeat)
d.后面的粒子效果(帧动画)
e.整体向上平移并且逐渐消失(translationy + alpha)
3.送礼的区域有两块(a,b),如何分配?
因为用户送礼的数量不固定,所以动画持续的时间也不一定。但是我们希望这两块区域能得到充分的使用,即我们需要一个队列存放这些礼物实例,a和b谁空闲,就分配给谁处理。
4.以上所有内容是否使用原生的空间就能实现?
正如上面的分析,我们有时操作整体,有时操作局部。这时我们最好能自定义一个布局继承framelayout,其实也就是封装一层,这样我们就可以很好的控制整个布局。除此之外,还有我们注意到礼物数量是带描边的,貌似需要我们自定义实现了。
功能实现
需求分析完了,接下来我们说说功能的实现。
首先来打我们的整体布局。
<framelayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="wrap_content"> <relativelayout android:id="@+id/animation_person_rl" android:layout_width="wrap_content" android:layout_height="39dp" android:layout_gravity="left" android:layout_margintop="22dp" android:background="@drawable/bg_giftlayout"> <imageview android:id="@+id/gift_userheader_iv" android:layout_width="39dp" android:layout_height="39dp" android:layout_margin="3dp" android:layout_alignparentleft="true" android:layout_centervertical="true" android:src="@mipmap/ember" /> <textview android:id="@+id/gift_usernickname_tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginleft="6dp" android:layout_margintop="4dp" android:layout_torightof="@id/gift_userheader_iv" android:text="库日天" android:textcolor="#ffffff" android:textsize="12sp" /> <textview android:id="@+id/gift_usersign_tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignleft="@id/gift_usernickname_tv" android:layout_below="@id/gift_usernickname_tv" android:layout_margintop="4dp" android:ellipsize="end" android:text="送一个超级无敌" android:textcolor="#ffea79" android:textsize="11sp" /> <imageview android:id="@+id/animation_gift" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_torightof="@id/gift_usersign_tv" android:background="@mipmap/diamond2x" /> </relativelayout> <imageview android:id="@+id/animation_light" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginleft="120dp" android:src="@drawable/light_star_anim" /> <com.example.work.animationdemo.stroketextview android:id="@+id/animation_num" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginleft="185dp" android:layout_margintop="12dp" android:text="x 1" android:textcolor="#0076ff" android:textsize="24sp" app:innnercolor="#ffffff" app:outercolor="#0076ff" /> </framelayout>
这里比较简单不多说了,重点看下stroketextview,带描边的textview,其实就是重写了ondraw方法先绘制外层,在绘制内层。
@override protected void ondraw(canvas canvas) { if (m_bdrawsideline) { // 描外层 settextcolorusereflection(moutercolor); m_textpaint.setstrokewidth(5); m_textpaint.setstyle(paint.style.fill_and_stroke); super.ondraw(canvas); // 描内层,恢复原先的画笔 settextcolorusereflection(minnercolor); m_textpaint.setstrokewidth(0); m_textpaint.setstyle(paint.style.fill_and_stroke); } super.ondraw(canvas); } /** * 使用反射的方法进行字体颜色的设置 * @param color */ private void settextcolorusereflection(int color) { field textcolorfield; try { textcolorfield = textview.class.getdeclaredfield("mcurtextcolor"); textcolorfield.setaccessible(true); textcolorfield.set(this, color); textcolorfield.setaccessible(false); } catch (nosuchfieldexception e) { e.printstacktrace(); } catch (illegalargumentexception e) { e.printstacktrace(); } catch (illegalaccessexception e) { e.printstacktrace(); } m_textpaint.setcolor(color); }
定义礼物的实体类
public class giftsendmodel { private int giftcount; private string useravatarres; private string nickname; private string sig; private int giftres; private string gift_id; private int star; public giftsendmodel(int giftcount) { this.giftcount = giftcount; } public int getgiftcount() { return giftcount; } public void setgiftcount(int giftcount) { this.giftcount = giftcount; } ......
封装整体布局
public class giftframelayout extends framelayout { private layoutinflater minflater; relativelayout anim_rl; imageview anim_gift, anim_light, anim_header; textview anim_nickname, anim_sign; stroketextview anim_num; /** * 礼物数量的起始值 */ int starnum = 1; int repeatcount = 0; private boolean isshowing = false; public giftframelayout(context context) { this(context, null); } public giftframelayout(context context, attributeset attrs) { super(context, attrs); minflater = layoutinflater.from(context); initview(); } private void initview() { view view = minflater.inflate(r.layout.animation, this, false); anim_rl = (relativelayout) view.findviewbyid(r.id.animation_person_rl); anim_gift = (imageview) view.findviewbyid(r.id.animation_gift); anim_light = (imageview) view.findviewbyid(r.id.animation_light); anim_num = (stroketextview) view.findviewbyid(r.id.animation_num); anim_header = (imageview) view.findviewbyid(r.id.gift_userheader_iv); anim_nickname = (textview) view.findviewbyid(r.id.gift_usernickname_tv); anim_sign = (textview) view.findviewbyid(r.id.gift_usersign_tv); this.addview(view); } public void hideview() { anim_gift.setvisibility(invisible); anim_light.setvisibility(invisible); anim_num.setvisibility(invisible); } public void setmodel(giftsendmodel model){ if (0!=model.getgiftcount()) { this.repeatcount = model.getgiftcount(); } if (!textutils.isempty(model.getnickname())) { anim_nickname.settext(model.getnickname()); } if (!textutils.isempty(model.getsig())) { anim_sign.settext(model.getsig()); } } public boolean isshowing(){ return isshowing; } public animatorset startanimation( final int repeatcount) { hideview(); //布局飞入 objectanimator flyfromltor = giftanimationutil.createflyfromltor(anim_rl, -getwidth(), 0, 400,new overshootinterpolator()); flyfromltor.addlistener(new animatorlisteneradapter() { @override public void onanimationstart(animator animation) { super.onanimationstart(animation); giftframelayout.this.setvisibility(view.visible); giftframelayout.this.setalpha(1f); isshowing = true; anim_num.settext("x " + 1); log.i("tag", "flyfromltor a start"); } }); //礼物飞入 objectanimator flyfromltor2 = giftanimationutil.createflyfromltor(anim_gift, -getwidth(), 0, 400,new decelerateinterpolator()); flyfromltor2.addlistener(new animatorlisteneradapter() { @override public void onanimationstart(animator animation) { anim_gift.setvisibility(view.visible); } @override public void onanimationend(animator animation) { giftanimationutil.startanimationdrawable(anim_light); anim_num.setvisibility(view.visible); } }); //数量增加 objectanimator scalegiftnum = giftanimationutil.scalegiftnum(anim_num, repeatcount); scalegiftnum.addlistener(new animatorlisteneradapter() { @override public void onanimationrepeat(animator animation) { anim_num.settext("x " + (++starnum)); } }); //向上渐变消失 objectanimator fadeanimator = giftanimationutil.createfadeanimator(giftframelayout.this, 0, -100, 300, 400); fadeanimator.addlistener(new animatorlisteneradapter() { @override public void onanimationend(animator animation) { giftframelayout.this.setvisibility(view.invisible); } }); // 复原 objectanimator fadeanimator2 = giftanimationutil.createfadeanimator(giftframelayout.this, 100, 0, 20, 0); animatorset animatorset = giftanimationutil.startanimation(flyfromltor, flyfromltor2, scalegiftnum, fadeanimator, fadeanimator2); animatorset.addlistener(new animatorlisteneradapter() { @override public void onanimationend(animator animation) { starnum = 1; isshowing = false; } }); return animatorset;
我们将所有的动画方法都写到了giftanimationutil中便于管理
public class giftanimationutil { /** * @param target * @param star 动画起始坐标 * @param end 动画终止坐标 * @param duration 持续时间 * @return * 创建一个从左到右的飞入动画 * 礼物飞入动画 */ public static objectanimator createflyfromltor(final view target, float star, float end, int duration, timeinterpolator interpolator) { //1.个人信息先飞出来 objectanimator anim1 = objectanimator.offloat(target, "translationx", star, end); anim1.setinterpolator(interpolator); anim1.setduration(duration); return anim1; } /** * @param target * @return * 播放帧动画 */ public static animationdrawable startanimationdrawable(imageview target){ animationdrawable animationdrawable = (animationdrawable) target.getdrawable(); if(animationdrawable!=null) { target.setvisibility(view.visible); animationdrawable.start(); } return animationdrawable; } /** * @param target * @param drawable * 设置帧动画 */ public static void setanimationdrawable(imageview target, animationdrawable drawable){ target.setbackground(drawable); } /** * @param target * @param num * @return * 送礼数字变化 */ public static objectanimator scalegiftnum(final textview target , int num){ propertyvaluesholder anim4 = propertyvaluesholder.offloat("scalex", 1.7f, 0.8f,1f); propertyvaluesholder anim5 = propertyvaluesholder.offloat("scaley", 1.7f, 0.8f,1f); propertyvaluesholder anim6 = propertyvaluesholder.offloat("alpha", 1.0f, 0f,1f); objectanimator animator = objectanimator.ofpropertyvaluesholder(target, anim4, anim5, anim6).setduration(480); animator.setrepeatcount(num); return animator; } /** * @param target * @param star * @param end * @param duration * @param startdelay * @return * 向上飞 淡出 */ public static objectanimator createfadeanimator(final view target, float star, float end, int duration, int startdelay){ propertyvaluesholder translationy = propertyvaluesholder.offloat("translationy", star,end); propertyvaluesholder alpha = propertyvaluesholder.offloat("alpha", 1.0f,0f); objectanimator animator = objectanimator.ofpropertyvaluesholder(target, translationy, alpha); animator.setstartdelay(startdelay); animator.setduration(duration); return animator; } /** * @param animators * @return * 按顺序播放动画 */ public static animatorset startanimation(objectanimator animator1, objectanimator animator2, objectanimator animator3, objectanimator animator4, objectanimator animator5){ animatorset animset = new animatorset(); // animset.playsequentially(animators); animset.play(animator1).before(animator2); animset.play(animator3).after(animator2); animset.play(animator4).after(animator3); animset.play(animator5).after(animator4); animset.start(); return animset; } }
所有的动画效果均是用属性动画完成,其中不仅有单个的动画,还有组合动画。属性动画用起来方面而且功能十分强大!
最后看下mainactivity中的实现
public class mainactivity extends appcompatactivity { private giftframelayout giftframelayout1; private giftframelayout giftframelayout2; list<giftsendmodel> giftsendmodellist = new arraylist<giftsendmodel>(); @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); giftframelayout1 = (giftframelayout) findviewbyid(r.id.gift_layout1); giftframelayout2 = (giftframelayout) findviewbyid(r.id.gift_layout2); findviewbyid(r.id.action).setonclicklistener(new view.onclicklistener() { @override public void onclick(view view) { stargiftanimation(creategiftsendmodel()); } }); } private giftsendmodel creategiftsendmodel(){ return new giftsendmodel((int)(math.random()*10)); } private void stargiftanimation(giftsendmodel model){ if (!giftframelayout1.isshowing()) { sendgiftanimation(giftframelayout1,model); }else if(!giftframelayout2.isshowing()){ sendgiftanimation(giftframelayout2,model); }else{ giftsendmodellist.add(model); } } private void sendgiftanimation(final giftframelayout view, giftsendmodel model){ view.setmodel(model); animatorset animatorset = view.startanimation(model.getgiftcount()); animatorset.addlistener(new animatorlisteneradapter() { @override public void onanimationend(animator animation) { super.onanimationend(animation); synchronized (giftsendmodellist) { if (giftsendmodellist.size() > 0) { view.startanimation(giftsendmodellist.get(giftsendmodellist.size() - 1).getgiftcount()); giftsendmodellist.remove(giftsendmodellist.size() - 1); } } } }); } @override public boolean oncreateoptionsmenu(menu menu) { // inflate the menu; this adds items to the action bar if it is present. getmenuinflater().inflate(r.menu.menu_main, menu); return true; } @override public boolean onoptionsitemselected(menuitem item) { // handle action bar item clicks here. the action bar will // automatically handle clicks on the home/up button, so long // as you specify a parent activity in androidmanifest.xml. int id = item.getitemid(); //noinspection simplifiableifstatement if (id == r.id.action_settings) { return true; } return super.onoptionsitemselected(item); } }
其中关于缓存区的策略大家可以根据实际需求进行定制。
以上所述是小编给大家介绍的android开发仿映客送礼物效果,希望对大家有所帮助
推荐阅读
-
Android开发仿映客送礼物效果
-
Android开发实现带有反弹效果仿IOS反弹scrollview教程详解
-
Android开发之模仿微信打开网页的进度条效果(高仿)
-
Android开发仿映客送礼物效果
-
Android仿zaker用手向上推动的特效开发【推动门效果】(附demo源码下载)
-
Android开发之模仿微信打开网页的进度条效果(高仿)
-
Android仿zaker用手向上推动的特效开发【推动门效果】(附demo源码下载)
-
Android开发仿QQ空间根据位置弹出PopupWindow显示更多操作效果
-
Android开发仿QQ空间根据位置弹出PopupWindow显示更多操作效果
-
Android UI设计与开发之ViewPager仿微信引导界面以及动画效果