I'm having a hard time with Android as a pretty newbie in the platform, and as I learnt, many people too. I don't know how many hours I have lost so far - it's terrifying even to count them.
I want basically to add a new TextView (or any other View) to LinearLayout after clicking the button. Here's this part of the code:
public void btnClick(View view) {
final LinearLayout ll = (LinearLayout) findViewById(R.id.test_story_screen_layout);
//checking if child is being added - it is, this value is increase with every button click
android.util.Log.d("child: ", Integer.toString(ll.getChildCount()));
android.util.Log.d("height: ", Integer.toString(ll.getMeasuredHeight()));
ll.post(new Runnable() {
public void run() {
final TextView tv = new TextView(MainActivity.this);
tv.setText("new one " + (new Random()).nextInt());
tv.setTextColor(Color.YELLOW);
LayoutParams lp = new LayoutParams(200, 500);
tv.setLayoutParams(lp);
tv.setVisibility(View.VISIBLE);
ll.addView(tv, ll.getChildCount());
//checking if the run() is called - it is
ll.setBackgroundColor(Color.GREEN);
}
});
ll.requestLayout(); //invalidate() and postInvalidate() also not working
}
But the newly added View is not visible (but is added as a child to LinearLayout).
After hours of checking what's wrong, I only found out that when I replace this line:
ll.addView(tv, ll.getChildCount());
with this:
ll.addView(tv, ll.getChildCount() - 1);
Then new View is visible, but it replaces the previous one - which is not desired.
I already checked some solutions, like this: Refreshing a LinearLayout after adding a view
They didn't help me with the issue.
UPDATE:
Linear Layout looks just fine with predefined in XML two Views (as in the code below). Here's intersting part of the XML:
<LinearLayout
android:id="#+id/test_story_screen_layout"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:orientation="vertical"
android:layout_margin="16dp" >
<ImageView
android:id="#+id/imageView1"
android:src="#drawable/example"
android:layout_width="16dp"
android:layout_height="16dp" />
<TextView
android:id="#+id/textView1"
android:text="Hello World!"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
Nothing unusual I think. So these two Views (Image and Text) are visible of course. Then any new TextView I add to this LinearLayout dynamically (via the btnClick() as in the code above) is not visible, but it is indeed added to Layout, as the ll.getChildCount() is increased every time when the child view is added (= button clicked).
In order to test this further, I added these two lines at the end of the btnClick() method:
android.util.Log.d("tv.getMeasuredWidth: ", Integer.toString(tv.getMeasuredWidth()));
android.util.Log.d("tv.getMeasuredHeight: ", Integer.toString(tv.getMeasuredHeight()));
I'm guessing the problem is the tv (TextView) rather than ll (LinearLayout), as tv gets width and height both 0.
Issue is you are initializing your linearlayout ll with what defined in your xml every time on button click and due to this your linearlayout is getting renewed. Move
final LinearLayout ll = (LinearLayout) findViewById(R.id.test_story_screen_layout);
in your onCreate if using Activity, onCreateView if using Fragment.
Your layout horizontal by default, When you add new view it set it up out from right border of screen. Just set orientation vertical in layout for you linear layout
The linearLayout orientation is horizontal by default set orientation to vertical.
if you want to add View as last element in Layout you can just do this:
ll.addView(tv);
There were number of things related in this Android issue, some of this were:
ll.post() made sense and appared to be required in my case
UI in Android should be updated in a special, not any, way, and it refers to e.g. Async Tasks
visibility of an item (item should be visible)
proper margins (when a screen e.g. is rotated to landscape, margin appeared to be too big)
Damn it. I find Android to be one of least thought-through platforms I've seen.
Related
I'm working on my first android app and have run into some trouble. I'm trying to start a new activity which takes in an ArrayList from the previous activity and creates a button for each item in the list. I've tried looking at many other stack overflow posts but none of them have helped solve my problem.
I have tried many different strategies, and my code is currently as below:
public class VideoMenu extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_menu);
Bundle b = getIntent().getExtras();
ArrayList<String> videos = b.getStringArrayList("videos");
setContentView(R.layout.activity_video_menu);
for(int i=0;i<videos.size();i++){
Button myButton = new Button(this);
myButton.setText(videos.get(i));
myButton.setId(i);
RelativeLayout ll = (RelativeLayout)findViewById(R.id.videos);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
if (i != 0) {
lp.addRule(RelativeLayout.END_OF, i - 1);
}
myButton.setLayoutParams(lp);
ll.addView(myButton,lp);
}
}
}
and XML code:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/videos"
android:layout_alignParentBottom="true"
android:layout_alignWithParentIfMissing="true"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:orientation="vertical">
This compiles just fine, but all of the buttons appear on top of one another. If you could include reasons for why any code suggestions might work I would appreciate it. I'd like to learn more about this :)
Edit: fixed typo
The reason is that you are adding the buttons to a RelativeLayout. RelativeLayout basically add all views to wherever you specify in the view. You're not really specifying, so they're all appearing on top of each other.
In order to fix this, change your RelativeLayout to a LinearLayout, with orientation of either horizontal or vertical. horizontal will add views side by side in a horizontal line, and vertical will add them in a vertical line.
I have a custom XML file. I want to repeat this in a layout (say Relative) n number of times, dynamically (obviously).
I have seen many posts, but none helped. I am not looking for a ListView or Adapters or so. It's as simple as - A RelativeLayout. Inside it, adding the custom XML one above another. Any number of times.
With a static LinearLayout (Vertical orientation), adding the view dynamically results in rendering it once, not one below another. Don't know why. Although a TextView or so do repeat one below the other in a loop inside a LinearLayout (Vertical).
Then I dynamically created the layout (Relative), and inflated the custom XML. Displayed one. When I tried for another below the first it told me to remove child's parent first (Exception). If I do that and add again, its as good as removing the first rendered view and adding it again.
So how can I get multiple views in same layout?
A rough presentation of what I've attempted:
mainLayout = (RelativeLayout)findViewById(R.id.mainlay); //Mainlayout containing some views already
params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.BELOW,R.id.sideLayout); //sideLayout is an existing LinearLayout within the main layout.
View child = getLayoutInflater().inflate(R.layout.dynamiccustomlayout,null);
RelativeLayout r1 = new RelativeLayout(this);
r1.setLayoutParams(params);
r1.addView(child);
mainLayout.addView(r1);
mainLayout.setLayoutParams(params);
mainLayout.addView( child);
/* r2 = new RelativeLayout(this);
r2.setLayoutParams(params);
r2.addView(contentLayout); [Gives exception] */
This is how it worked out for me...
Before that, the issue with android is:
If you add dynamic views inside a LinearLayout (Horizontal), they will appear horizontally with new created instances, added to the view.
However, shockingly, it's not the same in case of LinearLayout (Vertical orientation). Hence the whole mess.
Solution:
The RelativeLayout layout file was binded with the variable, somewhat like this:
customLay = (RelativeLayout) mainLay.findViewById(R.id.dynamicCustomLayout);
Then, a Dynamic RelativeLayout was created within which the former variable is added/wrapped.
customLayout = new RelativeLayout(this);
customLayout.addView(customLay);
Every layout is assigned an id:
customLayout.setId(i);
And then a loop is run (2 if conditions for i=0 and i>0)
for i>0 (indicates the 2nd dynamic layout, to be added below the first), LayoutParameter is created:
params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
And then for i>0, using the ids of dynamic views, they are added one below the other:
//Following code below used id to place these views below each other to attain list type arrangement of views//
// i==0 for first view on top//
if (i == 0) {
params.addRule(RelativeLayout.BELOW, R.id.sideLayout);
customLayout.setLayoutParams(params);
}
// i>0 for views that will follow the first top//
else {
params.addRule(RelativeLayout.BELOW, i - 1);
customLayout.setLayoutParams(params);
}
Then added to main root layout, where all these views or cards need to be displayed:
includeLayout.addView(customLayout);
Ofcourse, the code is not just this. I have written the essential points that helped me achieve the target and that may help others in future.
So the main essence was ---
using a Dynamic RelativeLayout, to
bind the static RelativeLayout, and
assigning ids to the Dynamic RelativeLayout wrappers, and
on basis of ids use RelativeLayoutParameters to place the following
ids below the previous ones.
You have to instanciate every child by itself
View child = getLayoutInflater().inflate(R.layout.dynamiccustomlayout,null);
r1.addView(child);
View child2 = getLayoutInflater().inflate(R.layout.dynamiccustomlayout,null);
r1.addView(child2);
//ok, i do a analog thing in obne of my apps. here is the code:
public class FlxForm extends LinearLayout {
public FlxForm(Context context) {
super(context);
inflater = LayoutInflater.from(context);
inflater.inflate(R.layout.flxform, this);
this.setPadding(0, 0, 0, 0);
container = (LinearLayout) this.findViewById(R.id.flxform);
this.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT));
//here is my funtion to calculate the items i want to add, its a little bit too complicated, but in the end it works like:
for(int i=0;i<10;i++){
View x = inflater.inflate(R.layout.dynamiccustomlayout,null);
container.addview(x);
}
}
}
XML for the Form
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/flxform"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:focusable="false"
android:background="#android:color/transparent"
android:orientation="vertical" >
</LinearLayout>
Then you can instantiate a "Form" Objekt and add it into a ScrollView
For doing this You would have to nest your RelativeLayout inside a ScrollView and Manage all the Scrolling, items adding, memory management, etc manually.
So the simple solution for adding n Number of Custom Views is to use a RecyclerView, ListView, GridView, etc with a neat CustomAdapter and Your Custom View.
Here is a nice example of using RecyclerView with custom Adapter :
http://code.tutsplus.com/tutorials/getting-started-with-recyclerview-and-cardview-on-android--cms-23465
I hope this Helps.
How does one programmatically, via one button click at a time, add multiple TextViews to an existing RelativeLayout without the TextViews overlapping onto one another.
I am trying something like this -
The following code exists inside the onCreate() method:
TextView textViewToSeeFirst = (TextView) findViewById(R.id.textView1);
RelativeLayout rLayout1 = (RelativeLayout) findViewById(R.id.relativeLayout1);
Button button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
RelativeLayout.LayoutParams lparams = new RelativeLayout.LayoutParams
(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
TextView newTextView = new TextView(TheActivityYouAreUsingActivity.this);
newTextView.setText("text you want");
rLayout1.addView(newTextView, lparams);
}
The TextViews are being added to the RelativeLayout, but they are all on top of one another, how does one fix this?
The goal is to programmatically, via one button click at a time, add multiple TextViews to an existing RelativeLayout, and without the TextViews overlapping onto one another.
Here is what I finally came to, this works but I am unsure if it is the best way to go (or even a good way).
The following code exists inside the onCreate() method:
// Creates a variable to keep track of the amount of TextViews, the
// first TextView, an array, and then stores the TextView in the Array
int numberOfTextViews = 1;
TextView[] textViewArray = new TextView[20];
TextView textViewToSeeFirst = (TextView) findViewById(R.id.textView1);
textViewArray[numberOfTextViews - 1] = textViewToSeeFirst;
// Also need to reference the RelativeLayout we are putting TextViews in
RelativeLayout rLayout1 = (RelativeLayout) findViewById(R.id.relativeLayout1);
Button button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
RelativeLayout.LayoutParams lparams = new RelativeLayout.LayoutParams
(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lparams.addRule(RelativeLayout.RIGHT_OF, textViewArray[numberOfTextViews - 1].getId());
lparams.addRule(RelativeLayout.ALIGN_TOP, textViewArray[numberOfTextViews - 1].getId());
TextView newTextView = new TextView(TheActivityYouAreUsingActivity.this);
newTextView.setText("text you want");
newTextView.setId(numberOfTextViews);
textViewArray[numberOfTextViews] = newTextView;
rLayout1.addView(textViewArray[numberOfTextViews], lparams);
numberOfTextViews = numberOfTextViews + 1;
}
Some things to keep in mind:
the parameters for RelativeLayout.LayoutParams are important, see developer material on these parameters. WRAP_CONTENT was chosen for my needs because it causes the TextViews to only take up the size of their text, rather than their entire parent... Overlapping was occurring before I changed this.
the id of each new TextView must be set if it is to be referenced later on for new layout parameters
the id must be a positive number, and zero is not positive
the RelativeLayout is holding the TextViews and handling them, the textViewArray is just so the ids of each TextView can be stored and referenced if need be
The corresponding XML has this going for it inside the main parent:
<RelativeLayout
android:id="#+id/relativeLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight=".2" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/die_one" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" >
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/name_a_button" />
</RelativeLayout>
Notice that the first RelativeLayout, and the TextView both have an id, this is so the addRule() method in the activity can reference them.
This code allows a user to click a button and add new TextViews to a RelativeLayout without them overlapping.
Why dont you add all your text views in your xml file (as much as you want) before running you app. Just set the visibilities to the textviews which you dont want to show initially to "GONE" and then in button click listener just keep changing the visibility of textview to "View.Visible" .
If you want a new textview to appear each time you press a button then set a counter to a button and each time a counter increments you change the desire textview's visibility to View.Visible. If you understood what i am saying you will be able to make the code on your own.
You can use Linear Layout with orientation as vertical instead of Relative Layout. It will align all the textviews vertically one below the other.
I dont consider adding large number of textviews to the xml file a valid solution, as the number of times the user will click a button is unknown.
I have created a layout with a database call where a button will be created for each item inside the database. The buttons are created like I need and I also found out, how to set up the layout_width and layout_height but all buttons are placed in the same position and overlap each other so that only the last created button can be accessed. My code for creating the buttons looks like this:
Button bt = new Button(this);
bt.setText("Button Title");
bt.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
linerLayout.addView(bt);
The activity looks like that, after generating the buttons:
I looked at each method that can be used for the button but didn't find anything to define the position. I just thought about following method:
bt.layout(l, t, r, b);
but don't know exactly how to use it and thought there might be a simpler method to solve this problem. Anybody who knows a workaround?!
CHANGED CODE
I just tryied to set the layout parameters like explained from "Chen doron". I have a relative layout inside my xml file:
<RelativeLayout
android:id="#+id/llActOver"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="2" >
</RelativeLayout>
and formatted the generated buttons like this:
Button bt = new Button(context);
bt.setText(c.getString(iDef));
fontClass.setFont(bt);
//RelativeLayout placeholder = new RelativeLayout(context);
RelativeLayout.LayoutParams layoutParam =
new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.WRAP_CONTENT);
if(rowCount < 1){
layoutParam.addRule(RelativeLayout.ALIGN_PARENT_TOP);
rowCount++;
}else{
layoutParam.addRule(RelativeLayout.ALIGN_PARENT_TOP, lastButtonId);
}
lastButtonId = bt.getId();
//placeholder.addView(bt, layoutParam);
linearLayout.addView(bt, layoutParam);
I save the buttons id at the end of the loop so that the last buttons id can be accessed in the next round.
I also tryied to create a new relativ layout for each new button like the commented part of the code shows but even without the new layout nothing happens and i still just have all buttons overlapped.
I finally solved the problem, but just by trying each possible combination of layout types.
I found out, i have to define a LinearLayout inside the XML file and the attribute android:orientation="vertical" is affordable. all the other parameters that have been set before were unimportant.
So now i have a LinearLayout inside a ScrollView in my xml file:
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="2" >
<LinearLayout
android:id="#+id/llActOver"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
</LinearLayout>
</ScrollView>
and inside the java database i have following code to create a button for each row in the db, setting its Text and Font and add it to the loaded layout:
for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
Button bt = new Button(context);
bt.setText(c.getString(iDef));
fontClass.setFont(bt);
linearLayout.addView(bt);
}
and here a screenshot of the result:
maybe somebody else who has the same problem won't need to worry as long as me with this description.
You could use a Relative Layout, and have the first Button align to the parent's top.
Then each button aligned to the previous button's bottom.
Use:
params.addRule(RelativeLayout.ALIGN_PARENT_TOP);
And then:
params.addRule(RelativeLayout.BELOW,ID-Of-Previous-Button);
Also check out:
http://developer.android.com/reference/android/widget/RelativeLayout.LayoutParams.html
I have a relativeLayout like below:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal"
android:id="#+id/parent" >
<ListView
android:layout_width="360dp"
android:layout_height="600dp"
android:id="#+id/list"
android:inputType="text"
android:maxLines="1"
android:layout_margin="50dp"
android:layout_alignParentRight="true"
/>
</RelativeLayout>
In the java code, I want to add a view to the left of the listview, but it didn't worked:
m_relativeLayout = (RelativeLayout)findViewById(R.id.parent);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.LEFT_OF, m_listView.getId());
Button button2 = new Button(this);
button2.setText("I am button 2");
m_relativeLayout.addView(button2, layoutParams);
only if I set the listview to alignParentRight, it will work. Is this an android bug or I'm missing something?
I always try addView(View child, int index, LayoutParams params), but it might only work in the linearlayout. So is there an normal solution to make the RelativeLayout.LEFT_OF work?
EDIT
I have tried RelativeLayout.BELOW and RelativeLayout.RIGHT_OF, and they worked perfectly, so it means I don't have enough place to get the button? I tried to give more space, but it still not work.
I use Toshiba AT100 (1280*800) and landscape, so the space is enough. Test below and right just same as the left. I think If i put an control A in the relativelayout, then I add control B and decalare it's on the left of the control A, the result should be the control B will push the control A to its right, right?
I think If i put an control A in the relativelayout, then i add control B and declare it's on the left of the control A, the result should be the control B will push the control A to its right, right?
Your assumption is incorrect, the control A will not be pushed to the right unless you specified this with a RelativeLayout.LayoutParams rule. RelativeLayout places its children one one top of each other starting at the top-left corner of the screen if you don't specify placement rules for them. When you add the View A to the RelativeLayout without any rules(like layout_alignParentRight) it will be placed starting from the top-left corner of the screen. Then, when you add the View B, the rule to_leftOf will apply to this View position but this rule doesn't mean anything for the View A who will maintain its position on the screen. This will make View B to be place to the left of View A but outside of the screen as View A bounds start from the left border of the screen.
The Button will be placed to the left of the ListView when you use layout_alignParentRight="true" because there is now space to actually see the Button(it's not outside anymore). addView(View child, int index, LayoutParams params) works in a LinearLayout because the LinearLayout arranges its children in a row or column(depending on orientation) so when you add a View at a specific position, it will push the other Views after it to the right or below(depending on orientation)(there is no relative positioning of the views in a LinearLayout, the only rule is that the children come one after the other).
Starting with the ListView without any rules set on it, here is an example on how to make the Button to appear on the left of the ListView:
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
Button button2 = new Button(this);
button2.setText("I am button 2");
button2.setId(1000);
m_relativeLayout.addView(button2, layoutParams);
RelativeLayout.LayoutParams rlp = (RelativeLayout.LayoutParams) m_listView
.getLayoutParams();
rlp.addRule(RelativeLayout.RIGHT_OF, button2.getId());
The Button will be added as normal to the screen and it will appear starting from the top-left corner of the screen. Without the two lines from the code above the Button and ListView will overlap as this is the normal behavior of RelativeLayout for children without any rules on them. We then explicitly modify the position of the ListView to move it to the right(with the last two line from the code above).
If your variable names are indicative, it's because you are adding the widget to a LinearLayout, so tags for a RelativeLayout get ignored.
This line is the one I'm talking about:
m_linearLayout.addView(button2, layoutParams);
EDIT
You say alignParentRight works... the only difference there is that ot doesn't take an anchor parameter. Perhaps m_listView.getId() isn't returning the proper id. You could step through with the debugger and see if it's returning a proper value.
Maybe you could try calling the id specifically...
layoutParams.addRule(RelativeLayout.LEFT_OF, R.id.list);
To perform it, use predefined view ID or declare one. In values folder create ids.xml then add a Item like this:
<item name="imageViewID" type="id"/>
use this id in your code where you are creating new Instance of view like this:
RelativeLayout layout=new RelativeLayout(context);
ImageView imageView = new ImageView(context);
imageView.setId(R.id.imageViewID);
RelativeLayout.LayoutParams layoutParams = new LayoutParams(50, 50);
layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
layout.addView(imageView, layoutParams);
TextView textView = new TextView(context);
RelativeLayout.LayoutParams textViewParams= new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
textViewParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
textViewParams.addRule(RelativeLayout.BELOW, imageView.getId());
layout.addView(nameView, nameLayoutParams);
or we can directly use this function View.generateViewId() to perform the same. Like this:
imageView.setId(View.generateViewId());
I think you might have forgotten to add m_listView to the RelativeLayout or m_listView's visibility would be GONE.
Can you please check for that?
setId before align is called, especially for the new object view.
If you are using a custom id and not a regular generated Android id (eg. R.id.my_id), make sure that the id is not equal to 0 (or negative), otherwise the rule will be ignored.