您的位置:首页 > 其它

自定义View-----汽泡效果

2015-09-09 07:55 357 查看
先来看一下这次要实现的最终效果:





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

新建工程,并定义一个自定义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效果。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: