I have layout:
<?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="100dp"
android:background="#535353">
<TextView
android:id="#+id/promo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp"
android:padding="5dp"
android:layout_weight="1"
/>
<TextView
android:id="#+id/title"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp"
android:padding="5dp"
android:layout_weight="1"
/>
</LinearLayout>
and I want to replace promo and title. Set first title, then promo.
I use this snippet.
public class AdView extends LinearLayout{
TextView promo;
public AdView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
LayoutInflater.from(getContext()).inflate(R.layout.view, this);
promo = (TextView) findViewById(R.id.promo);
title = (TextView) findViewById(R.id.title);
}
private void changePosition() {
removeView(promo);
addView(promo);
}
But, I get error: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
So, could you help me? How can I do this?
Related
I am trying to create a custom button layout. I created an xml file "custom_button.xml" which I then inflate in the "ButtonLanguageSelection" class. In this class I added the onClickListener. I added this class to the "main_activity.xml". It behaves like it should except it won't receive any touch events. Then I copied the code from "custom_button.xml" and added it directly without inflating it, I just added the android:onClick parameter and in this way it worked. I can't find out what would be the problem, the layouts are the same.
Has some of you have similar problems? What could be the problem? I attached the code if it helps someone to find the problem.
Thank you for any help!
custom_button.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#drawable/button_language_selection_states"
android:weightSum="1"
android:clickable="true"
>
<TextView
android:id="#+id/textview_select_language"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_gravity="center"
android:gravity="center_horizontal"
android:paddingTop="30px"
android:layout_weight="0.4"
android:textSize="21sp"
android:textColor="#333333"
/>
<View
android:layout_width="match_parent"
android:background="#drawable/gradient"
android:layout_height="1dp"
android:layout_margin="10px">
</View>
<TextView
android:id="#+id/textview_language"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_gravity="center"
android:paddingTop="40px"
android:layout_weight="0.6"
android:textSize="28sp"
android:textColor="#ffffff"
/>
</LinearLayout>
ButtonLanguageSelection
public class ButtonLanguageSelection extends LinearLayout
{
private TextView introductoryTextView;
private TextView language;
private final String TAG = "ButtonLanguageSelection";
public ButtonLanguageSelection(Context context) {
super(context);
}
public ButtonLanguageSelection(Context context, AttributeSet attrs)
{
super(context, attrs);
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
layoutInflater.inflate(R.layout.button_language_selection, this);
introductoryTextView = (TextView)findViewById(R.id.textview_select_language);
language = (TextView)findViewById(R.id.textview_language);
this.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "onclick");
}
});
}
public ButtonLanguageSelection(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setIntroductoryTextView(String s)
{
introductoryTextView.setText(s);
}
public void setLanguage(String s)
{
language.setText(s);
}
}
main_activity.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#drawable/screen_welcome_bg">
<defaultpackage.ButtonLanguageSelection
android:id="#+id/ButtonLanguageSelection_Slovene"
android:layout_width="341px"
android:layout_height="260px"
android:layout_marginRight="2px"/>
<defaultpackage.ButtonLanguageSelection
android:id="#+id/ButtonLanguageSelection_Italian"
android:layout_width="341px"
android:layout_height="260px"
android:layout_marginRight="2px"/>
<!--<defaultpackage.ButtonLanguageSelection-->
<!--android:id="#+id/ButtonLanguageSelection_English"-->
<!--android:layout_width="341px"-->
<!--android:layout_height="260px" />-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="341px"
android:layout_height="260px"
android:background="#drawable/button_language_selection_states"
android:weightSum="1"
android:clickable="true"
android:onClick="sendMessage"
>
<TextView
android:id="#+id/textview_select_language"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_gravity="center"
android:gravity="center_horizontal"
android:paddingTop="30px"
android:layout_weight="0.4"
android:textSize="21sp"
android:textColor="#333333"
/>
<View
android:layout_width="match_parent"
android:background="#drawable/gradient"
android:layout_height="1dp"
android:layout_margin="10px">
</View>
<TextView
android:id="#+id/textview_language"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_gravity="center"
android:paddingTop="40px"
android:layout_weight="0.6"
android:textSize="28sp"
android:textColor="#ffffff"
/>
</LinearLayout>
I think the problem is writing all the init code in only one constructor. Consider making an init function and call it from all the constructors. Also consider setting the onClick events listeners in your activity rather than than the layout constructor itself.
My Application freezes and LogCat grows heap (frag case) to 14mb if add more then 3 own "components" to current activity/layout. It takes very long until the fourth "component" is added and Android asks to close my application.
I've written a Activity which has some components (EditTexts) and a own "component". I want to do something similar to the contacts app (Number adding) but with ingredients. So I've created a class IngredientsLayout which extends LinearLayout and a Layout xml file with a TextView and a "Add"-Button which I've placed on the Activity-Layout. Every time I press the Add Button, a single IngredientLayout with 3 EditTexts and a ImageButton will be added to the current IngredientsLayout which should "hold" all added ingredients.
What I'm doing wrong?
IngredientsLayout class:
public class IngredientsLayout extends LinearLayout {
ImageButton imageButtonAddIngredient;
ArrayList<IngredientLayout> arrayList;
public IngredientsLayout(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.ingredients_list, this);
arrayList = new ArrayList<IngredientLayout>();
imageButtonAddIngredient = (ImageButton) findViewById(R.id.imageButtonAddIngredient);
imageButtonAddIngredient.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
LinearLayout layout = (LinearLayout) findViewById(R.id.linearLayoutIngredientsList);
IngredientLayout ingredientLayout = new IngredientLayout(getContext(), null);
ingredientLayout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT));
arrayList.add(ingredientLayout);
layout.addView(ingredientLayout);
}
});
}
}
ingredients_layout.xml:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/linearLayoutIngredientsList"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:orientation="horizontal" >
<TextView
android:id="#+id/textView1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="left|center_vertical"
android:layout_weight="1"
android:text="#string/ingredients_list_name" />
<ImageButton
android:id="#+id/imageButtonAddIngredient"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:src="#drawable/ic_menu_add_ingredient" />
</LinearLayout>
</LinearLayout>
</merge>
IngredientLayout class:
public class IngredientLayout extends LinearLayout {
public IngredientLayout(Context context, AttributeSet attrs) {
super(context, attrs);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.ingredients_list_row, this);
}
}
ingredients_list_row.xml
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/linearLayoutIngredientRow"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="#+id/editTextAmount"
android:layout_width="45dp"
android:layout_height="fill_parent"
android:layout_weight="0.11"
android:ems="10"
android:hint="#string/ingredients_list_listrow_amount"
android:inputType="numberDecimal" >
<requestFocus />
</EditText>
<EditText
android:id="#+id/editTextUnit"
android:layout_width="60dp"
android:layout_height="fill_parent"
android:layout_weight="0.06"
android:ems="10"
android:hint="#string/ingredients_list_listrow_unit" /><EditText
android:id="#+id/editTextName"
android:layout_width="127dp"
android:layout_height="match_parent"
android:layout_gravity="left"
android:layout_weight="0.005"
android:ems="10"
android:hint="#string/ingredients_list_listrow_name" />
<ImageButton
android:id="#+id/imageButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:src="#drawable/ic_menu_cancel" />
</LinearLayout>
</merge>
LinearLayouts are not meant for this kind of dynamic accessing.
What you need to do is a ListView and a adapter.
And every time you add a new item into the adapter you call it's NottifyDataChanged to update the list view with the new item.
I have created a custom view name StepView . Here is the code for StepView.
public class StepView extends LinearLayout {
private Context cont;
private LinearLayout stepHolder;
public StepView(Context context, AttributeSet attrs) {
super(context);
// TODO Auto-generated constructor stub
LayoutInflater.from(context).inflate(R.layout.stepview, this, true);
stepHolder = (LinearLayout) findViewById(R.id.stepHolder);
cont = context;
}
public void addStep(String title, int drawableId, View.OnClickListener stepAction){
View step = LayoutInflater.from(cont).inflate(R.layout.step, this, true);
TextView txtText = (TextView) step.findViewById(R.id.txtText);
ImageView imgStep = (ImageView) step.findViewById(R.id.imgStep);
txtText.setText(title);
imgStep.setImageResource(drawableId);
stepHolder.addView(step);
}
}
It is the code for stepview.xml.
<?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" >
<LinearLayout
android:id="#+id/stepHolder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
</LinearLayout>
</LinearLayout>
step.xml
<?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" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/imgStep"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/txtText">
</TextView>
</LinearLayout>
Now I used the XML in main layout.
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal" >
..................
</LinearLayout>
<com.orthokeys.view.StepView
android:id="#+id/stepMenu"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</com.orthokeys.view.StepView>
</merge>
I have cheched that it is appearing properly. But when I add the following code it is giving NullPointerException.
StepView stepMenu = (StepView) findViewById(R.id.stepMenu);
stepMenu.addStep("Picture", R.drawable.step01, null);
I have checked that stepMenu is giving null.
Whatis the problem in my code?
I solved the prob...change the code in constructor of StepView Class
public StepView(Context context, AttributeSet attrs) {
super(context, attrs);
....................
}
You are not showing the full code block where you get the error, but it looks like you may have forgotten to use setContentView(R.layout.main_layout); to set the layout in use.
I created a compound component Box which I want to add to the layout. Box xml:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/linearLayoutForBlock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="#+id/linearLayout1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" android:background="#drawable/screen_background" android:layout_marginLeft="5dp" android:layout_marginTop="5dp">
<ImageButton
android:id="#+id/imageButtonContent"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
android:scaleType="fitCenter"
android:src="#drawable/beach_bed" android:background="#drawable/buttonbackground" android:clickable="true" android:layout_margin="5dp" android:contentDescription="#string/sample_text"/>
<TextView
android:id="#+id/textViewContent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="#string/sample_text"
android:textColor="#color/deep_blue" android:layout_margin="5dp"/>
</LinearLayout>
</merge>
Box class:
public class Box extends LinearLayout {
private TextView textContent;
private ImageView imageContent;
public Box(Context context, AttributeSet attrs) {
super(context, attrs);
((Activity)getContext()).getLayoutInflater().inflate(R.layout.box, this);
setupViewItems();
}
private void setupViewItems() {
textContent = (TextView) findViewById(R.id.textViewContent);
imageContent = (ImageView) findViewById(R.id.imageButtonContent);
}
public void setTextContent(String text) {
this.textContent.setText(text);
}
public void setImageContent(String tag) {
this.imageContent.setContentDescription(tag);
}
}
Everything works if I add the Box to the main xml file like:
<com.mypackage.alexey.Box
android:id="#+id/mybox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
/>
The problem is that I'd like to create many of boxes programmatically something like this: Box mybox= new Box();. How to do it?
I would suggest you also implement the constructors of the LinearLayout that takes only a Context:
public Box(Context context) {
super(context);
}
Then in your Activity, when you want to add a new Box, instantiate the Box class and add it to your layout:
// I assumed you have a LinearLayout to which you want to add your Box
LinearLayout parent = (LinearLayout) findViewById(R.id.parent_id);
//create the Box and added to the parent above
Box theBox = new Box(this); // if you are in an Activity
//some LayoutParams to replicate your xml attributes
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
// set the Orientation for the Box
theBox.setOrientation(LinearLayout.HORIZONTAL);
//add the Box
parent.addView(theBox);
Why do some views in a linearlayout within a scrollview fill the parent when fill_parent is set when inflated from xml directly but don't when added via a custom component that inflates the same xml?
The result of inflating the same xml in two different places:
The top item of the two is as a result of inflating the xml in a class that extends linearLayout and then adding that class to the linearLayout inside the scroll view. This view isn't filling the parent and I can't work out why.
The bottom item is the result of inflating the xml directly in the activity that this is all contained within. It appears as I expected.
This is the code that adds the two views in the activity:
scrollList.addView(new CommunityTaskListItem(this, null));
scrollList.addView(View.inflate(getBaseContext(), R.layout.communitytask, null));
This is the code that inflates the activity within a custom component class "CommunityTaskListItem":
View communityTask = View.inflate(context, R.layout.communitytask, null);
addView(communityTask);
I assume the xml is okay because it works fine when inflated directly in the activity. My question is why the fill_parent property seems to be lost when the xml is inflated in a custom component's class?
For good measure, here is the xml being inflated: EDIT: I gave you the wrong one before!!! Here's the right one:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent" android:layout_height="wrap_content">
<LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="#+id/linearLayout1" android:background="#drawable/plainbutton" android:layout_marginBottom="5px" android:layout_marginTop="5px" android:layout_weight="1">
<TextView android:layout_height="wrap_content" style="#style/CommunityTaskText" android:layout_width="fill_parent" android:layout_weight="1" android:id="#+id/textViewCommunityTaskTimes" android:text="xx:xx - xx:xx"></TextView>
<TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1" style="#style/CommunityTaskText" android:id="#+id/textViewCommunityTaskDescription" android:text="Task"></TextView>
</LinearLayout>
</LinearLayout>
I'd like to keep the custom component and not inflate the xml within my activity if I could.
EDIT: Code where CommunityTaskListItem is inflated:
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
public class CommunityTaskListItem extends LinearLayout {
TextView textViewCommunityTaskTimes;
TextView textViewCommunityTaskDescription;
public CommunityTaskListItem(Context context, AttributeSet attrs) {
super(context, attrs);
View communityTask = View.inflate(context, R.layout.communitytask, null);
addView(communityTask);
textViewCommunityTaskTimes = (TextView)findViewById(R.id.textViewCommunityTaskTimes);
textViewCommunityTaskDescription = (TextView)findViewById(R.id.textViewCommunityTaskDescription);
}
...
EDIT: All working now! Here's the final code for anyone else incase they have a similar issue:
Contructor for the custom object:
public CommunityTaskListItem(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(VERTICAL);
addView(inflate(context, R.layout.communitytask, null));
textViewCommunityTaskTimes = (TextView)findViewById(R.id.textViewCommunityTaskTimes);
textViewCommunityTaskDescription = (TextView)findViewById(R.id.textViewCommunityTaskDescription);
}
Custom object xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="#+id/linearLayout1" android:background="#drawable/plainbutton" android:layout_marginBottom="5px" android:layout_marginTop="5px" android:layout_weight="1">
<TextView android:layout_height="wrap_content" style="#style/CommunityTaskText" android:layout_width="fill_parent" android:layout_weight="1" android:id="#+id/textViewCommunityTaskTimes" android:text="xx:xx - xx:xx"></TextView>
<TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1" style="#style/CommunityTaskText" android:id="#+id/textViewCommunityTaskDescription" android:text="Task"></TextView>
</LinearLayout>
</LinearLayout>
To be honest I don't know exactly what the mistake I was making was so if someone could make that clear I'd be very grateful.
The first line should be:
scrollList.addView(new CommunityTaskListItem(this, null)
new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
You call inflate() inside CommunityTaskListItem class and inflated view becomes a child view of the CommunityTaskListItem view. But you don't set width and height for this view so it doesn't match its parent's width.
EDIT: I think your XML should look like this:
<?xml version="1.0" encoding="utf-8"?>
<packagename.CommunityTaskListItem
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent" android:layout_height="wrap_content">
<LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="#+id/linearLayout1" android:background="#drawable/plainbutton" android:layout_marginBottom="5px" android:layout_marginTop="5px" android:layout_weight="1">
<TextView android:layout_height="wrap_content" style="#style/CommunityTaskText" android:layout_width="fill_parent" android:layout_weight="1" android:id="#+id/textViewCommunityTaskTimes" android:text="xx:xx - xx:xx"></TextView>
<TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1" style="#style/CommunityTaskText" android:id="#+id/textViewCommunityTaskDescription" android:text="Task"></TextView>
</LinearLayout>
</packagename.CommunityTaskListItem>
where packagename is the name of the package of the CommunityTaskListItem class.
In this case you should change the CommunityTaskListItem:
public class CommunityTaskListItem extends LinearLayout {
TextView textViewCommunityTaskTimes;
TextView textViewCommunityTaskDescription;
public CommunityTaskListItem(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onFinishInflate() {
textViewCommunityTaskTimes = (TextView)findViewById(R.id.textViewCommunityTaskTimes);
textViewCommunityTaskDescription = (TextView)findViewById(R.id.textViewCommunityTaskDescription);
}
// ....
}
This class cannot be created using its constructor. It can be only inflated.
The second way is to inflate children in the constructor like you do. But the XML should differ:
<?xml version="1.0" encoding="utf-8"?>
<merge
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="#+id/linearLayout1" android:background="#drawable/plainbutton" android:layout_marginBottom="5px" android:layout_marginTop="5px" android:layout_weight="1">
<TextView android:layout_height="wrap_content" style="#style/CommunityTaskText" android:layout_width="fill_parent" android:layout_weight="1" android:id="#+id/textViewCommunityTaskTimes" android:text="xx:xx - xx:xx"></TextView>
<TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1" style="#style/CommunityTaskText" android:id="#+id/textViewCommunityTaskDescription" android:text="Task"></TextView>
</LinearLayout>
</merge>
The CommunityTaskListItem will be:
public class CommunityTaskListItem extends LinearLayout {
TextView textViewCommunityTaskTimes;
TextView textViewCommunityTaskDescription;
public CommunityTaskListItem(Context context) {
this(context, null);
}
public CommunityTaskListItem(Context context, AttributeSet attrs) {
super(context, attrs);
setOrientation(VERTICAL);
inflate(context, R.layout.communitytask);
textViewCommunityTaskTimes = (TextView)findViewById(R.id.textViewCommunityTaskTimes);
textViewCommunityTaskDescription = (TextView)findViewById(R.id.textViewCommunityTaskDescription);
}
// ....
}
This class can be created using its contructor and inflated from an XML. But if you want to inflate it, you should create a separate XML file. Let's call it task.xml.
<?xml version="1.0" encoding="utf-8"?>
<packagename.CommunityTaskListItem
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_height="wrap_content"/>
And that's how you can use it:
scrollList.addView(new CommunityTaskListItem(this),
new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
scrollList.addView(View.inflate(getBaseContext(), R.layout.task, null));