Adding Hello Virtual World text overlay

For starters, we're just going to put some text on the screen that you might use for a toast message to the user, or a heads-up display (HUD) with informative content. We're going to implement this incrementally in small steps:

  1. Create a simple overlay view with some text.
  2. Center it on the screen.
  3. Add parallax for stereoscopic viewing.

A simple text overlay

First, we'll add some overlay text in a simple way, not stereoscopically, just text on the screen. This will be our initial implementation of the OverlayView class.

Open the activity_main.xml file, and add the following lines to add an OverlayView to your layout:

<.OverlayView
   android:id="@+id/overlay"
   android:layout_width="fill_parent"
   android:layout_height="fill_parent"
   android:layout_alignParentLeft="true"
   android:layout_alignParentTop="true" />

Note that we reference the OverlayView class with just .OverlayView. You may do this if your view class is in the same package as your MainActivity class. We did the same earlier for .MainActivity.

Next, we write the Java class. Right-click on the app/java folder (app/src/main/java/com.cardbookvr.launcherlobby/), and navigate to New | Java Class. Name it OverlayView.

Define the class so that it extends LinearLayout, and add a constructor method, as follows:

public class OverlayView extends LinearLayout{

    public OverlayView(Context context, AttributeSet attrs) {
        super(context, attrs);

        TextView textView = new TextView(context, attrs);
        addView(textView);

        textView.setTextColor(Color.rgb(150, 255, 180));
        textView.setText("Hello Virtual World!");
        setVisibility(View.VISIBLE);
    }
}

The OverlayView() constructor method creates a new TextView instance with a pleasant greenish color and the text Hello Virtual World!.

Run the app, and you will notice our text in the top-left corner of the screen, as shown in the following screenshot:

A simple text overlay

Center the text using a child view

Next, we create a separate view group and use it to control the text object. Specifically, to center it in the view.

In the OverlayView constructor, replace the TextView with an instance of a different ViewGroup helper class that we're going to write called EyeView. Presently, it's monoscopic but soon we'll use this class to create two views: one for each eye:

    public OverlayView(Context context, AttributeSet attrs) {
        super(context, attrs);

        LayoutParams params = new LayoutParams(
            LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1.0f);
        params.setMargins(0, 0, 0, 0);

        OverlayEye eye = new OverlayEye(context, attrs);
        eye.setLayoutParams(params);
        addView(eye);

        eye.setColor(Color.rgb(150, 255, 180));
        eye.addContent("Hello Virtual World!");
        setVisibility(View.VISIBLE);
  }

We create a new instance of OverlayEye named eye, set its color, and add the text string.

When using a ViewGroup class, you need to specify LayoutParams to tell the parent how to lay out the view, which we want to be full screen size with no margins (refer to http://developer.android.com/reference/android/view/ViewGroup.LayoutParams.html).

In the same OverlayView.java file, we're going to add the private class named OverlayEye, as follows:

    private class OverlayEye extends ViewGroup {
        private Context context;
        private AttributeSet attrs;
        private TextView textView;
        private int textColor;

        public OverlayEye(Context context, AttributeSet attrs) {
            super(context, attrs);
            this.context = context;
            this.attrs = attrs;
        }

        public void setColor(int color) {
            this.textColor = color;
        }

        public void addContent(String text) {
            textView = new TextView(context, attrs);
            textView.setGravity(Gravity.CENTER);
            textView.setTextColor(textColor);
            textView.setText(text);
            addView(textView);
        }
    }

We have separated the TextView creation from the OverlayEye constructor. The reason for this will soon become clear.

The OverlayEye constructor registers the context and attributes needed to add new content views to the group.

Then, addContent creates the TextView instance and adds it to the layout.

Now we define onLayout for OverlayEye, which sets the margins of textview, specifically the top margin, as a mechanism to force the text to be vertically centered:

        @Override
        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
            final int width = right - left;
            final int height = bottom - top;

            final float verticalTextPos = 0.52f;

            float topMargin = height * verticalTextPos;
            textView.layout(0, (int) topMargin, width, bottom);
        }

To center the text vertically, we push it down from the top of the screen using a top margin. The text will be positioned vertically just below the center of the screen, as specified by verticalTextPos, a percentage value where 1.0 is the full height of the screen. We picked a value of 0.52 to push the top of the text down to an extra 2% just below the middle of the screen.

Run the app, and you will notice that our text is now centered on the screen:

Center the text using a child view

Create stereoscopic views for each eye

Now, we get real. Virtually, that is. For VR, we need stereoscopic left and right eye views. Fortunately, we have this handy OverlayEye class that we can reuse for each eye.

Your eyes are separated by a measurable distance, which is referred to as your interpupillary distance (IPD). When you view a stereoscopic image in a Cardboard headset, there are separate views for each eye, offset (horizontally) by a corresponding distance.

Let's assume that our text is on a plane perpendicular to the view direction. That is, we're looking straight at the text plane. Given a numeric value corresponding to the distance of the text from your eyes, we can shift the views for the left and right eyes horizontally by a fixed number of pixels to create the parallax effect. We'll call this the depthOffset value. A larger depth offset will cause the text to appear closer; a smaller depth offset will cause the text to appear further away. A depth offset of zero will indicate no parallax, as if the text is very far away (greater than 20 feet).

For our application, we're going to choose a depth offset factor of 0.01, or 1% measured in screen coordinates (a fraction of screen size). The icons will appear to be about 2 meters away (6 feet), which is a comfortable distance for VR, although this value is an ad hoc approximation. Using percentages of screen size instead of actual pixel amounts, we can ensure that our application will adapt to any screen/device size.

Let's implement this now.

To begin, declare variables for the leftEye and rightEye values at the top of the OverlayView class:

public class OverlayView extends LinearLayout{
    private final OverlayEye leftEye;
    private final OverlayEye rightEye;

Initialize them in the OverlayView constructor method:

    public CardboardOverlayView(Context context, AttributeSet attrs) {
        super(context, attrs);

        LayoutParams params = new LayoutParams(
                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 1.0f);
        params.setMargins(0, 0, 0, 0);

        leftEye = new OverlayEye(context, attrs);
        leftEye.setLayoutParams(params);
        addView(leftEye);

        rightEye = new OverlayEye(context, attrs);
        rightEye.setLayoutParams(params);
        addView(rightEye);

        setDepthFactor(0.01f);
        setColor(Color.rgb(150, 255, 180));
        addContent("Hello Virtual World!");
        setVisibility(View.VISIBLE);
   }

Notice the six lines in the middle where we define leftView and rightView and call addView for them. The setDepthFactor call will set that value in the views.

Add the setter methods for the depth, color, and text content:

    public void setDepthFactor(float factor) {
        leftEye.setDepthFactor(factor);
        rightEye.setDepthFactor(-factor);
    }

    public void setColor(int color) {
        leftEye.setColor(color);
        rightEye.setColor(color);
    }

    public void addContent(String text) {
        leftEye.addContent(text);
        rightEye.addContent(text);
    }

Note

Important: notice that for the rightEye value we use a negative of the offset value. To create the parallax effect, it needs to be shifted to the opposite direction of the left eye view. We can still achieve parallax by only shifting one eye, but then all of the content will appear to be slightly off center.

The OverlayEye class needs the depth factor setter, which we convert to pixels as depthOffset. Also, declare a variable for the physical view width (in pixels):

        private int depthOffset;
        private int viewWidth;

In onLayout, set the view width in pixels after it's been calculated:

            viewWidth = width;

Define the setter method, which converts the depth factor to a pixel offset:

        public void setDepthFactor(float factor) {
            this.depthOffset = (int)(factor * viewWidth);
        }

Now, when we create textView in addContent, we can shift it by the depthOffset value in pixels:

            textView.setX(depthOffset);
            addView(textView);

When you run the app, your screen will look like this:

Create stereoscopic views for each eye

The text is now in stereo views, although it's "stuck to your face" as it doesn't move when your head moves. It's attached to a visor or HUD.

Controlling the overlay view from MainActivity

The next step is to remove some of the hardcoded properties and control them from the MainActivity class.

In MainActivity.java, add an overlayView variable at the top of the class:

public class MainActivity extends CardboardActivity implements CardboardView.StereoRenderer {
    private OverlayView overlayView;

Initialize its value in onCreate. We'll display the text using the addContent() method:

        ...
        setCardboardView(cardboardView);
        overlayView = (OverlayView) findViewById(R.id.overlay);
        overlayView.addContent("Hello Virtual World");

Don't forget to remove the call to addContent from the OverlayView method:

        setDepthOffset(0.01f);
        setColor(Color.rgb(150, 255, 180));
        addContent("Hello Virtual World!");
        setVisibility(View.VISIBLE); 
   }

Run the app one more time. It should look the same as shown earlier.

You can use code like this to create a 3D toast, such as a text notification message. Or, it can be used to construct a HUD panel to share in-game status or report the current device attributes. For example, to show the current screen parameters you can put them into MainActivity:

        ScreenParams sp = cardboardView.getHeadMountedDisplay().getScreenParams();
        overlayView.setText(sp.toString());

This will show the phone's physical width and height in pixels.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset