Android应用程序开发中,可以通过SurfaceView来呈现介质流例如视频、动画等,SurfaceView是一个特殊的View,含有一块专门用来绘制的Surface,相比于普通的View,SurfaceView具有更强的绘图性能。而在使用SurfaceView时,SurfaceView对象的生命周期的变化会对渲染状态产生重要影响,那么如何掌握SurfaceView的变化过程就成了很重要的问题。本文将围绕SurfaceChanged事件来详细阐述SurfaceView的状态变化,给开发者提供一些解决方法与技巧。
什么是SurfaceChanged事件
"Sruface Changed"事件指在SurfaceView发生变化时,系统调用SurfaceHolder.Callback接口中的surfaceChanged方法。SurfaceChanged事件发生于SurfaceView被底层Window创建后,但在Surface创建之前,即在SurfaceHolder#surfaceCreated方法之后、SurfaceHolder#surfaceDestroyed之前。当SurfaceView处于背景状态时,也就是不可见的状态,此时SurfaceChanged事件不会触发。通过surfaceChanged方法,您可以获得有关SurfaceView大小、形状和新/旧SurfaceHolder的信息,可以通过这些信息重建您的SurfaceView的呈现,以保证与新状态的SurfaceView匹配。
手机屏幕的尺寸和方向可能随时变化,因此,我们必须及时跟踪SurfaceView的变化。在SurfaceView变化时,我们有时需要更新我们的Canvas或程序来适应这些变化。通常,当SurfaceView变化时,我们必须重新计算SurfaceView的大小和位置,以便将其与系统中其他对象的视图重叠并与其他系统元素正确对齐。
如何使用SurfaceChanged
在实际开发中,我们可以利用SurfaceChanged事件来重新计算Surface对象的大小和位置。对于界面更新方法的执行,我们有以下两种选择:
方法一:使用SurfaceChanged
在SurfaceHolder.Callback的实现中,我们可以利用surfaceChanged方法重新定义Surface绘制区域的大小和精度。SurfaceChanged方法接收三个参数,分别用来获取Surface的宽、高和像素格式。在重写这个方法时,我们可以重新设置顶部和左侧的位置,也可以对表面的内容进行自定义工作。
例如,对于绘制全屏视频的情况,我们可以使用以下代码块:
``` java
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
//检查当前持有SurfaceHolder的属性
if (mSurfaceHolder.getSurface() == null) {
//停止Surface更新
return;
}
//停止绘制现有内容
mMediaPlayer.stop();
// 更改大小得到新的宽高。
mSurfaceHolder.setFixedSize(width, height);
//为MediaPlayer重新设置外部SurfaceView
mMediaPlayer.setSurface(mSurfaceHolder.getSurface());
//开始播放视频
mMediaPlayer.start();
}
```
方法二:使用View#onSizeChanged方法
View#onSizeChanged()有两个参数,它们表示新的View的宽高,以像素为单位。这是在View大小更改时调用的回调方法。在这种情况下,我们可以使用以下代码块:
``` java
@Override
protected void onSizeChanged(int newWidth, int newHeight, int oldWidth, int oldHeight) {
super.onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
// 更新UI
}
```
**重点:**使用SurfaceChanged回调与onSizeChanged回调都可以处理SurfaceView的调整,然而在实际应用中,如果SurfaceView的宽高比例与屏幕的宽高比例相同,则它们可以等价地执行。但是,在同时使用onSizeChanged和SurfaceChanged时,应该更新Surface并保证二者的一致性。
关于SurfaceView中的容器ScrollView
相信大家都有过在ScrollView中使用SurfaceView时的经历,由于ScrollView的特殊性,当它包含SurfaceView时,我们可以看到这样一种情况:即可能整个视图只显示一个很小的SurfaceView,而其它部分被截断了。这是由于SurfaceView会首先拥有他的专有Surface,他会在另外一个Windows上完成绘图操作,当SurfaceView被放入一个容器中,容器的绘制就发生了不一致的难题,就像沉没的冰山,超出它底端的部分是不可见的。
为了避免出现这个问题,我们不仅要考虑滚动视图中SurfaceHolder的位置地点,还需要考虑在显示画布时使用坐标系转换,设置正确的x和y坐标,以判断横向滚动和纵向滚动的情况。下面是如何在ScrollView容器中实现更大的SurfaceView的示例:
```
public class MyScrollView extends ScrollView {
public MyScrollView(Context context) {
super(context);
}
public MyScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
//考虑两种方向滚动
int maxHorizontalScrollPosition = 0;
int maxVerticalScrollPosition = 0;
//大的SurfaceView
SurfaceView bigSurface;
SurfaceHolder holder;
Canvas canvas;
@Override
protected void onFinishInflate() {
super.onFinishInflate();
//重新计算SurfaceView的大小
bigSurface = (SurfaceView) findViewById(R.id.lg_surface);
holder = bigSurface.getHolder();
holder.setFixedSize(800,400);
bigSurface.setLayoutParams(new ViewGroup.LayoutParams(800,400));
// 滚动时使用此刻Canvas将调整为正确的地方
canvas = new Canvas();
}
@Override
public void onDraw(Canvas scrollCanvas) {
super.onDraw(scrollCanvas);
// 滚动时使用此刻Canvas将调整为正确的地方。
if (canvas != null && maxHorizontalScrollPosition > 0 || maxVerticalScrollPosition > 0) {
scrollCanvas.save();
scrollCanvas.translate(getScrollX(), getScrollY());
scrollCanvas.drawBitmap(canvasBitmap, 0,0,null);
scrollCanvas.restore();
}
}
//计算滚动的最大值
@Override
protected int computeMaxScrollY() {
return maxVerticalScrollPosition;
}
//计算滚动的最大值
@Override
protected int computeMaxScrollX() {
return maxHorizontalScrollPosition;
}
//SurfaceView更新视图时调用此方法
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
canvas.drawColor(Color.BLUE);
canvas.drawCircle(50,50,30,myPaint);
}
}
```
在上面的代码片段中,我们在滚动视图中使用宽度为800像素,高度为400像素的大SurfaceView,并在重绘时重新计算大小。我们设置了最大滚动程度,可以通过覆盖computeMaxScrollY和computeMaxScrollX方法进行计算。最后,在Surface View更新后,即surfaceChanged()回调中,使用Canvas重新绘制并重绘Canvas。
总结
在使用SurfaceView的过程中,我们将涉及许多与状态相关的问题。SurfaceChanged事件是处理这些问题的一个很好的方法,它可以提供有关Surface的信息,使您可以动态地更改您的UI,以保持UI的完整性。在处理SurfaceView状态变化的同时,我们还可以使用ScrollView容器或其他容器,这些容器需要调整SurfaceView的大小和位置,以保持UI的完成性。我们可以使用两种方法:surfaceChanged()和onSizeChanged()来处理SurfaceView状态变化,但需要维护二者的同步。我们希望本文可以帮助到Android开发人员更好地掌握SurfaceView的状态变化,更好地应对实际开发中可能遇到的相关问题。