簡單實用的Android UI微博動態點贊效果_Android

來源:互聯網
上載者:User

  說起空間動態、微博的點贊效果,網上也是很泛濫,各種實現與效果一大堆。而詳細實現的部分,講述的也是參差不齊,另一方面估計也有很多大俠也不屑一顧,覺得完全沒必要單獨開篇來寫和講解吧。畢竟,也就是兩個view和一些簡單的動畫效果罷了。
  單若是只講這些,我自然也是不願花這番功夫的。雖然自己很菜,可也不甘於太菜。所以偶爾看到些好東西,可以延伸學寫下,我還是很情願拿出來用用,順帶秀一秀逼格什麼的。
  不扯太多,先說說今天實現點贊效果用到的自以為不錯的兩個點:

Checkable 用來擴充View實現選中狀態切換
AndroidViewAnimations 基於nineoldandroids封裝的android動畫簡易類庫。究竟有多簡單呢,就像這樣

AnimHelper.with(new PulseAnimator()).duration(1000).playOn(imageView);
作用: 在imageView上使用PulseAnimator這個動畫效果,播放一秒。

當然是從實現角度來看這個庫啦,如果僅僅是使用,google/百度一大堆啦。   

結合前兩篇富文本摺疊展開,加上我們的點贊view 做出的demo整合效果圖:

1.從實現看門道

  其實從效果看無非就是點擊切換圖片,並添加一些簡單動畫效果而已,確實沒什麼難度。這裡是因為引入了兩個不錯的新內容,使用下,權當新手嘗鮮。

1.1 Checkable介面實現CheckedImageView

  系統本身提供了android.widget.Checkable這樣一個介面,方便我們繼承實現View的選中和取消的狀態。看下這個類:

public interface Checkable { /** * 設定view的選中狀態 */ void setChecked(boolean checked); /** * 當前view是否被選中 */ boolean isChecked(); /** *改變view的選中狀態到相反的狀態 */ void toggle();}

  通常這個介面用來協助我們快速實現view的可選效果,增加了選中和取消兩種狀態和切換方法。另外為了方便View在狀態改變時候快速地變看到效果(更背景或圖片),我們可以直接通過selector控製圖片,而其本身並不會自動改變drawable狀態,因此這裡還有必要重寫drawableStateChanged
方法。我們先以定義一個通用的CheckedImageView為例:

public class CheckedImageView extends ImageView implements Checkable{ protected boolean isChecked;//選中狀態 protected OnCheckedChangeListener onCheckedChangeListener;//狀態改變事件監聽 public static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked }; public CheckedImageView(Context context) { super(context); initialize(); } public CheckedImageView(Context context, AttributeSet attrs) { super(context, attrs); initialize(); } private void initialize() { isChecked = false; } @Override public boolean isChecked() { return isChecked; } @Override public void setChecked(boolean isChecked) { if (this.isChecked != isChecked) {  this.isChecked = isChecked;  refreshDrawableState();  if (onCheckedChangeListener != null) {  onCheckedChangeListener.onCheckedChanged(this, isChecked);  } } } @Override public void toggle() {//改變狀態 setChecked(!isChecked); } //初始DrawableState時候為它添加一個CHECKED_STATE,ImageView本身是沒有這個狀態的 @Override public int[] onCreateDrawableState(int extraSpace) { int[] states = super.onCreateDrawableState(extraSpace + 1); if (isChecked()) {  mergeDrawableStates(states, CHECKED_STATE_SET); } return states; } //當view的選中狀態被改變的時候通知ImageView改變背景或內容,這個view會自動在drawable狀態集中選擇與目前狀態匹配的圖片 @Override protected void drawableStateChanged() { super.drawableStateChanged(); Drawable drawable = getDrawable(); if (drawable != null) {  int[] myDrawableState = getDrawableState();  drawable.setState(myDrawableState);  invalidate(); } } //設定狀態改變監聽事件 public void setOnCheckedChangeListener(OnCheckedChangeListener onCheckedChangeListener) { this.onCheckedChangeListener = onCheckedChangeListener; } //當選中狀態改變時監聽介面觸發該事件 public static interface OnCheckedChangeListener { public void onCheckedChanged(CheckedImageView checkedImgeView, boolean isChecked); }}

這是一個通用的可被選中ImageView,當點擊之後被選中,再次點擊則取消。而其背景/內容也會隨之改變。比如下圖所示效果:

  

  從代碼上看,我們本身並沒有直接定義當view點擊之後,調用setImage()或者setBackground()來改變內容,而是通過使用View本身的DrawableState來繪製和更改,尋找與它對應匹配的圖片,而這些狀態所對應的圖片,都預先在selector中配置好。關於selector這裡不做介紹,自行查閱學習。   

  既然提到selector,順帶提下之前遇到的坑,關於他的匹配原則。比如下邊這樣一個selector:

<?xml version="1.0" encoding="utf-8"?><selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:drawable="@drawable/icon_pressed"></item> <item android:state_checked="true" android:drawable="@drawable/icon_checked"></item> <item android:drawable="@drawable/icon_normal"></item></selector>

  當view同時有上邊兩個狀態(如state_pressed和state_checked)的時候,view優先顯示第一個狀態時候的圖片(icon_pressed)。這是因為它是由上到下有序尋找的,當找到第一個狀態與他定義的所相符所在行時,就優先顯示這行的圖片。所以當我們將最後一行

< item android:drawable=”@drawable/icon_normal”>< /item>

放在第一行時,無論是否選中狀態或按下狀態,都顯示的是icon_normal。初學者一定要注意,我當初就因為這個原因耗費了很多時間尋找緣由。

  回到我們的點贊實現。這裡實現的點贊View PraiseView 包含了一個 CheckedImageView 和一個 TextView ,點贊之後,ImageView會放大回縮並彈出一個TextView”+1”的動畫效果。

public class PraiseView extends FrameLayout implements Checkable{//同樣繼承Checkable protected OnPraisCheckedListener praiseCheckedListener; protected CheckedImageView imageView; //點贊圖片 protected TextView textView; //+1 protected int padding; public PraiseView(Context context) { super(context); initalize(); } public PraiseView(Context context, AttributeSet attrs) { super(context, attrs); initalize(); } protected void initalize() { setClickable(true); imageView = new CheckedImageView(getContext()); imageView.setImageResource(R.drawable.blog_praise_selector); FrameLayout.LayoutParams flp = new LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT,Gravity.CENTER); addView(imageView, flp); textView = new TextView(getContext()); textView.setTextSize(10); textView.setText("+1"); textView.setTextColor(Color.parseColor("#A24040")); textView.setGravity(Gravity.CENTER); addView(textView, flp); textView.setVisibility(View.GONE); } @Override public boolean performClick() { checkChange(); return super.performClick(); } @Override public void toggle() { checkChange(); } public void setChecked(boolean isCheacked) { imageView.setChecked(isCheacked); } public void checkChange() {//點擊切換狀態 if (imageView.isChecked) {  imageView.setChecked(false); } else {  imageView.setChecked(true);  textView.setVisibility(View.VISIBLE);  //放大動畫  AnimHelper.with(new PulseAnimator()).duration(1000).playOn(imageView);  //飄 “+1”動畫  AnimHelper.with(new SlideOutUpAnimator()).duration(1000).playOn(textView); } //調用點贊事件 if (praiseCheckedListener != null) {  praiseCheckedListener.onPraisChecked(imageView.isChecked); } } public boolean isChecked() { return imageView.isChecked; } public void setOnPraisCheckedListener(OnPraisCheckedListener praiseCheckedListener) { this.praiseCheckedListener = praiseCheckedListener; } public interface OnPraisCheckedListener{ void onPraisChecked(boolean isChecked); }}

  過於自定的View大概就這麼多了,Checkable這個小巧方便的類,不知道你會用了沒。至於上邊用到的兩個動畫效果集:

AnimHelper.with(new PulseAnimator()).duration(1000).playOn(imageView);
AnimHelper.with(new SlideOutUpAnimator()).duration(1000).playOn(textView);
感覺封裝的挺簡潔實用,所以很有必要學習分析一下。

1.2 動畫庫的封裝和快速架構

  提到動畫,Android本身內建的動畫類Animation已經做到支援3.0及以上了,雖然也做了很好的封裝,但是做起複雜動畫來還是不夠像上邊那樣簡潔。在關於動畫相容方面,github上的大牛Jake Wharton又做了一套動畫開源庫NineOldAndroids,效果很好而且支援3.0級以前的版本,確實很值得稱讚。而在此基礎上,有很多高手又做了二次封裝,實現了複雜動畫,同時保證方便簡潔,而且通用性和擴充性更高。我們這裡的動畫使用的就是這樣一個簡單的封裝。
  比如,要在XXView上時用XXAnimator這樣的動畫,持續Duration秒。就這麼一行代碼:

AnimHelper.with(new SlideOutUpAnimator()).duration(1000).playOn(textView);
  來看一下基於NineOldAndroids的ViewAnimations具體實現。

1). 首先定義一個基本動畫效果類BaseViewAnimator

  這個BaseViewAnimator動畫類使用一個動畫集合AnimatorSet,封裝成單個動畫類似的用法,並定義了一個abstract方法prepare():

 public abstract class BaseViewAnimator { public static final long DURATION = 1000; private AnimatorSet mAnimatorSet; private long mDuration = DURATION; { mAnimatorSet = new AnimatorSet(); } protected abstract void prepare(View target); public BaseViewAnimator setTarget(View target) { reset(target); prepare(target); return this; } public void animate() { start(); } /** * reset the view to default status * * @param target */ public void reset(View target) { ViewHelper.setAlpha(target, 1); ViewHelper.setScaleX(target, 1); ViewHelper.setScaleY(target, 1); ViewHelper.setTranslationX(target, 0); ViewHelper.setTranslationY(target, 0); ViewHelper.setRotation(target, 0); ViewHelper.setRotationY(target, 0); ViewHelper.setRotationX(target, 0); ViewHelper.setPivotX(target, target.getMeasuredWidth() / 2.0f); ViewHelper.setPivotY(target, target.getMeasuredHeight() / 2.0f); } /** * start to animate */ public void start() { mAnimatorSet.setDuration(mDuration); mAnimatorSet.start(); } public BaseViewAnimator setDuration(long duration) { mDuration = duration; return this; } public BaseViewAnimator setStartDelay(long delay) { getAnimatorAgent().setStartDelay(delay); return this; } public long getStartDelay() { return mAnimatorSet.getStartDelay(); } public BaseViewAnimator addAnimatorListener(AnimatorListener l) { mAnimatorSet.addListener(l); return this; } public void cancel(){ mAnimatorSet.cancel(); } public boolean isRunning(){ return mAnimatorSet.isRunning(); } public boolean isStarted(){ return mAnimatorSet.isStarted(); } public void removeAnimatorListener(AnimatorListener l) { mAnimatorSet.removeListener(l); } public void removeAllListener() { mAnimatorSet.removeAllListeners(); } public BaseViewAnimator setInterpolator(Interpolator interpolator) { mAnimatorSet.setInterpolator(interpolator); return this; } public long getDuration() { return mDuration; } public AnimatorSet getAnimatorAgent() { return mAnimatorSet; }}

  複雜動畫效果基類BaseViewAnimator使用一個AnimatorSet集合來添加各種動畫 ,並綁定到目標targetView ,使用 prepare(View target) 的abstract方法供其子類實現具體的動畫效果。   

2). 其次基於這個類實現我們的各種動畫效果XXAnimator

 當我們要實現具體的動畫效果時,可以直接繼承這個類並實現prepaer方法。比如這裡定義的上劃消失SlideOutUpAnimator 和放大回縮動畫PulseAnimator

/***上劃消失(飄+1)*/public class SlideOutUpAnimator extends BaseViewAnimator { @Override public void prepare(View target) { ViewGroup parent = (ViewGroup)target.getParent(); getAnimatorAgent().playTogether(  ObjectAnimator.ofFloat(target, "alpha", 1, 0),  ObjectAnimator.ofFloat(target,"translationY",0,-parent.getHeight()/2) ); }}/***放大效果*/public class PulseAnimator extends BaseViewAnimator { @Override public void prepare(View target) { getAnimatorAgent().playTogether(  ObjectAnimator.ofFloat(target, "scaleY", 1, 1.2f, 1),  ObjectAnimator.ofFloat(target, "scaleX", 1, 1.2f, 1) ); }}

 上邊兩種動畫效果就是對BaseViewAnimator的兩種實現,動畫本身使用的庫是NineOldAndroids。

3). 最後封裝一個動畫管理工具類AnimHelper供外部使用

 首先定義了一個靜態類,使用helper來執行個體化這個靜態類,並設定各個參數選項。

 public class AnimHelper { private static final long DURATION = BaseViewAnimator.DURATION; private static final long NO_DELAY = 0; /** *執行個體化得到AnimationComposer的唯一介面 */ public static AnimationComposer with(BaseViewAnimator animator) { return new AnimationComposer(animator); } /** *定義與動畫效果相關聯的各種參數, *使用這種方法可以保證對象的構建和他的表示相互隔離開來 */ public static final class AnimationComposer { private List<Animator.AnimatorListener> callbacks = new ArrayList<Animator.AnimatorListener>(); private BaseViewAnimator animator; private long duration = DURATION; private long delay = NO_DELAY; private Interpolator interpolator; private View target; private AnimationComposer(BaseViewAnimator animator) {  this.animator = animator; } public AnimationComposer duration(long duration) {  this.duration = duration;  return this; } public AnimationComposer delay(long delay) {  this.delay = delay;  return this; } public AnimationComposer interpolate(Interpolator interpolator) {  this.interpolator = interpolator;  return this; } public AnimationComposer withListener(Animator.AnimatorListener listener) {  callbacks.add(listener);  return this; } public AnimManager playOn(View target) {  this.target = target;  return new AnimManager(play(), this.target); } private BaseViewAnimator play() {  animator.setTarget(target);  animator.setDuration(duration)   .setInterpolator(interpolator)   .setStartDelay(delay);  if (callbacks.size() > 0) {  for (Animator.AnimatorListener callback : callbacks) {   animator.addAnimatorListener(callback);  }  }  animator.animate();  return animator; } } /** *動畫管理類 */ public static final class AnimManager{ private BaseViewAnimator animator; private View target; private AnimManager(BaseViewAnimator animator, View target){  this.target = target;  this.animator = animator; } public boolean isStarted(){  return animator.isStarted(); } public boolean isRunning(){  return animator.isRunning(); } public void stop(boolean reset){  animator.cancel();  if(reset)  animator.reset(target); } }}

 這段代碼使用了類似Dialog的builder模式,感興趣的可以搜一下 JAVA設計模式-Builder.晚點會另開一篇講解。
(註: 複雜動畫這一部分的內容這裡只是拿出來展示和使用,封裝和實現是由代碼家大大原創,有想瞭解更多動畫及效果的請點其名字連結)

 運行一下,就可以看到前面所示範的效果了。點擊第一下,,伴隨著表徵圖變大一下並飄出“+1”的效果,圖片切換到選中狀態;再點則恢複未選中,而且不會觸發動畫。  

 至此,點贊這塊內容和關注點也說完了,希望各位能有點兒收穫,另外便於自己也能加深理解。
 最後,附上樣本源碼地址:

以上就是本文的全部內容,希望對大家的學習有所協助,也希望大家多多支援雲棲社區。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.