Unity3D技术之实现声波支付时的波纹详解

来源:互联网 发布:淘宝大学讲师名单公布 编辑:程序博客网 时间:2024/06/04 19:26

自从支付宝声波支付的波纹效果出来以后,这种形式就慢慢流行开来,比如各种安全软件在扫描时会采用这种动画效果,这种波纹荡漾起来也是增加了动感十足呢,如图1


今天我们就来学习如何实现这种波纹效果,以及最大限度的支持低版本的系统。波纹实现

 

看到这种效果,最直接的感官就是波纹视图慢慢的变大、并且颜色变淡,因此我在第一次摸索的过程中直接继承自View,然后开启一个线程来计算这个视图的此时的大小以及颜色值,效果可以出来,但是有点卡。后面搜索了一些资料,发现有更好的方式可以实现。文章出处【狗刨学习网】

 

新的方式就是使用属性动画,但是属性动画在api 11及其以上才支持,因此这里我们使用了NineOldAnimations动画库。基本原理就是自定义一个布局,在这个布局中会添加几个背景视图,也就是上述效果中的圆形视图,然后用户再指定一个自己的视图,如上如中的支付按钮。当用户点击支付按钮时,启动动画。此时,几个背景视图就会执行一个属性动画集,这些背景视图的x, y轴都会放大,同时视图的alpha属性会慢慢的变小。这样就产生了视图变大、颜色慢慢淡化的效果,如图2所示。



 



图 2

1. The MIT License (MIT)

2. 

3. *

4. 

5. * Copyright (c) 2014-2015 [email]bboyfeiyu@gmail.com[/email]

6. 

7. *

8. 

9. * Permission is hereby granted, free of charge, to any person obtaining a copy

10. 

11. * of this software and associated documentation files (the “Software”), to deal

12. 

13. * in the Software without restriction, including without limitation the rights

14. 

15. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell

16. 

17. * copies of the Software, and to permit persons to whom the Software is

18. 

19. * furnished to do so, subject to the following conditions:

20. 

21. *

22. 

23. * The above copyright notice and this permission notice shall be included in

24. 

25. * all copies or substantial portions of the Software.

26. 

27. *

28. 

29. * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR

30. 

31. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,

32. 

33. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

34. 

35. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER

36. 

37. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,

38. 

39. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN

40. 

41. * THE SOFTWARE.

42. 

43. */

44. 

45. package org.simple.ripple;

46. 

47. import android.content.Context;

48. 

49. import android.content.res.TypedArray;

50. 

51. import android.graphics.Canvas;

52. 

53. import android.graphics.Color;

54. 

55. import android.graphics.Paint;

56. 

57. import android.util.AttributeSet;

58. 

59. import android.view.View;

60. 

61. import android.view.animation.AccelerateDecelerateInterpolator;

62. 

63. import android.widget.RelativeLayout;

64. 

65. import com.nineoldandroids.animation.Animator;

66. 

67. import com.nineoldandroids.animation.AnimatorSet;

68. 

69. import com.nineoldandroids.animation.ObjectAnimator;

70. 

71. import org.simple.ripplelayout.R;

72. 

73. import java.util.ArrayList;

74. 

75. /**

76. 

77. 这是一个类似支付宝声波支付的波纹效果布局,该布局中默认添加了不可见的圆形的视图,启动动画时会启动缩放、颜色渐变动画使得产生波纹效果.

78. 

79. 这些动画都是无限循环的,并且每个View的动画之间都有时间间隔,这些时间间隔就会导致视图有大有小,从而产生波纹的效果.

80. 

81. *

82. 

83. * @author mrsimple

84. 

85. */

86. 

87. public class RippleLayout extends RelativeLayout {

88. 

89. /**

90. 

91. * static final fields

92. 

93. */

94. 

95. private static final int DEFAULT_RIPPLE_COUNT = 6;

96. 

97. private static final int DEFAULT_DURATION_TIME = 3000;

98. 

99. private static final float DEFAULT_SCALE = 4.0f;

100. 

101. private static final int DEFAULT_RIPPLE_COLOR = Color.rgb(0x33, 0x99, 0xcc);

102. 

103. private static final int DEFAULT_STROKE_WIDTH = 0;

104. 

105. private static final int DEFAULT_RADIUS = 60;

106. 

107. /**

108. 

109. *

110. 

111. */

112. 

113. private int mRippleColor = DEFAULT_RIPPLE_COLOR;

114. 

115. private float mStrokeWidth = DEFAULT_STROKE_WIDTH;

116. 

117. private float mRippleRadius = DEFAULT_RADIUS;

118. 

119. private int mAnimDuration;

120. 

121. private int mRippleViewNums;

122. 

123. private int mAnimDelay;

124. 

125. private float mRippleScale;

126. 

127. private boolean animationRunning = false;

128. 

129. /**

130. 

131. *

132. 

133. */

134. 

135. private Paint mPaint = new Paint();

136. 

137. /**

138. 

139. 动画集,执行缩放、alpha动画,使得背景色渐变

140. 

141. */

142. 

143. private AnimatorSet mAnimatorSet = new AnimatorSet();

144. 

145. /**

146. 

147. 动画列表,保存几个动画

148. 

149. */

150. 

151. private ArrayList mAnimatorList = new ArrayList();

152. 

153. /**

154. 

155. * RippleView Params

156. 

157. */

158. 

159. private LayoutParams mRippleViewParams;

160. 

161. /**

162. 

163. * @param context

164. 

165. */

166. 

167. public RippleLayout(Context context) {

168. 

169. super(context);

170. 

171. init(context, null);

172. 

173. }

174. 

175. public RippleLayout(Context context, AttributeSet attrs) {

176. 

177. super(context, attrs);

178. 

179. init(context, attrs);

180. 

181. }

182. 

183. public RippleLayout(Context context, AttributeSet attrs, int defStyleAttr) {

184. 

185. super(context, attrs, defStyleAttr);

186. 

187. init(context, attrs);

188. 

189. }

190. 

191. private void init(final Context context, final AttributeSet attrs) {

192. 

193. if (isInEditMode()) {

194. 

195. return;

196. 

197. }

198. 

199. if (null != attrs) {

200. 

201. initTypedArray(context, attrs);

202. 

203. }

204. 

205. initPaint();

206. 

207. initRippleViewLayoutParams();

208. 

209. generateRippleViews();

210. 

211. }

212. 

213. private void initTypedArray(Context context, AttributeSet attrs) {

214. 

215. final TypedArray typedArray = context.obtainStyledAttributes(attrs,

216. 

217. R.styleable.RippleLayout);

218. 

219. //

220. 

221. mRippleColor = typedArray.getColor(R.styleable.RippleLayout_color,

222. 

223. DEFAULT_RIPPLE_COLOR);

224. 

225. mStrokeWidth =

226. 

227. typedArray.getDimension(R.styleable.RippleLayout_strokeWidth, DEFAULT_STROKE_WIDTH);

228. 

229. mRippleRadius = typedArray.getDimension(R.styleable.RippleLayout_radius,

230. 

231. DEFAULT_RADIUS);

232. 

233. mAnimDuration = typedArray.getInt(R.styleable.RippleLayout_duration,

234. 

235. DEFAULT_DURATION_TIME);

236. 

237. mRippleViewNums = typedArray.getInt(R.styleable.RippleLayout_rippleNums,

238. 

239. DEFAULT_RIPPLE_COUNT);

240. 

241. mRippleScale = typedArray.getFloat(R.styleable.RippleLayout_scale,

242. 

243. DEFAULT_SCALE);

244. 

245. // oh, baby, don’t forget recycle the typedArray !!

246. 

247. typedArray.recycle();

248. 

249. }

250. 

251. private void initPaint() {

252. 

253. mPaint = new Paint();

254. 

255. mPaint.setAntiAlias(true);

256. 

257. mStrokeWidth = 0;

258. 

259. mPaint.setStyle(Paint.Style.FILL);

260. 

261. mPaint.setColor(mRippleColor);

262. 

263. }

264. 

265. private void initRippleViewLayoutParams() {

266. 

267. // ripple view的大小为 半径 笔宽的两倍

268. 

269. int rippleSide = (int) (2 * (mRippleRadius + mStrokeWidth));

270. 

271. mRippleViewParams = new LayoutParams(rippleSide, rippleSide);

272. 

273. // 居中显示

274. 

275. mRippleViewParams.addRule(CENTER_IN_PARENT, TRUE);

276. 

277. }

278. 

279. /**

280. 

281. 计算每个RippleView之间的动画时间间隔,从而产生波纹效果

282. 

283. */

284. 

285. private void calculateAnimDelay() {

286. 

287. mAnimDelay = mAnimDuration / mRippleViewNums;

288. 

289. }

290. 

291. /**

292. 

293. 初始化RippleViews,并且将动画设置到RippleView,使之在x, y不断扩大,并且背景色逐渐淡化

294. 

295. */

296. 

297. private void generateRippleViews() {

298. 

299. calculateAnimDelay();

300. 

301. initAnimSet();

302. 

303. // 添加RippleView

304. 

305. for (int i = 0; i < mRippleViewNums; i++) {

306. 

307. RippleView rippleView = new RippleView(getContext());

308. 

309. addView(rippleView, mRippleViewParams);

310. 

311. // 添加动画

312. 

313. addAnimToRippleView(rippleView, i);

314. 

315. }

316. 

317. // x, y, alpha动画一块执行

318. 

319. mAnimatorSet.playTogether(mAnimatorList);

320. 

321. }

322. 

323. private void initAnimSet() {

324. 

325. mAnimatorSet.setDuration(mAnimDuration);

326. 

327. mAnimatorSet.setInterpolator(new AccelerateDecelerateInterpolator());

328. 

329. }

330. 

331. /**

332. 

333. 为每个RippleView添加动画效果,并且设置动画延时,每个视图启动动画的时间不同,就会产生波纹

334. 

335. *

336. 

337. * @param rippleView

338. 

339. * @param i 视图所在的索引

340. 

341. */

342. 

343. private void addAnimToRippleView(RippleView rippleView, int i) {

344. 

345. // x轴的缩放动画

346. 

347. final ObjectAnimator scaleXAnimator = ObjectAnimator.ofFloat(rippleView, “scaleX”,

348. 

349. 1.0f, mRippleScale);

350. 

351. scaleXAnimator.setRepeatCount(ObjectAnimator.INFINITE);

352. 

353. scaleXAnimator.setRepeatMode(ObjectAnimator.RESTART);

354. 

355. scaleXAnimator.setStartDelay(i * mAnimDelay);

356. 

357. scaleXAnimator.setDuration(mAnimDuration);

358. 

359. mAnimatorList.add(scaleXAnimator);

360. 

361. // y轴的缩放动画

362. 

363. final ObjectAnimator scaleYAnimator = ObjectAnimator.ofFloat(rippleView, “scaleY”,

364. 

365. 1.0f, mRippleScale);

366. 

367. scaleYAnimator.setRepeatMode(ObjectAnimator.RESTART);

368. 

369. scaleYAnimator.setRepeatCount(ObjectAnimator.INFINITE);

370. 

371. scaleYAnimator.setStartDelay(i * mAnimDelay);

372. 

373. scaleYAnimator.setDuration(mAnimDuration);

374. 

375. mAnimatorList.add(scaleYAnimator);

376. 

377. // 颜色的alpha渐变动画

378. 

379. final ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(rippleView, “alpha”, 1.0f,

380. 

381. 0f);

382. 

383. alphaAnimator.setRepeatMode(ObjectAnimator.RESTART);

384. 

385. alphaAnimator.setRepeatCount(ObjectAnimator.INFINITE);

386. 

387. alphaAnimator.setDuration(mAnimDuration);

388. 

389. alphaAnimator.setStartDelay(i * mAnimDelay);

390. 

391. mAnimatorList.add(alphaAnimator);

392. 

393. }

394. 

395. public void startRippleAnimation() {

396. 

397. if (!isRippleAnimationRunning()) {

398. 

399. makeRippleViewsVisible();

400. 

401. mAnimatorSet.start();

402. 

403. animationRunning = true;

404. 

405. }

406. 

407. }

408. 

409. private void makeRippleViewsVisible() {

410. 

411. int childCount = this.getChildCount();

412. 

413. for (int i = 0; i < childCount; i++) {

414. 

415. View childView = this.getChildAt(i);

416. 

417. if (childView instanceof RippleView) {

418. 

419. childView.setVisibility(VISIBLE);

420. 

421. }

422. 

423. }

424. 

425. }

426. 

427. public void stopRippleAnimation() {

428. 

429. if (isRippleAnimationRunning()) {

430. 

431. mAnimatorSet.end();

432. 

433. animationRunning = false;

434. 

435. }

436. 

437. }

438. 

439. public boolean isRippleAnimationRunning() {

440. 

441. return animationRunning;

442. 

443. }

444. 

445. /**

446. 

447. * RippleView产生波纹效果默认不可见,当启动动画时才设置为可见

448. 

449. *

450. 

451. * @author mrsimple

452. 

453. */

454. 

455. private class RippleView extends View {

456. 

457. public RippleView(Context context) {

458. 

459. super(context);

460. 

461. this.setVisibility(View.INVISIBLE);

462. 

463. }

464. 

465. @Override

466. 

467. protected void onDraw(Canvas canvas) {

468. 

469. int radius = (Math.min(getWidth(), getHeight())) / 2;

470. 

471. canvas.drawCircle(radius, radius, radius – mStrokeWidth, mPaint);

472. 

473. }

474. 

475. }

476. 

477. }

478. 

479. 自定义属性attrs.xml:

480. 

481. NineOldAnimations动画库

482. 

483. NineOldAnimations

484. 

485. 使用示例

486. 

487. github clone一份或者将上述代码和attrs.xml拷贝到你的工程中,在布局文件中添加如下:

488. 

489. <org.simple.ripple.ripplelayout

490. 

491. xmlns:ripple=”[url]http://schemas.android.com/apk/org.simple.ripplelayout[/url]”

492. 

493. android:id=”@+id/ripple_layout”

494. 

495. android:layout_width=”match_parent”

496. 

497. android:layout_height=”match_parent”

498. 

499. ripple:duration=”3000″

500. 

501. ripple:radius=”32dp”

502. 

503. ripple:rippleNums=”1″

504. 

505. ripple:scale=”4″

506. 

507. ripple:color=”#8899CC” >

508. 

509. <imageview

510. 

511. android:id=”@+id/centerImage”

512. 

513. android:layout_width=”64dp”

514. 

515. android:layout_height=”64dp”

516. 

517. android:layout_centerInParent=”true”

518. 

519. android:contentDescription=”@string/app_name”

520. 

521. android:src=”@drawable/phone2″ />

522. 

523. 注意,这里引入了xmlns:ripple,也就是自定义RippleLayout属性生成的R的包路径.

524. 

525. 代码中启动动画:

526. 

527. ImageView imageview;

528. 

529. RippleLayout layout;

530. 

531. @Override

532. 

533. protected void onCreate(Bundle savedInstanceState) {

534. 

535. super.onCreate(savedInstanceState);

536. 

537. setContentView(R.layout.activity_main);

538. 

539. layout = (RippleLayout) findViewById(R.id.ripple_layout);

540. 

541. imageview = (ImageView) findViewById(R.id.centerImage);

542. 

543. imageview.setOnClickListener(new OnClickListener() {

544. 

545. @Override

546. 

547. public void onClick(View v) {

548. 

549. if (layout.isRippleAnimationRunning()) {

550. 

551. layout.stopRippleAnimation();

552. 

553. } else {

554. 

555. layout.startRippleAnimation();

556. 

557. }

558. 

559. }

560. 

561. });

0 0
原创粉丝点击