這里寫鏈接內容仿映客送小禮物的特效,順便復習一下屬性動畫,話不多說先看效果圖。

需求分析
可以看到整個動畫有幾部分組成,那我們就把每個部分拆分出來各個擊破。
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"><RelativeLayoutandroid: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"><ImageViewandroid: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" /><TextViewandroid: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" /><TextViewandroid: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" /><ImageViewandroid: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><ImageViewandroid: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.StrokeTextViewandroid: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方法先繪制外層,在繪制內層。
@Overrideprotected 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() {@Overridepublic 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() {@Overridepublic void onAnimationStart(Animator animation) {anim_gift.setVisibility(View.VISIBLE);}@Overridepublic void onAnimationEnd(Animator animation) {GiftAnimationUtil.startAnimationDrawable(anim_light);anim_num.setVisibility(View.VISIBLE);}});//數量增加ObjectAnimator scaleGiftNum = GiftAnimationUtil.scaleGiftNum(anim_num, repeatCount);scaleGiftNum.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationRepeat(Animator animation) {anim_num.setText("x " + (++starNum));}});//向上漸變消失ObjectAnimator fadeAnimator = GiftAnimationUtil.createFadeAnimator(GiftFrameLayout.this, 0, -100, 300, 400);fadeAnimator.addListener(new AnimatorListenerAdapter() {@Overridepublic 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() {@Overridepublic 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>();@Overrideprotected 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() {@Overridepublic 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() {@Overridepublic 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);}}}});}@Overridepublic 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;}@Overridepublic 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 SimplifiableIfStatementif (id == R.id.action_settings) {return true;}return super.onOptionsItemSelected(item);}}其中關于緩存區的策略大家可以根據實際需求進行定制。
以上所述是小編給大家介紹的Android開發仿映客送禮物效果,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對武林網網站的支持!
新聞熱點
疑難解答