一个卡片控件,输入不同天气类型,显示不同场景。
一图胜千言,先看图吧
实现效果
因为此控件是根据需求实现的,所以一共实现了6种场景
- 白天 && 晴天,显示时有个太阳升起动画;
- 白天 && 下雨,白天场景动态下雨;
- 白天 && 下雪,白天场景动态下雪;
- 夜晚 && 晴天,夜晚场景,随机时间、位置产生流星;
- 夜晚 && 下雨,夜晚场景动态下雨;
- 夜晚 && 下雪,夜晚场景动态下雪;
拆分动态场景
1.太阳升起,可拆分为X轴和Y轴的变化。
2.流星,可用Random随机生成流星位置。
3.下雨,关键在于控制雨滴的个数,保证屏幕上雨滴总数变化不大,间隔时间内生成的雨滴总数要大致相同
4.下雪,比下雨多了一个参数,就是雪花的大小,在生成雪花位置之后用随机函数生成大小即可
代码实现
1.太阳升起。核心代码
float sun= 0;//太阳位置
float sunSize=0;//太阳Bitmap的大小(边长)//可在设置数据时初始化
/**太阳升起动画*/
private void startSunAnimation(){
ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(this,"sun",0,1);
objectAnimator.setInterpolator(new DecelerateInterpolator());//减速插值器
objectAnimator.setDuration(2500);//动画执行时间
objectAnimator.start();
}
/**绘制太阳位置*/
private void drawSun(Canvas canvas) {
float half = sunSize / 2;//太阳图片的一半长度
float lastheight = measuredHeight - half;//总高度减去图片一半,即太阳中心最终所在的位置
//左边距
float left = sunX + sunPath * sun;//sunX太阳初始位置,这里是控件长度的1/3,sunPath太阳起点到终点的总长度
//顶部边距
float top = lastheight * (1-sun);
//计算绘制太阳的矩形
RectF rectF = new RectF(left, top,left+sunSize, top +sunSize);
//在计算和的矩形绘制太阳
canvas.drawBitmap(taiyangBitmap,null,rectF,paint);
}
public void setSun(float sun) {
this.sun = sun;
invalidate();
}
上面只列出核心代码,完整代码会传到GitHub
2.流星,核心代码
//启动流星
private void startMyAnimatio(){
animator = ObjectAnimator.ofInt(this,"lx",0,DensityUtils.dipTopx(getContext(),65)+55);
animator.setDuration(1500);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.setStartDelay(2000);
animator.start();
}
/**流星动画设置函数,暂定同时只能存在一个流星*/
public void setLx(int lx) {
this.lx = lx;
if(liuxingList.size()==0 || lx>measuredHeight+51){
liuxingList.clear();
if(getRandomPoint()>0.98f){//概率模拟不定时产生流星
liuxingList.add(new WeatherBean(getRandomX()));
}
}else {
invalidate();
}
}
//绘制流星
private void drawLX(Canvas canvas) {
if(lxBitmap!=null){
for(int i=0;i<liuxingList.size();i++){
WeatherBean weatherBean = liuxingList.get(i);
Rect rect = new Rect(weatherBean.x-lx-48,lx-55,weatherBean.x-lx,lx);//48是图片长度,55是图片高度,这里图省事就写死了
canvas.drawBitmap(lxBitmap,null,rect,paint);
}
}
}
3.下雨
List<WeatherBean> pointList ;
private void startMyAnimation() {
animator = ObjectAnimator.ofInt(this,"dd",0,450);
animator.setDuration(5000);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.start();
}
public void setDd(int dd) {
this.dd = dd;
addAble = (dd%5==0);
int size=0;
//每次重新设置DD值 叠加spex的值,达到50移除
//
while (size<pointList.size()){
WeatherBean weatherBean = pointList.get(size);
weatherBean.spex+=interval;
if( weatherBean.spex>=50 ){
pointList.remove(size);
}else {
size++;
}
}
invalidate();
}
private void initPoint(){
//循环,给所有spex加1
//移除大于500的
if(addAble){
//循环,给pointList补满30个
int k = 30 - pointList.size();
for(int i=0;(i<k && i<3);i++){//每次最多添加3个点
WeatherBean wb = new WeatherBean();
wb.x = (int) (getRandomPoint()*measuredWidth);
pointList.add(wb);
}
}
}
@Override
public void draw(Canvas canvas) {
initPoint();
//drawBg(canvas);
//drawPre(canvas);
drawRain(canvas);
}
/**
* 绘制下雨
* @param canvas
*/
private void drawRain(Canvas canvas) {
for(int i=0;i<pointList.size();i++){
WeatherBean weatherBean = pointList.get(i);
float startY = spe*weatherBean.spex;
float startX = weatherBean.x-startY*0.364f;
//经三角函数计算得出
canvas.drawLine(startX,startY,startX-longX,startY+longY,paintLine);
}
}
/**每一个点的实体类*/
private class WeatherBean{
int x=0;//X坐标
float spex= -longY;//下落速度
int size;//大小(雪花)
public WeatherBean() { }
public WeatherBean(int x) {
this.x = x;
}
}
4.下雪,核心代码
List<WeatherBean> pointList ;
private void startMyAnimation() {
animator = ObjectAnimator.ofInt(this,"dd",0,450);
animator.setDuration(5000);
animator.setInterpolator(new LinearInterpolator());
animator.setRepeatCount(ValueAnimator.INFINITE);
animator.start();
}
public void setDd(int dd) {
this.dd = dd;
addAble = (dd%5==0);
int size=0;
//每次重新设置DD值 叠加spex的值,达到50移除
//
while (size<pointList.size()){
WeatherBean weatherBean = pointList.get(size);
weatherBean.spex+=interval;
if( weatherBean.spex>=50 ){
pointList.remove(size);
}else {
size++;
}
}
invalidate();
}
@Override
public void draw(Canvas canvas) {
initSnowPoint();
//drawBg(canvas);
drawSnow(canvas);
}
private void initSnowPoint(){
//循环,给所有spex加1
//移除大于500的
if(addAble){
//循环,给pointList补满30个
int k = 30 - pointList.size();
for(int i=0;(i<k && i<1);i++){
WeatherBean wb = new WeatherBean();
wb.x = (int) (getRandomPoint()*measuredWidth);
wb.size = getRandomSize();
pointList.add(wb);
}
}
}
/**
* 绘制下xue
* @param canvas
*/
private void drawSnow(Canvas canvas) {
for(int i=0;i<pointList.size();i++){
WeatherBean weatherBean = pointList.get(i);
float startY = spe*weatherBean.spex;
float startX = weatherBean.x-startY*0.264f;
RectF rectF = new RectF(startX,startY,startX+weatherBean.size,startY+weatherBean.size);
canvas.drawBitmap(snowBitmap,null,rectF,paint);
}
}
/**每一个点的实体类*/
private class WeatherBean{
int x=0;//X坐标
float spex= -longY;//下落速度
int size;//大小(雪花)
public WeatherBean() { }
public WeatherBean(int x) {
this.x = x;
}
}
总结
自己写得控件要及时记录思路,不然过几个月来炒冷饭自己看的都蒙蔽,控件上传后会来更新
项目地址
https://github.com/YsInHZ/WeatherCard