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
Related
I am developing an app in which I want to add textviews in a column-wise order. Atmost 3 textviews can fill horizontally the screen width. If there are more than 3 textviews to be added, the new ones should be added in the next row, and so on. See the diagram below-
---1st---------------2nd--------------3rd
---4th----------------5th-----and so on
I know we need to use a nested linear layout with different orientation parameters. But how do I get around this "maximum 3 textviews per row constraint". I know that this has to do something with the weight parameter. The code I have written so far is-
LinearLayout out = (LinearLayout) findViewById(R.id.linear_params);
TextView mView = new TextView(this);
mView.setText("placeholder");
mView.setLayoutParams(new LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
out.addView(mView);
And for the layout I have written-
<LinearLayout
android:id="#+id/out
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="5dp">
<LinearLayout
android:id="#+id/in
android:layout_width="match_parent"
android:layout_height="10dp"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:orientation="horizontal">
<TextView ... />
</LinearLayout>
</LinearLayout>
How should I modify the above xml and code so that I am able to place the views dynamically as required ?
The correct way to do this will be to use a GridView Component with three columns. It will allow you to add the views as well.
If you use LinearLayout you will have to use weights and keep adding new LinearLayout per row, this will take a lot of processing for calculations and overdrawing as well.
Using GridView will help to avoid writing the code to calculate and add every row and every view and will keep the layout optimized as well.
I have a simple layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="300dp"
android:padding="15dp">
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="#+id/scrollLayout">
</LinearLayout>
</ScrollView>
</RelativeLayout>
Now, I inflate the outer RelativeLayout to retrieve the inner LinearLayout to put items in it.
RelativeLayout relative = (RelativeLayout) LayoutInflater.from(activity).inflate(R.layout.gradient_pick_view, null);
LinearLayout view = (LinearLayout) relative.findViewById(R.id.scrollLayout);
After that I created a method to add some buttons to it:
for(int i=0;i<10;i++){
LinearLayout wrapper = (LinearLayout) LayoutInflater.from(activity).inflate(R.layout.button_wrapper, null);
Button button = (Button)wrapper .findViewById(R.id.button);
view.addView(layout);
}
Everything works fine, but it doesn't scroll.
What am I doing wrong here?
Here's the screenshot (displaying 7 of 10 buttons):
I forgot to mention - I'm using a MaterialDialog library and add this RelativeLayout as a custom view to a dialog.
Try to set the following attribute to your scrollview,
android:fillViewport="true"
above attribute is used to make your scrollview to use entire screen of your application.
I had a false parameter passed to a customView in a MaterialDialog.
dialog = new MaterialDialog.Builder(activity)
.title(R.string.about)
.customView(view, true)
.positiveText(R.string.changing_fragments)
.show();
As doc says:
If wrapInScrollView is true, then the library will place your custom view inside of a ScrollView for you. This allows users to scroll your custom view if necessary (small screens, long content, etc.). However, there are cases when you don't want that behavior. This mostly consists of cases when you'd have a ScrollView in your custom layout, including ListViews, RecyclerViews, WebViews, GridViews, etc. The sample project contains examples of using both true and false for this parameter.
Now it's working.
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 am building something like a ListView, but I'm rolling my own because I want to do some custom stuff and have more control than using ArrayAdapters.
So I've defined part of my layout in XML, including one LinerLayout inside a ScrollView. My goal is to bind to that Linearlayout in code, then insert additional RelativeLayouts inside the LinearLayout using no XML, just code.
Here is my XML:
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/ListScroll"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:id="#+id/ListHolder"
android:layout_height="400px"
android:background="#323232"
>
<!--Here is where I want the RelativeLayouts to go... -->
</LinearLayout>
</ScrollView>
Then in code, I'm trying to add RelativeLayouts, each 50px in height, to the LinearLayout, the one above that has a height of 400px.
//The parent container - is defined above in XML.
itemContainer = new LinearLayout(context);
itemContainer = (LinearLayout)findViewById(R.id.ListHolder);
Layouts = new ArrayList<RelativeLayout>();
Layouts = LoadWithRelativeLayouts();
for(RelativeLayout listItem: Layouts){
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.FILL_PARENT, 40);
listItem.setLayoutParams(params);
itemContainer.addView(listItem);
}
Each one of the layouts in the array has a text view in it that says "Test". When I step through the code, there are 10 elements in the array, and all of the textviews are there, so I would expect to see the 400px LinearLayout filled with 10 Relative layouts one after another, each with 50px height (and fill_parent width) reading "Test" - but all I see is one, as if only one got added, or they are all positioned on top of one another.
Getting screenshot now...
When you add something to a layout, you have to use layout params of that kind. So as you're adding to a LinearLayout, you should use LinearLayout.LayoutParams.
Then you'll probably also need to set your LinearLayout orientation to vertical, because right now the items you don't see are all in a row offscreen at the right :)
Try adding android:orientation="vertical" to the LinearLayout holding the RelativeLayouts.
currently i have the following layout
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_marginTop="9px"
android:layout_below="#+id/desc"
android:id="#+id/ll_item"
android:orientation="vertical"
android:paddingRight="3px"
android:paddingLeft="3px"
android:paddingBottom="5px"
android:paddingTop="5px"
android:background="#drawable/rounded_corner_lists" >
<!--
<ListView android:drawSelectorOnTop="false" android:id="#+id/lv" android:layout_height="fill_parent" android:layout_width="wrap_content" android:divider="#ddd" android:dividerHeight="1px" android:background="#drawable/white" />
-->
</LinearLayout>
the listview that i have commented out, i have tried to make this in the xml, with the height set to wrap_content, fill_parent, currently i am doing this programatically with the following code
LinearLayout ll_item = (LinearLayout) this.findViewById(R.id.ll_item);
if(list.length() > 0)
{
ll_item.setVisibility(View.VISIBLE);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,calcListHeight(list));
listview = new ListView(this);
listview.setBackgroundResource(R.drawable.white);
listview.setDivider( new ColorDrawable(this.getResources().getColor(R.drawable.dividercolor)) );
listview.setDividerHeight(1);
listview.setCacheColorHint(0);
mAdapter = new JSONAdapter( list, this );
listview.setAdapter(mAdapter);
mAdapter.notifyDataSetChanged();
ll_item.addView(listview, lp);
}
this is the result
so you can see in this image, that since i'm containing the listview in a linearlayout to get the rounded corner look, it doesn't just automatically stretch to contain the entire listview, is there any way to have the two elements just wrap the content vertically so there is no scrolling without me programatically setting the height ? ? ?
i guess one other thing i should mention is that i have all this layout in a scrollview, because i want this listview to be a tiny subsection of the entire layout, so it would be something like
-scrollview
-textview
-textview
-linearlayout
-listview
- button
here is a simpler layout of what i have
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:orientation="vertical" android:layout_height="fill_parent" android:layout_width="fill_parent"
android:background="#drawable/bg" xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/titlebar">
<ScrollView android:id="#+id/sv" android:layout_width="fill_parent" android:background="#drawable/bg"
android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android">
<RelativeLayout android:id="#+id/widget28"
android:layout_width="fill_parent" android:layout_height="fill_parent" android:padding="4dip"
>
<LinearLayout android:orientation="vertical" style="#style/rounded_corner_full_width_button"
android:id="#+id/editfields">
<ListView android:drawSelectorOnTop="false" android:id="#+id/lv" android:layout_height="fill_parent"
android:layout_width="wrap_content" android:divider="#ddd" android:dividerHeight="1px"
android:background="#drawable/white"/>
</LinearLayout>
</RelativeLayout>
</ScrollView>
</LinearLayout>
ListViews do not go in ScrollViews.
ListView is for displaying a limited window into unbounded content efficiently. If you were to "disable scrolling" on a ListView to put it within a ScrollView you lose all practical reason for using a ListView in the first place.
If you want to use a ListView to show lots of content or unbounded content but also have content above and below that scrolls with it, add header or footer views to the ListView using addHeaderView or addFooterView. If the list content is going to be a small portion of your overall layout as you describe, this probably isn't the best approach for you.
If you have a small, bounded set of content to present, go ahead and use a ScrollView and programmatically generate child views for your "list items" where appropriate.
A common pattern used in the framework to mix inflated XML content with programmatically generated content is to add a placeholder view in the layout XML, usually a LinearLayout or FrameLayout. Use findViewById to locate it at runtime and add generated child views to it.
You could even still use a ListAdapter with this approach if you have one written already, just call content.addView(adapter.getView(position, null, content)) in a loop for all adapter positions (where content is the placeholder view you located with findViewById). Note that this is only practical if you know that you have a small number of list items in the adapter!
Add a empty item on list end
Example:
ArrayList<String> options = new ArrayList<String>();
String lastItem = "";
int lastPosition;
options.add(lastItem);
public function addItem() {
lastPosition = options.size() - 1;
lastItem = options.get(lastPosition);
options.remove(lastPosition);
//add new items dynamically
for (int i = 0; i < 5; i++)
options.add("new item: "+i);
//add empty item
options.add(lastItem);
}