实现波浪效果view,可以自定义view,也可以自定义drawable,我个人比较喜欢重写drawable,因此这里是自定义drawable实现效果,费话少说,先看效果。

这里用了两种方式实现波浪效果,一种是通过正弦函数去画路径,一种是通过三阶贝塞尔曲线画出类似正弦曲线的效果
先看看实现波浪效果需要用到的一些参数,看注释大概就能了解

  1. /**
  2. * 画布的宽
  3. */
  4. int mWidth;
  5. /**
  6. * 画布的高
  7. */
  8. int mHeight;
  9. /**
  10. * 初始偏移量
  11. */
  12. float offset = 0;
  13. /**
  14. * 线的宽度,当lineWidth>0时,是画线模式,否则是填充模式
  15. */
  16. float lineWidth = 0;
  17. /**
  18. * 显示的周期数
  19. */
  20. float period = 1;
  21. /**
  22. * 移动速度,每秒钟移动的周期数
  23. */
  24. float speedPeriod = 0.5f;
  25. /**
  26. * 波浪的振幅,单位px
  27. */
  28. float mSwing = 20;

再来看看正弦函数的实现方式

  1. private class WaveSin extends Wave {
  2.  
  3. /**
  4. * 初始偏移量
  5. */
  6. float offRadian = 0;
  7. /**
  8. * 每个像素占的弧度
  9. */
  10. double perRadian;
  11. /**
  12. * 每秒移动的弧度数
  13. */
  14. float speedRadian;
  15.  
  16. @Override
  17. public void onDraw(Canvas canvas, boolean isBottom) {
  18. float y = mHeight;
  19. mPath.reset();
  20. //计算路径点的初始位置
  21. if (lineWidth > 0) {
  22. y = (float) (mSwing * Math.sin(offRadian) + mSwing);
  23. mPath.moveTo(-lineWidth, isBottom ? mHeight - y - lineWidth / 2 : y + lineWidth / 2);
  24. } else {
  25. mPath.moveTo(0, isBottom ? 0 : mHeight);
  26. }
  27.  
  28. //步长越小越精细,当然越消耗cpu性能,过大则会有锯齿
  29. int step = mWidth / 100 > 20 ? 20 : mWidth / 100;
  30.  
  31. //通过正弦函数计算路径点,放入mPath中
  32. for (int x = 0; x <= mWidth + step; x += step) {
  33. y = (float) (mSwing * Math.sin(perRadian * x + offRadian) + mSwing);
  34. mPath.lineTo(x, isBottom ? mHeight - y - lineWidth / 2 : y + lineWidth / 2);
  35. }
  36.  
  37. //填充模式时,画完完整路径
  38. if (lineWidth <= 0) {
  39. mPath.lineTo(mWidth, isBottom ? mHeight - y : y);
  40. mPath.lineTo(mWidth, isBottom ? 0 : mHeight);
  41. mPath.lineTo(0, isBottom ? 0 : mHeight);
  42. mPath.close();
  43. }
  44.  
  45. canvas.drawPath(mPath, mPaint);
  46. }
  47.  
  48. @Override
  49. void init() {
  50. perRadian = (float) (2 * Math.PI * period / mWidth);
  51. speedRadian = (float) (speedPeriod * Math.PI * 2);
  52. offRadian = (float) (offset * 2 * Math.PI);
  53. }
  54.  
  55. @Override
  56. public void move(float delta) {
  57. offRadian += speedRadian * delta;
  58. }
  59. }

首先`init()`方法中,perRadian是计算每弧度所占的宽度,speedRadian计算每秒移动的弧度,offRadian是当前偏移弧度,在`move(float delta)`中可以看到delta是时间变化量,所以
`下一次的偏移量 = 当前偏移量+每秒移动的弧度*时间的变化量`,即`offRadian += speedRadian * delta;`
再来看看主要的onDraw方法,Canvas是画布,isBottom是指波浪是否在整个画布的底部。

下面是通过贝塞尔曲线实现波浪效果

  1. private class WaveBezier extends Wave {
  2. /**
  3. * 根据贝塞尔曲线公式计算的一个常量值
  4. */
  5. private static final double MAX_Y = 0.28867513459481287;
  6. /**
  7. * 一个周期的宽度
  8. */
  9. float periodWidth;
  10. /**
  11. * 每秒钟移动的宽度
  12. */
  13. float speedWidth;
  14. /**
  15. * 贝塞尔曲线控制点的Y轴坐标
  16. */
  17. float conY;
  18. /**
  19. * 当前偏移量
  20. */
  21. float currentOffset = 0;
  22.  
  23. @Override
  24. public void onDraw(Canvas canvas, boolean isBottom) {
  25. mPath.reset();
  26. // 移动到第一个周期的起始点
  27. mPath.moveTo(-currentOffset, 0);
  28. float conX = periodWidth / 2;
  29. int w = (int) -currentOffset;
  30. for (int i = 0; i <= mWidth + currentOffset; i += periodWidth) {
  31. mPath.rCubicTo(conX, conY, conX, -conY, periodWidth, 0);//注意,这里用的是相对坐标
  32. w += periodWidth;
  33. }
  34.  
  35. // 闭合路径
  36. if (lineWidth <= 0) {
  37. mPath.rLineTo(0, isBottom ? -mHeight : mHeight);
  38. mPath.rLineTo(-w, 0);
  39. mPath.close();
  40. }
  41.  
  42. // 对Y轴整体偏移
  43. mPath.offset(0, (isBottom ? mHeight - mSwing - lineWidth / 2 : mSwing + lineWidth / 2));
  44.  
  45. canvas.drawPath(mPath, mPaint);
  46. }
  47.  
  48. @Override
  49. void init() {
  50. periodWidth = mWidth / period;
  51. speedWidth = speedPeriod * periodWidth;
  52. currentOffset = offset * periodWidth;
  53. conY = (float) (mSwing / MAX_Y);
  54. isReInit = false;
  55. }
  56.  
  57. @Override
  58. public void move(float delta) {
  59. if (periodWidth <= 0) {
  60. isReInit = true;
  61. return;
  62. }
  63. currentOffset += speedWidth * delta;
  64. if (currentOffset < 0) {
  65. currentOffset += periodWidth;
  66. } else {
  67. if (currentOffset > periodWidth) {
  68. currentOffset -= periodWidth;
  69. }
  70. }
  71. }
  72. }

在 `init()`方法中periodWidth为单个周期宽度,speedWidth为每秒移动的宽度,currentOffset为当前偏移量,conY为控制点的Y轴坐标。

最后贴上完整代码

  1. package cn.sskbskdrin.wave;
  2.  
  3. import android.animation.ValueAnimator;
  4. import android.graphics.Canvas;
  5. import android.graphics.ColorFilter;
  6. import android.graphics.Paint;
  7. import android.graphics.Path;
  8. import android.graphics.PixelFormat;
  9. import android.graphics.Rect;
  10. import android.graphics.drawable.Animatable;
  11. import android.graphics.drawable.Drawable;
  12. import android.view.animation.LinearInterpolator;
  13.  
  14. import java.util.ArrayList;
  15. import java.util.List;
  16. import java.util.Map;
  17. import java.util.WeakHashMap;
  18.  
  19. /**
  20. * Created by sskbskdrin on 2018/4/4.
  21. *
  22. * @author sskbskdrin
  23. */
  24. public class WaveDrawable extends Drawable implements Animatable {
  25.  
  26. private final List<Wave> list;
  27.  
  28. private int mWidth;
  29. private int mHeight;
  30.  
  31. private boolean animIsStart = false;
  32.  
  33. private boolean isBottom = false;
  34.  
  35. public WaveDrawable() {
  36. this(1);
  37. }
  38.  
  39. public WaveDrawable(int count) {
  40. this(count, false);
  41. }
  42.  
  43. public WaveDrawable(int count, boolean isSin) {
  44. if (count <= 0) {
  45. throw new IllegalArgumentException("Illegal count: " + count);
  46. }
  47. list = new ArrayList<>(count);
  48. for (int i = 0; i < count; i++) {
  49. list.add(isSin ? new WaveSin() : new WaveBezier());
  50. }
  51. }
  52.  
  53. public void setBottom(boolean isBottom) {
  54. this.isBottom = isBottom;
  55. }
  56.  
  57. /**
  58. * 设置填充的颜色
  59. *
  60. * @param color
  61. */
  62. public void setColor(int color) {
  63. for (Wave wave : list) {
  64. wave.setColor(color);
  65. }
  66. }
  67.  
  68. /**
  69. * 设置填充的颜色
  70. *
  71. * @param color
  72. */
  73. public void setColor(int color, int index) {
  74. if (index < list.size()) {
  75. list.get(index).setColor(color);
  76. }
  77. }
  78.  
  79. public void setOffset(float offset) {
  80. for (Wave wave : list) {
  81. wave.offset(offset);
  82. }
  83. }
  84.  
  85. /**
  86. * 设置初始相位
  87. *
  88. * @param offset
  89. * @param index
  90. */
  91. public void setOffset(float offset, int index) {
  92. if (index < list.size()) {
  93. list.get(index).offset(offset);
  94. }
  95. }
  96.  
  97. /**
  98. * 波浪的大小
  99. *
  100. * @param swing
  101. */
  102. public void setSwing(int swing) {
  103. for (Wave wave : list) {
  104. wave.setSwing(swing);
  105. }
  106. }
  107.  
  108. /**
  109. * 波浪的大小
  110. *
  111. * @param swing
  112. * @param index
  113. */
  114. public void setSwing(int swing, int index) {
  115. checkIndex(index);
  116. list.get(index).setSwing(swing);
  117. }
  118.  
  119. /**
  120. * 设置波浪流动的速度
  121. *
  122. * @param speed
  123. */
  124. public void setSpeed(float speed) {
  125. for (Wave wave : list) {
  126. wave.setSpeed(speed);
  127. }
  128. }
  129.  
  130. /**
  131. * 设置波浪流动的速度
  132. *
  133. * @param speed
  134. */
  135. public void setSpeed(float speed, int index) {
  136. checkIndex(index);
  137. list.get(index).setSpeed(speed);
  138. }
  139.  
  140. /**
  141. * 设置波浪周期数
  142. *
  143. * @param period (0,--)
  144. */
  145. public void setPeriod(float period) {
  146. for (Wave wave : list) {
  147. wave.setPeriod(period);
  148. }
  149. }
  150.  
  151. public void setPeriod(float period, int index) {
  152. checkIndex(index);
  153. list.get(index).setPeriod(period);
  154. }
  155.  
  156. private void checkIndex(int index) {
  157. if (index < 0 || index >= list.size()) {
  158. throw new IllegalArgumentException("Illegal index. list size=" + list.size() + " index=" + index);
  159. }
  160. }
  161.  
  162. public void setLineWidth(float width) {
  163. for (Wave wave : list) {
  164. wave.setLineWidth(width);
  165. }
  166. }
  167.  
  168. public void setLineWidth(float width, int index) {
  169. if (index >= 0 && index < list.size()) {
  170. list.get(index).setLineWidth(width);
  171. }
  172. }
  173.  
  174. @Override
  175. protected void onBoundsChange(Rect bounds) {
  176. mWidth = bounds.width();
  177. mHeight = bounds.height();
  178. for (Wave wave : list) {
  179. wave.onSizeChange(mWidth, mHeight);
  180. }
  181. }
  182.  
  183. @Override
  184. public void draw(Canvas canvas) {
  185. for (Wave wave : list) {
  186. if (wave.isReInit) {
  187. wave.init();
  188. wave.isReInit = false;
  189. }
  190. wave.onDraw(canvas, isBottom);
  191. }
  192. }
  193.  
  194. @Override
  195. public int getIntrinsicWidth() {
  196. return mWidth;
  197. }
  198.  
  199. @Override
  200. public int getIntrinsicHeight() {
  201. return mHeight;
  202. }
  203.  
  204. private void move(float delta) {
  205. for (Wave wave : list) {
  206. wave.move(delta);
  207. }
  208. }
  209.  
  210. @Override
  211. public void setAlpha(int alpha) {
  212. for (Wave wave : list) {
  213. wave.mPaint.setAlpha(alpha);
  214. }
  215. }
  216.  
  217. @Override
  218. public void setColorFilter(ColorFilter cf) {
  219. for (Wave wave : list) {
  220. wave.mPaint.setColorFilter(cf);
  221. }
  222. }
  223.  
  224. @Override
  225. public int getOpacity() {
  226. return PixelFormat.TRANSLUCENT;
  227. }
  228.  
  229. @Override
  230. public boolean setVisible(boolean visible, boolean restart) {
  231. if (visible) {
  232. if (animIsStart) {
  233. AnimateListener.start(this);
  234. }
  235. } else {
  236. if (animIsStart) {
  237. AnimateListener.start(this);
  238. }
  239. }
  240. return super.setVisible(visible, restart);
  241. }
  242.  
  243. @Override
  244. public void start() {
  245. animIsStart = true;
  246. AnimateListener.start(this);
  247. }
  248.  
  249. @Override
  250. public void stop() {
  251. AnimateListener.cancel(this);
  252. animIsStart = false;
  253. }
  254.  
  255. @Override
  256. public boolean isRunning() {
  257. return AnimateListener.isRunning(this);
  258. }
  259.  
  260. private static class AnimateListener implements ValueAnimator.AnimatorUpdateListener {
  261. private static WeakHashMap<WaveDrawable, Boolean> map = new WeakHashMap<>();
  262. private static int lastTime = 0;
  263. private static ValueAnimator valueAnimator;
  264.  
  265. private static void initAnimation() {
  266. valueAnimator = ValueAnimator.ofInt(0, 1000);
  267. valueAnimator.setDuration(1000);
  268. valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
  269. valueAnimator.setInterpolator(new LinearInterpolator());
  270. valueAnimator.addUpdateListener(new AnimateListener());
  271. }
  272.  
  273. private static void start(WaveDrawable drawable) {
  274. if (!map.containsKey(drawable)) {
  275. map.put(drawable, true);
  276. }
  277. if (valueAnimator == null) {
  278. initAnimation();
  279. }
  280. if (!valueAnimator.isRunning()) {
  281. valueAnimator.start();
  282. }
  283. }
  284.  
  285. private static void cancel(WaveDrawable drawable) {
  286. if (map.containsKey(drawable)) {
  287. map.put(drawable, false);
  288. }
  289. }
  290.  
  291. private static boolean isRunning(WaveDrawable drawable) {
  292. return map.containsKey(drawable) && map.get(drawable);
  293. }
  294.  
  295. @Override
  296. public void onAnimationUpdate(ValueAnimator animation) {
  297. int current = (int) animation.getAnimatedValue();
  298. int delta = current - lastTime;
  299. if (delta < 0) {
  300. delta = current + 1000 - lastTime;
  301. }
  302. float deltaF = delta / 1000f;
  303. lastTime = current;
  304. if (map.size() == 0) {
  305. animation.cancel();
  306. valueAnimator = null;
  307. return;
  308. }
  309. for (Map.Entry<WaveDrawable, Boolean> wave : map.entrySet()) {
  310. if (wave != null && wave.getValue()) {
  311. WaveDrawable drawable = wave.getKey();
  312. drawable.move(deltaF);
  313. drawable.invalidateSelf();
  314. }
  315. }
  316. }
  317. }
  318.  
  319. private abstract class Wave {
  320.  
  321. /**
  322. * 画布的宽
  323. */
  324. int mWidth;
  325. /**
  326. * 画布的高
  327. */
  328. int mHeight;
  329. Path mPath = new Path();
  330. Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  331. /**
  332. * 初始偏移量
  333. */
  334. float offset = 0;
  335. /**
  336. * 线的宽度,当lineWidth>0时,是画线模式,否则是填充模式
  337. */
  338. float lineWidth = 0;
  339. /**
  340. * 显示的周期数
  341. */
  342. float period = 1;
  343. /**
  344. * 移动速度,每秒钟移动的周期数
  345. */
  346. float speedPeriod = 0.5f;
  347. /**
  348. * 波浪的振幅,单位px
  349. */
  350. float mSwing = 20;
  351.  
  352. boolean isReInit = true;
  353.  
  354. /**
  355. * drawable 大小改变
  356. *
  357. * @param width
  358. * @param height
  359. */
  360. void onSizeChange(int width, int height) {
  361. mWidth = width;
  362. mHeight = height;
  363. isReInit = true;
  364. }
  365.  
  366. abstract void onDraw(Canvas canvas, boolean isBottom);
  367.  
  368. abstract void init();
  369.  
  370. /**
  371. * 移动的时间变化量
  372. *
  373. * @param delta
  374. */
  375. abstract void move(float delta);
  376.  
  377. /**
  378. * 设置线的宽度
  379. *
  380. * @param width
  381. */
  382. void setLineWidth(float width) {
  383. lineWidth = width;
  384. if (lineWidth > 0) {
  385. mPaint.setStyle(Paint.Style.STROKE);
  386. mPaint.setStrokeWidth(lineWidth);
  387. } else {
  388. mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
  389. }
  390. isReInit = true;
  391. }
  392.  
  393. void setColor(int color) {
  394. mPaint.setColor(color);
  395. }
  396.  
  397. /**
  398. * 每秒移动的像素数
  399. *
  400. * @param speedPeriod
  401. */
  402. void setSpeed(float speedPeriod) {
  403. this.speedPeriod = speedPeriod;
  404. isReInit = true;
  405. }
  406.  
  407. /**
  408. * 振幅大小
  409. *
  410. * @param swing
  411. */
  412. void setSwing(float swing) {
  413. if (swing <= 0) {
  414. throw new IllegalArgumentException("Illegal swing: " + swing);
  415. }
  416. mSwing = swing;
  417. isReInit = true;
  418. }
  419.  
  420. /**
  421. * 显示周期数
  422. *
  423. * @param period
  424. */
  425. void setPeriod(float period) {
  426. if (period <= 0) {
  427. throw new IllegalArgumentException("Illegal period: " + period);
  428. }
  429. this.period = period;
  430. isReInit = true;
  431. }
  432.  
  433. /**
  434. * 起始偏移量
  435. *
  436. * @param offPeriod
  437. */
  438. void offset(float offPeriod) {
  439. this.offset = offPeriod;
  440. isReInit = true;
  441. }
  442. }
  443.  
  444. private class WaveSin extends Wave {
  445.  
  446. /**
  447. * 初始偏移量
  448. */
  449. float offRadian = 0;
  450. /**
  451. * 每个像素占的弧度
  452. */
  453. double perRadian;
  454. /**
  455. * 每秒移动的弧度数
  456. */
  457. float speedRadian;
  458.  
  459. @Override
  460. public void onDraw(Canvas canvas, boolean isBottom) {
  461. float y = mHeight;
  462. mPath.reset();
  463. //计算路径点的初始位置
  464. if (lineWidth > 0) {
  465. y = (float) (mSwing * Math.sin(offRadian) + mSwing);
  466. mPath.moveTo(-lineWidth, isBottom ? mHeight - y - lineWidth / 2 : y + lineWidth / 2);
  467. } else {
  468. mPath.moveTo(0, isBottom ? 0 : mHeight);
  469. }
  470.  
  471. //步长越小越精细,当然越消耗cpu性能,过大则会有锯齿
  472. int step = mWidth / 100 > 20 ? 20 : mWidth / 100;
  473.  
  474. //通过正弦函数计算路径点,放入mPath中
  475. for (int x = 0; x <= mWidth + step; x += step) {
  476. y = (float) (mSwing * Math.sin(perRadian * x + offRadian) + mSwing);
  477. mPath.lineTo(x, isBottom ? mHeight - y - lineWidth / 2 : y + lineWidth / 2);
  478. }
  479.  
  480. //填充模式时,画完完整路径
  481. if (lineWidth <= 0) {
  482. mPath.lineTo(mWidth, isBottom ? mHeight - y : y);
  483. mPath.lineTo(mWidth, isBottom ? 0 : mHeight);
  484. mPath.lineTo(0, isBottom ? 0 : mHeight);
  485. mPath.close();
  486. }
  487.  
  488. canvas.drawPath(mPath, mPaint);
  489. }
  490.  
  491. @Override
  492. void init() {
  493. perRadian = (float) (2 * Math.PI * period / mWidth);
  494. speedRadian = (float) (speedPeriod * Math.PI * 2);
  495. offRadian = (float) (offset * 2 * Math.PI);
  496. }
  497.  
  498. @Override
  499. public void move(float delta) {
  500. offRadian += speedRadian * delta;
  501. }
  502. }
  503.  
  504. private class WaveBezier extends Wave {
  505. /**
  506. * 根据贝塞尔曲线公式计算的一个常量值
  507. */
  508. private static final double MAX_Y = 0.28867513459481287;
  509. /**
  510. * 一个周期的宽度
  511. */
  512. float periodWidth;
  513. /**
  514. * 每秒钟移动的宽度
  515. */
  516. float speedWidth;
  517. /**
  518. * 贝塞尔曲线控制点的Y轴坐标
  519. */
  520. float conY;
  521. /**
  522. * 当前偏移量
  523. */
  524. float currentOffset = 0;
  525.  
  526. @Override
  527. public void onDraw(Canvas canvas, boolean isBottom) {
  528. mPath.reset();
  529. // 移动到第一个周期的起始点
  530. mPath.moveTo(-currentOffset, 0);
  531. float conX = periodWidth / 2;
  532. int w = (int) -currentOffset;
  533. for (int i = 0; i <= mWidth + currentOffset; i += periodWidth) {
  534. mPath.rCubicTo(conX, conY, conX, -conY, periodWidth, 0);//注意,这里用的是相对坐标
  535. w += periodWidth;
  536. }
  537.  
  538. // 闭合路径
  539. if (lineWidth <= 0) {
  540. mPath.rLineTo(0, isBottom ? -mHeight : mHeight);
  541. mPath.rLineTo(-w, 0);
  542. mPath.close();
  543. }
  544.  
  545. // 对Y轴整体偏移
  546. mPath.offset(0, (isBottom ? mHeight - mSwing - lineWidth / 2 : mSwing + lineWidth / 2));
  547.  
  548. canvas.drawPath(mPath, mPaint);
  549. }
  550.  
  551. @Override
  552. void init() {
  553. periodWidth = mWidth / period;
  554. speedWidth = speedPeriod * periodWidth;
  555. currentOffset = offset * periodWidth;
  556. conY = (float) (mSwing / MAX_Y);
  557. isReInit = false;
  558. }
  559.  
  560. @Override
  561. public void move(float delta) {
  562. if (periodWidth <= 0) {
  563. isReInit = true;
  564. return;
  565. }
  566. currentOffset += speedWidth * delta;
  567. if (currentOffset < 0) {
  568. currentOffset += periodWidth;
  569. } else {
  570. if (currentOffset > periodWidth) {
  571. currentOffset -= periodWidth;
  572. }
  573. }
  574. }
  575. }
  576. }

自定义view 波浪效果的更多相关文章

  1. Android -- 自定义view实现keep欢迎页倒计时效果

    1,最近打开keep的app的时候,发现它的欢迎页面的倒计时效果还不错,所以打算自己来写写,然后就有了这篇文章. 2,还是老规矩,先看一下我们今天实现的效果 相较于我们常见的倒计时,这次实现的效果是多 ...

  2. 嵌套RecyclerView左右滑动替代自定义view

    以前的左右滑动效果采用自定义scrollview或者linearlayout来实现,recyclerview可以很好的做这个功能,一般的需求就是要么一个独立的左右滑动效果,要么在一个列表里的中间部分一 ...

  3. 自定义view实现水波纹效果

    水波纹效果: 1.标准正余弦水波纹: 2.非标准圆形液柱水波纹: 虽说都是水波纹,但两者在实现上差异是比较大的,一个通过正余弦函数模拟水波纹效果,另外一个会运用到图像的混合模式(PorterDuffX ...

  4. Android 自定义view实现水波纹效果

    http://blog.csdn.net/tianjian4592/article/details/44222565 在实际的开发中,很多时候还会遇到相对比较复杂的需求,比如产品妹纸或UI妹纸在哪看了 ...

  5. Android自定义View之圆环交替 等待效果

    学习了前面两篇的知识,对于本篇实现的效果,相信大家都不会感觉太困难,我要实现的效果是什么样呢?下面请先看效果图: 看上去是不很炫的样子,它的实现上也不是很复杂,重点在与onDraw()方法的绘制. 首 ...

  6. Android自定义View 画弧形,文字,并增加动画效果

    一个简单的Android自定义View的demo,画弧形,文字,开启一个多线程更新ui界面,在子线程更新ui是不允许的,但是View提供了方法,让我们来了解下吧. 1.封装一个抽象的View类   B ...

  7. 分析自定义view的实现过程-实现雪花飞舞效果(转载有改动)

    声明:本文源码出自实现雪花飞舞效果(有改动)主要通过这篇文来分析自定义view的实现过程. 没事时,比较喜欢上网看看一些新的东西,泡在网上的日子就是一个很不错的网站. 下面开始了,哈哈.^_^ 大家都 ...

  8. Android 自定义View修炼-自定义弹幕效果View

    一.概述 现在有个很流行的效果就是弹幕效果,满屏幕的文字从右到左飘来飘去.看的眼花缭乱,看起来还蛮cool的 现在就是来实现这一的一个效果,大部分的都是从右向左移动漂移,本文的效果中也支持从左向右的漂 ...

  9. Android 自定义View修炼-【2014年最后的分享啦】Android实现自定义刮刮卡效果View

    一.简介: 今天是2014年最后一天啦,首先在这里,我祝福大家在新的2015年都一个个的新健康,新收入,新顺利,新如意!!! 上一偏,我介绍了用Xfermode实现自定义圆角和椭圆图片view的博文& ...

随机推荐

  1. maven工程,java代码加载resources下面资源文件的路径

    1 通过类加载器加载器, 1. URL resource = TestMain.class.getResource("/18500228040.txt");File file = ...

  2. Linux 内核模块编译 Makefile

    驱动编译分为静态编译和动态编译:静态编译即为将驱动直接编译进内核,动态编译即为将驱动编译成模块. 而动态编译又分为两种: a -- 内部编译 在内核源码目录内编译 b -- 外部编译 在内核源码的目录 ...

  3. CXF整合spring

    近公司需要弄webservics,还说不用框架整合(提倡使用hessian,他们既然说与操作系统有兼容问题,由于人员单薄,不得不屈服,哎),我想了老半天没弄明白他说的不用框架整合spring,尝试过直 ...

  4. Oracle数据库查询优化方案(处理上百万级记录如何提高处理查询速度)

    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引.2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引 ...

  5. 记录几个经典的字符串hash算法

    记录几个经典的字符串hash算法,方便以后查看: 推荐一篇文章: http://www.partow.net/programming/hashfunctions/# (1)暴雪字符串hash #inc ...

  6. 什么是设计思维Design Thinking——风靡全球的创造力培养方法

    “把学习带到现实中,让孩子用自己的力量创造改变,可以直接提升他们的幸福感和竞争力.” 这是“全球孩童创意行动”的发起人——Kiran Sethi在TED演讲时说的一句话,这个行动旨在引导中小学生主动寻 ...

  7. 7-25 :active :after :before :disabled

    1:<list,<datalist>,required,<select>,<option>,title,draggable,hidden 2:data-*和命 ...

  8. Log4j2中RollingFile的文件滚动更新机制

    一.什么是RollingFile RollingFileAppender是Log4j2中的一种能够实现日志文件滚动更新(rollover)的Appender. rollover的意思是当满足一定条件( ...

  9. &&和&、||和|的区别

    && 和 || 为短路与 短路或&&若前面的表达式为false,整个逻辑表达式为false,所以后面的表达式无论true和false都无法影响整个表达式的逻辑结果,所以 ...

  10. bzoj3534 [Sdoi2014]重建

    变形的$Martix-Tree$定理 发现我们要求的是$\prod_{i \in E}{p_{i}} * \prod_{i \notin E}{(1-p_{i})}$ 然后呢? 矩阵树对重边也有效对吧 ...