how do i create a multiple row app with the same data for each row. This is how all the rows are to look like. it contains three text views and two spinners. this app is to help calculate the gpa of a student for one semester.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.mbedobe.android.samplegpacalculator.app.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="C1"
android:id="#+id/course_textView"/>
<Spinner
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/spinner"
android:layout_alignParentTop="true"
android:layout_toRightOf="#+id/course_textView"
android:layout_toEndOf="#+id/course_textView"
android:layout_marginLeft="23dp"
android:layout_marginStart="23dp" />
<Spinner
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/spinner2"
android:layout_alignParentTop="true"
android:layout_toRightOf="#+id/spinner"
android:layout_toEndOf="#+id/spinner"
android:layout_marginLeft="23dp"
android:layout_marginStart="23dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:layout_alignParentTop="true"
android:layout_toRightOf="#+id/spinner2"
android:layout_toEndOf="#+id/spinner2"
android:id="#+id/gradePoints"
android:layout_marginLeft="23dp"
android:layout_marginStart="23dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:layout_alignParentTop="true"
android:layout_toRightOf="#+id/gradePoints"
android:layout_toEndOf="#+id/gradePoints"
android:id="#+id/gradeValue"
android:layout_marginLeft="23dp"
android:layout_marginStart="23dp"/>
</RelativeLayout>
You should think about using a ListView with BaseAdapter.
The best solution is to use RecycleView
Use a RecyclerView with a custom adapter. The layout above will be the row layout for each row in this case.
Use the links above to understand how you can create your own Custom RecyclerView Adapters with Custom Layouts.
EDIT:
Ok, I'll give you the basics here. There are three major things you need for a custom RecyclerView.
They are:
List of Objects (your data)
A custom layout for each row
An Adapter that will transform your data onto the above layout.
Now, let's take an example and try and explain this.
You must have seen popular apps like Gmail, WhatsApp, etc.
When you open these apps, you see a list of Emails/Chats etc.
One thing to notice is that the layout for each row is same, even though the content inside them is different.
This tells us 2 things: first that the data must be a list of the same kind of objects, and secondly that the layout of each row is the same. So, to create such a listView or RecyclerView, we shouldn't need to create as many layouts as there are rows. One layout can be reused for each row.
So, two of our three needs are understood. The final item required is the Adapter. The adapter is what takes your List and converts each item from the list into a row on your RecyclerView. These rows are automatically created by the Adapter as the user scrolls through the list, and removed when the user can no longer see those rows.
If you want code for this, let me know. I'll upload code explaining this. But I would recommend you try it out on your own. It's not that hard.
Either use ListView, recyclerView or dynamically add the items in scrollview.
Use of ListView or RecyclerView will be the best approach for you.
I'll give you the last option that should be worked from java only if you are with good knowledged in java. ( only the steps not complete code)
1) your xml file should contain scroll view.
2) for each item to be added (3 textview 2 spinner), add a layout and provide layout params for it i.e width/height/orientation/background etc.
3)add this layout to the scrollview you created in xml by
findViewById(R.id.scrollviewId).add(layout)
4)create your textviews/spinners in and give them layout params.{ this process is much logical in a pattern of your design so must be very careful}
5)add all these textviews/spinners in the layout you created in step 3.
Note: according to your design requirements you might need to add other layouts in between the layout created in step 2/3 and other items created in step 4.
Here is the code you liked:
LinearLayout myLayout = (LinearLayout)findViewById(R.id.layout_in_xml);
for(int i = 0; i < required_fields.length; i++){
LinearLayout lr = new RelativeLayout(DisplaySadip.this);
LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
lr.serLayoutParams(layoutParams);
lr.setOrientation(LinearLayout.HORIZONTAL);
//add linear layout to your layout in xml
myLayout.add(lr);
//add textview, spinner to this layout now
TextView tv1 = new TextView(YourActivity.this)
tv1.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
tv1.setText("......"+i);
lr1.add(tv1)
Spinner spinner1 = new Spinner(YourActivity.this)
spinner.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
Spinner spinner2 = new Spinner(YourActivity.this)
spinner.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
lr.add(spinner1);
lr.add(spinner2);
TextView tv2 = new TextView(YourActivity.this)
tv2.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
tv2.setText("......"+i);
lr.add(tv2)
TextView tv3 = new TextView(YourActivity.this)
tv3.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
tv3.setText("......"+i);
lr1.add(tv3)
}
it shall add add many no of views you want in your layout.
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.
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 have a horizontal list of items that i wish to share the available screen width equally.
Previously I had used index binding to each item and a static LinearLayout with each item having an equal layout_weight.
When I replace the static LinearLayout with an Mvx.MvxLinearLayout and ItemsSource binding, and move the markup for the items into an Itemtemplate the layout_weight is no longer respected by the MvxLinearLayout.
I've tried various ways of restructuring the item template and there seems to be no way to get this control to obey layout_weight when arranging its items.
Is there any way to get Mvx.MvxLinearLayout to respect android:layout_weight, or any alternative approach that could give me a dynamic list of items that will arrange equally given a boundary of fixed dimensions?
Edit
Assuming a list of 3 items, with a string property 'Name'.
This markup works as expected, giving 3 text items of equal width:
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
local:MvxBind="Text Items[0].Name"
/>
<TextView
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
local:MvxBind="Text Items[1].Name"
/>
<TextView
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
local:MvxBind="Text Items[2].Name"
/>
</LinearLayout>
This Approach does not work, the layout weight is not applied correctly to the TextViews:
<Mvx.MvxLinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
local:MvxBind="ItemsSource Items"
local:MvxItemTemplate="#layout/item_template" />
The ItemTemplate layout file:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="wrap_content"
local:MvxBind="Text Name" />
The problem is, mvvmcross inserts an additional view when using MvxLinearLayout with an ItemTemplate. So instead of getting a view hierarchy:
MvxLinearLayout -> TextView
you end up with:
MvxLinearLayout -> MvxListItemView -> TextView.
The layout properties applied to the TextView, notably the weight, are therefore invisible to the LinearLayout. The MvxListItemView is primed with the default layout properties (wrap content), and no weight effects are seen.
The location where the action happens is in MvxViewGroupExtensions (e.g. Refill), where the subviews are added to the LinearLayout, and the CreateBindableView method in MvxAdapter, where the subview is packed inside an IMvxListItemView.
One way to solve this is to override the methods in MvxViewGroupExtensions, and after the views are added to update the layout properties of the MvxListItemViews (e.g. copy them from the underlying view). For example:
private static void Refill(this ViewGroup viewGroup, IAdapter adapter) {
.... // rest of code
Invalidate();
for (int j = 0; j < viewGroup.ChildCount; j++)
{
var child = GetChildAt(j) as MvxListItemView;
var param = new LinearLayout.LayoutParams(
0, ViewGroup.LayoutParams.WrapContent, 1.0f);
child.LayoutParameters = param;
}
}
any way to get MvxLinearLayout to respect android:layout_weight
None that I know of - but it sounds like you are in a perfect position to debug and experiment.
To debug layout issues, one very useful tool is the hierarchy viewer - http://developer.android.com/tools/debugging/debugging-ui.html - using this on both layouts may show you what the difference is when they are actually running on a device.
The MvxLinearLayout is just a LinearLayout - it inherits from it and simply adds a data-bound Adapter to allow you to add/remove children dynamically - see MvxLinearLayout.cs.
Based on this, one hypothesis is that maybe LinearLayout itself does not support layout_weight with such dynamic children. To investigate this:
you could try prototyping the UI without using Mvx - does a plain LinearLayout "respect layout_weight" as you add/remove children? If the answer is yes, then what is the difference between your prototype and MvxLinearLayout.cs.. If the answer is no then you know that the issue is in LinearLayout somewhere.
you could try looking at the LinearLayout source - e.g. http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/1.5_r4/android/widget/LinearLayout.java - this investigation may uncover some way forward - for example, it may find some way you can force the LinearLayout to remeasure and you could then call that 'force remeasure' method from the ChildViewAdded and ChildViewRemoved events.
any alternative approach that could give me a dynamic list of items that will arrange equally given a boundary of fixed dimensions?
If the built-in layouts do not work for you, then it's pretty straight-forward to create your own custom layouts - e.g. see http://arpitonline.com/blog/2012/07/01/creating-custom-layouts-for-android/ - once you have done that, then adding binding is as simple as copying and pasting the ViewGroup binding code from MvxLinearLayout.cs.
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
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);
}