I have a custom view that extends SurfaceView
overlaying the rest of my interface, it works on emulators and when the debugger is connected on my phone, but but when the phone is running on battery the view never clears.
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;
}
}
I'm aware that some of the calls here are redundant - that is mostly the legacy of trying lots of different things to try to make it work. You will notice that I have already done everything recommended in this answer.
The layout works like this:
<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_width="match_parent"
android:layout_height="match_parent"
tools:context="com.custom.CustomViewApp">
<FrameLayout
android:id="@+id/control"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentStart="true">
<com.custom.AutoFitTextureView
android:id="@+id/texture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true" />
<ImageButton
android:id="@+id/gpsNotification"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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_width="wrap_content"
android:layout_height="wrap_content"
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_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true" />
</FrameLayout>
</FrameLayout>
This is pulled in from a 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);
}
I have tried to simplify this as far as I can and obviously there is a lot more happening in this scenario, but hopefully this is enough to indicate where the problem might be coming from.
The AutoFitTextureView
is displaying the view from the camera.
- When I run it in an emulator, everything displays as expected, regardless of the battery settings.
- When I run it on my phone connected by USB everything displays as expected.
- When I run it on my phone disconnected, the view will usually, but not always, show as plain black - the
AutoFitTextureView
is completely obscured, but the line on theCustomView
is drawn. The other components are visible, which leads me to suspect there is a problem with when or how I callsetZOrderOnTop()
. - If I hit a breakpoint in the fragment and set the visibility of the
CustomView
toINVISIBLE
I can immediately see everything else, but as you might expect I lose the overlay. Manually callingsetZOrderOnTop
at that point doesn't seem to change anything. - When it is running correctly, I can examine the structure with the Layout Inspector tool, but when the SurfaceView is opaque the Layout Inspector raises an
Unexpected Error: Empty View Hierarchy
message. I haven't been able to locate a corresponding error message in Logcat.
How do I ensure my SurfaceView-derived View is always transparent? If I can't guarantee that, is there any way I can test whether it currently is transparent so that I can intercede if it is not?
from How do I guarantee that my Android SurfaceView is transparent instead of black?
No comments:
Post a Comment