如何保证我的 Android SurfaceView 是透明的而不是黑色的?

Posted

技术标签:

【中文标题】如何保证我的 Android SurfaceView 是透明的而不是黑色的?【英文标题】:How do I guarantee that my Android SurfaceView is transparent instead of black? 【发布时间】:2018-11-03 19:13:30 【问题描述】:

我有一个自定义视图,它扩展了SurfaceView,覆盖了我的界面的其余部分,它适用于模拟器,当调试器连接到我的手机时,但当手机使用电池运行时,视图永远不会清除。

public class CustomView extends SurfaceView 

    private final Paint paint;
    private final SurfaceHolder holder;
    private final Context context;

    private float strokeWidth = 4;

    private boolean canvasAlreadyLocked = false;

    public CustomView(Context viewContext, AttributeSet attrs)
    
        super(viewContext, attrs);
        Log.i("CustomView", "CustomView create context & attrs");
        holder = getHolder();
        context = viewContext;
        paint = new Paint(Paint.ANTI_ALIAS_FLAG);
        paint.setColor(Color.WHITE);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(strokeWidth);
        drawLine();
        setZOrderOnTop(true);
        holder.setFormat(PixelFormat.TRANSPARENT);
    

    public void resume() 
        Log.i("CustomView", "Resume the customview display.");
        setZOrderOnTop(true);
        holder.setFormat(PixelFormat.TRANSPARENT);
    

    @Override
    public void onAttachedToWindow()
        super.onAttachedToWindow();
        setZOrderOnTop(true);
    

    @Override
    protected void onDraw(Canvas canvas) 
        super.onDraw(canvas);
    

    protected void drawLine() 
        if (!canvasAlreadyLocked) 
            invalidate();
            if (holder.getSurface().isValid()) 
                try 
                    final Canvas canvas = holder.lockCanvas();
                    canvasAlreadyLocked = true;
                    if (canvas != null) 
                        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                        paint.setColor(Color.BLACK);
                        paint.setStrokeWidth(strokeWidth * 2);
                        canvas.drawLine(0, getY(), getWidth(), getY(), paint);
                        paint.setColor(Color.WHITE);
                        paint.setStrokeWidth(strokeWidth);
                        canvas.drawLine(0, getY(), getWidth(), getY(), paint);
                        holder.unlockCanvasAndPost(canvas);
                        canvasAlreadyLocked = false;
                    
                
                catch (IllegalArgumentException iae)
                
                    Log.w("CustomView", "Exception trying to lock canvas: "+iae.getMessage());
                    Log.getStackTraceString(iae);

                
            
        
     

    private float getY() 
        getHeight()/2;
    


我知道这里的一些调用是多余的 - 这主要是尝试许多不同的事情以使其工作的遗留问题。您会注意到我已经完成了this answer 中推荐的所有操作。

布局是这样的:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_
    android:layout_
    tools:context="com.custom.CustomViewApp">

    <FrameLayout
        android:id="@+id/control"
        android:layout_
        android:layout_
        android:layout_alignParentBottom="true"
        android:layout_alignParentStart="true">

        <com.custom.AutoFitTextureView
            android:id="@+id/texture"
            android:layout_
            android:layout_
            android:layout_alignParentStart="true"
            android:layout_alignParentTop="true" />

        <ImageButton
            android:id="@+id/gpsNotification"
            android:layout_
            android:layout_
            android:src="@drawable/gps_unfixed"
            android:layout_gravity="right"
            android:tint="@color/gps_unfixed"
            android:background="@null" />

        <ProgressBar
            android:id="@+id/camera_spinner"
            style="?android:attr/progressBarStyle"
            android:layout_
            android:layout_
            android:layout_gravity="center_horizontal|center_vertical"
            android:gravity="center"
            android:layout_centerHorizontal="true"
            android:layout_centerVertical="true"
            android:visibility="invisible"
            />

        <com.custom.CustomView
            android:id="@+id/custom_view"
            android:background="@color/transparent"
            android:layout_
            android:layout_
            android:layout_alignParentStart="true"
            android:layout_alignParentTop="true" />

    </FrameLayout>
</FrameLayout>

这是从 ViewFragment 中提取的:

@Override
public void onViewCreated(final View view, Bundle savedInstanceState) 
    mTextureView = (AutoFitTextureView) view.findViewById(R.id.texture);
    gpsNotification = (ImageButton) view.findViewById(R.id.gpsNotification);
    customView = (CustomView) view.findViewById(R.id.custom_view);
    spinner = (ProgressBar) view.findViewById(R.id.camera_spinner);
    spinner.setVisibility(VISIBLE);

我已尽我所能简化这一点,显然在这种情况下发生了更多事情,但希望这足以表明问题可能来自哪里。

AutoFitTextureView 正在显示来自摄像头的视图。

当我在模拟器中运行它时,一切都按预期显示, 无论电池设置如何。 当我在通过 USB 连接的手机上运行它时,一切都按预期显示。 当我在断开连接的手机上运行它时,视图通常(但不总是)显示为纯黑色 - AutoFitTextureView 完全被遮挡,但 CustomView 上的线被绘制。其他组件可见的,这让我怀疑我何时或如何调用setZOrderOnTop() 存在问题。 如果我在片段中打断点并将CustomView 的可见性设置为INVISIBLE,我可以立即看到其他所有内容,但正如您所料,我失去了覆盖。此时手动调用setZOrderOnTop 似乎没有任何改变。 当它正常运行时,我可以使用 Layout Inspector 工具检查结构,但是当 SurfaceView 不透明时,Layout Inspector 会引发Unexpected Error: Empty View Hierarchy 消息。我一直无法在 Logcat 中找到相应的错误消息。

如何确保我的 SurfaceView 派生视图始终透明?如果我不能保证,有什么方法可以测试它当前是否透明的,如果不是,我可以进行调解?

【问题讨论】:

我使用相机 SurfaceView,另一个用于绘图的 SurfaceView 和 2 个用于相机控件的其他布局,它工作正常,我也尝试过其他布局和其他示例。我唯一使用的是holder.setFormat(PixelFormat.TRANSLUCENT); 来设置 SurfaceView 的透明度,我用于在它的构造函数中绘图。您可能需要从相机预览中剥离代码并逐个添加其他组件以了解发生了什么问题。无需为holder 设置格式即可获得透明的surfaceview。 谢谢你,我目前正在尝试剥离一些东西,如果失败,我可能会看看我是否可以将视频提要与绘图结合在一个视图上,但看起来 SurfaceView 是为其他所有人工作,所以我觉得我一定是在做一些奇怪的事情。 记住SurfaceView的Surface是独立于SurfaceView的View的。 View 的 Z 位置和透明度独立于 Surface 的 Z 位置和透明度。 TextureViews 的行为类似于 Views。我不知道为什么空调状态很重要。如果你想进一步挖掘,可以用dumpsys检查各个表面的状态;参见例如source.android.com/devices/graphics/arch-sv-glsv. 【参考方案1】:

认为我现在已经解决了这个问题,问题是我试图将SurfaceView 派生类型放在TextureView 派生类型上。将AutofitTextureView 换成另一个SurfaceView 显示相机预览似乎已经成功了。

【讨论】:

以上是关于如何保证我的 Android SurfaceView 是透明的而不是黑色的?的主要内容,如果未能解决你的问题,请参考以下文章

android 使用canvas画线,如何保证快速画出圆滑的曲线?

[转] Android开发之如何保证Service不被杀掉(broadcast+system/app)

Android 如何保证逻辑业务类只初始化一次

Android 如何保证逻辑业务类只初始化一次

Android 如何保证逻辑业务类只初始化一次

Android 开发之Service 探索如何保证Service不被杀死或被kill之后自动重启