这次的自定义view的要求效果为:能随机生成气泡 气泡会匀速上浮 保持屏幕始终存在n个气泡;
(一)随机生成气泡
首先要绘制一个气泡需要以下几个参数: float x, y; 气泡坐标 int radius 气泡半径 int color 气泡颜色 int speed 气泡移动速度 为了方便绘制这里建议把这几个参数分装起来成为一个气泡对象。
//定义buble 对象
public class Bubble{
//颜色
public int color;
//位置
public float point_y;
public float point_x;
//移动速度
public float speed;
//半径
public int radius;
public Bubble(){}
}
现在要做到随机生成气泡的color,x轴位置 ,移动速度,半径。y轴位置固定为view的最底部。
这里写一个Bubble_create(int width)方法 传入当前view的宽度。
private Bubble Bubble_create(int witdh){
Bubble bubble = new Bubble();
Random rand = new Random();
switch (rand.nextInt(10)){
case 0:
bubble.color = Color.BLUE;
break;
case 1:
bubble.color = Color.BLACK;
break;
case 2:
bubble.color = Color.CYAN;
break;
case 3:
bubble.color = Color.DKGRAY;
break;
case 4:
bubble.color = Color.GRAY;
break;
case 5:
bubble.color = Color.GREEN;
break;
case 6:
bubble.color = Color.YELLOW;
break;
case 7:
bubble.color = Color.LTGRAY;
break;
case 8:
bubble.color = Color.MAGENTA;
break;
case 9:
bubble.color = Color.RED;
break;
default:
bubble.color = Color.BLUE;
break;
}
bubble.radius = rand.nextInt(70)+10;
bubble.point_x = rand.nextInt(witdh);
bubble.point_y = getHeight();
bubble.speed = rand.nextInt(10)+1;
return bubble;
}
//调用方法
Bubble mbuble = Bubble_create(getWidth());
(二)气泡匀速上升
重点!重点!这里开始自定义surfaceView. 先搭自定义surfaceView的框架
第一步: 继承suerfaceview 实现它的几个构造函数。 第二步: implements SurfaceHolder.Callback与 Runnable的接口
//surfaceview 框架
public class BubbleView extends SurfaceView implements SurfaceHolder.Callback,Runnable {
//surfaceHolders 这个很重要用来锁定canvas和解锁canvas
private SurfaceHolder mSurfaceHolder;
//程序运行标志 用来控制是否继续绘制view
private boolean running;
// 画布
private Canvas mCanvas;
public BubbleView(Context context) {
super(context);
init();
}
public BubbleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public BubbleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
//将初始化操作放到这里
protected void init(){
mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
this.setKeepScreenOn(true);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
running = true;
new Thread(this).start();
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
// running = false;
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
running = false;
}
@Override
public void run() {
//运行线程 循环绘制
while(running){
draw();
}
try {
//等60毫秒绘制一次
Thread.sleep(60);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//这里执行绘制方法
public void draw(){
try {
mCanvas = mSurfaceHolder.lockCanvas();
//这里很重要 每次绘制的时候绘制一个白色背景来作为刷新界面的方式
//因为surfaceview 不会清除上次绘制的图形
mCanvas.drawColor(Color.WHITE);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mCanvas != null){
mSurfaceHolder.unlockCanvasAndPost(mCanvas);
}
}
}
}
第三步:实现气泡上升。
//定义
private List<Bubble> bubbles;
private Paint bubbles_paint;
//初始化
bubbles = new ArrayList<>();
bubbles_paint = new Paint();
bubbles_paint.setStyle(Paint.Style.FILL);
//下面一部分要放到绘制里面
//这里是遍历气泡list里的所有气泡并且绘制出来
for (Bubble temp_bubble:bubbles) {
//这里一段是实现气泡的颜色渐变
RadialGradient radialGradient =new RadialGradient(temp_bubble.point_x,temp_bubble.point_y,temp_bubble.radius, new int[]{Color.WHITE, 0xFFf1f2f2, temp_bubble.color },null, Shader.TileMode.CLAMP);
bubbles_paint.setShader(radialGradient);
//这里绘制气泡圆形
mCanvas.drawCircle(temp_bubble.point_x,temp_bubble.point_y,temp_bubble.radius,bubbles_paint);
//绘制完后把气泡的y轴坐标往上挪speed的距离
temp_bubble.point_y -= temp_bubble.speed;
}
(三)保持屏幕始终存在n个气泡:
1.在每次绘制的时候判断气泡list的数量是否少于n个少于n个的话就创建。 2.当气泡飞出屏幕外的时候要把它从列表中移除掉。
//1.在每次绘制的时候判断气泡list的数量是否少于n个少于n个的话就创建。
while(bubbles.size()<=10){
Log.d(this.toString(),bubbles.size()+"");
bubbles.add(Bubble_create(getWidth()));
}
····
这里绘制气泡
····
// 2.当气泡飞出屏幕外的时候要把它从列表中移除掉。
//这里是创建一个temp气泡列表 把还在屏幕里的气泡塞到这个列表中,最后替换掉之前的气泡列表。
List<Bubble> temp_bubbles = new ArrayList<>();
for(Bubble temp_bubble:bubbles){
if((int)temp_bubble.point_y+temp_bubble.radius>=0){
temp_bubbles.add(temp_bubble);
}
}
bubbles = temp_bubbles;
所有需求都实现后把它们整合到自定义view里面 最后的源码:
public class BubbleView extends SurfaceView implements SurfaceHolder.Callback,Runnable {
private SurfaceHolder mSurfaceHolder;
private boolean running;
// 画布
private Canvas mCanvas;
//气泡列队
private List<Bubble> bubbles;
private Paint bubbles_paint;
public BubbleView(Context context) {
super(context);
init();
}
public BubbleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public BubbleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
protected void init(){
mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
this.setKeepScreenOn(true);
bubbles = new ArrayList<>();
bubbles_paint = new Paint();
bubbles_paint.setStyle(Paint.Style.FILL);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder) {
running = true;
new Thread(this).start();
}
@Override
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
// running = false;
}
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
running = false;
}
@Override
public void run() {
while(running){
draw();
}
try {
Thread.sleep(60);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void draw(){
try {
mCanvas = mSurfaceHolder.lockCanvas();
mCanvas.drawColor(Color.WHITE);
while(bubbles.size()<=10){
Log.d(this.toString(),bubbles.size()+"");
bubbles.add(Bubble_create(getWidth()));
}
for (Bubble temp_bubble:bubbles) {
RadialGradient radialGradient =new RadialGradient(temp_bubble.point_x,temp_bubble.point_y,temp_bubble.radius, new int[]{Color.WHITE, 0xFFf1f2f2, temp_bubble.color },null, Shader.TileMode.CLAMP);
bubbles_paint.setShader(radialGradient);
mCanvas.drawCircle(temp_bubble.point_x,temp_bubble.point_y,temp_bubble.radius,bubbles_paint);
temp_bubble.point_y -= temp_bubble.speed;
}
List<Bubble> temp_bubbles = new ArrayList<>();
for(Bubble temp_bubble:bubbles){
if((int)temp_bubble.point_y+temp_bubble.radius>=0){
temp_bubbles.add(temp_bubble);
}
}
bubbles = temp_bubbles;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mCanvas != null){
mSurfaceHolder.unlockCanvasAndPost(mCanvas);
}
}
}
//定义buble 对象
public class Bubble{
//颜色
public int color;
//位置
public float point_y;
public float point_x;
//移动速度
public float speed;
//半径
public int radius;
public Bubble(){
}
}
private Bubble Bubble_create(int witdh){
Bubble bubble = new Bubble();
Random rand = new Random();
switch (rand.nextInt(10)){
case 0:
bubble.color = Color.BLUE;
break;
case 1:
bubble.color = Color.BLACK;
break;
case 2:
bubble.color = Color.CYAN;
break;
case 3:
bubble.color = Color.DKGRAY;
break;
case 4:
bubble.color = Color.GRAY;
break;
case 5:
bubble.color = Color.GREEN;
break;
case 6:
bubble.color = Color.YELLOW;
break;
case 7:
bubble.color = Color.LTGRAY;
break;
case 8:
bubble.color = Color.MAGENTA;
break;
case 9:
bubble.color = Color.RED;
break;
default:
bubble.color = Color.BLUE;
break;
}
bubble.radius = rand.nextInt(70)+10;
bubble.point_x = rand.nextInt(witdh);
bubble.point_y = getHeight();
bubble.speed = rand.nextInt(10)+1;
return bubble;
}
}
在layout布局中放入这个view就可以了。