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

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

新建工程,并定义一个自定义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. CentOS 7启动与切换图形界面

    安装图形界面 默认情况下是不会安装图形界面的,所以需要自己手动安装,步骤如下: 1.开启系统,以root身份进入 安装X(X Window System),命令如下: yum groupinstall ...

  2. 【CUDA开发】论CUDA和LAV解码器是否真的实用

    先说配置,我电脑E3V3+GTX780TI视频就一个普通的720P AVC1编码MP4视频,实时检测软件是CPU-Z和GPU-Z,AIDA64[全默认设置]全部用ptoplayer默认播放时候,播放3 ...

  3. 【计算机视觉】纹理特征之LBP局部二值化模式

    转自http://blog.csdn.NET/ty101/article/details/8905394 本文的PDF版本,以及涉及到的所有文献和代码可以到下列地址下载: 1.PDF版本以及文献:ht ...

  4. VS2010 安装boost库

    1.下载boost库 boost官网:www.boost.org,目前最新的版本是1.64,直接下载地址:https://dl.bintray.com/boostorg/release/1.64.0/ ...

  5. yii2中通过migration创建数据表

    ### yii2中通过migration创建数据表 准备工作: 1.首先保证php写入了环境变量 2.在项目内创建migrations目录(base版的需要手动创建) 3.配置文件中正确配置了数据库信 ...

  6. docker 实践二:操作镜像

    本篇我们来详细介绍 docker 镜像的操作. 注:环境为 CentOS7,docker 19.03 之前已经说过,容器是 docker 的核心概念之一,所以对应的就需要知道它的使用方法,接下来我们就 ...

  7. 2019杭电多校二 F. Fantastic Magic Cube (FWT)

    大意: 给定$N^3$立方体, 每个单位立方体权值为三个坐标异或, 每次沿坐标轴切一刀, 得分为两半内权值和的乘积, 求切成$n^3$块的最大得分. 可以发现得分与切法无关, 假设每个点权值为$a_i ...

  8. C#利用反射和泛型给不同对象赋值

    /// <summary> /// 适用于初始化新实体 /// </summary> static public T RotationMapping<T, S>(S ...

  9. 怎样解决Chrome浏览器因为禁止音频自动播放所造成的视频无法自动播放且报错: Uncaught (in promise) DOMException的问题

    这个问题是谷歌基于用户体验方面的考虑, 对页面加载时自动播放的音频作了限制, 试想一下, 如果你打开某个页面就立刻自动播放某种不可描述的声音, 那体验想必是十分酸爽. 尽管这个设定是针对音频的, 但实 ...

  10. (五)Redis之List

    一.List常用命令 两端添加 两端弹出 1.2. 两端添加和两端弹出 package myRedis01; import java.util.HashMap; import java.util.Li ...