I have 2 layout xml files: "highlights.xml" and "highlights_cell.xml".
Here is a simplified version of each. I've removed the width/height/etc and just kept the important attributes...
highlights.xml
<LinearLayout>
<uk.co.jasonfry.android.tools.ui.SwipeView android:id="#+id/swipe_view" />
<uk.co.jasonfry.android.tools.ui.PageControl android:id="#+id/page_control" />
</LinearLayout>
highlights_cell.xml
<LinearLayout android:orientation="vertical">
<LinearLayout android:id="#+id/linear_layout1" android:orientation="horizontal">
<ImageView android:id="#+id/logo" />
<LinearLayout android:id="#+id/linear_layout2" android:orientation="vertical">
<TextView android:id="#+id/title" />
<TextView android:id="#+id/subtitle" />
</LinearLayout>
</LinearLayout>
<ScrollView android:id="#+id/scroll_view">
<TextView android:id="#+id/description" />
</ScrollView>
</LinearLayout>
The idea is that I want to add several "highlights_cell" to "highlights" through a loop.
I've thrown together some test code as follows but, as it's not working, I suspect that I'm not adding the cell layouts correctly, or perhaps I shouldn't be using "inflater"...
/** Declare shared variables */
SwipeView mSwipeView;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState){
//Initialise layout and variables
super.onCreate(savedInstanceState);
setContentView(R.layout.highlights);
//Setup controls
mSwipeView = (SwipeView) findViewById(R.id.swipe_view);
LayoutInflater inflater = (LayoutInflater)getSystemService(LAYOUT_INFLATER_SERVICE);
//Loop through collection and add views
for(int i=0; i<7;i++)
{
//Create the itemView to use layout xml for each cell
View itemView = inflater.inflate(R.layout.highlights_cell, null);
//Set values within cell
TextView title = (TextView) itemView.findViewById(R.id.title);
title.setText("HELLO WORLD_" + i);
//add the itemView to main view
mSwipeView.addView(itemView);
}
}
Is this the correct way to add layouts dynamically to a parent layout?
Thanks!
It looks good except for a few things.
Because you are adding views to your custom ViewGroup, you will have to be sure that it correctly lays out and displays its children.
Also, when you add a View to a ViewGroup, you specify the LayoutParams that views can have in that ViewGroup.
Some more info about creating a custom ViewGroup
http://about-android.blogspot.com/2010/05/create-dynamic-view-group.html
custom ViewGroup example?
Related
I am guessing my problem is with inflating layouts. I have 2 layout files:
activity_mail.xlm:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/results"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/profilesCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textSize="18sp" />
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
... and listitem_device.xlm:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/device_label"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"/>
<TextView
android:id="#+id/device_service"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1.5"/>
</LinearLayout>
in my main_activity java file, I have:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
....
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
ViewHolder viewHolder;
if (view == null) {
view = mInflator.inflate(R.layout.listitem_device, null);
viewHolder = new ViewHolder();
viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_label); // from listitem_device.xml
viewHolder.profilesCount = (TextView) view.findViewById(R.id.profilesCount); // from activity_main.xml
The problem is the subsequent code:
viewHolder.profilesCount.setText("HELLO");
generates the error "Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference".
How do I fix this?
I don't see anywhere that you have even have a view with id "profilesCount".You only have device label and device service as ids in the layout file that you are using to inflate the rows of the list view.Better use Recycler view also, it provides more flexibility.
Also, you can't access something from activity_mail.xml in the getView() function of your adapter because by using "view" in getfunction, as that corrspond to only the row of the list view and since that id is not present in the xml file associated with the row of list view, you are getting "viewholders.profileCount" as null and hence the Nullpointer exception.
If you wanna use a TextView or any view in your adapter, better create an interface and implement that interface in your activity to use as a call back and then set the textview field of your activity.xml file, thats the best way as it would keep your code cleaner.Other way is to pass the reference of the activity to your adapter and then find the text view or whatever that you wanna use from actvity_mail.xml file, but its not recommended and is a very bad way as it may require additional memory.
I have an UI like this:
<RelativeLayout>
<TextView />
<RelativeLayout>
<TextView />
<TextView />
<LinearLayout>
<TextView />
</LinearLayout>
</RelativeLayout>
</RelativeLayout>
I want to access all TextViews programmatically and change some attributes but
I don't want use id and findViewById, because I have a multi layout which I want to perform the same changes in them, i.e.: change the fonts of TextViews.
How can I access all the TextViews whether direct or hierarchy of a view, like RelativeLayout in a loop or in a list?
LinearLayout mLinearLayout = (LinearLayout) findViewById(R.id.my_linear_layout);
for(i = 0; i <= mLinearLayout.getChildCount(); i++){
View view = mLinearLayout.getChildAt(i);
if (view instanceof TextView){
TextView textView = (TextView) view;
// TODO do your stuff
}
}
if you have ViewGroups in your RelativeLayout then you need to write a recursive method.
I have a layout called view.xml which contains a View called view_related. Basically, in my dictionary app, if there are related words to an entry, I will replace view_related with a LinearLayout that contains a header "Related entries" and a bunch of TextViews representing every related word.
I keep getting a NullPointerException everytime I'm adding a TextView to the LinearLayout in my Activity's onCreate() method, and I don't understand why though my code looks pretty straightforward.
view.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView android:id="#+id/view_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="12pt"
android:textColor="#fff"
android:textStyle="bold"
android:background="#990011"
android:padding="10dp"
android:text="lalala word"
/>
<ScrollView android:id="#+id/view_description"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/view_header">
<LinearLayout android:id="#+id/view_description_content"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:text="#string/demo_definition"
/>
<!-- THIS WILL BE REPLACED -->
<View android:id="#+id/view_related"
android:layout_width="match_parent"
android:layout_height="wrap_content"></View>
</LinearLayout>
</ScrollView>
</RelativeLayout>
view_related.xml, the LinearLayout that will replace the View element in view.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/view_related_entries"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView android:id="#+id/view_related_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:textColor="#fff"
android:textStyle="bold"
android:textSize="7pt"
android:background="#000"
android:text="Related entries"
/>
<LinearLayout android:id="#+id/view_related_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
</LinearLayout>
</LinearLayout>
And finally, the onCreate method, which for now assumes the related entries are "Hello" and "Goodbye:"
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view);
LinearLayout ll = (LinearLayout) findViewById(R.id.view_related_list);
TextView tv = new TextView(this);
tv.setText("Hello");
ll.addView(tv); // NULL POINTER EXCEPTION
tv = new TextView(this);
tv.setText("Goodbye");
ll.addView(tv);
LayoutInflater li = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
View view = findViewById(R.id.view_related);
ViewGroup parent = (ViewGroup) view.getParent();
int index = parent.indexOfChild(view);
parent.removeView(view);
View llview = li.inflate(R.id.view_related_entries, parent, false);
parent.addView(llview, index);
}
setContentView(R.layout.view);
LinearLayout ll = (LinearLayout) findViewById(R.id.view_related_list);
The function findViewById only finds views that are actually set. Your current setContentView does not set an xml that has the id you are looking for in it, so the first line I quoted will not result in anything.
You should either load the correct XML, or inflate the view you are looking for with an inflater
the line (LinearLayout ll = (LinearLayout) findViewById(R.id.view_related_list);) will search for view_related_list in view.xml, as in the line before that is the view you are using..
You need to inflate view_related.xml first and then get the view from it..
You're doing a lot of wrong things in your code. First you search for a LinearLayout that isn't in the current layout and also isn't in any inflated layout at that moment. Second, you try to inflate an id reference instead of a layout reference. Last, you should use another approach for what you're trying to do. Your code should be(from what I understood):
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.view);
LayoutInflater li = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
View view = findViewById(R.id.view_related);
ViewGroup parent = (ViewGroup) view.getParent();
int index = parent.indexOfChild(view);
parent.removeView(view);
View llview = li.inflate(R.layout.view_related, parent, false); // is view_related the name of the extra layout file?
parent.addView(llview, index);
LinearLayout ll = (LinearLayout) findViewById(R.id.view_related_list); // or search for it in llview, llview.findViewById(R.id.view_related_list);
TextView tv = new TextView(this);
tv.setText("Hello");
ll.addView(tv); // NULL POINTER EXCEPTION
tv = new TextView(this);
tv.setText("Goodbye");
ll.addView(tv);
}
This is a situation when you should use a ViewStub to add the new layout file. Although, I don't understand why don't you add the new layout file directly in the view.xml if you're going to add the layout's content in the onCreate method.
i think you need to use setContentView(R.layout.view_related); instead of setContentView(R.layout.view);
or another simple thing you can do is take the linear layout in the same layout "view" and make it invisible when not required and visible when required
You may forgetting to declare your Activity in manifest.xml
I created mainLayout with two buttons
add: to add other layout
remove : to remove other layout.
<Button
android:id="#+id/btnAdd"
android:textStyle="bold"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Add View"
android:onClick="addView" />
<Button
android:id="#+id/btnRemove"
android:textStyle="bold"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Remove View"
android:onClick="removeView" />
now i wrote following code to add the view
when I click on addView button
LayoutInflater inflater= (LayoutInflater)this.getSystemService(LAYOUT_INFLATER_SERVICE);
view=inflater.inflate(R.layout.other_layout,null);
mainLayout.addView(view);
the view is added below the main layout.
But I want the view to add in middle of the screen not at the bottom of screen.
How can I do that?
Modify this line and put ur parent view.
view=inflater.inflate(R.layout.other_layout,PARENT_VIEW_OBJECT);
It should work
get the parentLayout like this
parentLayout=(YourParentLayout)view.findViewById(R.id.parentLayout);
then
parentLayout.addView(yourview);
You can check below code, its working fine for me
private View view = null ;
Button addbutton = null;
ViewGroup parent = null;
LayoutInflater inflater = null;
inflater= (LayoutInflater)this.getSystemService(LAYOUT_INFLATER_SERVICE);
addbutton = (Button)findViewById(R.id.btnAdd);
parent = (ViewGroup) findViewById(R.id.mainxml);
addbutton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
view = inflater.inflate(R.layout.other, null);
parent.addView(view);
}
});
I would put an empty LinearLayout placeholder at the top of your main layout where you want your view to be and add the view to that instead of the main layout.
You can adjust the placeholder first and modify it's location and looks.
<LinearLayout
android:id="#+id/placeholder"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
..../>
<Button
..../>
I would look into using a ViewStub it holds a place until .inflate() is called. That way the layout isn't taking resources until it's needed.
I have a layout for a view -
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="0px"
android:orientation="vertical">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/items_header"
style="#style/Home.ListHeader" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/items_none"
android:visibility="gone"
style="#style/TextBlock"
android:paddingLeft="6px" />
<ListView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/items_list" />
</LinearLayout>
What I want to do, is in my main activity with a layout like this
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="0px"
android:id="#+id/item_wrapper">
</LinearLayout>
I want to loop through my data model and inject multiple views consisting of the first layout into the main layout. I know I can do this by building the controls completely within the code, but I was wondering if there was a way to dynamically build the views so that I can continue using a layout instead of putting everything in code.
Use the LayoutInflater to create a view based on your layout template, and then inject it into the view where you need it.
LayoutInflater vi = (LayoutInflater) getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = vi.inflate(R.layout.your_layout, null);
// fill in any details dynamically here
TextView textView = (TextView) v.findViewById(R.id.a_text_view);
textView.setText("your text");
// insert into main view
ViewGroup insertPoint = (ViewGroup) findViewById(R.id.insert_point);
insertPoint.addView(v, 0, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
You may have to adjust the index where you want to insert the view.
Additionally, set the LayoutParams according to how you would like it to fit in the parent view. e.g. with FILL_PARENT, or MATCH_PARENT, etc.
See the LayoutInflater class.
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ViewGroup parent = (ViewGroup)findViewById(R.id.where_you_want_to_insert);
inflater.inflate(R.layout.the_child_view, parent);
It looks like what you really want a ListView with a custom adapter to inflate the specified layout. Using an ArrayAdapter and the method notifyDataSetChanged() you have full control of the Views generation and rendering.
Take a look at these tutorials
http://www.softwarepassion.com/android-series-custom-listview-items-and-adapters/
http://developerlife.com/tutorials/?p=327
http://www.androidguys.com/2008/07/14/fancy-listviews-part-one/
To make #Mark Fisher's answer more clear, the inserted view being inflated should be a xml file under layout folder but without a layout (ViewGroup) like LinearLayout etc. inside. My example:
res/layout/my_view.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/i_am_id"
android:text="my name"
android:textSize="17sp"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"/>
Then, the insertion point should be a layout like LinearLayout:
res/layout/activity_main.xml
<?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/aaa"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="#+id/insert_point"
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>
</RelativeLayout>
Then the code should be
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shopping_cart);
LayoutInflater inflater = getLayoutInflater();
View view = inflater.inflate(R.layout.my_view, null);
ViewGroup main = (ViewGroup) findViewById(R.id.insert_point);
main.addView(view, 0);
}
The reason I post this very similar answer is that when I tried to implement Mark's solution, I got stuck on what xml file should I use for insert_point and the child view. I used layout in the child view firstly and it was totally not working, which took me several hours to figure out. So hope my exploration can save others' time.
// Parent layout
LinearLayout parentLayout = (LinearLayout)findViewById(R.id.layout);
// Layout inflater
LayoutInflater layoutInflater = getLayoutInflater();
View view;
for (int i = 1; i < 101; i++){
// Add the text layout to the parent layout
view = layoutInflater.inflate(R.layout.text_layout, parentLayout, false);
// In order to get the view we have to use the new view with text_layout in it
TextView textView = (TextView)view.findViewById(R.id.text);
textView.setText("Row " + i);
// Add the text view to the parent layout
parentLayout.addView(textView);
}