Android Tablet UI : multi-pane fragment or linear layout - android-layout

I need to create app that will initially work only for tablets. I'm aware of the “List-and-Detail” pattern in which there’s a list of something, where clicking on the list brings up details on the item that was clicked upon.
That’s just perfect as long as all the layouts always have two panes: one for the list of items and one for the detail of the selected item.
Well, in my app I want to use the first pane that is usually used for the list of items as the menu of all the available option in the app and the second pane, the one that takes up most of the available space, to show a specific layout based on the option that was selected in the first fragment.
The problem is that the second pane will not always be composed of a single part.
These are all the possible scenarios I will have(the left part with the red frame represents the menu that should always be displayed):
1. Details pane composed by a single part:
2. Details pane composed by two parts of the same size, which do not have a master-detail relationship.
3. Details pane composed by two parts, which do have a mater-detail relationship, but the master part is wider than the details part
#Edit :
Since the main goal of my app is to help waiters take orders, this third scenario comes into play when:
The waiter selects the option "Take order" from the left menu (let's pretend it's the "Item two" in the image)
In the first part of the details pane, the waiter can choose among all the categories of food they serve (pretend it's the list at the top) and according to the category selected, the below list shows all the dishes under that specific category.
Once the waiter taps on a dish it's automatically added to the second part of the details pane (the one that is hidden in the first scenario)
4. Details pane composed by two parts, which do have a mater-detail relationship, but the details part is wider than the master part.
I know that if want to reuse the layout from different size of screens and orientations I have to use fragments, but have to determine on the fly one to show two or one part (fragments) in the details pane or which part will be wider than the other seems like a lot of work and to be honest I’m not sure how to do it since all I know now is that the main activity, the one with the slots for the menu pane and the details pane should be similar to this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/menu"
android:layout_weight="30"
android:layout_width="0px"
android:layout_height="match_parent"
/>
<FrameLayout
android:id="#+id/content"
android:layout_weight="70"
android:layout_width="0px"
android:layout_height="match_parent"
/>
</LinearLayout>
Do you think you could give some UI guidelines here? Maybe I’m over thinking all this I should go with a single linear layout with the number of parts I need.
Thanks in advance.

Have you seen the Retrieving a List of Contacts example ? Here they have shown how to properly implement multi-pane master-detail flow. The same code provides a multi-pane landscape layout on tablets, and a single-pane portrait layout on normal handsets. It is an interesting, though slightly complicated, example.
About having three panes in some cases: I don't think this is a great idea. I think that at any given time there should be only one list screen and one detail screen, and you should implement your app's UI flow to support such an idiom. It would not be very difficult to implement a three-pane layout, but I doubt it would be useful or innovative.
EDIT:
I've understood your requirement, and I have to admit that I did not realize the possibility of this type of usage. Its a good design. What I feel is, you don't really need to think in terms of three panes: rather, treat the left list as the list-pane, and the entire right side as a single detail-pane. Inside this one detail-pane, you can have three nested Fragments for the three separate regions. Or instead, since nested Fragments can sometimes create more problems than they solve, let there be three LinearLayouts. That would be easier to handle, What I am trying to do is simplify the layout so that it works properly and without complicated code.

What I got from your question is that you want to set different layout weight for different situations right? If it is so then you can do it programmatically, also you want to hide your layout for first case so use 3 layouts for menu, content1 and content2 hide content2 for first case as
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/menu"
android:layout_weight="30"
android:layout_width="0px"
android:layout_height="match_parent"
/>
<FrameLayout
android:id="#+id/content2"
android:layout_width="0px"
android:layout_height="match_parent"
/>
<FrameLayout
android:id="#+id/content2"
android:layout_width="0px"
android:layout_height="match_parent"
/>
</LinearLayout>
For first case use:
LinearLayout.LayoutParams paramsmenu = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT);
paramsmenu.weight = 30f;
FrameLayout frameLayoutmenu = (FrameLayout)findViewById(R.id.menu);
frameLayoutmenu.setLayoutParams(paramsmenu);
LinearLayout.LayoutParams params1 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT);
params1.weight = 70f;
FrameLayout frameLayoutContent1 = (FrameLayout)findViewById(R.id.content1);
frameLayoutContent1.setLayoutParams(params1);
FrameLayout frameLayoutContent2 = (FrameLayout)findViewById(R.id.content2);
frameLayoutContent2.setVisibility(View.GONE);
For second case use:
LinearLayout.LayoutParams paramsmenu = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT);
paramsmenu.weight = 30f;
FrameLayout frameLayoutmenu = (FrameLayout)findViewById(R.id.menu);
frameLayoutmenu.setLayoutParams(paramsmenu);
LinearLayout.LayoutParams params1 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT);
params1.weight = 35f;
FrameLayout frameLayoutContent1 = (FrameLayout)findViewById(R.id.content1);
frameLayoutContent1.setLayoutParams(params1);
FrameLayout frameLayoutContent2 = (FrameLayout)findViewById(R.id.content2);
LinearLayout.LayoutParams params2 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT);
params2.weight = 35f;
frameLayoutContent2.setLayoutParams(params2);
For third case use:
LinearLayout.LayoutParams paramsmenu = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT);
paramsmenu.weight = 30f;
FrameLayout frameLayoutmenu = (FrameLayout)findViewById(R.id.menu);
frameLayoutmenu.setLayoutParams(paramsmenu);
LinearLayout.LayoutParams params1 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT);
params1.weight = 40f;
FrameLayout frameLayoutContent1 = (FrameLayout)findViewById(R.id.content1);
frameLayoutContent1.setLayoutParams(params1);
FrameLayout frameLayoutContent2 = (FrameLayout)findViewById(R.id.content2);
LinearLayout.LayoutParams params2 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT);
params2.weight = 30f;
frameLayoutContent2.setLayoutParams(params2);
For fourth case use:
LinearLayout.LayoutParams paramsmenu = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT);
paramsmenu.weight = 30f;
FrameLayout frameLayoutmenu = (FrameLayout)findViewById(R.id.menu);
frameLayoutmenu.setLayoutParams(paramsmenu);
LinearLayout.LayoutParams params1 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT);
params1.weight = 20f;
FrameLayout frameLayoutContent1 = (FrameLayout)findViewById(R.id.content1);
frameLayoutContent1.setLayoutParams(params1);
FrameLayout frameLayoutContent2 = (FrameLayout)findViewById(R.id.content2);
LinearLayout.LayoutParams params2 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT);
params2.weight = 50f;
frameLayoutContent2.setLayoutParams(params2);

Related

Why doesn't textView21.SetLeftTopRightBottom(1,200,45, 275) change the size of a textview?

Why doesn't textView21.SetLeftTopRightBottom(1,200,45, 275) change the size of a textview?
<TextView
android:id="#+id/textView21_id"
android:layout_width="60dp"
android:layout_height="wrap_content"
android:layout_x="22dp"
android:layout_y="105dp"
android:height="50dp"
android:gravity="center"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text=""
android:textSize="30sp"
android:textColor="#000000"
android:background="#9999cc"
android:singleLine="false"/>
There's the activity_main.xml snippet. Is there something there which overrides the SetLeftTopRightBottom?
I am still trying to get two different but ordinary mobile phones (cell phones) to display a view the same.
pic of same app in two phones
You can see the left hand pic shows to below the grey '6:' textview and includes the 'Go' textview and all the right hand 5th-letter-in-the-word textview areas whereas the other pic doesn't.
I've figured out how to get each phone's display metrics
var metrics = Resources.DisplayMetrics;
gnumWidthDp = ConvertPixelsToDp(metrics.WidthPixels);
gnumHeightDp = ConvertPixelsToDp(metrics.HeightPixels);
I thought it would be easy to do the SetLeftTopRightBottom thing and set each textview in the right place.
Please can you tell me what I have missed? Thank you all.
In the official document, SetLeftTopRightBottom method is meant to be used in animations only as it applies this position and size for the view only temporary and it can be changed back at any time by the layout.
More information: https://developer.android.com/reference/android/view/View#setLeftTopRightBottom(int,%20int,%20int,%20int)
In addition, the size of the textview21 had been ensured in the xml. If you want to set the size of it dynamically, you need to use the LayoutParams. Such as:
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(300, 200);
//300 is width and 200 is height
textView.LayoutParameters = layoutParams;

how do I change the android:layout_marginTop of a button?

I need to change the property android:layout_marginTop of a button programmatically. But the code below does not produce the same result as changing on the xml. I wonder if anyone can spot the issue, here's my button:
<Button
android:id="#+id/cta_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="0dp"
android:background="#color/cta_button_background"
android:text="CTA"
android:textColor="#color/cta_button_foreground"
android:textSize="18sp"
android:visibility="visible" />
now I need to change the "marginTop" programmatically based on some logic. Here is what I have tired but does not work properly.
// Adjust button to float above the image
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
int marginTop = Pixel.convertPxToDp(someNumber);
params.setMargins(0, marginTop, 0, 0);
mCallToActionButton.setLayoutParams(params);
mCallToActionButton.invalidate();
"mCallToActionButton" has a valid pointer to the button and 'someNumber' can be anything value.
I used this thread but the button is moving somewhere else, not just changing its topMargin
How to set a button's parameters programatically
thank you.
This way works:
ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) mCallToActionButton.getLayoutParams();
params.topMargin = Pixel.convertDpToPx(newImageMargin);
mCallToActionButton.setLayoutParams(params);
Try to set gravity to your LayoutParams
params.gravity = Gravity.CENTER_HORIZONTAL;

getSupportActionBar().setCustomView(view, layout); does not cover entire actionbar

I'm trying to set a custom actionbar background but it does not fill up the entire space.
it leaves like a 5dp grey actionbar from left side.
I used the following:
android.support.v7.app.ActionBar.LayoutParams layout = new android.support.v7.app.ActionBar.LayoutParams(android.support.v7.app.ActionBar.LayoutParams.FILL_PARENT, android.support.v7.app.ActionBar.LayoutParams.FILL_PARENT);
getSupportActionBar().setCustomView(view, layout);
getSupportActionBar().setDisplayShowCustomEnabled(true);
this is my custom actionbar xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="fill_horizontal"
android:background="#color/actionbar_color"
></RelativeLayout>
I have tried different styles but none worked.
please help
fixed it by doing:
//to set same background color on entire actiobar
getSupportActionBar().setBackgroundDrawable( getResources().getDrawable(R.drawable.actionbar_color));
//to display custom layout with same BG color
android.support.v7.app.ActionBar.LayoutParams layout = new android.support.v7.app.ActionBar.LayoutParams(android.support.v7.app.ActionBar.LayoutParams.FILL_PARENT, android.support.v7.app.ActionBar.LayoutParams.FILL_PARENT);
getSupportActionBar().setCustomView(view, layout);
getSupportActionBar().setDisplayShowCustomEnabled(true);

Why is my image clipped after scaling?

I am having a strange problem. I scale an image and, while scaling works correctly, the image always gets clipped. I tried different scale types - things changed but I never could make it work.
Just to be clear, here's what I need to solve:
1. I have a HorizontalScrollView around the ImageView and a ScrollView around the HorizontalView.
2. I scroll around (using scrollTo of both scroll views) and, upon a certain event, zoom in.
3. What I'd like to happen is for the ImageView to scale around my current scroll position.
Here's the layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:overScrollMode="never">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:overScrollMode="never">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="3dp"
android:scaleType="fitCenter" />
</HorizontalScrollView>
</ScrollView>
</FrameLayout>
And here's the scaling code (originalWidth/originalHeight are calculated at scale of 1; targetView points to the ImageView):
public synchronized void changeScale(float newScaleFactor) {
this.scaleFactor = Math.max(min_zoom, Math.min(newScaleFactor, max_zoom));
if (targetView != null && originalWidth > 0) {
int newWidth = (int)(originalWidth * scaleFactor);
int newHeight = (int)(originalHeight * scaleFactor);
onScaleChanged(targetView, scaleFactor, newWidth, newHeight);
}
}
public void onScaleChanged(View targetView, float scaleFactor, int newWidth, int newHeight) {
ViewGroup.LayoutParams layoutParams = targetView.getLayoutParams();
layoutParams.width = newWidth;
layoutParams.height = newHeight;
// This is needed to increase the pane size (rather than zoom within the initial layout)
targetView.setLayoutParams(layoutParams);
// Tell the system to recalculate the layout
targetView.requestLayout();
// This is needed to specify the center of scaling
HorizontalScrollView horizontalScrollView = (HorizontalScrollView)targetView.getParent();
ScrollView vertScrollView = (ScrollView)horizontalScrollView.getParent();
// ~~~ the pivot points are probably wrong
targetView.setPivotX(horizontalScrollView.getScrollX() * scaleFactor);
targetView.setPivotY(vertScrollView.getScrollY() * scaleFactor);
// This is needed for actual zooming
targetView.setScaleX(scaleFactor);
targetView.setScaleY(scaleFactor);
};
public void zoomIn(float scaleDelta) {
changeScale(scaleFactor + scaleDelta);
}
public void zoomOut(float scaleDelta) {
changeScale(scaleFactor - scaleDelta);
}
Question 1: How do I prevent clipping? I can't find the right combination of scaleType and layout resizing.
Question 2: When I use setScaleX/setScaleY, should my pivot be calculated after applying the new scale factor or does the renderer take care of that automatically?
After updating the scale you need to invalidate(), and requestLayout() the views.
targetView.invalidate();
targetView.requestLayout();
I usually calculate the scale differently for images. You could try to scale the image view using the MATRIX scale type. You will need to know the size of your bound DPI.
// Get the scaled DPI
int boundBoxInDp = getResources().getDisplayMetrics().densityDpi * scaleFactor
// Determine how much to scale: the dimension requiring less scaling is
// closer to its side. This way the image always stays inside your
// bound AND either x/y axis touches the bound but neither will go over.
float xScale = ((float) boundBoxInDp) / newWidth;
float yScale = ((float) boundBoxInDp) / newHeight;
float scale = (xScale <= yScale) ? xScale : yScale;
// scale using our calculated scale
targetView.setScaleX(scale);
targetView.setScaleY(scale);
As to your second question about the Pivot. That will probably need to be set to the center of the visible scroll area. The the scrollable area should be increased when you change the image size since you are using FIT_CENTER;
See this article for code that works quite well. I ditched my HorizontalScrollView and ScrollView and attached this PanAndZoomListener to my FrameLayout and from then on it was all rainbows and unicorns.
I tried to do the same thing as you, but without success. It would seem that you can scroll an ImageView without using HorizontalScrollView and ScrollView. I'm still unclear as to what makes it happen, but I'm leaning toward the use of the image matrix (as in, setImageMatrix on ImageView) or possibly the use of MarginLayoutParams. In looking at the Gallery source code from the picture Gallery available on the Android, I'm seeing heavy use of Matrix. Unfortunately, the documentation on this seems to be quite light in my estimation.
Other people have figured it out, so plug in the PanAndZoomListener and you're done. That's what I did.

TextView gravity="top" but changing font size dynamically doesn't keep text align to the top

I have an app that needs to dynamically align text views over certain locations on an image. The image is scaled to fit the view. Text entries are added by the user. I'm using a RelativeLayout and margins to properly position the text. All is good.
When the user rotates the screen, I save the text locations and bitmap using onRetainNonConfigurationInstance(). When the view is recreated, I add the views back into the RelativeLayout. Now, because the view is a different size, the scale of the image is different. To keep the text boxes in the same location, I have to account for that. I have all that code working except for one issue....
If I create a TextView and set the fontSize to a small size (e.g. <11px), the top of the text box is positioned where I want it to be but the text is aligned with a baseline as if it were 11px font. I don't know the scale factor in the onCreate method so I can't change the font size until after onMeasure and onLayout are called. I've tried forceLayout() and requestLayout() but nothing seems to fix the alignment problem.
Here is the layout:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/scrollView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="0dp"
>
<com.xyx.ScalableLayout
android:id="#+id/layout"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="0dp"
android:clickable="true"
android:gravity="top"
>
<com.xyx.ScaledImageView
android:id="#+id/formView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="0dp"
/>
<com.xyx.ScalableLayout>
</ScrollView>
A textView is added with the following code:
textView = new TextView(getContext());
textView.setPadding(0, 0, 0, 0);
textView.setSingleLine();
textView.setFocusableInTouchMode(false);
textView.setClickable(false);
textView.setGravity(Gravity.TOP);
textView.setText(entry.mText);
float fontSize = mImageView.mapSizeToView(entry.mPointSize);
textView.setTextSize(TypedValue.COMPLEX_UNIT_PT, fontSize);
params = new RelativeLayout.LayoutParams(
ViewGroup.MarginLayoutParams.WRAP_CONTENT,
ViewGroup.MarginLayoutParams.WRAP_CONTENT);
params.leftMargin = Math.round(viewLoc.x);
params.topMargin = Math.round(viewLoc.y);
addView(textView, params);
When ScaledImageView gets an onSizeChanged() callback, it sets the scale value and updates the font size and location of TextViews with the following code:
float fontSize = mImageView.mapSizeToView(entry.mPointSize);
textView.setTextSize(TypedValue.COMPLEX_UNIT_PT, fontSize);
PointF viewLoc = computeTopLeft(entry, entry.mTextView.getPaint());
params = (RelativeLayout.LayoutParams) textView.getLayoutParams();
params.leftMargin = Math.round(viewLoc.x);
params.topMargin = Math.round(viewLoc.y);
textView.setLayoutParams(params);
textView.forceLayout();
And finally, I do a
requestLayout() on the ScalableLayout
I know that's a lot of code but my problem is this. If I know the proper scale factor (used in mapSizeToView()) when I am creating the TextView and adding it to the screen everything works fine. If the value is incorrect, then nothing I can find will do a full recalculate that will get the TextView to be the same as if it were created with that font size. I believe it is tied to Android Layout structure which I generally understand. However, I don't understand why I can't get it to recompute from scratch (short of tearing down all the views and recreating them).

Resources