在android中自定义主要设计到三个比较重要的类
view surfaceview drawable
1:普通的view,这里指的是没有自己的窗口系统(window),必须是依附在一个窗口系统中,也就是说被当做一个view添加到window上面去的
这种view可以进行一序列的操作:旋转,缩放,偏移等
2:surfaceview:有自己的窗口系统,也就是说有自己的layer,这种view不能像其他的view一样进行一序列的操作
有一点疑问:查看surfaceview的源码发现其实他也是extends view的啊 ,为啥不能像普通的view一样进行操作列
这里就涉及到他具体的实现了
从谷歌的源码我们可以看到
public class SurfaceView extends View {
static private final String TAG = "SurfaceView";
static private final boolean DEBUG = false;
final ArrayList<SurfaceHolder.Callback> mCallbacks
= new ArrayList<SurfaceHolder.Callback>();
final int[] mLocation = new int[2];
final ReentrantLock mSurfaceLock = new ReentrantLock();
final Surface mSurface = new Surface(); // Current surface in use
final Surface mNewSurface = new Surface(); // New surface we are switching to
boolean mDrawingStopped = true;
final WindowManager.LayoutParams mLayout
= new WindowManager.LayoutParams();
IWindowSession mSession;
MyWindow mWindow;
final Rect mVisibleInsets = new Rect();
final Rect mWinFrame = new Rect();
final Rect mOverscanInsets = new Rect();
final Rect mContentInsets = new Rect();
final Rect mStableInsets = new Rect();
final Rect mOutsets = new Rect();
final Configuration mConfiguration = new Configuration();
static final int KEEP_SCREEN_ON_MSG = 1;
static final int GET_NEW_SURFACE_MSG = 2;
static final int UPDATE_WINDOW_MSG = 3;
int mWindowType = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
它是有自己的独立的窗口系统的
surfaceview及其子类,一般都用于相机拍照,视频播放等容易阻塞主线程的应用场景
3:drawable
对于drawable,我们从源码中可以看到它到底是个啥东西
* A Drawable is a general abstraction for "something that can be drawn." Most
* often you will deal with Drawable as the type of resource retrieved for
* drawing things to the screen; the Drawable class provides a generic API for
* dealing with an underlying visual resource that may take a variety of forms.
* Unlike a {@link android.view.View}, a Drawable does not have any facility to
* receive events or otherwise interact with the user.
可以看出,drawable也是可以绘制的,只是不能去接受或是处理用户的输入事件
也就是说没有ontouch等一序列可以和用户交互的接口,通道
应用场景
view---常见的2d动画,像支付宝,360卫士等界面均为一些很不错的动画,这种动画是可以通过自定义的view来实现
这里面就可能设计到插值器,贝塞尔曲线等动画里面常用的知识点
surfaceview(glsurfaceview)--3d场景,给人一些立体感,这里面就设计到opengl es2.0,3.0等一些相关的知识:顶点着色器,片源着色器,
glsl语言等相关的知识点,更重要的一点是线性代数等相关的数学知识
drawable---2d动效,可以通过一个imageview来展示它的效果
此时的drawable其实就是imageview的内容了
/**
* Sets a drawable as the content of this ImageView.
*
* @param drawable the Drawable to set, or {@code null} to clear the
* content
*/
public void setImageDrawable(@Nullable Drawable drawable) {
if (mDrawable != drawable) {
mResource = 0;
mUri = null;
final int oldWidth = mDrawableWidth;
final int oldHeight = mDrawableHeight;
updateDrawable(drawable);
if (oldWidth != mDrawableWidth || oldHeight != mDrawableHeight) {
requestLayout();
}
invalidate();
}
}
用的时候直接set就好了
怎么完成一个动效
1:2d场景
贝塞尔曲线,通俗的说就是描述物体运动(变化的情况),物体从a--b是先加速再减速还是一直加速
这就是一根简单的贝塞尔曲线,它可以直观描述物体运动的变化情况
在android中动画的类型还是蛮多的,下面描述下插值器是怎么使用的
startAnimator = ValueAnimator.ofFloat(0.0f , 1.0f);
startAnimator.setDuration(600);
startAnimator.setRepeatMode(ValueAnimator.RESTART);
startAnimator.setRepeatCount(-1);
startAnimator.setInterpolator(new LinearInterpolator());
startAnimator.addUpdateListener(new AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// TODO Auto-generated method stub
float fraction = animation.getAnimatedFraction();
setPoints(fraction);
lsp=buildCloseSmoothPath(listPoints);
lsp1=buildCloseSmoothPath(listPoints1);
lsp2=buildCloseSmoothPath(listPoints2);
lsp3=buildCloseSmoothPath(listPoints3);
}
});
//监听动画的各种状态,开始,结束等等
startAnimator.addListener(new AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationRepeat(Animator animation) {
// TODO Auto-generated method stub
resetPointOffRadio();
}
@Override
public void onAnimationEnd(Animator animation) {
// TODO Auto-generated method stub
}
@Override
public void onAnimationCancel(Animator animation) {
// TODO Auto-generated method stub
}
});
这里面有个线性的插值器,也就是说我们得到的是均匀变化的 没有加速度的
2:需要了解andorid里面的一些常用工具类
paint,path,canvas
因为我们话的时候都是通过canvas去绘制的,paint是画笔,我们画出来的东西跟画笔是有关系的,像颜色,粗细等
一般的paint的初始化都是
Paint paint_blue = new Paint(); paint_blue.setColor(Color.BLUE); paint_blue.setStyle(Style.STROKE); paint_blue.setStrokeWidth(10);
setAntiAlias: 设置画笔的锯齿效果。
setColor: 设置画笔颜色
setARGB: 设置画笔的a,r,p,g值。
setAlpha: 设置Alpha值
setTextSize: 设置字体尺寸。
setStyle: 设置画笔风格,空心或者实心。
setStrokeWidth: 设置空心的边框宽度。
getColor: 得到画笔的颜色
getAlpha: 得到画笔的Alpha值。
canvas是画笔
path是路径的意思,你画个不规则的图形可能就需要用到它,他能连接你给定的顶点,连接起来就是封闭的图形了
3:3d动画,opengl 2.0及更高版本是必须抓握的,opengl2.0之后都是可编程管线的,这里面你可以去完成一些丰富的功能
由于opengl 绘制部分是在gpu里面完成的,数据的初始化部分依然是在cpu里面完成的,屏幕上的一个点就是一个像素,那么我们在opengl当中怎样去绘制一个点了,我们知道像素是具备A,R,G,B等相关的属性的,屏幕能够展现出五彩斑斓的效果,这就设计到我们怎么样颜色,顶点坐标等相关的信息从CPU送到GPU,这里面从涉及到数据的初始化,数据从应用程序到顶点着色器--片源着色器,然后是怎么渲染到屏幕上的,总之就是opengl编程的glsl语言,shader编程了
有了上面的一些基本知识就能画出一些简单的图形,设计一些简单的动画
总结一下上面的几个主要类
View,Drawable,GLSurfaceView,Paint,Path,插值器等
下面就介绍下具体的实例
上面的这个动画 我们就可以将他拆分,我们可以在程序中设计多个画笔,将不同的部分分开来绘制
我们都知道由于动画每时每刻都在绘制(invalidate),那么肯定是很耗性能的,因此我们需要注意measure,layout,draw的时间
也就是说我们需要绘制的时候才去调用invalidate,这个时候才会去调ondraw方法,同时我们应该尽量的开启硬件加速,这样绘制的只是脏区域,而不是全部重新绘制
一句话也就是动画里面的几个常用类
View,Drawable,Paint,Path,ValueAninmatior,插值器
3D里面这次就是贴一些代码
加载着色器
public static int loadShader(int shaderType, String source) {
int shader = GLES20.glCreateShader(shaderType);
if (shader != 0){
GLES20.glShaderSource(shader, source);
GLES20.glCompileShader(shader);
int[] compiled = new int[1];
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
android.util.Log.e("Compile error ",GLES20.glGetShaderInfoLog(shader));
android.util.Log.d("source",source);
GLES20.glDeleteShader(shader);
shader = 0;
}
}
return shader;
}
创建一个program
public static int createProgram(String vertexSource, String fragmentSource){
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
if (vertexShader == 0){
return 0;
}
int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
if (pixelShader == 0)
{
return 0;
}
int program = GLES20.glCreateProgram();
checkGlError("garbage");
if (program != 0){
GLES20.glAttachShader(program, vertexShader);
checkGlError("glAttachShader");
GLES20.glAttachShader(program, pixelShader);
checkGlError("glAttachShader");
GLES20.glLinkProgram(program);
int[] linkStatus = new int[1];
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);
if (linkStatus[0] != GLES20.GL_TRUE){
android.util.Log.e("Link error",GLES20.glGetProgramInfoLog(program));
GLES20.glDeleteProgram(program);
program = 0;
}
}
return program;
}
一个简单的顶点着色器代码
#version 300 es
uniform mat4 u_MVPMatrix;
in vec4 a_Position;
void main()
{
gl_Position = u_MVPMatrix * a_Position;
}
总结:
做好动效一般的需要掌握view,drawable的绘制,以及opengl的一些相关知识