Android源碼解析--Material Design之水波紋點擊效果RippleEffect使用
來自: 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>- <span style="font-size:14px;"><span style="white-space: pre;"> </span>paint = new Paint();
- paint.setAntiAlias(true); //設置畫布抗鋸齒標志
- paint.setStyle(Paint.Style.FILL); //設置畫圖實心
- paint.setColor(rippleColor); //設置畫圖顏色
- paint.setAlpha(rippleAlpha); //設置透明度
- this.setWillNotDraw(false); //設置將不繪畫</span>
- <span style="font-size:14px;"><span style="white-space:pre"> </span>/**
- * 創建新的手勢
- */
- gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
- @Override
- public void onLongPress(MotionEvent event) {
- super.onLongPress(event);
- animateRipple(event); //創建動畫
- sendClickEvent(true); //發送長點擊事件
- }
- @Override
- public boolean onSingleTapConfirmed(MotionEvent e) {
- return true;
- }
- @Override
- public boolean onSingleTapUp(MotionEvent e) {
- return true;
- }
- });
- this.setDrawingCacheEnabled(true); //更新cache,提高繪圖速度
- this.setClickable(true);</span>
- <span style="font-size:14px;">@Override
- public void draw(Canvas canvas) {
- super.draw(canvas);
- if (animationRunning) {
- if (rippleDuration <= timer * frameRate) {
- animationRunning = false;
- timer = 0;
- durationEmpty = -1;
- timerEmpty = 0;
- canvas.restore();
- invalidate();
- if (onCompletionListener != null) onCompletionListener.onComplete(this);
- return;
- } else
- canvasHandler.postDelayed(runnable, frameRate);
- if (timer == 0)
- canvas.save();
- canvas.drawCircle(x, y, (radiusMax * (((float) timer * frameRate) / rippleDuration)), paint); //畫圓的半徑
- paint.setColor(Color.parseColor("#ffff4444")); //設置顏色
- if (rippleType == 1 && originBitmap != null && (((float) timer * frameRate) / rippleDuration) > 0.4f) {
- if (durationEmpty == -1)
- durationEmpty = rippleDuration - timer * frameRate;
- timerEmpty++;
- //創建圓的bitmap
- final Bitmap tmpBitmap = getCircleBitmap((int) ((radiusMax) * (((float) timerEmpty * frameRate) / (durationEmpty))));
- canvas.drawBitmap(tmpBitmap, 0, 0, paint);
- tmpBitmap.recycle();
- }
- paint.setColor(rippleColor);
- if (rippleType == 1) {
- if ((((float) timer * frameRate) / rippleDuration) > 2f)
- paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timerEmpty * frameRate) / (durationEmpty)))));
- else
- paint.setAlpha(rippleAlpha);
- }
- else
- paint.setAlpha((int) (rippleAlpha - ((rippleAlpha) * (((float) timer * frameRate) / rippleDuration))));
- timer++;
- }
- }</span>
最重要的核心部分也就是創建動畫了:
- <span style="font-size:18px;"> </span><span style="font-size:14px;">/**
- * Create Ripple animation centered at x, y
- *
- * @param x Horizontal position of the ripple center
- * @param y Vertical position of the ripple center
- */
- private void createAnimation(final float x, final float y) {
- if (this.isEnabled() && !animationRunning) {
- if (hasToZoom)
- this.startAnimation(scaleAnimation);
- radiusMax = Math.max(WIDTH, HEIGHT);
- if (rippleType != 2)
- radiusMax /= 1;
- radiusMax -= ripplePadding;
- if (isCentered || rippleType == 1) {
- this.x = getMeasuredWidth() ;
- this.y = getMeasuredHeight() ;
- } else {
- this.x = x;
- this.y = y;
- }
- animationRunning = true;
- if (rippleType == 1 && originBitmap == null)
- originBitmap = getDrawingCache(true);
- invalidate();
- }
- }</span>
那我們的動畫怎么設置呢?當然用ScaleAnimation動畫了
- <span style="font-size:14px;">@Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- WIDTH = w;
- HEIGHT = h;
- scaleAnimation = new ScaleAnimation(2.0f, zoomScale, 2.0f, zoomScale, w / 2, h / 2);
- scaleAnimation.setDuration(zoomDuration);
- scaleAnimation.setRepeatMode(Animation.REVERSE);
- scaleAnimation.setRepeatCount(1);
- }</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上的庫集成到我們的項目中,
- <span style="font-size:14px;">dependencies {
- compile 'com.github.traex.rippleeffect:library:1.2.3'
- } </span>
- <span style="font-size:14px;"><com.Hankkin.library.RippleView
- android:id="@+id/more"
- android:layout_width="?android:actionBarSize"
- android:layout_height="?android:actionBarSize"
- android:layout_toLeftOf="@+id/more2"
- android:layout_margin="5dp"
- ripple:rv_centered="true">
- <ImageView
- android:layout_width="?android:actionBarSize"
- android:layout_height="?android:actionBarSize"
- android:src="@android:drawable/ic_menu_edit"
- android:layout_centerInParent="true"
- android:padding="10dp"
- android:background="@android:color/holo_blue_dark"/>
- </com.Hankkin.library.RippleView> </span>
——————————————————————————————————————————————————————————————————————————————————————————————————————
下面再和大家說一下比較重要的一點吧,這個網上的demo都沒有說,是我自己用的時候發現的
也就是我們的點擊事件,這時候如果你還用普通的OnClickListener()是不行的,因為動畫還沒有結束,就直接startIntent()跳轉界面了,如果你的界面沒有finish()掉的話,返回的時候動畫會繼續執行完。
那么怎么破呢?
我們就需要給我們的RippleView設置監聽事件而不是我們的控件設置監聽事件了,因為我們的RippleView中有這樣一個接口:
- <span style="font-size:14px;">public interface OnRippleCompleteListener {
- void onComplete(RippleView rippleView);
- } </span>
- <span style="font-size:14px;">RippleView view = (RippleView) findViewById(R.id.reView);
- view.setOnRippleCompleteListener(new RippleView.OnRippleCompleteListener() {
- @Override
- public void onComplete(RippleView rippleView) {
- Intent intent = new Intent(getApplicationContext(),HelloActivity.class);
- startActivity(intent);
- }
- }); </span>
小伙伴們,快試一下吧。
當然我們的ListView的item點擊也可以實現這樣的效果,因為我們的RippleView中是支持Listview點擊的
- /**
- * Send a click event if parent view is a Listview instance
- * 若為Listview發送點擊事件
- * @param isLongClick Is the event a long click ?
- */
- private void sendClickEvent(final Boolean isLongClick) {
- if (getParent() instanceof AdapterView) {
- final AdapterView adapterView = (AdapterView) getParent();
- final int position = adapterView.getPositionForView(this);
- final long id = adapterView.getItemIdAtPosition(position);
- if (isLongClick) {
- if (adapterView.getOnItemLongClickListener() != null)
- adapterView.getOnItemLongClickListener().onItemLongClick(adapterView, this, position, id);
- } else {
- if (adapterView.getOnItemClickListener() != null)
- adapterView.getOnItemClickListener().onItemClick(adapterView, this, position, id);
- }
- }
- }
這里先提一下,以后會詳細說怎么用的.....
github地址:
https://github.com/traex/RippleEffect