明天就是五一劳动节了,在这里先祝各位程序猿劳动节快乐,别在加班了!
自从尝试过写自定义View(Android自定义View初探(一)——饼图)之后,每当看到别人的应用时,总是在想别人的实现方式,或许,这就是程序猿的悲哀吧O(∩_∩)O~。
前两天就想尝试去用自定义View实现360的垃圾清理界面了,只是最近一直在忙dicuz自定义修改,所以就先放下了。不过马上放五一了,没太多事,今天就来做一些新的尝试吧。
我这里既然写到了是“仿”360,所以大家千万别在看完后丢砖头哈,毕竟我只看到了它的表象,谁知道它是个神马呢?
其实当时看完它的界面后觉得应该就是简单的背景色切换随机元素的绘制:
1.刚进界面的时候是一种颜色,比如深绿,点击“开始扫描”(360可木有,他是自动扫描);
2.当垃圾的数量达到一定值后,比如100MB,背景色变色,比如蓝色,;当垃圾值达到200MB的时候再变色,比如红色;
3.垃圾值的增量(就是那些飞来飞去的小垃圾块)的位置是随机的,只不过搜集垃圾时,坐标是从四面往中间移动;释放垃圾时,坐标是从中间往四面移动(效果可自己随意定);
4.释放垃圾时,按钮变成了进度条(这个可以实现,但我没做^_^)。
5.还有些细节有待发现。。。
说了这么多,大家也不知道我在说什么^_^,我就不唠叨了,代码有注释。
照例先上效果图:
释放状态和搜集状态的颜色变化是相同的,不同的是小垃圾的移动方式,这里就不贴图片了,太多了。
下面就贴大家最喜欢大代码了^_^:
/**
* @author MR.yan
*
*/
public class ShaderView extends View
{
/**
* 绘画初始状态
*/
private static final int STATE_DRAW_INIT = 1;
/**
* 绘画垃圾搜集中状态
*/
private static final int STATE_DRAW_COLLECTING_GABAGE = STATE_DRAW_INIT + 1;
/**
* 绘画垃圾搜集完成状态
*/
private static final int STATE_DRAW_COLLECTED_GABAGE = STATE_DRAW_INIT + 2;
/**
* 绘画垃圾搜释放中状态
*/
private static final int STATE_DRAW_RELEASE_GABAGE = STATE_DRAW_INIT + 3;
/**
* 默认水平间距(dp)
*/
private static final float DEFAULT_WIDTH_PADDING = 15;
/**
* 默认垂直间距(dp)
*/
private static final float DEFAULT_HEIGHT_PADDING = 5;
/**
* 画笔
*/
private Paint paint;
/**
* 屏幕密度
*/
private float density;
/**
* 屏幕宽度px
*/
private float width;
/**
* 屏幕高度px
*/
private float height;
/**
* 当前绘画状态
*/
private int mState = STATE_DRAW_INIT;
/**
* 圆角矩形
*/
private RectF rectF;
/**
* 矩形
*/
private Rect rect;
/**
* 随机数器
*/
private Random random = new Random();
/**
* 已搜集的垃圾总数
*/
private float gabageSize = 0.0f;
/**
* 垃圾逻辑处理的线程开关
*/
private boolean isRun;
/**
* 小垃圾的x坐标(px)
*/
private float littleGabageX;
/**
* 小垃圾的y坐标(px)
*/
private float littleGabageY;
/**
* 每次随机产生的小垃圾数量
*/
private float tempgabage;
/**
* 已清理的总的垃圾数量
*/
private float allGabages;
/**
* 小数格式化工具
*/
private DecimalFormat decimalFormat;
/**
* 初始化标识
*/
private boolean isInit;
/**
* 背景色
*/
private int bgColor = Color.parseColor("#29A600");
public ShaderView(Context context)
{
this(context, null);
}
public ShaderView(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public ShaderView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
init(context);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b)
{
super.onLayout(changed, l, t, r, b);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
/**
* 初始化
*
* @param c 上下文
*/
private void init(Context c)
{
density = c.getResources().getDisplayMetrics().density;
paint = new Paint();
rectF = new RectF();
rect = new Rect();
decimalFormat = new DecimalFormat();
decimalFormat.setMaximumFractionDigits(2);
}
/**
* 画背景色
*
* @param canvas
*/
private void drawBackground(Canvas canvas)
{
if (!isInit)
{
isInit = !isInit;
width = getWidth();
height = getHeight();
}
paint.setColor(bgColor);
canvas.drawRect(0, 0, width, height, paint);
}
/**
* 画位置不变的部分
*
* @param canvas 画布
* @param gabages 搜集的垃圾数量
* @param title 垃圾数量上面的标题
* @param endDescription 垃圾数量右下角的描述
* @param uinTg 垃圾的单位(MB、GB)
* @param btnDescription 按钮文本
*/
private void drawStationaryChildren(Canvas canvas, String gabages, String title, String endDescription, String uinTg, String btnDescription)
{
resetPaint();
paint.setColor(Color.WHITE);
paint.setTextSize(15 * density);
canvas.drawText(title, width / 5, height / 3, paint);
paint.setTextSize(75 * density);
paint.setTypeface(Typeface.create("System", Typeface.BOLD));
paint.getTextBounds(gabages, 0, gabages.length(), rect);
canvas.drawText(gabages, width / 4, height / 3 + 2 * rect.height(), paint);
paint.setTextSize(18 * density);
paint.setTypeface(Typeface.create("System", Typeface.NORMAL));
canvas.drawText(uinTg, width / 4 + rect.width() + DEFAULT_WIDTH_PADDING / 5 * density, height / 3 + rect.height(), paint);
canvas.drawText(endDescription, width / 4 + rect.width() + DEFAULT_WIDTH_PADDING / 5 * density, height / 3 + 2 * rect.height()
+ DEFAULT_HEIGHT_PADDING * density, paint);
paint.setColor(Color.parseColor("#50B62E"));
rectF.set(width / 4, height - DEFAULT_HEIGHT_PADDING * 24 * density, width - width / 4, height - DEFAULT_HEIGHT_PADDING * 15 * density);
canvas.drawRoundRect(rectF, 55f, 55f, paint);
//想画进度条的可以在这里再画一个矩形覆盖在原来矩形上面,新矩形的宽度是已清理的垃圾数量
paint.setColor(Color.WHITE);
paint.setTextSize(25 * density);
paint.getTextBounds(btnDescription, 0, btnDescription.length(), rect);
canvas.drawText(btnDescription, width / 2 - rect.width() / 2, height - DEFAULT_HEIGHT_PADDING * 15 * density - rect.height() / 2, paint);
}
/**
* 画不固定的小垃圾
*
* @param canvas 画布
*
*/
private void drawNotFixChildren(Canvas canvas)
{
if (mState != STATE_DRAW_COLLECTING_GABAGE && mState != STATE_DRAW_RELEASE_GABAGE)
return;
resetPaint();
String tempGabageStr = tempgabage + "MB";
Rect ry = new Rect();
paint.setColor(bgColor);
paint.setTextSize(16 * density);
paint.getTextBounds(tempGabageStr, 0, tempGabageStr.length(), ry);
RectF rx = new RectF(littleGabageX, littleGabageY - 2 * ry.height(), littleGabageX + ry.width(), littleGabageY);//不建议在这里new矩形,如果放在onDraw方法里,会有警告
canvas.drawRoundRect(rx, 2, 2, paint);
paint.setColor(Color.WHITE);
canvas.drawText(tempGabageStr, littleGabageX, littleGabageY, paint);
}
/**
* 画笔重置
*/
private void resetPaint()
{
paint.reset();
paint.setAntiAlias(true);
}
/**
* 绘画的逻辑处理器
*/
private Runnable drawRunnable = new Runnable()
{
public void run()
{
// 线程暂停或当搜集垃圾时搜集的数量大于500或当释放垃圾时剩余的垃圾数量小于0
if (!isRun || (mState == STATE_DRAW_COLLECTING_GABAGE && gabageSize > 500) || (mState == STATE_DRAW_RELEASE_GABAGE && gabageSize < 0))
{
switch (mState)
{
case STATE_DRAW_COLLECTING_GABAGE:
mState = STATE_DRAW_COLLECTED_GABAGE;
allGabages += gabageSize;
break;
case STATE_DRAW_RELEASE_GABAGE:
mState = STATE_DRAW_INIT;
break;
default:
break;
}
postInvalidate();
return;
}
switch (mState)
{
case STATE_DRAW_COLLECTING_GABAGE:
dealCollectGabage();
break;
case STATE_DRAW_RELEASE_GABAGE:
dealRelaseGabage();
break;
default:
break;
}
// 改变背景色的判断
if (gabageSize <= 100)
bgColor = Color.GREEN;
else if (gabageSize <= 300)
bgColor = Color.MAGENTA;
else bgColor = Color.RED;
postInvalidate();
postDelayed(this, 5);
}
};
/**
* 处理垃圾搜集
*/
private void dealCollectGabage()
{
if (littleGabageX >= (width / 2 - 50) && littleGabageX <= (width / 2 + 50) && littleGabageY >= (height / 2 - 50)
&& littleGabageY <= (height / 2 + 50))
{
resetCollectDatas();
}
else
{
if (littleGabageX > width / 2)
littleGabageX -= 43.000024;
else if (littleGabageX < width / 2)
littleGabageX += 33.000024;
if (littleGabageY > height / 2)
littleGabageY -= 33.000024;
else if (littleGabageY < height / 2)
littleGabageY += 33.000024;
}
}
/**
* 处理垃圾释放
*/
private void dealRelaseGabage()
{
if ((littleGabageX <= 50 || littleGabageX >= width - 50) && (littleGabageY <= 50 || littleGabageY >= height - 50))
{
resetReleaseDatas();
}
else
{
if (littleGabageX > width / 2)
littleGabageX += 63.000024;
else if (littleGabageX < width / 2)
littleGabageX -= 63.000024;
if (littleGabageY > height / 2)
littleGabageY += 63.000024;
else if (littleGabageY < height / 2)
littleGabageY -= 63.000024;
}
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
float dX = event.getX();
float dY = event.getY();
// 比对当前按下的坐标和上面画的按钮(圆角矩形)的坐标范围,在按钮坐标范围内则触发
if ((dX >= width / 4 && dX <= width - width / 4)
&& (dY <= height - DEFAULT_HEIGHT_PADDING * 15 * density && dY >= height - DEFAULT_HEIGHT_PADDING * 24 * density))
{
switch (mState)
{
case STATE_DRAW_INIT:
mState = STATE_DRAW_COLLECTING_GABAGE;
startCollectGabage();
break;
case STATE_DRAW_COLLECTED_GABAGE:
mState = STATE_DRAW_RELEASE_GABAGE;
startReleaseGabage();
break;
default:
break;
}
}
break;
default:
break;
}
return super.onTouchEvent(event);
}
/**
* 开始处理垃圾搜集
*/
private void startCollectGabage()
{
gabageSize = 0;
resetCollectDatas();
postDelayed(drawRunnable, 10);
if (!isRun)
isRun = true;
}
/**
* 开始处理垃圾释放
*/
private void startReleaseGabage()
{
resetReleaseDatas();
postDelayed(drawRunnable, 10);
if (!isRun)
isRun = true;
}
/**
* 重置释放垃圾时小垃圾坐标和垃圾值等
*/
private void resetReleaseDatas()
{
int c = random.nextInt(4);
switch (c)
{
case 0:
littleGabageX = width / 2 + 5;
littleGabageY = height / 2 + 5;
break;
case 1:
littleGabageX = width / 2 - 5;
littleGabageY = height / 2 - 5;
break;
case 2:
littleGabageX = width / 2 + 5;
littleGabageY = height / 2 - 5;
break;
case 3:
littleGabageX = width / 2 - 5;
littleGabageY = height / 2 + 5;
break;
default:
break;
}
tempgabage = -5 - (float) random.nextInt(15);
gabageSize += tempgabage;
}
/**
* 重置搜集垃圾时小垃圾坐标和垃圾值等
*/
private void resetCollectDatas()
{
littleGabageX = 100 + (float) random.nextInt((int) width - 200);
littleGabageY = 100 + (float) random.nextInt((int) height - 200);
tempgabage = 5 + (float) random.nextInt(15);
gabageSize += tempgabage;
}
@Override
protected void onDraw(Canvas canvas)
{
resetPaint();
String gabage = "", title = "", endTag = "", btnDescription = "";
float gabg = 0;
switch (mState)
{
case STATE_DRAW_INIT:
bgColor = Color.parseColor("#29A600");
gabg = allGabages;
title = "累计清理 : ";
endTag = "垃圾文件";
btnDescription = "开始扫描";
break;
case STATE_DRAW_COLLECTING_GABAGE:
gabg = gabageSize;
title = "";
btnDescription = "正在扫描...";
endTag = "建议清理";
break;
case STATE_DRAW_COLLECTED_GABAGE:
gabg = gabageSize;
title = "";
endTag = "建议清理";
btnDescription = "一键清理";
break;
case STATE_DRAW_RELEASE_GABAGE:
gabg = gabageSize;
title = "";
endTag = "建议清理";
btnDescription = "正在清理...";
break;
}
gabage = gabg >= 1000 ? decimalFormat.format(gabg / 1024) : String.valueOf(gabg);
drawBackground(canvas);
drawNotFixChildren(canvas);
drawStationaryChildren(canvas, gabage, title, endTag, gabg >= 1000 ? "GB" : "MB", btnDescription);
}
}
整个视图的大致思路、效果和代码就是这样子了,我依然还是处于尝试的过程中,肯定有很多地方有不足之处,希望大家交流讨论,共同进步!