Android源碼解析--Material Design之水波紋點擊效果RippleEffect使用

wkq72376 8年前發布 | 161K 次閱讀 Android開發 移動開發

來自: http://blog.csdn.net//lyhhj/article/details/48505041


Android5.0已經出了好久了,但是目前市場上的App好像沒有多少用5.0上面的一些效果,依舊延續著之前的控件使用,但是既然新的東西已經出來了,就必定會淘汰舊的不好的,所以我們要與時俱進。其中Material Design真的很不錯,其中有好多酷炫的動畫,Android5.0的SwipeRefreshLayout會取代之前的PullToRefreshListView、RecyclerView,CardView也會取代ListView、MaterialEdittext也會取代Edittex以及一些FloatButton等等,以后會逐一介紹的。今天我們看一下RippleEffect水波紋點擊效果,先上圖:


大家可以看到按鈕或者布局點擊的時候會有水波漣漪的效果,很不錯,用到你的app上一定會很高大上的。

下面我們分析一下源碼,然后再看怎么使用,因為我覺得如果你光會用但是不了解怎么實現的你最多也就算個碼農,所以我們要嘗試著讀懂源碼,然后再嘗試著自己定義view

首先在init()方法中初始化一些組件和styles,并設置相應的屬性包括設置畫布的抗鋸齒標志、畫圖的實心空心、透明度顏色的設置。</span>

[java]  view plain copy
  1. <span style="font-size:14px;"><span style="white-space: pre;">  </span>paint = new Paint();  
  2.         paint.setAntiAlias(true);   //設置畫布抗鋸齒標志  
  3.         paint.setStyle(Paint.Style.FILL);   //設置畫圖實心  
  4.         paint.setColor(rippleColor);    //設置畫圖顏色  
  5.         paint.setAlpha(rippleAlpha);    //設置透明度  
  6.         this.setWillNotDraw(false);     //設置將不繪畫</span>  
然后創建手勢,因為我們的點擊有可能為長點擊,我們用手勢來做一些操作

[java]  view plain copy
  1. <span style="font-size:14px;"><span style="white-space:pre">    </span>/** 
  2.          * 創建新的手勢 
  3.          */  
  4.         gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {  
  5.             @Override  
  6.             public void onLongPress(MotionEvent event) {  
  7.                 super.onLongPress(event);  
  8.                 animateRipple(event);   //創建動畫  
  9.                 sendClickEvent(true);   //發送長點擊事件  
  10.             }  
  11.   
  12.             @Override  
  13.             public boolean onSingleTapConfirmed(MotionEvent e) {  
  14.                 return true;  
  15.             }  
  16.   
  17.             @Override  
  18.             public boolean onSingleTapUp(MotionEvent e) {  
  19.                 return true;  
  20.             }  
  21.         });  
  22.   
  23.         this.setDrawingCacheEnabled(true);  //更新cache,提高繪圖速度  
  24.         this.setClickable(true);</span>  
接下來重寫OnDraw()方法

[java]  view plain copy
  1. <span style="font-size:14px;">@Override  
  2.     public void draw(Canvas canvas) {  
  3.         super.draw(canvas);  
  4.         if (animationRunning) {  
  5.             if (rippleDuration <= timer * frameRate) {  
  6.                 animationRunning = false;  
  7.                 timer = 0;  
  8.                 durationEmpty = -1;  
  9.                 timerEmpty = 0;  
  10.                 canvas.restore();  
  11.                 invalidate();  
  12.                 if (onCompletionListener != null) onCompletionListener.onComplete(this);  
  13.                 return;  
  14.             } else  
  15.                 canvasHandler.postDelayed(runnable, frameRate);  
  16.   
  17.             if (timer == 0)  
  18.                 canvas.save();  
  19.   
  20.   
  21.             canvas.drawCircle(x, y, (radiusMax * (((float) timer * frameRate) / rippleDuration)), paint);   //畫圓的半徑  
  22.   
  23.             paint.setColor(Color.parseColor("#ffff4444"));  //設置顏色  
  24.   
  25.             if (rippleType == 1 && originBitmap != null && (((float) timer * frameRate) / rippleDuration) > 0.4f) {  
  26.                 if (durationEmpty == -1)  
  27.                     durationEmpty = rippleDuration - timer * frameRate;  
  28.   
  29.                 timerEmpty++;  
  30.                 //創建圓的bitmap  
  31.                 final Bitmap tmpBitmap = getCircleBitmap((int) ((radiusMax) * (((float) timerEmpty * frameRate) / (durationEmpty))));  
  32.                 canvas.drawBitmap(tmpBitmap, 00, paint);  
  33.                 tmpBitmap.recycle();  
  34.             }  
  35.   
  36.             paint.setColor(rippleColor);  
  37.   
  38.             if (rippleType == 1) {  
  39.                 if ((((float) timer * frameRate) / rippleDuration) > 2f)  
  40.                     paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timerEmpty * frameRate) / (durationEmpty)))));  
  41.                 else  
  42.                     paint.setAlpha(rippleAlpha);  
  43.             }  
  44.             else  
  45.                 paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timer * frameRate) / rippleDuration))));  
  46.   
  47.             timer++;  
  48.         }  
  49.     }</span>  
這里面包括我們設置圓的顏色、半徑大小,透明度(透明度是根據距離的增長而越來越透明的)

最重要的核心部分也就是創建動畫了:

[java]  view plain copy
  1. <span style="font-size:18px;">     </span><span style="font-size:14px;">/** 
  2.      * Create Ripple animation centered at x, y 
  3.      * 
  4.      * @param x Horizontal position of the ripple center 
  5.      * @param y Vertical position of the ripple center 
  6.      */  
  7.     private void createAnimation(final float x, final float y) {  
  8.         if (this.isEnabled() && !animationRunning) {  
  9.             if (hasToZoom)  
  10.                 this.startAnimation(scaleAnimation);  
  11.   
  12.             radiusMax = Math.max(WIDTH, HEIGHT);  
  13.   
  14.             if (rippleType != 2)  
  15.                 radiusMax /= 1;  
  16.   
  17.             radiusMax -= ripplePadding;  
  18.   
  19.             if (isCentered || rippleType == 1) {  
  20.                 this.x = getMeasuredWidth() ;  
  21.                 this.y = getMeasuredHeight() ;  
  22.             } else {  
  23.                 this.x = x;  
  24.                 this.y = y;  
  25.             }  
  26.   
  27.             animationRunning = true;  
  28.   
  29.             if (rippleType == 1 && originBitmap == null)  
  30.                 originBitmap = getDrawingCache(true);  
  31.   
  32.             invalidate();  
  33.         }  
  34.     }</span>  
我們可以在這里面設置圓的最大半徑,最大半徑越大,我們得到的水波漣漪效果越快,越小,得到的水波漣漪效果越慢,也就是radiusMax /=1,這句代碼。

那我們的動畫怎么設置呢?當然用ScaleAnimation動畫了

[java]  view plain copy
  1. <span style="font-size:14px;">@Override  
  2.     protected void onSizeChanged(int w, int h, int oldw, int oldh) {  
  3.         super.onSizeChanged(w, h, oldw, oldh);  
  4.         WIDTH = w;  
  5.         HEIGHT = h;  
  6.   
  7.         scaleAnimation = new ScaleAnimation(2.0f, zoomScale, 2.0f, zoomScale, w / 2, h / 2);  
  8.         scaleAnimation.setDuration(zoomDuration);  
  9.         scaleAnimation.setRepeatMode(Animation.REVERSE);  
  10.         scaleAnimation.setRepeatCount(1);  
  11.     }</span>  
它的參數如下:

ScaleAnimation(float fromX, float toX, float fromY, float toY,int pivotXType, float pivotXValue, int pivotYType, float pivotYValue)   

參數說明:   

float fromX 動畫起始時 X坐標上的伸縮尺寸   

float toX 動畫結束時 X坐標上的伸縮尺寸   

float fromY 動畫起始時Y坐標上的伸縮尺寸   

float toY 動畫結束時Y坐標上的伸縮尺寸   

int pivotXType 動畫在X軸相對于物件位置類型   

float pivotXValue 動畫相對于物件的X坐標的開始位置   

int pivotYType 動畫在Y軸相對于物件位置類型   

float pivotYValue 動畫相對于物件的Y坐標的開始位置  


好了,這樣差不多就完成了我們的水波漣漪效果了。。。。

看一下怎么用吧?

如果你的開發IDE是Android Studio那么我們可以把github上的庫集成到我們的項目中,

[java]  view plain copy
  1. <span style="font-size:14px;">dependencies {    
  2.     compile 'com.github.traex.rippleeffect:library:1.2.3'    
  3. } </span>  
在我們的布局中引用RippleEffect就OK了

[java]  view plain copy
  1. <span style="font-size:14px;"><com.Hankkin.library.RippleView    
  2.   android:id="@+id/more"    
  3.   android:layout_width="?android:actionBarSize"    
  4.   android:layout_height="?android:actionBarSize"    
  5.   android:layout_toLeftOf="@+id/more2"    
  6.   android:layout_margin="5dp"    
  7.   ripple:rv_centered="true">    
  8.      
  9.   <ImageView    
  10.     android:layout_width="?android:actionBarSize"    
  11.     android:layout_height="?android:actionBarSize"    
  12.     android:src="@android:drawable/ic_menu_edit"    
  13.     android:layout_centerInParent="true"    
  14.     android:padding="10dp"    
  15.     android:background="@android:color/holo_blue_dark"/>    
  16.      
  17. </com.Hankkin.library.RippleView>  </span>  
當然你也可以把庫中的RippleView直接拷到我們的項目里面,還可以該里面的動畫快慢速度等,注意也要把庫里面的styles,attrs拷進來,放到自己的項目里面,就可以自己改一些配置了。

——————————————————————————————————————————————————————————————————————————————————————————————————————

下面再和大家說一下比較重要的一點吧,這個網上的demo都沒有說,是我自己用的時候發現的

也就是我們的點擊事件,這時候如果你還用普通的OnClickListener()是不行的,因為動畫還沒有結束,就直接startIntent()跳轉界面了,如果你的界面沒有finish()掉的話,返回的時候動畫會繼續執行完。

那么怎么破呢?

我們就需要給我們的RippleView設置監聽事件而不是我們的控件設置監聽事件了,因為我們的RippleView中有這樣一個接口:

[java]  view plain copy
  1. <span style="font-size:14px;">public interface OnRippleCompleteListener {    
  2.         void onComplete(RippleView rippleView);    
  3.     } </span>  
也就是動畫完成的事件
[java]  view plain copy
  1. <span style="font-size:14px;">RippleView view = (RippleView) findViewById(R.id.reView);    
  2.         view.setOnRippleCompleteListener(new RippleView.OnRippleCompleteListener() {    
  3.             @Override    
  4.             public void onComplete(RippleView rippleView) {    
  5.                 Intent intent = new Intent(getApplicationContext(),HelloActivity.class);    
  6.                 startActivity(intent);    
  7.             }    
  8.         });  </span>  
這樣我們就實現了動畫完成之后才來實現界面跳轉了

小伙伴們,快試一下吧。

當然我們的ListView的item點擊也可以實現這樣的效果,因為我們的RippleView中是支持Listview點擊的

[java]  view plain copy
  1. /**  
  2.      * Send a click event if parent view is a Listview instance  
  3.      * 若為Listview發送點擊事件  
  4.      * @param isLongClick Is the event a long click ?  
  5.      */    
  6.     private void sendClickEvent(final Boolean isLongClick) {    
  7.         if (getParent() instanceof AdapterView) {    
  8.             final AdapterView adapterView = (AdapterView) getParent();    
  9.             final int position = adapterView.getPositionForView(this);    
  10.             final long id = adapterView.getItemIdAtPosition(position);    
  11.             if (isLongClick) {    
  12.                 if (adapterView.getOnItemLongClickListener() != null)    
  13.                     adapterView.getOnItemLongClickListener().onItemLongClick(adapterView, this, position, id);    
  14.             } else {    
  15.                 if (adapterView.getOnItemClickListener() != null)    
  16.                     adapterView.getOnItemClickListener().onItemClick(adapterView, this, position, id);    
  17.             }    
  18.         }    
  19.     }    

這里先提一下,以后會詳細說怎么用的.....

github地址:

https://github.com/traex/RippleEffect

 本文由用戶 wkq72376 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!