先来看一下这次要实现的最终效果:

首先来实现效果一,为实现效果二做充足的准备,下面开始:

新建工程,并定义一个自定义View,然后将其定义在布局文件中,里面是空实现,之后会一步步来填充代码:

MyRing.java:

public class MyRing extends View {

    public MyRing(Context context, AttributeSet attrs) {
super(context, attrs);
} }

activity_main.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" > <com.example.waveview.MyRing
android:layout_width="match_parent"
android:layout_height="match_parent"
/> </RelativeLayout>

接下来一步步来实现第一张效果图所示的效果,先重写父类的某些方法:

public class MyRing extends View {

    public MyRing(Context context, AttributeSet attrs) {
super(context, attrs);
} /**
* 大小的测量按系统的默认规则
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} /**
* 绘制我们的内容
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
}

在点击时需要画一个圆,这是第一步,所以需要监听触摸事件:

public class MyRing extends View {

    /**
* 圆环圆心的X坐标
*/
private int cx; /**
* 圆环圆心的Y坐标
*/
private int cy;
private Paint paint;
/**
* 线条的厚度
*/
private float strokeWidth; public MyRing(Context context, AttributeSet attrs) {
super(context, attrs);
// 初始化paint
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStyle(Style.STROKE); // 刻画,画线条
paint.setStrokeWidth(strokeWidth); // 设置条线的厚度
} /**
* 大小的测量按系统的默认规则
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} /**
* 绘制我们的内容
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/**
* 绘制圆环,为了看到效果先将半径写死
*/
canvas.drawCircle(cx, cy, 100, paint);
} @Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: // 点击,获得圆环的中心
cx = (int) event.getX();
cy = (int) event.getY();
invalidate();
break;
}
return true;
}
}

效果如下:

默认圆画在(0,0)的位置,有了第一步之后,接下来则要让这个圆进行动态渐变,半径、圆边线的粗度都得动态去改变,具体代码如下:

public class MyRing extends View {

    /**
* 圆环圆心的X坐标
*/
private int cx; /**
* 圆环圆心的Y坐标
*/
private int cy;
private Paint paint;
/**
* 线条的厚度
*/
private float strokeWidth;
/**
* 圆环的半径
*/
private float radius;
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
flushState();
// 刷新页面 执行onDraw()方法
invalidate();
if (paint.getAlpha() != 0) {
handler.sendEmptyMessageDelayed(0, 100);
}
};
}; public MyRing(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
} private void initView() {
// 初始化paint
paint = new Paint();
paint.setAntiAlias(true); // 抗矩齿
paint.setColor(Color.RED);
paint.setStyle(Style.STROKE); // 刻画,画线条
paint.setStrokeWidth(strokeWidth); // 设置条线的厚度
paint.setAlpha(255); // 设置透明度 ,0--255 0代表完全透明 //
this.radius = 0;
strokeWidth = 0;
} /**
* 大小的测量按系统的默认规则
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
} /**
* 绘制我们的内容
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/**
* 绘制圆环
*/
canvas.drawCircle(cx, cy, radius, paint);
} @Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: // 点击,获得圆环的中心
cx = (int) event.getX();
cy = (int) event.getY();
// 初始化画笔
initView();
handler.sendEmptyMessage(0);
break;
}
return true;
} /*
* 刷新状态
*/
private void flushState() {
this.radius += 10;
this.strokeWidth = radius / 4;
paint.setStrokeWidth(strokeWidth); int nextAlpha = paint.getAlpha() - 20;
if (nextAlpha <= 20) {
nextAlpha = 0;
}
paint.setAlpha(nextAlpha); }
}

这时就是图一的效果了,有了这个基础,要实现图二的效果也就不难了,下面来试一下:

由于是多个圆,所以肯定是需要一个集合来存储,另外需要用一个实体来表示一个圆的多个属性,如下:

    /**
* 定义一个波浪
* @author leo
*/
private class Wave {
//圆心
int cx;
int cy; //画笔
Paint p;
//半径
int r;
}

然后在按下或滑动时,则把相应的点给添加到集合中,并且需要点与点之间有一定的距离才行,挨得太近则不添加,具体逻辑如下:

    @Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event); switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE: int x = (int) event.getX();
int y = (int) event.getY(); addPoint(x,y); break; default:
break;
} return true; }

而添加的方法如下:

/**
* 添加新的波浪中心点
* @param x
* @param y
*/
private void addPoint(int x, int y) {
if(wList.size() == 0){
addPoint2List(x,y);
/*
* 第一次启动动画
*/
isRunning = true;
handler.sendEmptyMessage(0);
}else{
Wave w = wList.get(wList.size()-1); if(Math.abs(w.cx - x)>DIS_SOLP || Math.abs(w.cy-y)>DIS_SOLP){
addPoint2List(x,y);
} }; } /**
* 添加新的波浪
* @param x
* @param y
*/
private void addPoint2List(int x, int y) {
Wave w = new Wave();
w.cx = x;
w.cy=y;
Paint pa=new Paint();
pa.setColor(colors[(int)(Math.random()*4)]);
pa.setAntiAlias(true);
pa.setStyle(Style.STROKE); w.p = pa; wList.add(w);
}

其中当一第一次添加时,则会开启handler去跑圆环动画,其它的则相继往集合中添加,而handler的写法跟一个圆环的写法差不多,这里就不多解释,直接把整个代码贴上来,都比较好理解:

/**
* 水波纹效果
*
*/
public class MyRingWave extends View{ /**
* 二个相临波浪中心点的最小距离
*/
private static final int DIS_SOLP = 13; protected boolean isRunning = false; private ArrayList<Wave> wList; public MyRingWave(Context context, AttributeSet attrs) {
super(context, attrs);
wList = new ArrayList<MyRingWave.Wave>();
} private Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) { //刷新数据
flushData();
//刷新页面
invalidate();
//循环动画
if (isRunning) {
handler.sendEmptyMessageDelayed(0, 50);
} };
}; @Override
protected void onDraw(Canvas canvas) {
for (int i = 0; i < wList.size(); i++) {
Wave wave = wList.get(i);
canvas.drawCircle(wave.cx, wave.cy, wave.r, wave.p);
}
} @Override
public boolean onTouchEvent(MotionEvent event) {
super.onTouchEvent(event); switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE: int x = (int) event.getX();
int y = (int) event.getY(); addPoint(x,y); break; default:
break;
} return true; } /**
* 添加新的波浪中心点
* @param x
* @param y
*/
private void addPoint(int x, int y) {
if(wList.size() == 0){
addPoint2List(x,y);
/*
* 第一次启动动画
*/
isRunning = true;
handler.sendEmptyMessage(0);
}else{
Wave w = wList.get(wList.size()-1); if(Math.abs(w.cx - x)>DIS_SOLP || Math.abs(w.cy-y)>DIS_SOLP){
addPoint2List(x,y);
} }; } /**
* 添加新的波浪
* @param x
* @param y
*/
private void addPoint2List(int x, int y) {
Wave w = new Wave();
w.cx = x;
w.cy=y;
Paint pa=new Paint();
pa.setColor(colors[(int)(Math.random()*4)]);
pa.setAntiAlias(true);
pa.setStyle(Style.STROKE); w.p = pa; wList.add(w);
} private int [] colors = new int[]{Color.BLUE,Color.RED,Color.YELLOW,Color.GREEN};
/**
* 刷新数据
*/
private void flushData() { for (int i = 0; i < wList.size(); i++) { Wave w = wList.get(i); //如果透明度为 0 从集合中删除
int alpha = w.p.getAlpha();
if(alpha == 0){
wList.remove(i); //删除i 以后,i的值应该再减1 否则会漏掉一个对象,不过,在此处影响不大,效果上看不出来。
continue;
} alpha-=5;
if(alpha<5){
alpha =0;
}
//降低透明度
w.p.setAlpha(alpha); //扩大半径
w.r = w.r+3;
//设置半径厚度
w.p.setStrokeWidth(w.r/3);
} /*
* 如果集合被清空,就停止刷新动画
*/
if(wList.size() == 0){
isRunning = false;
}
} /**
* 定义一个波浪
* @author leo
*/
private class Wave {
//圆心
int cx;
int cy; //画笔
Paint p;
//半径
int r;
}
}

对于这个自定义效果倒不是很复杂,但效果还是挺炫滴,以后坚持收集学习好的UI效果。

自定义View-----汽泡效果的更多相关文章

  1. android自定义view实现公章效果

    上次去一个公司面试,面试官问了一个题,怎么用android的自定义view实现一个公章的效果,据说这是华为之前的面试题,我想了下,要是公章的效果,最外层是一个圆,里面是一个五角星,但是这文字怎么画呢, ...

  2. 自定义View实现钟摆效果进度条PendulumView

    转载请注明出处:http://blog.csdn.net/fightlei/article/details/52556755 在网上看到了一个IOS组件PendulumView,实现了钟摆的动画效果. ...

  3. Android 自定义View跑马灯效果(一)

    今天通过书籍重新复习了一遍自定义VIew,为了加强自己的学习,我把它写在博客里面,有兴趣的可以看一下,相互学习共同进步: 通过自定义一个跑马灯效果,来诠释一下简单的效果: 一.创建一个类继承View, ...

  4. android 自定义view之侧滑效果

    效果图: 看网上的都是两个view拼接,默认右侧的不显示,水平移动的时候把右侧的view显示出来.但是看最新版QQ上的效果不是这样的,但给人的感觉却很好,所以献丑来一发比较高仿的. 知识点: 1.Vi ...

  5. HTML5 汽泡效果

    又到了晚上了,精神只有在晚上的时候才能爆发,可能程序员的命吧.废话就不多说了,今天我为大家带来的是一个气泡的效果.  代码下载 下面请看效果图

  6. 在RecyclerView中集成QQ汽泡二

    上次已经将GooView集成到RecyclerView当中了[http://www.cnblogs.com/webor2006/p/7787511.html],但是目前还有很多问题,下面先来运行看一下 ...

  7. 在RecyclerView中集成QQ汽泡一

    上次已经实现了QQ汽泡的自定义View的效果[http://www.cnblogs.com/webor2006/p/7726174.html],接着再将它应用到列表当中,这样才算得上跟QQ的效果匹配, ...

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

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

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

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

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

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

随机推荐

  1. 到处抄来的SUCTF2019 web wp

    0x01 EasySQL 这是一个考察堆叠注入的题目,但是这道题因为作者的过滤不够完全所以存在非预期解 非预期解 直接构造 *,1 这样构造,最后拼接的查询语句就变成了 select *,1||fla ...

  2. Dapper连接MySql数据库

    首先通过NuGet在项目中分别安装Dapper和MySql.Data的引用 PM> Install-Package Dapper -Version PM> Install-Package ...

  3. [转帖]Oracle数据库lob大对象数据类型字段总结,值得收藏

    Oracle数据库lob大对象数据类型字段总结,值得收藏 原创 波波说运维 2019-07-11 00:02:00 https://www.toutiao.com/i67108943269703357 ...

  4. CentOs7环境下手动配置JDK7

    下载: JDK7下载地址:http://www.oracle.com/technetwork/java/javase/downloads/java-archive-downloads-javase7- ...

  5. @ConfigurationProperties和@Value的区别

    @ConfigurationProperties @Value 功能: 批量注入配置文件中的属性 一个个指定,多个属性多个@Value 松散绑定: 支持 不支持 SpEL: 不支持    支持 JSR ...

  6. Python 【文件的读写】

    文件读写 A 读取文件 读文件三步:开——读——关.file1 = open('/Users/Ted/Desktop/test/abc.txt','r',encoding='utf-8')第一个参数是 ...

  7. Python笔记003-字符串(1)

    1. 字符串基本特点 很多人初学编程时,总是担心自己数学不行,潜意识里认为数学好才能更好编程.但实际上,大多数程序员打交道最多的是 “ 字符串 ” 而不是 “ 数字 ”.因为,编程时用来解决现实问题的 ...

  8. delete删除数据造成归档日志增加,操作系统空间不足导致数据库hang住

    业务需求,对日志表历史数据进行清理.历史表均很大,使用delete 操作删除90天前的数据. 第一部分:快速删除数据 SQL> alter table CC.F_LOG parallel ; S ...

  9. hdu 2647 还是逆向拓扑

    Problem Description Dandelion's uncle is a boss of a factory. As the spring festival is coming , he ...

  10. ORACLE:锁被未决分布式事务处理 18.27.160617 持有

    1. 以管理员账号登陆 2. rollback  force '18.27.160617 ';