在Android中实现来自Material设计的Ripple效果:RippleEffect

网络示例图:

RippleView.java

  1 /*
  2  * The MIT License (MIT)
  3  *
  4  * Copyright (c) 2014 Robin Chutaux
  5  *
  6  * Permission is hereby granted, free of charge, to any person obtaining a copy
  7  * of this software and associated documentation files (the "Software"), to deal
  8  * in the Software without restriction, including without limitation the rights
  9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10  * copies of the Software, and to permit persons to whom the Software is
 11  * furnished to do so, subject to the following conditions:
 12  *
 13  * The above copyright notice and this permission notice shall be included in
 14  * all copies or substantial portions of the Software.
 15  *
 16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 22  * THE SOFTWARE.
 23  */
 24 
 25 package com.maomao.beautymovie.widget;
 26 
 27 
 28 import com.maomao.beautymovie.R;
 29 
 30 import android.content.Context;
 31 import android.content.res.TypedArray;
 32 import android.graphics.Bitmap;
 33 import android.graphics.Canvas;
 34 import android.graphics.Paint;
 35 import android.graphics.PorterDuff;
 36 import android.graphics.PorterDuffXfermode;
 37 import android.graphics.Rect;
 38 import android.os.Handler;
 39 import android.util.AttributeSet;
 40 import android.view.GestureDetector;
 41 import android.view.MotionEvent;
 42 import android.view.View;
 43 import android.view.ViewGroup;
 44 import android.view.animation.Animation;
 45 import android.view.animation.ScaleAnimation;
 46 import android.widget.RelativeLayout;
 47 
 48 /**
 49  * Author :    Chutaux Robin
 50  * Date :      10/8/2014
 51  */
 52 public class RippleView extends RelativeLayout
 53 {
 54     private int WIDTH;
 55     private int HEIGHT;
 56     private int FRAME_RATE = 10;
 57     private int DURATION = 400;
 58     private int PAINT_ALPHA = 90;
 59     private Handler canvasHandler;
 60     private float radiusMax = 0;
 61     private boolean animationRunning = false;
 62     private int timer = 0;
 63     private int timerEmpty = 0;
 64     private int durationEmpty = -1;
 65     private float x = -1;
 66     private float y = -1;
 67     private int zoomDuration;
 68     private float zoomScale;
 69     private ScaleAnimation scaleAnimation;
 70     private Boolean hasToZoom;
 71     private Boolean isCentered;
 72     private Integer rippleType;
 73     private Paint paint;
 74     private Bitmap originBitmap;
 75     private int rippleColor;
 76     private View childView;
 77     private int ripplePadding;
 78     private GestureDetector gestureDetector;
 79     private Runnable runnable = new Runnable()
 80     {
 81         @Override
 82         public void run()
 83         {
 84             invalidate();
 85         }
 86     };
 87 
 88     public RippleView(Context context)
 89     {
 90         super(context);
 91     }
 92 
 93     public RippleView(Context context, AttributeSet attrs)
 94     {
 95         super(context, attrs);
 96         init(context, attrs);
 97     }
 98 
 99     public RippleView(Context context, AttributeSet attrs, int defStyle)
100     {
101         super(context, attrs, defStyle);
102         init(context, attrs);
103     }
104 
105     private void init(final Context context, final AttributeSet attrs)
106     {
107         if (isInEditMode())
108             return;
109 
110         final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.RippleView);
111         rippleColor = typedArray.getColor(R.styleable.RippleView_rv_color, getResources().getColor(R.color.rippelColor));
112         rippleType = typedArray.getInt(R.styleable.RippleView_rv_type, 0);
113         hasToZoom = typedArray.getBoolean(R.styleable.RippleView_rv_zoom, false);
114         isCentered = typedArray.getBoolean(R.styleable.RippleView_rv_centered, false);
115         DURATION = typedArray.getInteger(R.styleable.RippleView_rv_rippleDuration, DURATION);
116         FRAME_RATE = typedArray.getInteger(R.styleable.RippleView_rv_framerate, FRAME_RATE);
117         PAINT_ALPHA = typedArray.getInteger(R.styleable.RippleView_rv_alpha, PAINT_ALPHA);
118         ripplePadding = typedArray.getDimensionPixelSize(R.styleable.RippleView_rv_ripplePadding, 0);
119         canvasHandler = new Handler();
120         zoomScale = typedArray.getFloat(R.styleable.RippleView_rv_zoomScale, 1.03f);
121         zoomDuration = typedArray.getInt(R.styleable.RippleView_rv_zoomDuration, 200);
122         paint = new Paint();
123         paint.setAntiAlias(true);
124         paint.setStyle(Paint.Style.FILL);
125         paint.setColor(rippleColor);
126         paint.setAlpha(PAINT_ALPHA);
127         this.setWillNotDraw(false);
128 
129         gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener()
130         {
131             @Override
132             public boolean onSingleTapConfirmed(MotionEvent e)
133             {
134                 return true;
135             }
136 
137             @Override
138             public boolean onSingleTapUp(MotionEvent e)
139             {
140                 return true;
141             }
142         });
143 
144         this.setDrawingCacheEnabled(true);
145     }
146 
147     @Override
148     public void addView(View child, int index, ViewGroup.LayoutParams params)
149     {
150         childView = child;
151         super.addView(child, index, params);
152     }
153 
154     @Override
155     public void draw(Canvas canvas)
156     {
157         super.draw(canvas);
158         if (animationRunning)
159         {
160             if (DURATION <= timer * FRAME_RATE)
161             {
162                 animationRunning = false;
163                 timer = 0;
164                 durationEmpty = -1;
165                 timerEmpty = 0;
166                 canvas.restore();
167                 invalidate();
168                 return;
169             }
170             else
171                 canvasHandler.postDelayed(runnable, FRAME_RATE);
172 
173             if (timer == 0)
174                 canvas.save();
175 
176 
177             canvas.drawCircle(x, y, (radiusMax * (((float) timer * FRAME_RATE) / DURATION)), paint);
178 
179             paint.setColor(getResources().getColor(android.R.color.holo_red_light));
180 
181             if (rippleType == 1 && originBitmap != null && (((float) timer * FRAME_RATE) / DURATION) > 0.4f)
182             {
183                 if (durationEmpty == -1)
184                     durationEmpty = DURATION - timer * FRAME_RATE;
185 
186                 timerEmpty++;
187                 final Bitmap tmpBitmap = getCircleBitmap((int) ((radiusMax) * (((float) timerEmpty * FRAME_RATE) / (durationEmpty))));
188                 canvas.drawBitmap(tmpBitmap, 0, 0, paint);
189                 tmpBitmap.recycle();
190             }
191 
192             paint.setColor(rippleColor);
193 
194             if (rippleType == 1)
195             {
196                 if ((((float) timer * FRAME_RATE) / DURATION) > 0.6f)
197                     paint.setAlpha((int) (PAINT_ALPHA - ((PAINT_ALPHA) * (((float) timerEmpty * FRAME_RATE) / (durationEmpty)))));
198                 else
199                     paint.setAlpha(PAINT_ALPHA);
200             }
201             else
202                 paint.setAlpha((int) (PAINT_ALPHA - ((PAINT_ALPHA) * (((float) timer * FRAME_RATE) / DURATION))));
203 
204             timer++;
205         }
206     }
207 
208     @Override
209     protected void onSizeChanged(int w, int h, int oldw, int oldh)
210     {
211         super.onSizeChanged(w, h, oldw, oldh);
212         WIDTH = w;
213         HEIGHT = h;
214 
215         scaleAnimation = new ScaleAnimation(1.0f, zoomScale, 1.0f, zoomScale, w / 2, h / 2);
216         scaleAnimation.setDuration(zoomDuration);
217         scaleAnimation.setRepeatMode(Animation.REVERSE);
218         scaleAnimation.setRepeatCount(1);
219     }
220 
221     @Override
222     public boolean onTouchEvent(MotionEvent event)
223     {
224         if (gestureDetector.onTouchEvent(event) && !animationRunning)
225         {
226             if (hasToZoom)
227                 this.startAnimation(scaleAnimation);
228 
229             radiusMax = Math.max(WIDTH, HEIGHT);
230 
231             if (rippleType != 2)
232                 radiusMax /= 2;
233 
234             radiusMax -= ripplePadding;
235 
236             if (isCentered || rippleType == 1)
237             {
238                 this.x = getMeasuredWidth() / 2;
239                 this.y = getMeasuredHeight() / 2;
240             }
241             else
242             {
243                 this.x = event.getX();
244                 this.y = event.getY();
245             }
246 
247             animationRunning = true;
248 
249             if (rippleType == 1 && originBitmap == null)
250                 originBitmap = getDrawingCache(true);
251 
252             invalidate();
253             this.performClick();
254         }
255 
256         childView.onTouchEvent(event);
257         return true;
258     }
259 
260     @Override
261     public boolean onInterceptTouchEvent(MotionEvent event)
262     {
263        return true;
264     }
265 
266     private Bitmap getCircleBitmap(final int radius) {
267         final Bitmap output = Bitmap.createBitmap(originBitmap.getWidth(), originBitmap.getHeight(), Bitmap.Config.ARGB_8888);
268         final Canvas canvas = new Canvas(output);
269         final Paint paint = new Paint();
270         final Rect rect = new Rect((int)(x - radius), (int)(y - radius), (int)(x + radius), (int)(y + radius));
271 
272         paint.setAntiAlias(true);
273         canvas.drawARGB(0, 0, 0, 0);
274         canvas.drawCircle(x, y, radius, paint);
275 
276         paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
277         canvas.drawBitmap(originBitmap, rect, rect, paint);
278 
279         return output;
280     }
281 }
View Code

xml中声明:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout android:orientation="vertical" 
 3     android:padding="5.0dip" 
 4     android:fitsSystemWindows="true" 
 5     android:clipToPadding="true" 
 6     android:layout_width="fill_parent" 
 7     android:layout_height="fill_parent"
 8     xmlns:android="http://schemas.android.com/apk/res/android"
 9     xmlns:ripple="http://schemas.android.com/apk/res-auto"
10     xmlns:tools="http://schemas.android.com/tools"
11     tools:context=".MainActivity">
12     <EditText 
13         android:textSize="16.0sp" 
14         android:id="@+id/edit_email" 
15         android:layout_width="fill_parent" 
16         android:layout_height="wrap_content" 
17         android:layout_marginTop="10.0dip" 
18         android:hint="您的邮箱(选填)" 
19         android:singleLine="true" />
20     <EditText 
21         android:textSize="16.0sp" 
22         android:id="@+id/edit_advice" 
23         android:layout_width="fill_parent" 
24         android:layout_height="wrap_content" 
25         android:layout_marginTop="20.0dip" 
26         android:hint="您的意见或建议" />
27 
28    
29     
30         <com.maomao.beautymovie.widget.RippleView
31         android:id="@+id/rect"
32         android:layout_width="fill_parent"
33         android:layout_marginTop="20dp"
34         android:layout_marginLeft="10dp"
35         android:layout_marginRight="10dp"
36         android:layout_height="wrap_content"
37         ripple:rv_type="rectangle"
38         ripple:rv_zoom="true">
39 
40         <TextView
41             android:id="@+id/rect_child"
42             android:layout_width="fill_parent"
43             android:layout_height="50dp"
44             android:layout_centerInParent="true"
45             android:textColor="@android:color/white"
46             android:textSize="20sp"
47             android:gravity="center"
48             android:text="提交"
49             android:background="@android:color/holo_blue_light"/>
50 
51     </com.maomao.beautymovie.widget.RippleView>
52     
53     
54     
55 </LinearLayout>

activity中监听使用:

 1 package com.maomao.beautymovie;
 2 
 3 
 4 
 5 import com.maomao.beautymovie.widget.RippleView;
 6 
 7 
 8 public class FeedbackActivity extends BaseActivity {
 9 
10     @Override
11     protected void onCreate(Bundle savedInstanceState) {
12         super.onCreate(savedInstanceState);
13         setContentView(R.layout.activity_feedback);
14         final RippleView rippleView = (RippleView) findViewById(R.id.rect);
15 
16         rippleView.setOnClickListener(new View.OnClickListener()
17         {
18             @Override
19             public void onClick(View v)
20             {
21                 Toast.makeText(FeedbackActivity.this, "这是一个Toast提示", Toast.LENGTH_LONG).show();
22             }
23         });
24  
25             
26     }
27 
28     @Override
29     public boolean onOptionsItemSelected(MenuItem item) {
30         if (item.getItemId() == android.R.id.home) {
31             finish();
32             return true;
33         }
34         return super.onOptionsItemSelected(item);
35     }
36 
37 
38 
39 }

demo:链接: http://pan.baidu.com/s/1i3zPZoD 密码: 96cz

原文地址:https://www.cnblogs.com/ning1121/p/4378375.html