How can I mirror the whole display? - android

I want to be able to mirror my app so it can be viewed in the windshield of a vehicle.
My XML has several nested LinearLayouts, TextViews and ImageViews. Currently I'm transforming each one and although it is mirrored, the structure of the elements is not (what was at the top is now at the bottom).
I've been looking for days and so far have tried a couple of approaches that have failed.
An animation that uses a matrix to flip on the X axis kind of works, except that it either reverts back or it stays and doesn't update, which is no good for interacting with the app.
I just tried to create a custom LinearLayout extending the parent one, hoping that I could apply a Matrix in its onDraw() method but that gives me a blank screen (I had to set setWillNotDraw(false); to hit the onDraw()).

Well eventually I found a solution that works well for me (so far it's caused no issues for users).
My solution was to override dispatchDraw to scale the canvas in my custom LinearLayout. Then I just needed to flip the touch events by overriding dispatchTouchEvent:
public class CustomContainer extends LinearLayout {
public CustomContainer(Context context) {
super(context);
this.setWillNotDraw(false);
}
public CustomContainer(Context context, AttributeSet attrs) {
super(context, attrs);
this.setWillNotDraw(false);
}
public CustomContainer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.setWillNotDraw(false);
}
#Override
protected void dispatchDraw(Canvas canvas) {
canvas.save(Canvas.MATRIX_SAVE_FLAG);
// Flip the view canvas
if (MyHUDActivity.mHUDMode) canvas.scale(1,-1, getWidth(), getHeight()/2f);
super.dispatchDraw(canvas);
canvas.restore();
}
#Override
public boolean dispatchTouchEvent(MotionEvent event) {
// If in HUD mode then flip the touch zones
if (MyHUDActivity.mHUDMode) event.setLocation(event.getX(), getHeight()-event.getY());
return super.dispatchTouchEvent(event);
}
}

You can use the new Animation api to deal with reverting back after the horizontal flip.

Related

Rotated View's onTouch wrong coordinates

I must get upside down view w/o moving the System menu. So I do:
getWindow().getDecorView().getRootView().setRotation(180);
But all child buttons get onTouch event not when they being touched, but when I touch the areas where they would be without rotation.
How can I fix the problem?
Got solution without translation matrix and such... Just create a new Layout class like this:
public class RotatableView extends FrameLayout {
public RotatableView(Context context) {
this(context, null);
}
public RotatableView(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
setUpsideDown();
}
private void setUpsideDown() {
this.setRotation(180);
}
}
and use it for your layouts

Render content of ViewGroup twice

Im trying to create a sort of HUD overlay for Google Cardboard.
The HUD needs to be duplicated (one for each eye). A simplistic solution would be to manually copy all the XML elements into another view but giving them different names. This feels like a bad approach since it involves lots of code duplication.
So i came up with the following solution for a ViewGroup with is supposed to render everything two times:
public class StereoView extends FrameLayout {
private static final String TAG = StereoView.class.getSimpleName();
public StereoView(Context context) {
super(context);
init(context);
}
private void init(Context context) {
testPaint.setColor(Color.RED);
}
private Paint testPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right/2, bottom);
}
#Override
protected void dispatchDraw(Canvas canvas) {
canvas.save();
canvas.translate(getWidth() / 2, 0);
super.dispatchDraw(canvas);
canvas.restore();
super.dispatchDraw(canvas);
}
public StereoView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public StereoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public StereoView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context);
}
}
The first problem is that neither dispatchDraw or onDraw is called except from one or two times. It is not called when child views are invalidated.
The second problem is that background on elements which has a with of MATCH_PARENT renders outside the ViewGroups inner bounds:
200DP width
MATCH_PARENT
Is this approach hoping for too much, or am i thinking wrong? Creating a completely custom view to handle complex layouts and images seems like lots of work while copying my layout seems like bad design.
You say:
A simplistic solution would be to manually copy all the XML elements
into another view but giving them different names. This feels like a
bad approach since it involves lots of code duplication.
Actually you can go ahead and use the <include> tag. All you need to do is create a layout that contains all the views that you are going to show to a single eye. Then in your main layout you have to <include> this layout twice, one for the left eye and the other for the right eye.
You might wonder, if this is the case then how can i use findViewById() on this main layout, since now there will be two views with the same id. Well, you can fix that by doing it as follows. Let's say you have created the eye.xml layout. Then your main_layout should look like below.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<include
android:id="#+id/leftEye"
layout="#layout/eye" />
<include
android:id="#+id/rightEye"
layout="#layout/eye" />
</LinearLayout>
When you do the findViewById() in your code, you could do that as follows:
RelativeLayout leftEye = (RelativeLayout)findViewById(R.id.leftEye);
ImageView iv = (ImageView)leftEye.findViewById(R.id.something);
You need write a simple method in your activity where you just pass the leftEye or rightEye as a parameter and perform all code in this method. This lets you perform UI changes in leftEye and rightEye simultaneously.
In the future, you could write a custom View in which you could just inflate the eye.xml. That would modularize your idea.
This is my thoughts to your problem.
A ViewGroup hosts Views; any xml layout are Views, so extend a ViewGroup of your choice, either LinearLayout ,Framelayout-(i prefer), and in your initialisation process, inflate your Layout twice and add them as Views later you can research on how to use onLayout() to position your Views in your preferred Location.
And what ever you call a View 1, View 2 needs to be onboard, you can bind the two, using any approach you want, interfaces or beans
Note
you create one layout and inflate it twice. which will give you two separate View objects, hence this won't be code duplication as its more of
Elltz _20yearElltz = new Elltz(20),_21yearElltz = new Elltz(21);
Hope it helps

Extended LinearLayout not rotating

I'm trying to create a custom LinearLayout that can rotate all children in the layout by a set angle. That is, I want to rotate the entire canvas and the children, not the children individually.
I need to support API Level 10, so android:rotate which was introduced in API Level 11 will not help me here.
I've been looking around and think I need to do something along the lines of this code below, but with this code nothing gets rotated.
Any ideas on what I'm missing here?
public class RotationLayout extends LinearLayout {
public RotationLayout(Context context) {
super(context);
init();
}
public RotationLayout(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public RotationLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
setWillNotDraw(false);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.save();
canvas.rotate(350,0,0);
super.onDraw(canvas);
canvas.restore();
}
}`
I tried your code with a few changes.
In the onCreate(Bundle) of an activity class, I initialized the RotationLayout:
RotationLayout rl = new RotationLayout(this);
ImageView iv = new ImageView(this);
iv.setImageDrawable(getResources().getDrawable(R.drawable.some_drawable));
rl.addView(iv);
setContentView(rl);
And the view was indeed rotated 350 degrees(or 10 degrees counter-clockwise) around (0,0). This was what I changed:
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.rotate(350,0,0);
}
From notes on Canvas.save() (Link):
Saves the current matrix and clip onto a private stack. Subsequent
calls to translate,scale,rotate,skew,concat or clipRect,clipPath will
all operate as usual, but when the balancing call to restore() is
made, those calls will be forgotten, and the settings that existed
before the save() will be reinstated.

Mirror (flip) a View / ProgressBar

I have a custom built circular progress bar used for a seconds counter on a clock, which I'd like to flip, so that the clock is counting counter-clockwise.
Searching here for a solution, I found this:
Right to Left ProgressBar?
Which obviously works for a horizontal progress bar, but I can't simply rotate it 180 degrees, since that will just move the clock 180 degrees and it will still be ticking clockwise.
Is there any other way to mirror a ProgressBar, or any View?
Edit:
Just found "android:rotationY" and the programmatic equivalent, but this ideally needs to be for 2.2 and above..
I was able to flip ProgressBar simply with attribute scaleX in XML:
android:scaleX="-1"
This works for other View as well.
Extend ProgressBar in the same way you would for a horizontal progress bar, but in the onDraw method, flip the canvas rather than rotate it:
public class InverseProgressBar extends ProgressBar {
public InverseProgressBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public InverseProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public InverseProgressBar(Context context) {
super(context);
}
#Override
protected synchronized void onDraw(Canvas canvas) {
canvas.scale(-1f, 1f, super.getWidth() * 0.5f, super.getHeight() * 0.5f);
super.onDraw(canvas);
}
}
Yes. In newer versions of material design library, the circular progress bar can be mirrored:
in the layout with this attribute
app:indicatorDirectionCircular
or programmatically with this method
setIndicatorDirection()
Refer here for more info.

Android RotationAnimation Complete

I am trying to use the RotationAnimation to move an ImageButton. I have extended ImageButton to override onAnimationEnd to avoid the screen flicker at the end of the animation, and would like to update the position of the actual button [since the animation is just a superficial move]. How can I determine the exact coordinates of the button at the end of the animation? Attached is some of my code..
public class HomeNavigationButton extends ImageButton {
public HomeNavigationButton(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}
public HomeNavigationButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public HomeNavigationButton(Context context) {
super(context);
}
#Override
protected void onAnimationEnd() {
super.onAnimationEnd();
int l = ?
int t = ?
int r = ?
int b = ?
this.layout(l,t,r,b);
}
The position of the button animation? I would go out on a limb and guess you can't. Animations are interesting. They're not "real". Think of them as a mirage. Try this. Rotate the image VERY slowly. If the image is a rectangle, when the image is about 45 degrees, click where the image used to be, but is no long. You'll actually register a click, because the image isn't actually being rotate in real coordinates.
I would consider actually animating the button like a game sprite, or do some trial and error with the animation and set values manually. If you do the second one, make sure you dynamically convert to DIP coordinates rather than raw ints.

Categories

Resources