Android 自定义View的一点理解

来源:互联网 发布:达达主义摄影知乎 编辑:程序博客网 时间:2024/05/21 20:30

在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的一些相关知识








0 0
原创粉丝点击