Surfaceholder.addCallback不工作; surfaceCreated,surfaceChanged和surfaceDestroyed永远不会被调用

时间:2014-07-12 03:18:54

标签: android scala

我正在使用Scala基于Android ApiDemos中com.example.android.apis.graphics.CameraPreview中的代码实现相机预览。但是,SurfaceHolder.CallbacksurfaceCreatedsurfaceChangedsurfaceDestroyed)的方法似乎从未被调用过。这是代码:

class TakePictureActivity extends ActivityWithLog {
  lazy val preview = findViewById(R.id.preview).asInstanceOf[CameraPreview]

  override def onCreate(savedInstanceState: Bundle) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.take_picture)
  }

  override def onResume {
    super.onResume()
    preview.camera = Camera.open()
  }

  override def onPause {
    super.onPause()
    for (c <- Option(preview.camera)) {
      c.release()
      preview.camera = null
    }
  }
}

class CameraPreview(context:  Context,
                    attrs:    AttributeSet,
                    defStyle: Int)
    extends ViewGroup(context, attrs, defStyle)
    with SurfaceHolder.Callback
    with LogTag {
  def this(context: Context, attrs: AttributeSet) = this(context, attrs, 0)
  def this(context: Context) = this(context, null)

  private var maybeCamera: Option[Camera] = None

  def camera = maybeCamera.getOrElse(null)

  def camera_=(c: Camera) {
    maybeCamera = Option(c)
    if (maybeCamera.nonEmpty) requestLayout()
  }

  private var previewSize: Option[Camera#Size] = None

  def optimalPreviewSize(sizes: Seq[Camera#Size],
                         targetWidth: Int, targetHeight: Int) = {
    val targetRatio = targetWidth.asInstanceOf[Double] / targetHeight
    val targetArea  = targetWidth * targetHeight
    def closestInArea(sizes: Seq[Camera#Size]) =
      sizes.minBy(s => abs(s.width * s.height - targetArea))
    sizes.filter { size =>
      val r = size.width.asInstanceOf[Double] / size.height
      abs(r - targetRatio) < 0.1
    } match {
      case Seq() => closestInArea(sizes)
      case xs    => closestInArea(xs)
    }
  }

  override def onMeasure(widthMeasureSpec: Int,
                         heightMeasureSpec: Int) {
    val w = View.resolveSize(getSuggestedMinimumWidth, widthMeasureSpec)
    val h = View.resolveSize(getSuggestedMinimumHeight, heightMeasureSpec)
    setMeasuredDimension(w, h)
    for (c <- maybeCamera) {
      val sizes = c.getParameters.getSupportedPreviewSizes.asScala
      previewSize = Some(optimalPreviewSize(sizes, w, h))
    }
  }

  override def onLayout(changed: Boolean,
                        l: Int, t: Int, r: Int, b: Int) {
    if (changed && getChildCount > 0) {
      val child = getChildAt(0)
      val (w, h) = (r - l, b - t)
      val (pw, ph) = previewSize match {
        case Some(s) => (s.width, s.height)
        case None    => (w, h)
      }
      if (w * ph > h * pw) {
        val scaledChildWidth = pw * h / ph
        child.layout((w - scaledChildWidth) / 2, 0,
                     (w + scaledChildWidth) / 2, h)
      } else {
        val scaledChildHeight = ph * w / pw
        child.layout(0, (h - scaledChildHeight) / 2,
                     w, (h - scaledChildHeight) / 2)
      }
    }
  }

  val surfaceView = new SurfaceView(context)
  addView(surfaceView)
  surfaceView.getHolder.addCallback(this)

  def surfaceCreated(holder: SurfaceHolder) {
    Log.d(TAG, "surfaceCreated")
    for (c <- maybeCamera) c.setPreviewDisplay(holder)
  }

  def surfaceChanged(holder: SurfaceHolder,
                     format: Int, width: Int, height: Int) {
    Log.d(TAG, "surfaceChanged")
    for (camera  <- maybeCamera;
         preSize <- previewSize) {
      val params = camera.getParameters
      params.setPreviewSize(preSize.width, preSize.height)
      Log.d(TAG, s"setPreviewSize: ${preSize.width}x${preSize.height}")
      requestLayout()
      camera.setParameters(params)
      camera.startPreview()
    }
  }

  def surfaceDestroyed(holder: SurfaceHolder) {
    Log.d(TAG, "surfaceDestroyed")
    for (c <- maybeCamera) c.stopPreview()
  }
}

和布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent">
  <CameraPreview android:id="@+id/preview"
                 android:layout_width="match_parent"
                 android:layout_height="match_parent" />
  <Button android:text="Capture"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content" />
</RelativeLayout>

任何人都可以帮我解决这个问题吗?谢谢!

1 个答案:

答案 0 :(得分:1)

我自己解决了。这是CameraPreview.onLayout中的拼写错误;这些行

child.layout(0, (h - scaledChildHeight) / 2,
             w, (h - scaledChildHeight) / 2)

应该改为这些

child.layout(0, (h - scaledChildHeight) / 2,
             w, (h + scaledChildHeight) / 2)

原因是当表面没有正确定位在屏幕上时(即完全离开屏幕),将永远不会调用回调。很高兴知道!