android绘制黑色画面,Android音视频技术入门之绘制一张图片

article/2025/11/3 15:11:52

Android 的音视频入门学习,首先了解一下绘制图片。在 Android 平台绘制一张图片,使用至少 3 种不同的 API,ImageView,SurfaceView,自定义 View作绘制图片。下面我以SurfaceView作重点来讲,为什么不用其他的来作例子,分析完SurfaceView就是知道为什么要用SurfaceView作例子。

SurfaceView

我们以下面几个点来了解SurfaceView

SurfaceView 有那些相关类。

SurfaceView 有那些特点。

如何使用SurfaceView呢。

SurfaceView的优缺。

SurfaceView 在视频开发中应用在那里。

SurfaceView 其实是继承了View ,但与View又有一些区别,View是通过 onDraw(Canvas canvas)方法中的Canvas去绘制自身显示有界面上,而SurfaceView则不需要onDraw方法,有人会有些疑问,如果SurfaceView不需要实现onDraw方法怎么去绘制自身呢?其实View是在UI线程中绘制的,SurfaceView是在子线程中绘制的(即在一个子线程中对自己进行绘制)。在子线程中绘制怎么拿到canvas呢?下面我们去了解SurfaceView 有那些相关类。

SurfaceView 有那些相关类。

有三个重要的类,分别如下:

Surface

SurfaceHolder

SurfaceView

Surface

我们看看Surface的源码

/**

* Handle onto a raw buffer that is being managed by the screen compositor.

*

*

A Surface is generally created by or from a consumer of image buffers (such as a

* {@link android.graphics.SurfaceTexture}, {@link android.media.MediaRecorder}, or

* {@link android.renderscript.Allocation}), and is handed to some kind of producer (such as

* {@link android.opengl.EGL14#eglCreateWindowSurface(android.opengl.EGLDisplay,android.opengl.EGLConfig,java.lang.Object,int[],int) OpenGL},

* {@link android.media.MediaPlayer#setSurface MediaPlayer}, or

* {@link android.hardware.camera2.CameraDevice#createCaptureSession CameraDevice}) to draw

* into.

*

*

Note: A Surface acts like a

* {@link java.lang.ref.WeakReference weak reference} to the consumer it is associated with. By

* itself it will not keep its parent consumer from being reclaimed.

*/

public class Surface implements Parcelable {

private static final String TAG = "Surface";

private static native long nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture)

throws OutOfResourcesException;

private static native long nativeCreateFromSurfaceControl(long surfaceControlNativeObject);

private static native long nativeLockCanvas(long nativeObject, Canvas canvas, Rect dirty)

throws OutOfResourcesException;

private static native void nativeUnlockCanvasAndPost(long nativeObject, Canvas canvas);

private static native void nativeRelease(long nativeObject);

private static native boolean nativeIsValid(long nativeObject);

.......

/**

* Create Surface from a {@link SurfaceTexture}.

*

* Images drawn to the Surface will be made available to the {@link

* SurfaceTexture}, which can attach them to an OpenGL ES texture via {@link

* SurfaceTexture#updateTexImage}.

*

* @param surfaceTexture The {@link SurfaceTexture} that is updated by this

* Surface.

* @throws OutOfResourcesException if the surface could not be created.

*/

public Surface(SurfaceTexture surfaceTexture) {

if (surfaceTexture == null) {

throw new IllegalArgumentException("surfaceTexture must not be null");

}

mIsSingleBuffered = surfaceTexture.isSingleBuffered();

synchronized (mLock) {

mName = surfaceTexture.toString();

setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture));

}

}

/* called from android_view_Surface_createFromIGraphicBufferProducer() */

private Surface(long nativeObject) {

synchronized (mLock) {

setNativeObjectLocked(nativeObject);

}

}

........

}

也不难看出,其实Surface就充当着Model层,也是一个原始数据的缓冲区,表面通常是由图像缓冲区的使用者创建的。

SurfaceHolder

看看SurfaceHolder的源码

/**

* Abstract interface to someone holding a display surface. Allows you to

* control the surface size and format, edit the pixels in the surface, and

* monitor changes to the surface. This interface is typically available

* through the {@link SurfaceView} class.

*

*

When using this interface from a thread other than the one running

* its {@link SurfaceView}, you will want to carefully read the

* methods

* {@link #lockCanvas} and {@link Callback#surfaceCreated Callback.surfaceCreated()}.

*/

public interface SurfaceHolder {

/** @deprecated this is ignored, this value is set automatically when needed. */

@Deprecated

public static final int SURFACE_TYPE_NORMAL = 0;

/** @deprecated this is ignored, this value is set automatically when needed. */

@Deprecated

public static final int SURFACE_TYPE_HARDWARE = 1;

/** @deprecated this is ignored, this value is set automatically when needed. */

@Deprecated

public static final int SURFACE_TYPE_GPU = 2;

/** @deprecated this is ignored, this value is set automatically when needed. */

@Deprecated

public static final int SURFACE_TYPE_PUSH_BUFFERS = 3;

/**

* Exception that is thrown from {@link #lockCanvas} when called on a Surface

* whose type is SURFACE_TYPE_PUSH_BUFFERS.

*/

public static class BadSurfaceTypeException extends RuntimeException {

public BadSurfaceTypeException() {

}

public BadSurfaceTypeException(String name) {

super(name);

}

}

/**

* A client may implement this interface to receive information about

* changes to the surface. When used with a {@link SurfaceView}, the

* Surface being held is only available between calls to

* {@link #surfaceCreated(SurfaceHolder)} and

* {@link #surfaceDestroyed(SurfaceHolder)}. The Callback is set with

* {@link SurfaceHolder#addCallback SurfaceHolder.addCallback} method.

*/

public interface Callback {

/**

* This is called immediately after the surface is first created.

* Implementations of this should start up whatever rendering code

* they desire. Note that only one thread can ever draw into

* a {@link Surface}, so you should not draw into the Surface here

* if your normal rendering will be in another thread.

*

* @param holder The SurfaceHolder whose surface is being created.

*/

public void surfaceCreated(SurfaceHolder holder);

/**

* This is called immediately after any structural changes (format or

* size) have been made to the surface. You should at this point update

* the imagery in the surface. This method is always called at least

* once, after {@link #surfaceCreated}.

*

* @param holder The SurfaceHolder whose surface has changed.

* @param format The new PixelFormat of the surface.

* @param width The new width of the surface.

* @param height The new height of the surface.

*/

public void surfaceChanged(SurfaceHolder holder, int format, int width,

int height);

/**

* This is called immediately before a surface is being destroyed. After

* returning from this call, you should no longer try to access this

* surface. If you have a rendering thread that directly accesses

* the surface, you must ensure that thread is no longer touching the

* Surface before returning from this function.

*

* @param holder The SurfaceHolder whose surface is being destroyed.

*/

public void surfaceDestroyed(SurfaceHolder holder);

}

/**

* Additional callbacks that can be received for {@link Callback}.

*/

public interface Callback2 extends Callback {

/**

* Called when the application needs to redraw the content of its

* surface, after it is resized or for some other reason. By not

* returning from here until the redraw is complete, you can ensure that

* the user will not see your surface in a bad state (at its new

* size before it has been correctly drawn that way). This will

* typically be preceeded by a call to {@link #surfaceChanged}.

*

* @param holder The SurfaceHolder whose surface has changed.

*/

public void surfaceRedrawNeeded(SurfaceHolder holder);

}

/**

* Add a Callback interface for this holder. There can several Callback

* interfaces associated with a holder.

*

* @param callback The new Callback interface.

*/

public void addCallback(Callback callback);

.........

}

从源码有可以看出,SurfaceHolder是以接口的形式给持有显示表面使用,允许你控制表面尺寸和格式,编辑表面的像素。监视对表面的更改。我们可以理解为SurfaceHolder充当控制层,管理Surface的生命周期,让SurfaceView来绘制Surface的数据。

SurfaceView

SurfaceView就是视图层,SurfaceView 中包含一个专门用于绘制的Surface ,Surface中包含了一个Canvas。如果细心的一点,也不难发现Surface、SurfaceHolder、SurfaceView其实就是一个MVC模式。

那么问题不了,那么如何获取到Canvas?

在SurfaceView中有一个getHolder() -> SurfaceHolder。那么Holder包含了Canvas(Canvas+管理SurfaceView的生命周期)。所以Canvas = holder.lockCanvas()。调用生命周期的holder.addCallback(Callback callback)。

SurfaceView的生命周期管理有三个方法:

SurfaceCreated

SurfaceChanged

SurfaceDestoryed

如何使用SurfaceView呢?

获取SurfaceHolder对象,其是SurfaceView的内部类。

监听Surface生命周期。

只有当native层的Surface创建完毕之后,才可以调用lockCanvas(),否则失败。

holder.Callback。

调用holder.lockCanvas()。

绘制

调用SurfaceHolder.unlockCanvasAndPost,将绘制内容post到Surface中

注意:第3、4、5步是在子线程中执行的。

SurfaceView的特点有那些

具有独立的绘图表面Surface。

需要在宿主窗口上挖一个洞来显示自己,z轴比普通的window要小。

它的UI绘制可以在独立的线程中进行,这样就可以进行复杂的UI绘制,并且不会影响应用程序的主线程响应用户输入。

SurfaceView的优缺点

优点

在一个子线程中对自己进行绘制,避免造成UI线程阻塞。

高效复杂的UI效果。

独立Surface,独立的Window。

使用双缓冲机制,播放视频时画面更流畅。

缺点

每次绘制都会优先绘制黑色背景,更新不及时会出现黑边现象。

Surface不在View hierachy中,它的显示也不受View的属性控制,平移,缩放等变换。

SurfaceView的基本知道了解得差不多了,那么我们写一个SurfaceView绘制图片的一个公共View的实现。

public class CommonSurfaceView extends SurfaceView implements SurfaceHolder.Callback,Runnable {

private SurfaceHolder mHolder;

private Canvas mCanvas;

//用于绘制的线程

private Thread mThread;

//线程状态的标记(线程的控制开关)

private boolean isRunning;

public CommonSurfaceView(Context context) {

this(context,null);

}

public CommonSurfaceView(Context context, AttributeSet attrs) {

this(context, attrs,0);

}

public CommonSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

//初始化

mHolder = getHolder();

mHolder.addCallback(this);//管理生命周期

//获取焦点

setFocusable(true);

setFocusableInTouchMode(true);

//设置常量

setKeepScreenOn(true);

}

@Override

public void surfaceCreated(SurfaceHolder holder) {

isRunning = true;

mThread = new Thread(this);

mThread.start();//开启线程

}

@Override

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

}

@Override

public void surfaceDestroyed(SurfaceHolder holder) {

isRunning = false;

}

@Override

public void run() {

//不断地进行绘制

while (isRunning){

draw();

}

}

private void draw() {

//为什么要try catch 因为当view在主界面时有可能按Home键或Back键时回到界面,Surface被销毁了。

//这时有可能已经进入到draw() ,这时候获取的mCanvas可能为null。

// 还有一种可能,就是界面被销毁的,我们的线程还没有销毁,mCanvas可能为null。

try{

//获取Canvas

mCanvas = mHolder.lockCanvas();

if(mCanvas !=null){

//do something

}

}catch (Exception e){

e.printStackTrace();

}finally {

if(mCanvas !=null){

//释放Canvas

mHolder.unlockCanvasAndPost(mCanvas);

}

}

}

}

总结一下有那些问题、疑虑

不绘制任何东西,SurfaceView显示的是黑色?

SurfaceView 能绘制什么东西?

SurfaceVeiw双缓冲区

SurfaceView 和 SurfaceHolder 怎么交互?

SurfaceHolder与Surface的交互

SurfaceView 怎么进行旋转,透明操作的?

一般视频播放器可以横竖屏切换,是如何实现的?

SurfaceView 和普通的View的区别?

SurfaceView 挖洞原理

SurfaceView 生命周期

横屏录制横屏播放,竖屏录制竖屏播放

那么我们来解答一下上面的一些疑虑和问题,就浅析一下,有做得不好的请多多指出,谢谢。

不绘制任何东西,SurfaceView显示的是黑色?

每次更新视图时都会先将背景绘制成黑色。所以在移动或者缩放过程,会更新不及时时就会看黑边。

@Override

public void draw(Canvas canvas) {

if (mDrawFinished && !isAboveParent()) {

// draw() is not called when SKIP_DRAW is set

if ((mPrivateFlags & PFLAG_SKIP_DRAW) == 0) {

// punch a whole in the view-hierarchy below us

canvas.drawColor(0, PorterDuff.Mode.CLEAR);

}

}

super.draw(canvas);

}

//这句话表示PorterDuff.Mode.CLEAR会将像素设置为0,也就是黑色

//Destination pixels covered by the source are cleared to 0.

public enum Mode {

// these value must match their native equivalents. See SkXfermode.h

/**

*

* composite_CLEAR.png

*

Destination pixels covered by the source are cleared to 0.

*

*

\(\alpha_{out} = 0\)

*

\(C_{out} = 0\)

*/

CLEAR (0),

}

SurfaceView 能绘制什么东西?

从下面代码可以看到,SurfaceView 的绘制也是使用 Canvas 进行绘制的,绘制应该跟普通的 View 绘制差不多

/**

* 绘制

*/

private void draw() {

if (radius > getWidth()) {

return;

}

Canvas canvas = mHolder.lockCanvas();

if (canvas != null) {

canvas.drawCircle(300, 300, radius += 10, mPaint);

mHolder.unlockCanvasAndPost(canvas);

}

}

SurfaceVeiw双缓冲区

双缓冲:在运用时可以理解为:SurfaceView在更新视图时用到了两张 Canvas,一张 frontCanvas 和一张 backCanvas ,每次实际显示的是 frontCanvas ,backCanvas 存储的是上一次更改前的视图。当你在播放这一帧的时候,它已经提前帮你加载好后面一帧了,所以播放起视频很流畅。

当使用lockCanvas()获取画布时,得到的实际上是backCanvas 而不是正在显示的 frontCanvas ,之后你在获取到的 backCanvas 上绘制新视图,再 unlockCanvasAndPost(canvas)此视图,那么上传的这张 canvas 将替换原来的 frontCanvas 作为新的frontCanvas ,原来的 frontCanvas 将切换到后台作为 backCanvas 。例如,如果你已经先后两次绘制了视图A和B,那么你再调用 lockCanvas()获取视图,获得的将是A而不是正在显示的B,之后你将重绘的 A 视图上传,那么 A 将取代 B 作为新的 frontCanvas 显示在SurfaceView 上,原来的B则转换为backCanvas。

相当与多个线程,交替解析和渲染每一帧视频数据。

surfaceholder.lockCanvas--surfaceholder.unlockCanvasAndPost

SurfaceView 和 SurfaceHolder 怎么交互?

SurfaceHolder 是 SurfaceView 内部类,可以通过 SurfaceView.getHolder() 即可获取对应的 SurfaceHolder 对象。

通过 getHolder() 就可以将 SurfaceHolder ,然后将其传递给 MediaPlayer 或者 Camera 显示出来。实际上就是通过 SurfaceHolder 去控制 SurfaceView 的显示。

/**

* Return the SurfaceHolder providing access and control over this

* SurfaceView's underlying surface.

*

* @return SurfaceHolder The holder of the surface.

*/

public SurfaceHolder getHolder() {

return mSurfaceHolder;

}

SurfaceHolder与Surface的交互

SurfaceHolder 是一个接口,它具体的实现在 SurfaceView 中定义的一个内部类。对于 SurfaceHolder 的操作,实际上是操作Surface 的相关接口。

因为 Surface 会牵扯到 native 层的 Surface ,只有 Native 层的 Surface 创建成功之后,我们才能真正开始去绘制我们的视图。

那么如何去捕获到这个 Surface 的创建生命周期呢?

注册 SurfaceHolder.Callback 接口,监听这个接口的回调:

surfaceCreated

播放视频

surfaceDestroy

停止视频播放

Canvas canvas = mHolder.lockCanvas();

if (canvas != null) {

canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher, null), 0, 0, mPaint);

mHolder.unlockCanvasAndPost(canvas);

}

mHolder.lockCanvas(); 实际获取的是 Surface 中的 Canvas。

/**

* Gets a {@link Canvas} for drawing into the SurfaceView's Surface

*

* After drawing into the provided {@link Canvas}, the caller must

* invoke {@link #unlockCanvasAndPost} to post the new contents to the surface.

*

* The caller must redraw the entire surface.

* @return A canvas for drawing into the surface.

*/

@Override

public Canvas lockCanvas() {

return internalLockCanvas(null);

}

private final Canvas internalLockCanvas(Rect dirty) {

mSurfaceLock.lock();

Canvas c = null;

if (!mDrawingStopped && mWindow != null) {

try {

//实际调用的是 surface.lockCancas()

c = mSurface.lockCanvas(dirty);

} catch (Exception e) {

Log.e(LOG_TAG, "Exception locking surface", e);

}

}

...

return null;

}

canvas.drawXXX();

在 Canvas 中绘制内容。

mHolder.unlockCanvasAndPost(canvas);将 绘制在 Canvas 中的内容刷新到 Surface 中。

//将 backcanvas 中的内容刷新到 surface 中并且释放这个 canvas

/**

* Posts the new contents of the {@link Canvas} to the surface and

* releases the {@link Canvas}.

*

* @param canvas The canvas previously obtained from {@link #lockCanvas}.

*/

@Override

public void unlockCanvasAndPost(Canvas canvas) {

mSurface.unlockCanvasAndPost(canvas);

mSurfaceLock.unlock();

}

SurfaceView 怎么进行旋转,透明操作的?

普通View旋转后,View的内容也跟着同步做了旋转.

SurfaceView在旋转之后,其显示内容并没有跟着一起旋转.

比喻:这就好比在墙上开了一个窗(Surface),通过窗口可以看外面的花花世界,但窗口无论怎样变化,窗外面的世界是不会跟着窗口一同变化。

一般视频播放器可以横竖屏切换,是如何实现的?

在 Activity 中覆写 onConfigurationChanged 方法就可以。根据横竖屏切换,修改 SurfaceView 的 Parameter 的宽高参数即可。

android:configChanges="orientation|keyboardHidden|screenSize"

@Override

public void onConfigurationChanged(Configuration newConfig) {

super.onConfigurationChanged(newConfig);

if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {

//变成横屏了

} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {

//变成竖屏了

}

}

SurfaceView 挖洞原理

挖洞原理了解之后再补上吧

SurfaceView 和普通的View的区别?

surfaceView是在一个新起的单独线程中可以重新绘制画面。

View必须在UI的主线程中更新画面。

那么在UI的主线程中更新画面可能会引发问题,比如你更新画面的时间过长,那么你的主UI线程会被你正在画的函数阻塞。那么将无法响应按键,触屏等消息。

当使用surfaceView 由于是在新的线程中更新画面所以不会阻塞你的UI主线程。

SurfaceView 生命周期

使用:双缓冲

导致:需要更多的内存开销

为了节约系统内存开销:

SurfaceView 可见时 -> 创建 SurfaceHolder

SurfaecView 不可见时 -> 摧毁 SurfaceHolder

1、程序打开

Activity 调用顺序:onCreate()->onStart()->onResume()

SurfaceView 调用顺序: surfaceCreated()->surfaceChanged()

2、程序关闭(按 BACK 键)

Activity 调用顺序:onPause()->onStop()->onDestory()

SurfaceView 调用顺序: surfaceDestroyed()

3、程序切到后台(按 HOME 键)

Activity 调用顺序:onPause()->onStop()

SurfaceView 调用顺序: surfaceDestroyed()

4、程序切到前台

Activity 调用顺序: onRestart()->onStart()->onResume()

SurfaceView 调用顺序: surfaceChanged()->surfaceCreated()

5、屏幕锁定(挂断键或锁定屏幕)

Activity 调用顺序: onPause()

SurfaceView 什么方法都不调用

6、屏幕解锁

Activity 调用顺序: onResume()

SurfaceView 什么方法都不调用

横屏录制横屏播放,竖屏录制竖屏播放

通过以下方法可以获取到视频的宽高,根据视频的宽高就可以知道该视频是横屏还是竖屏录制的。

MediaPlayer# public void onVideoSizeChanged(MediaPlayer mp, int width, int height)

横屏判断:

width>height

旋转屏幕:

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

竖屏录制

height>width

旋转屏幕:

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

mediaPlayer.setOnVideoSizeChangedListener(new MediaPlayer.OnVideoSizeChangedListener() {

@Override

public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {

Log.e(TAG, "onVideoSizeChanged:WIDTH>>" + width);

Log.e(TAG, "onVideoSizeChanged:HEIGHT>>" + height);

if (width > height) {

//横屏录制

if (getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

}

} else {

//竖屏录制

if (getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) {

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

}

}

}

});

View的绘制要知道的知识

View的绘制其实是在UI线程(实现onCanvas方法进行绘制)。如果进行绘制高效复杂的UI,最好不用自定义View。要用SurfaceView进行绘制。

View的绘画三要素

Canvas (画布,绘制BitMap操作)

Paint (绘制的画笔Paint,颜色、样式)

Path (路径)

一、Canvas

如果直接extends View 可以重写onDraw(Canvas canvas)方法,直接用里面的canvas进行绘制。

可以直接利用Activity的绘制机制,用lockCanvas()方法来获得当前的Activity的Canvas。

在SurfaceView中,同2可以利用SurfaceHolder的对象的lockCanvas()方法来Canvas。

二、Paint

直接通过new关键字来实例化,然后通过Paint对象来对画笔进行相应的设置:

如:

1.1 去锯齿setAntiAlia(true)

1.2 去抖动setDither(true)

1.3 设置图层混合模式setXfermode(Xfermode,xfermode)

三、 path

1、Path路径 直接用new来实例化

2、通过path对象设置想要画图的轨迹或路线

如:矩形 、三角形 、圆、曲线等

实现一个自定义View,代码如下:

//自定义绘图类

public class BallView extends View {

private Paint paint; //定义画笔

private float cx = 150; //圆点默认X坐标

private float cy = 250; //圆点默认Y坐标

private int radius = 60; // 半径

//定义颜色数组

private int colorArray[] = {Color.BLACK,Color.BLACK,Color.GREEN,Color.YELLOW, Color.RED};

private int paintColor = colorArray[0]; //定义画笔默认颜色

private int screenW; //屏幕宽度

private int screenH; //屏幕高度

public BallView(Context context,int screenW,int screenH) {

super(context);

this.screenW=screenW;

this.screenH=screenH;

//初始化画笔

initPaint();

}

private void initPaint(){

paint = new Paint();

//设置消除锯齿

paint.setAntiAlias(true);

//设置画笔颜色

paint.setColor(paintColor);

}

//重写onDraw方法实现绘图操作

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

//将屏幕设置为白色

canvas.drawColor(Color.WHITE);

//修正圆点坐标

revise();

//随机设置画笔颜色

setPaintRandomColor();

//绘制小圆作为小球

canvas.drawCircle(cx, cy, radius, paint);

}

//为画笔设置随机颜色

private void setPaintRandomColor(){

Random rand = new Random();

int randomIndex = rand.nextInt(colorArray.length);

paint.setColor(colorArray[randomIndex]);

}

//修正圆点坐标

private void revise(){

if(cx <= radius){

cx = radius;

}else if(cx >= (screenW-radius)){//防止出边界

cx = screenW-radius;

}

if(cy <= radius){

cy = radius;

}else if(cy >= (screenH-radius)){//防止出边界

cy = screenH-radius;

}

}

@Override

public boolean onTouchEvent(MotionEvent event) {

switch (event.getAction()) {

case MotionEvent.ACTION_DOWN:

// 按下

cx = (int) event.getX();

cy = (int) event.getY();

// 通知重绘

postInvalidate(); //该方法会调用onDraw方法,重新绘图

break;

case MotionEvent.ACTION_MOVE:

// 移动

cx = (int) event.getX();

cy = (int) event.getY();

// 通知重绘

postInvalidate();

break;

case MotionEvent.ACTION_UP:

// 抬起

cx = (int) event.getX();

cy = (int) event.getY();

// 通知重绘

postInvalidate();

break;

}

/*

* 备注1:此处一定要将return super.onTouchEvent(event)修改为return true,原因是:

* 1)父类的onTouchEvent(event)方法可能没有做任何处理,但是返回了false。

* 2)一旦返回false,在该方法中再也不会收到MotionEvent.ACTION_MOVE及MotionEvent.ACTION_UP事件。

*/

//return super.onTouchEvent(event);

return true;

}

}

懂得运用View的绘画三要素,画出自己想要的图也不难。

ImageView 绘制图片就不多说了,看一下例子吧

public class RoundImageView extends ImageView {

private Bitmap mBitmap;

private Rect mRect = new Rect();

private PaintFlagsDrawFilter pdf = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG);

private Paint mPaint = new Paint();

private Path mPath=new Path();

public RoundImageView(Context context, AttributeSet attrs) {

super(context, attrs);

init();

}

//传入一个Bitmap对象

public void setBitmap(Bitmap bitmap) {

this.mBitmap = bitmap;

}

private void init() {

mPaint.setStyle(Paint.Style.STROKE);

mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);

mPaint.setAntiAlias(true);// 抗锯尺

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

if(mBitmap == null)

{

return;

}

mRect.set(0,0,getWidth(),getHeight());

canvas.save();

canvas.setDrawFilter(pdf);

mPath.addCircle(getWidth() / 2, getWidth() / 2, getHeight() / 2, Path.Direction.CCW);

canvas.clipPath(mPath, Region.Op.REPLACE);

canvas.drawBitmap(mBitmap, null, mRect, mPaint);

canvas.restore();

}

}

综上所述,为什么视频技术入门要先了解图片绘制,那么图片绘制的API也有多种,为什么选择用SurfaceView这个API,因为其一,绘制是在子线程中进行绘制的,其二,可能绘制出高效复杂的UI效果,其三,使用双缓冲机制,播放视频时画面更流畅。

所以我也把我了解到的,学习到的基础知识分享给大家。希望对大家有帮助。


http://chatgpt.dhexx.cn/article/e7ys6Vf5.shtml

相关文章

Android图形系统之Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback之间的联系

SurfaceHolder 版本&#xff1a;Android 2.2 r1 结构 继承关系 public interface SurfaceHolder android.view.SurfaceHolder 概述 抽象接口持有人显示表面。允许您控制面的大小和格式&#xff0c;编辑在suface的橡树&#xff0c;并监测到变化。此接口通常可通过SurfaceView类…

Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback之间的关系

一、Surface Surface就是“表面”的意思。在SDK的文档中&#xff0c;对Surface的描述是这样 的&#xff1a;“Handle onto a raw buffer that is being managed by the screen compositor”&#xff0c;翻译成中文就是“由屏幕显示内容合成器(screen compositor)所管理的原生缓…

android surfaceview学习(一)

推荐阅读&#xff1a; https://blog.csdn.net/luoshengyang/article/details/8661317 https://www.cnblogs.com/xuling/archive/2011/06/06/android.html https://blog.csdn.net/zcmain/article/details/14454953 1、surfaceview默认是黑色的背景&#xff0c;并且给sfv设置背景…

Activity、Fragment和Surface的生命周期

这里首先推荐大家一本Android的学习书籍&#xff1a;《第一行代码》&#xff0c;这本书还是写得非常好的。好了&#xff0c;进入正题&#xff0c;我们先来了解一下Activity、Fragment的的生命周期中经历的几个阶段。 ActivityFragment 下面我们假设有如下的场景&#xff0c;Act…

Java进度条

转自&#xff1a;点击打开链接 进度条用在很多的地方&#xff0c;比如登录验证等待过程、程序加载、数据下载、数据更新与删除等&#xff0c;它可以一目了然的让用户知道程序执行的进度。下面给大家看一个登录验证的进度条&#xff0c;首先上效果图&#xff1a; 在代码中未连接…

java窗口进度条_java进度条窗口具体如何制作?

可视化窗口是大部分游戏的常态&#xff0c;今天我们就来了解下如何编写进度条窗口&#xff0c;快跟小编一起看看吧。 一、在包右击new->other->WindowBuilder->JFrame->name叫Guithreadpackage guithread; import java.awt.BorderLayout; import java.awt.EventQue…

java进度条_「java进度条」Java JProgressBar类(进度条) - seo实验室

java进度条 简介 利用JprogressBar类可以实现一个进度条。它一般是一种颜色部分或完全填充的矩形。缺省情况下,进度条配备了一个凹陷的边框,并水平放置。 进度条还可以选择显示一个字符串,这个字符串在进度条矩形的中央位置上显示。这个字符串缺省时为耗时任务已完成的百分比…

Java 进度条

效果 代码 思路&#xff1a;使用NumberFormat类来格式化数字&#xff0c;显示完后&#xff0c;退格覆盖重新显示新的数字 import java.text.NumberFormat;public class Test {public static void main(String[] args) throws Exception {System.out.print("Progress:"…

Java图形化界面---进度条

目录 一、进度条的介绍 二、创建进度条 三、简单案例 四、子线程创建进度条 &#xff08;1&#xff09;为什么要使用子线程创建进度条 &#xff08;2&#xff09;子线程创建进度条的代码 五、创建进度对话框 &#xff08;1&#xff09;如何创建进度对话框 &#xff08;2&…

Java图形化界面编程超详细知识点(7)——进度条

目录 4 JProgressBar、ProgressMonitor、BoundedRangeModel实现进度条 4.1 创建进度条 4 JProgressBar、ProgressMonitor、BoundedRangeModel实现进度条 进度条是图形界面中广泛使用的GUI组件&#xff0c;当复制一个较大的文件时&#xff0c;操作系统会显示一个进度条&#…

Java制作进度条

【效果图】 【描述】 进度条宽度保持和窗口宽度一致&#xff0c;进度最小值为0&#xff0c;最大值为100&#xff0c;每100毫秒进度值1&#xff0c;进度条中间显示进度百分比 【代码】 package test;import java.awt.*; import java.awt.event.*;import javax.swing.*;public…

QT:QGraphicsScene与QGraphicsView使用render()函数渲染图片

render()函数看官方文档&#xff0c;会有很多不懂得地方&#xff0c;以下为实践出来的效果。 当我们想要用QPaint() 绘制我们在QGraphicsView看到的部分时&#xff0c;不仅要绘制正确的图片大小&#xff0c;还需要使用 render() 渲染正确的大小。当我们只想要绘制坐标系中矩形…

Qt图形特效:QGraphicsOpacityEffect

一、描述 不透明度效果使源具有不透明度。该效果对于使源半透明非常有用&#xff0c;类似于淡入/淡出序列。可以使用setOpacity()函数修改不透明度。 二、属性成员 1、opacity : qreal。此属性保存不透明度。该值应在0.0到1.0的范围内&#xff0c;其中0.0是完全透明的&#…

图形视图(02):【类】QGraphicsScene [官翻]

文章目录 详述事件处理和传播 公共类型enum ItemIndexMethodenum SceneLayer 属性backgroundBrush: QBrushbspTreeDepth: intfocusOnTouch: boolfont: QFontforegroundBrush: QBrushitemIndexMethod: ItemIndexMethodminimumRenderSize: qrealpalette: QPalette**sceneRect**: …

Qt中使用QGraphicsScene重写drawBackGround绘制背景

Qt中使用QGraphicsScene重写drawBackGround绘制背景 需求解释 我是想这学习Qt的界面设计&#xff0c;希望能够对界面背景进行优化然后使用Qt的界面与图形画背景网格。首先查资料 通过查找博客发现很简单&#xff0c;就是新建一个类继承于QGraphicsScene&#xff0c;于是我就…

图形视图框架QGraphicsScene(场景,概念)

QGraphicsScene 该类充当 QGraphicsItems 的容器。它与 QGraphicsView 一起使用&#xff0c;用于在 2D 表面上可视化图形项目&#xff0c;例如线条、矩形、文本甚至自定义项目。 QGraphicsScene具有的功能&#xff1a; 提供用管理大量数据项的高速接口传播事件到每一个图形项…

qt学习笔记(五) QGraphicsPixmapItem与QGraphicsScene的编程实例 图标拖动渐变效果

应大家的要求&#xff0c;还是把完整的工程文件贴出来&#xff0c;大家省点事&#xff1a;http://www.kuaipan.cn/file/id_48923272389086450.htm 先看看运行效果&#xff0c;我用的群创7寸屏&#xff0c;主机是mini2440&#xff0c;分辨率是800*480&#xff0c;程序写比较粗糙…

Qt QGraphicsScene、QGraphicsView类实现仪表盘

Qt QGraphicsScene、QGraphicsView类实现仪表盘 【1】UI界面设计【2】效果【3】QGraphicsScene简介【4】QGraphicsEllipseItem简介【5】QGraphicsPolygonItem简介【6】QGraphicsLineItem简介【7】QGraphicsView简介【8】仪表源码头文件源码 【1】UI界面设计 【2】效果 【3】QGr…

qt QGraphicsScene 简单例子

坐标 graphicsView是Widget这个界面的子控件&#xff0c; 他的位置是左上角坐标为&#xff08;30,30&#xff09; 他的宽度为431&#xff0c;高度为431. 如果在graphicsView 这个子控件中&#xff0c; 继续加入 子控件A&#xff0c;由于graphicsView比较特殊。 根据它的align…

QGraphicsScene设置SceneRect

简要说明 QGraphicsScene场景区域&#xff0c;可在构造QGraphicsScene对象时设定&#xff0c;也可通过函数setSceneRect设定。QGraphicsScene场景区域中坐标原点的位置&#xff0c;会影响到图形项的坐标设定&#xff0c;进而影响图形项在场景中的显示位置。以将图片显示在中心…