欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android开发仿映客送礼物效果

程序员文章站 2024-03-06 09:35:31
这里写链接内容仿映客送小礼物的特效,顺便复习一下属性动画,话不多说先看效果图。 需求分析 可以看到整个动画有几部分组成,那我们就把每个部分拆分出来各个击破。 1....

这里写链接内容仿映客送小礼物的特效,顺便复习一下属性动画,话不多说先看效果图。

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开发仿映客送礼物效果,希望对大家有所帮助