I want to create a custom layout to reduce redundancy in the code. Currently every layoutfile has about 30 Lines of code which are identical.
My goal was to create a custom layout/view which can hold in itself children.
<BaseLayout xmlns:...>
<!-- Normal Content -->
<Button />
<Label />
</BaseLayout>
While the above xml holds most of the content, the BaseLayout is in itself an xml containing other views and functionality:
<FrameLayout xmlns:...>
<LinearLayout><!-- contains the Header--></LinearLayout>
<LinearLayout><!-- INDIVIDUAL CONTENT HERE--></LinearLayout>
<FrameLayout><!-- contains the loading screen overlay --></FrameLayout>
</FrameLayout>
So all children from the above xml should be inserted into second linear-layout. I have already succeeded into doing so. But am confronted with layout problems (match parents does not match parents and only wraps)
My approach was extending the LinearLayout with following logic:
/**
* extracting all children and adding them to the inflated base-layout
*/
#Override
protected void onFinishInflate() {
super.onFinishInflate();
View view = LayoutInflater.from(getContext()).inflate(R.layout.base_layout, null);
LinearLayout linearLayout = (LinearLayout) view.findViewById(R.id.base_layout_children);
while(0 < getChildCount())
{
View child = getChildAt(0);
LinearLayout.MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams();
removeViewAt(0);
linearLayout.addView(child, layoutParams);
}
this.addView(view);
}
Is there a better, cleaner approach to capsule the xml and reuse a basis layout? How do I fix the match_parent issue?
While writing this post and thinking hard how to explain best, the solution for the match_parent issue became clear. Though the question remains if there is a better approach for the whole problem.
//Solution:
this.addView(view, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
//wrong:
this.addView(view);
Suppose you have two layout files. common_views.xml and layout_main.xml. You can include content of one layout file into another like this.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<include
android:id="#+id/common"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
layout="#layout/common_views" />
<WebView
android:id="#+id/webView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="#+id/common"
>
</WebView>
</RelativeLayout>
Related
So basically you can't place a ListView in a ScrollView because the Scrolling ability clashes in both layouts. When I tried to do it, the ListView becomes completely useless and many other problems arise.
How has Facebook done it?
As you can see, the work section is a ListView and it's also a Scrollable layout so that the user can scroll down to view the Education section, which is also a ListView.
My code:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#fff"
android:layout_marginBottom="40dp">
<!-- More layouts -->
<ListView
android:id="#+id/work_list"
android:layout_below="#+id/recentpic"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>
</ScrollView >
I do not want a ListView Scroll bar
Therefore the scrolling dilemma is completely removed from the equation. Even when I disable the scroll bars, the problem persists.
Solution that I have in mind:
Generating the XML rows (Each Workplace of the ListView) and injecting it to the layout and avoiding the use of ListViews, similar to HTML Code Generation using Javascript.
What method do you think Facebook has used in their android app to get this done and what changes should I make to my code? :)
Have you tried using NestedScrollView? I think it's a NestedScrollView which contains a ListView and the whole thing is enclosed in a ScrollView. This link might help:
http://ivankocijan.xyz/android-nestedscrollview/
Okay so I managed to code my own idea I mentioned. It's a very 'sexy' code and it gets the job done :D
Here you go, guys. I hope it helps someone :)
So basically I'm inflating a Parent Layout with multiple Child Layouts dynamically and completely getting rid of ListViews in the view. Which makes is very simple to use it with a ScrollView and forget about that dilemma.
Parent Layout:
<RelativeLayout
android:id="#+id/work_list"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</RelativeLayout>
Child Layout - work_single_item.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ffffff">
<ImageView
android:id="#+id/work_pic"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:src="#mipmap/image_placeholder"/>
</RelativeLayout>
Coded the following lines in the OnCreate function of the Parent Layout.
RelativeLayout parent = (RelativeLayout) findViewById(R.id.work_list);
//array containing all children ids
ArrayList<Integer> children = new ArrayList<>();
//adding 10 children to the parent
for(int i=0;i<10;i++) {
RelativeLayout child = new RelativeLayout(this);
View tempchild = getLayoutInflater().inflate(R.layout.work_single_item, null);
child.addView(tempchild);
child.setId(i); //setting an id for the child
children.add(i); //adding the child's id to the list
if(i!=0) //if it isn't the 1st child, stack them below one another, since the 1st child does not have a child to stack below
{
RelativeLayout.LayoutParams params
= new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.BELOW, children.get(i - 1)); //stack it below the previous child
child.setLayoutParams(params);
}
parent.addView(child); //add the new child
}
I wrote a small function
private void addTabIndicators(int tabCount){
LinearLayout indicatorsContainer = (LinearLayout)findViewById(R.id.indicators_container);
for(int i = 0; i<tabCount; i++){
ImageView indicator = (ImageView)this.getLayoutInflater().inflate(R.layout.tab_indicator, null);
indicatorsContainer.addView(indicator);
}
}
that is supposed to add circles to the linearlayout in my activity based on how many tabs are in the pager adapter. Everything would be cool BUT, the imageviews i add instead of beeing the size declared in the xml layout, are being resized to 1x1px... Any ideas where i could go wrong? Here are the layouts for the indicator and linear layout
tab_indicator.xml:
<?xml version="1.0" encoding="utf-8"?>
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_margin="8dp"
android:src="#drawable/floating_button_background"/>
The indicators container:
<LinearLayout
android:layout_marginBottom="16dp"
android:orientation="horizontal"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:id="#+id/indicators_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</LinearLayout>
The fault might be here
ImageView indicator = (ImageView)this.getLayoutInflater().inflate(R.layout.tab_indicator, null);
You need to pass the root view of your image view to provide the layouts defined in your XML. If you pass null instead, default layoutparams are set.
Place
ImageView indicator = (ImageView)this.getLayoutInflater().inflate(R.layout.tab_indicator, your root view of image view,false);
This is a very common mistake. Never pass null except you really know what you are doing.
Read more here:
http://developer.android.com/reference/android/view/LayoutInflater.html
Hope it helps
I'm doing this in Xamarin, so there will be slight deviations in the casing and names of methods.
I have a RelativeLayout that has ads and is placed at the bottom. Unfortunately, the ads block part of the playable map, so I'm attempting to move it to the top when the player moves near the bottom. I initialize the banner with the following code:
_banner = new RelativeLayout(this);
_lp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent);
_lp.AddRule(LayoutRules.AlignParentBottom);
_lp.AddRule(LayoutRules.AlignParentLeft);
AddContentView(_banner, _lp);
I'm now attempting to move it to the top, but am failing. I've tried removing and re-adding it, but that does nothing.
var lp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent);
((FrameLayout)(_banner.Parent)).RemoveView(_banner);
lp.AddRule(LayoutRules.AlignParentTop);
lp.AddRule(LayoutRules.AlignParentLeft);
AddContentView(_banner, lp);
I've also tried setting the LayoutParameters, but that throws an exception. What am I doing wrong?
Thank you.
-Nick
From your code I gather you are nesting a RelativeLayout (contains the ad) inside a FrameLayout. Your RelativeLayout only matches your parent in width, not height.
As such the RelativeLayout doesn't have the same size as your FrameLayout.
Your ad is thus touching both top and bottom of the RelativeLayout. And that RelativeLayout is always aligned to the top of the FrameLayout.
To solve this you have three options:
Make the RelativeLayout match the entire height of the FrameLayout and position your ad within that RelativeLayout.
Change the FrameLayout into a RelativeLayout, that way your alignParentBottom parameter will start working.
Use a 'layout_gravity' parameter on the RelativeLayout to tell the FrameLayout you want this View sent to the bottom.
Effectively your code is doing:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:alignParentBottom="true"
android:background="#FF0000"
android:layout_width="match_parent"
android:layout_height="240dp"
/>
</FrameLayout>
But for stuff to work you need to use android:layout_gravity which is a FrameLayout.LayoutParameter.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_gravity="bottom"
android:background="#FF0000"
android:layout_width="match_parent"
android:layout_height="240dp"
/>
</FrameLayout>
Just paste the two pieces in a layout.xml and use the Android Designer to view the difference.
I have a layout whose root ViewGroup has two children only one of which is always visible. The other child's visibility will be set at runtime to View.GONE when not applicable.
When both children are visible, the heights are set to wrap_content and the layout looks great. The problem is that I'd like to expand the visible view to match_parent when the other is gone.
Is there any way to accomplish this or the equivalent?
You can change any View's layout like this:
view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
The constructor takes width then height as in: new LayoutParams(int width, int height).
Also there is a LayoutParams class for each type of ViewGroup. Make sure you import the one that refers your particular ViewGroup. So if your ViewGroup is a LinearLayout use:
import android.widget.LinearLayout.LayoutParams;
I tried unsuccessfully figuring this strategy out
I'm not certain where you had trouble, but this approach only requires a couple extras lines:
// When you want to show both views
view1.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
view2.setVisibility(View.VISIBLE);
...
// When you want to hide the second view
view1.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
view2.setVisibility(View.GONE);
If you hide / show the views multiple times you can save a reference to each LayoutParams object rather than repeatedly creating new objects.
Have you tried working with android:layout_weight?
I put together this small example. Below I added a picture showing both views visible (left) and how it looks like with view2 visibility set to GONE (right). As you see, view1 uses up all available space then.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<View
android:id="#+id/view1"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#ff0000" />
<View
android:id="#+id/view2"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#0000ff" />
</LinearLayout>
i've seen this question added on the stack but the information hasn't been helpful or successful yet, so i remain quite not sure.
The gist of what I'm trying to do:
I have a layout defined in xml, some_details_view.xml for example.
setContentView(R.layout.some_details_view);
It has a bunch of text views laid out using a parent relative layout, a linear header layout, a linear footer layout, a middle scroll layout that contains a relative layout that hold some label - value type of text views.
And at the bottom of the scroll view relative layout, I currently placed a frame layout as a place holder.
On create of the corresponding activity, I set text in respective text views with data handed over from previous activity; basic stuff.
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#color/white" >
<LinearLayout
android:id="#+id/header"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true" >
...some header content
</LinearLayout>
<LinearLayout
android:id="#+id/footer"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:orientation="vertical" >
..some footer content
</LinearLayout>
<ScrollView
android:id="#+id/scroll"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="#+id/footer"
android:layout_below="#+id/header"
android:layout_margin="5dip" >
<RelativeLayout
android:layout_width="fill_parent"
android:id="#+id/relativeScroll"
android:layout_height="wrap_content" >
...text views in relative layout
<FrameLayout
android:id="#+id/placeholder"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="#+id/moreInfoValue" />
</RelativeLayout>
</ScrollView>
</RelativeLayout>
After setting up text views for the given data, I use an async task to get some additional data that I want to show as a history list type of thing at the bottom of the static form layout. There could be 0 or more items so I either add 1 or more text views or none at all.
In the post execute, which I understand to run on the main UI thread, I try to find an exiting container Layout/view group and add either a new Linear Layout to which I add new text Views, or just add the new text views directly to the existing container layout.
here's the latest thing I tried:
ViewGroup mContainer = null; //defined as member variable of activity class and instatiated in on create
mContainer = (ViewGroup)findViewById(R.id.placeholder); //set in on create
LinearLayout ll = new LinearLayout(context); //on post execute of async task
ll.setOrientation(LinearLayout.VERTICAL);
mContainer.addView(ll); //add a new linear layout to an existing container layout
//add some new text view widgets items dynamically
for(NewDisplayItem item : items)
{
TextView tv = new TextView(context);
tv.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
tv.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
tv.setText(item.getSomeText());
ll.addView(tv); //add text view to new linear layout
}
When the UI loads I don't see new items added to my layout after stepping through and watching the controls get added using the code above.
Not sure what it is but something doesn't seem right about this approach in addition to the fact that it's not working. When the activity loads up, all the static views are setup and in view. I pop up a loading dialog, step through the async task stuff and I guess expect to see the dynamic controls add to the layout one by one?
First of all textView.setWidth(int) takes as parameter the width in pixels.
Second you should also set your layout parameters on the LinearLayout you are adding.
The way you should set LayoutParams is as follows :
ll.setLayoutParams(new LinearLayout.LayoutParams(
LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT));
the same for yout TextViews.
Ovidiu Latcu has a good answer. A good question is: is there a reason why you aren't using a ListView (which btw there ARE cases where what he's doing works better)? A ListView has a lot of mechanisms to help you keep from running out of RAM