声明:本文源码出自实现雪花飞舞效果(有改动)主要通过这篇文来分析自定义view的实现过程。

没事时,比较喜欢上网看看一些新的东西,泡在网上的日子就是一个很不错的网站。

下面开始了,哈哈。^_^

  大家都知道,自定义view分成三个类型,1、是完全自定义,自己绘制,例如本文讲的例子。2、是Groupview,就是把一些安卓原生提供的控件组合起来,做成一个有多种功能的组合控件,如前面写过的android-oldman之TitleBar.就是这种。3、就是继承自安卓原来的控件,然后增加修改成自己需要的功能。如extends TextVeiw后,再里面做一些更改,text的位置,长度什么的。相比2,与3,第1种自定义view就是要继承自view,然后根据需要,测量,绘制。

  最终要的效果是:看到没有,上面一些彩色的点,就是原来的雪花,当然,你可以通过修改参数,改变雪花数量。

下面就开始贴代码了,哈哈哈。

首先,先是定义view

 package com.example.jwwsnow;

 import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View; public class SnowView extends View { Random random;
private static final int NUM_SNOWFLAKES = 15;//雪花数量
private static final int DELAY = 1;//画面延时刷新时间 private SnowFlake[] snowflakes;//雪花对像数组。 public SnowView(Context context) {
super(context);
} public SnowView(Context context, AttributeSet attrs) {
super(context, attrs);
} public SnowView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
} protected void resize(int width, int height) {
random = new Random();
snowflakes = new SnowFlake[NUM_SNOWFLAKES];
for (int i = 0; i < NUM_SNOWFLAKES; i++) {
//for循环生产雪花。
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.rgb(random.getColor(), random.getColor(), random.getColor()));
//返回的对象存入对象数组中去。
snowflakes[i] = SnowFlake.create(width, height, paint);
Log.i("SnowDemo", "时间::::::::::"+System.currentTimeMillis());
}
} /**
* View中方法的启动顺序onSizeChanged()>onDraw();
*/
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (w != oldw || h != oldh) {
resize(w, h);
} } @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (SnowFlake snowFlake : snowflakes) {
//得到雪花对象,绘制。
snowFlake.draw(canvas);
} getHandler().postDelayed(runnable, DELAY);//得到子线程,设置5ms延时,每5ms在主线程绘制一次界面。。
} private Runnable runnable = new Runnable() {
@Override
public void run() {
invalidate();//此方法会把原来的视图清除掉,并重新调用veiw.onDraw方法。
}
};
}

可以看到,重写了view中的几个方法:

onSizeChanged();
这个方法,是先于onDrow方法执行的。判断view的size是不是改变,如果改变,则由系统调用。然后才是执行onDrow()方法。
在本例此方法中,系统通过对view的尺寸的判断,来调用reSize()方法,并把width与heigh传递过去。
在reSize()方法中。定义了雪花对象数组SnowFlakes[],通过for循环,创建指定数量的雪花对象,并在for循环中创建Paint对象,设置画笔。(ps:Paint与Canvas,paint就像我们平时做画用的画笔,我们可以选择画笔的颜色,粗细,抗锯齿,空心,实心,是不是带阴影等。Canvas,画布,我们可以用画布来承载我们要画的具体事物,如矩形,圆形,线等。要把ptint与canvas区分开,因为功用不同。canvas决定要画的具体是什么,print决定用什么样的性质去画嘿嘿,被我说晕没。)。然后调用下面要贴的雪花Calss的create方法。这个方法主要是设置一些尺寸,位置等的。然后把这些尺寸位置等参数状态以对象的形式保存在SnowFalkes[]数组中,供下面的onDraw方法中去用这些参数做画。 onDraw()方法中,for循环,依次对SnowFalkes[]对象数组中的每个雪花进行绘画。然后用Handler在主线程中定时重绘一次。
snowFlake.draw(canvas);方法则是具体的进行绘制了。下面先贴代码,再接着讲。
 package com.example.jwwsnow;

 import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point; public class SnowFlake { private static final float ANGE_RANGE = 0.1f; //
private static final float HALF_ANGLE_RANGE = ANGE_RANGE / 2f;
private static final float HALF_PI = (float) Math.PI / 2f;
private static final float ANGLE_SEED = 25f;
private static final float ANGLE_DIVISOR = 10000f;
private static final float INCREMENT_LOWER = 2f;
private static final float INCREMENT_UPPER = 4f;
private static final float FLAKE_SIZE_LOWER = 7f;//最小雪花大小
private static final float FLAKE_SIZE_UPPER = 20f;//最大雪花大小 private final Random random;
private final Point position;
private float angle;
private final float increment;
private final float flakeSize;
private final Paint paint; public static SnowFlake create(int width, int height, Paint paint) {
Random random = new Random();
int x = random.getRandom(width);//得到[0~width)的整数width与height都是外层view的尺寸。
int y = random.getRandom(height);
Point position = new Point(x, y);//设置雪花刚一出来的随机位置。
//设置Random.getRandom(ANGLE_SEED)/ANGLE_SEED得到[0~1)再*ANGE_RANGE得到[0~0.1f)的数据,再减去0.05得到[-0.05~0.05)的数据。
float angle = random.getRandom(ANGLE_SEED) / ANGLE_SEED * ANGE_RANGE + HALF_PI - HALF_ANGLE_RANGE;
//得到[2f~4f)的随机数据
float increment = random.getRandom(INCREMENT_LOWER, INCREMENT_UPPER);
//得到[7f~20f)的随机数据
float flakeSize = random.getRandom(FLAKE_SIZE_LOWER, FLAKE_SIZE_UPPER);
//返回雪花对象。
return new SnowFlake(random, position, angle, increment, flakeSize, paint);
} SnowFlake(Random random, Point position, float angle, float increment, float flakeSize, Paint paint) {
this.random = random;
this.position = position;
this.angle = angle;//[-0.05~0.05)
this.increment = increment;//[2f~4f)
this.flakeSize = flakeSize;//[7f~20f)
this.paint = paint;
} private void move(int width, int height) {
//x方向的偏移量小,y方向的大,是为了让雪花快点落下。
double x = position.x + (increment * Math.cos(angle));//Math.cos(angle)约为1
double y = position.y + (increment * Math.sin(angle));//Math.sin(0.05~-0.05) = +-[0~8.7)
/*
*设置雪花的位置为正,不跑出屏幕。[0~1)*2*ANGLE_SEED-ANGLE_SEED等于+-(0~ANGLE_SEED],为+-(0~25f]。然后再/10000f====+-(0~0.0025],
*然后再加[-0.05~0.05)泥妈,快算晕了。大神们果然不好理解。(-0.0525~0.0525)
*/ angle += random.getRandom(-ANGLE_SEED, ANGLE_SEED) / ANGLE_DIVISOR; position.set((int) x, (int) y);//设置新的位置 if (!isInside(width, height)) {
//如果雪花不在view视图内,则重设置他们的位置。
reset(width);
}
} private boolean isInside(int width, int height) {
//TODO 设置雪花位置
int x = position.x;
int y = position.y;
//判断x坐标x>雪花尺寸加1距原点的距离(负方向)。x<background宽,同样的方式理解y。由于上面的设置,其实x方向,雪花是不可能跑出屏幕的,只有y方向上会。
return x >= -flakeSize - 1 && x + flakeSize <= width && y >= -flakeSize - 1 && y - flakeSize < height; } private void reset(int width) {
//当雪花落到屏幕最下方以下时,重新设置雪花从
position.x = random.getRandom(width);//x轴设设置一个view内的随机位置就行
position.y = (int) (-flakeSize - 1);//view的(0,0)点在view的左上角,所以,当y为负时,则在view的上方。看起来像是一个新的雪花从上落下。
angle = random.getRandom(ANGLE_SEED) / ANGLE_SEED * ANGE_RANGE + HALF_PI - HALF_ANGLE_RANGE;
} public void draw(Canvas canvas) {
//绘制方法
int width = canvas.getWidth();
int height = canvas.getHeight();//雪花所整个view高度与宽度
move(width, height);//雪花移动,传的参数为view的的北景大小。
//画一个个雪花。其实就是画一个个球。其坐标,尺寸,画笔都是原来在create方法时就定义 了的,之后保存在flakesnow数组里了。
canvas.drawCircle(position.x, position.y, flakeSize, paint);
} }

  先跳到前面的onSizeChange()方法来说。可以看到,当调用SnowFlake.create()方法后,最后返回的是:(里面的Random对象也是重新进行封装的,马上贴上)

 return new SnowFlake(random, position, angle, increment, flakeSize, paint);

  这个是用来绘制SnowFlake时需要的参数。然后在onSizeChange()方法调用的reSize()方法中保存进入了雪花数组SnowFlakes[i]。

再跳到上面大段代码前面的接着说,在onDraw()方法中,for循环会依次绘制每个雪花。snowFalke.draw().就是snowflakes[i] = SnowFlake snowFlake();

snowFlake再调用自己的draw()方法。由于前面每个SnowFlake对象都保存了每个雪花的参数,所以在draw()中,用的就直接使用了。

雪花是要下落的,则在draw()方法中,调用move()方法,来设置每个雪花的位置。当雪花位置跑出屏幕后再调用reset()方法,重新设置雪花从屏幕最上方重新落下。

下面是代码中用来设置随机数据的Random对象封装。

 package com.example.jwwsnow;

 import java.util.ArrayList;
import java.util.List; public class Random { private static final java.util.Random RANDOM = new java.util.Random(); public float getRandom(float lower, float upper) {
float min = Math.min(lower, upper);//返回两者较小的一个。
float max = Math.max(lower, upper);
return getRandom(max - min) + min;//返回的是比最大的小,比最小的大的数。
} public float getRandom(float upper) {
return RANDOM.nextFloat() * upper;//Random.nextFloat()生成[0~1)的数.
} public int getRandom(int upper) {
return RANDOM.nextInt(upper);//随机生成比[0~upper)的数值。
}
public int getColor(){
return RANDOM.nextInt(255);//随机生成[0~255)整数。 }
}

然后就是使用了:xml引用,要把路径写完整。

 <?xml version="1.0" encoding="utf-8"?>
<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="com.stylingandroid.snowfall.MainActivity"> <ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:contentDescription="@null"
android:scaleType="fitCenter"
android:src="@drawable/tree" /> <com.example.jwwsnow.SnowView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignBottom="@id/image"
android:layout_alignEnd="@id/image"
android:layout_alignLeft="@id/image"
android:layout_alignRight="@id/image"
android:layout_alignStart="@id/image"
android:layout_alignTop="@id/image" />
</RelativeLayout

本文主要用来复习自定义view的流程。建议先看原文。^_^

下面把我注释改变后的一些代码发上来,仅做研究用(eclipse版的。。。)。

ca,博客园不能上传代码。360云盘  访问密码 ce1b。

分析自定义view的实现过程-实现雪花飞舞效果(转载有改动)的更多相关文章

  1. Android自定义View 画弧形,文字,并增加动画效果

    一个简单的Android自定义View的demo,画弧形,文字,开启一个多线程更新ui界面,在子线程更新ui是不允许的,但是View提供了方法,让我们来了解下吧. 1.封装一个抽象的View类   B ...

  2. Android自定义View实现仿QQ实现运动步数效果

    效果图: 1.attrs.xml中 <declare-styleable name="QQStepView"> <attr name="outerCol ...

  3. 自定义view 之多个引导层动画效果

    SupernatantView 如果我英文还可以的话这个应该叫做漂浮在上层的view---引导层 今天闲来无事看了网上的一些引导层案例总感觉如果不是很舒服,就是类似于很死板的显示和消失 我在想能不能弄 ...

  4. 自定义View系列教程05--示例分析

    站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Android多分辨率适配框架(3)- 使用指南 自定 ...

  5. 自定义View系列教程04--Draw源码分析及其实践

    深入探讨Android异步精髓Handler 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Andr ...

  6. 自定义View系列教程03--onLayout源码详尽分析

    深入探讨Android异步精髓Handler 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Andr ...

  7. 自定义View系列教程02--onMeasure源码详尽分析

    深入探讨Android异步精髓Handler 站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Andr ...

  8. 分析实现Android自定义View之扇形图

    继承View基类,画了这样的扇形图 直接来步骤吧 (参考了GcsSloop的教程) 1.分析 自定义View需要认真的分析下,里面还是会用到一些数学知识 首先是扇形该怎么表现 1. 扇形的外观是个圆弧 ...

  9. 推翻自己和过往,重学自定义View

    http://blog.csdn.net/lfdfhl/article/details/51671038 深入探讨Android异步精髓Handler 站在源码的肩膀上全解Scroller工作机制 A ...

随机推荐

  1. Linux进程管理

    一.进程管理简介 进程是正在执行的程序或命令,每一个进程都是一个运行实体,都有自己的地址空间,并占用一定的系统资源. 进程管理的作用: 1.判断服务器的健康状态 2.查看系统中的所有进程 3.杀死进程 ...

  2. 一、Hello World

    刚创建的Android项目是一个基础的Hello World项目,包含一些默认文件,我们需要花一点时间看看最重要的部分: activity_main.xml  -- 这是刚才用Android Stud ...

  3. C#位运算讲解与示例

    首先每一个权限数都是2的N次方数 如:k1=2 ; //添加 k2=4 ; //删除 k3=8; //修改 ... 如此定义功能权限数,当需要组合权限时,就需要对各个所拥有的权限数按位或了. 如: p ...

  4. attr和prop区别

    今天写了个小程序,获取input[checked]属性,$('#check1').attr('checked'),结果返回undefined,查询了一番, 如果是具有true和false两个属性值的属 ...

  5. Js Map 实现

    /* * MAP对象,实现MAP功能 * * 接口: * size() 获取MAP元素个数 * isEmpty() 判断MAP是否为空 * clear() 删除MAP所有元素 * put(key, v ...

  6. conflict between "Chinese_PRC_CI_AI" and "Chinese_PRC_CI_AS" in the equal to operation

    在SQL SERVICE做关联查询的时候遇到了"conflict between "Chinese_PRC_CI_AI" and "Chinese_PRC_CI ...

  7. 第四周作业-yjw

    运动策略分析 首先我们可以得到我方击球手质心的位置,球的质心的位置,对方球门中心位置.同时以桌面边缘为坐标轴,建立笛卡尔坐标系.可以明确的一点是,击球手的运动为曲线,球进框的运动为直线,或者折线. 击 ...

  8. Centos安装vsftp服务

    1.安装vsftp yum install vsftpd 2.开启vsftp服务,设置开机自启 service vsftpd restart chkconfig vsftpd on 停止vsftpd: ...

  9. Java双循环break的用法

    break只跳出当前循环,也就是内循环,如果想跳出外循环有两种办法:1:for(int i = 0;i<9;i++){ //用两个breakfor(int j = 0;j<8;j++){b ...

  10. 解决driver.findElement(By)运行到此处报null指针问题

    1.由于自动化页面上的元素定位太多,主要是通过By来定位,而By提供了id,xpath,name差不多就可以定位到元素 可以使用一个配置文件存储页面上的定位By值,然后从配置文件获取by值,行程by方 ...