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 }