There are some Textview,I need to add them in RelativeLayout dynamically. How can I add them without overlap with previous ? thx :D
public class CusRelativeLayout extends RelativeLayout {
private String TAG = "CusRelativeLayout";
...
public void add(String s){
final int childCount = getChildCount();
final TextView tv = new TextView(context);
tv.setText(s);
tv.setTextColor(getResources().getColor(R.color.black));
LayoutParams params = (LayoutParams) generateDefaultLayoutParams();
int[] xy = getRandomXY();
if(childCount == 0){
//no view
params.setMargins(xy[0],xy[1],0,0);
}else{
//what shoud I do???
}
addView(tv,params);
}
...
}
It looks like this:
result
when I add the first tag, I just need to get random X and Y,and new a textview.I use the method setMargins(x,y,0,0) to adding in the RelativeLayout.
But when I add the second tag , I can use getTop(),getLeft(),getHeight(),getiWidth() to getting the previous position and range.And use getRandomXY() to getting random X and Y for the second.But I can't get the range of the second to determine whether the second overlaps with the first
I tried to do this
final TextView tv = new TextView(context);
tv.setText(s);
tv.setTextColor(getResources().getColor(R.color.black));
tv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT));
tv.post(new Runnable() {
#Override
public void run() {
Log.e(TAG,"height:" + tv.getHeight());
Log.e(TAG,"width:" + tv.getWidth());
}
});
but the value is 0
If I just determine by X and Y , It will be this case.determine by X and Y
I just want to get the range of the new tag before adding to the RelativeLayout ,so I can determine whether it will overlap with the previous.
if I can determine all the tag position before add in Relativelayout?(I know the exact number of tag)
RelativeLayout is made to allow views to overlap each other. You could use a LinearLayout.
If you for some reason need to stick to RelativeLayout, then you have to place your new View with some layout parameters relative to previously added children
params.addRule(RelativeLayout.BELOW, R.id.below_id);
instead of R.id.below_id you could use your own ids that you have set on other children
Take a look here
Programatically add view one below other in relative layout
Related
Click here to see the image
In the profile page of my application, I want to have an interest section as shown in the image. The user has a list of interest under his profile. I want to show his/her interests inside a horizontal LinearLayout. I have created an array of TextViews and add them dynamically inside the parent LinearLayout, but I do not want to add the TextViews when there is no more space. Instead, I want to add a TextView showing the number of remaining interests.
As shown in the picture (use the image link), the user had 24 interests, 4 of them fit horizontally on the same line and last TextView(+20) shows the number of remaining interests on the same line.
String interestList[]={"Travel","Music","Photography","Sports","Dance","Animals","SciFi Movies"};
int interestWidth =0, parentWidth=interestLinearLayout.getWidth();
for(String interest: interestList) {
TextView textView = new TextView(MainActivity.this);
textView.setBackground(getResources().getDrawable(R.drawable.interests_bg));
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMargins(2,0,10,2);
textView.setLayoutParams(params);
textView.setPadding(2, 2, 2, 2);
textView.setText(" "+interest+" ");
textView.setTextColor(getResources().getColor(R.color.white));
textView.setIncludeFontPadding(true);
interestWidth += textView.getWidth();
if(interestWidth<parentWidth) //both are 0 on first iteration of loop???
interestLinearLayout.addView(textView);
else
break;
}
You can add views dynamically but first you need a reference to the parent view to which you want to add a view.
You can do this by just using findViewById. Assuming it's a linear layout,
LinearLayout parent = findViewById(R.id.parent);
// Then create a textview
TextView textView = new TextView(this);
// Add the view to the parent
parent.addView(textView);
And that's it! To change properties about the TextView, you can use TextView getters and setters. If you want to change the margin, padding or height of width of the TextView, use LayoutParams
// Remember that I'm using LinearLayout.LayoutParams because the parent of the ttextview is a LinearLayout
LinearLayout.LayourParams params = textView.getLayoutParams();
// Remember these values are in pixels
params.height = 100;
params.width = 200;
There are tons of problems using this method, such as setting height and width in pixels instead of dps. And writing a lot of code when you could have done it in xml. You can however make this much easier by creating an xml file in your res/layout and then inflating it and finally adding it to the parent.
You can do this by -
// First get the layout inflater
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
TextView textView = inflater.inflate(R.layout.myTextView, null);
linearLayout.addView(textView);
Finally addressing your problem about adding only enough views that the linearLayout doesn't go beyond the screen width.
The easiest solution is, to loop through the interest list and in every iteration of the loop, measure the combined width of the TextViews created and then checking whether it exceeds the width of the linearLayout.
It would look similar to this -
int combinedWidth = 0;
int linearLayoutWidth = linearLayout.getMeasuredWidth();
for(String interest : interests){
TextView view = inflater.inflate(R.layout.textview, null);
combinedWidth += textView.getMeasuredWidth();
view.setText(interest);
if(combinedWidth > linearLayoutWidth){
// No need to add more views
break;
}else{
linearLayout.addView(textView);
}
}
However, the above solution may or may not work depending on when it is executed. So post the activity code along with the xml file so that I can better answer your question.
The interestWidth and parentWidth are initially 0 because they have not been laid out when getWidth is called.
get width for dynamically created textViews
The above link helped me getting width of dynamically created textViews from interestList.
And by using ViewTreeObserver on interestLinearLayout I was able to get the width of LinearLayout after it was laid out.
Finally, the above code should be modified as below to add textViews from JAVA inside a LinearLayout.
final LinearLayout interestLinearLayout = findViewById(R.id.interests);
interestLinearLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
interestLinearLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
String interestList[]={"Travel","Music","Photography","Sports","Dance","Animals","SciFi Movies"};
int interestWidth =0;
int parentWidth = interestLinearLayout.getWidth(); // got width inside view tree observer for linearlayout
for(String interest: interestList) {
TextView textView = new TextView(MainActivity.this);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.setMargins(2,0,10,2);
textView.setLayoutParams(params);
textView.setPadding(2, 2, 2, 2);
textView.setText(interest);
textView.setIncludeFontPadding(true);
textView.measure(0,0); //using approach mentioned in link to get width of text views
interestWidth += textView.getMeasuredWidth();
if(interestWidth<parentWidth)
interestLinearLayout.addView(textView);
else
break;
}
}
});
To create a LinearLayout,
LinearLayout layout = new LinearLayout(MainActivity.this);
To set background color of a layout,
layout.setBackgroundColor(Color.parseColor("#135517"));
To set width and height of the layout,
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams
(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
params.setMargins(15, 5, 5, 5);
layout.setLayoutParams(params);
The orientation,
layout.setOrientation(LinearLayout.HORIZONTAL);
layout.setHorizontalGravity(Gravity.CENTER_HORIZONTAL);
layout.setPadding(10, 10, 5, 5);
Then create a textview,
TextView textView = new TextView(this);
textView.setLayoutParams(params);
textView.setPadding(2, 2, 2, 2);
textView.setText(" "your" ");
textView.setTextColor(getResources().getColor(R.color.white));
textView.setIncludeFontPadding(true);
Add the view to the parent,
layout.addView(textView);
I'm adding multiple Views by code into Layout. I need each new View to be above previous one(top of the parent layout).
EDIT: To be more accurate I'll describe what the app module should does. User start with clean screen and one button at the bottom of the screen. The button adds a View at the top of the screen. Next clicks should add next views above previous ones to make the newest View be on the top of a container. The app saves state and on restart user see views in the same order.
Call the following method from Button's onClick Event.
private final int LAYOUT_TOP_INDEX = 0;
private void addViewOnTop(View view){
if(layout != null && view !=null)
layout.addView(view, LAYOUT_TOP_INDEX);
}
where 'layout' is your Layout (e.g., LinearLayout) to which the View is to be added.
Would really need more information from you to give a more accurate answer, but if you're saying what i think you are then you can just add these views to a LinearLayout with orientation set to vertical.
And assuming you're iterating through a list to dynamically add views, instead of incrementing from 0, increment down from the size of the list.
for(int i = size; i >= 0; i--){
linearLayout.add(new TextView(Context));
}
View positions inside ViewGroups are defined by the LayoutParams
How does this happen? Views pass their LayoutParams to their parent ViewGroups
//100% programatic approach with simple LayoutParams
LinearLayout myLinearLayout = new LinearLayout(this);
//if the **parent** of the new linear layout is a FrameLayout
FrameLayout.LayoutParams layoutParams =
new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
//or if you have the XML file you don't have to worry about this
//myLinearLayout = (LinearLayout)findViewById(R.id.my_simple_linear_layout);
//you could have a LinkedList<TextView>
LinkedList<TextView> textViewList = new LinkedList<>();
//assuming the order is the correct order to be displayed
Iterator<TextView> descendingIterator = textViewList.descendingIterator();
while(descendingIterator.hasNext())
{
//just add each TextView programatically to the ViewGroup
TextView tView = descendingIterator.next();
myLinearLayout.addView(tView);
}
Just like we defined LayoutParams for the LinearLayout we could also define LayoutParams for the TextView
IMPORTANT: when setting LayoutParams you need to be sure they fit the VIEWGROUP, that is the parent of the View being added
private TextView textViewFactory(String myText) {
TextView tView = new TextView(getBaseContext());
//controling the position relatively to the PARENT
//because you are adding the textview to a LINEAR LAYOUT
LinearLayout.LayoutParams paramsExample =
new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT, 1.0f);
tView.setLayoutParams(paramsExample);
//configuring the insides of the textview
//you can also do all kinds of stuff programatically
tView.setGravity(Gravity.CENTER_HORIZONTAL);
tView.setPadding(10, 10, 10, 10);
tView.setTypeface(Typeface.DEFAULT_BOLD);// (null, Typeface.BOLD_ITALIC);
tView.setTypeface(Typeface.SANS_SERIF);
tView.setTypeface(null, Typeface.ITALIC);
tView.setTypeface(Typeface.defaultFromStyle(R.style.AppTheme));
tView.setId(R.id.aux_info);
tView.setText(myText);
//.........all kinds of stuff really
return tView;
}
If you mean adding a view programmatically so that the new one is added above the previous one, instead of below it, then I suggest this:
Maintain an ArrayList with the items you want to turn into views
Put them into a ListView
When you want to add a new view that must appear at the top of the list, insert it as the first element of your ArrayList and recreate the ListView from it.
I am adding views dynamically to a relative layout (let's say container) in a for loop. There is some thing strange I am noticing. When adding rows one below the other in a relative layout in a for loop, I see that the first time a few of the views are overlapping. But when I lock and unlock the screen, I can see that the views are placed correctly.
Should I be aware of something when adding views dynamically to a relative layout?
Edit
I have found a solution as to how to get rid of this (please check my answer). But I would be more than glad to accept an answer that analyses this problem and tells me why this happens.
I have simplified to code and the comments should give a good idea as to what I am doing.
int prev_id=ID_OF_THE_ELEMENT_ABOVE;
/*Empty RelativeView with width and height as MATCH_PARENT and WRAP_CONTENT respectively*/
RelativeLayout container=(RelativeLayout) findViewById(R.id.container);
while(ThereIsData){
/*GET THE DATA HERE THAT HAS TO BE ASSIGNED TO EACH TEXTVIEW*/
...
/* ADD TEXTVIEW #1 below prev_id/
...
...
/*ADD TEXTVIEW #2 (WITH BASELINE OF TEXTVIEW#
...
...
/*TEXTVIEW #3 (BELOW TEXTVIEW#1)*/
...
...
/*TEXTVIEW #4 (BELOW TEXTVIEW#2)*/
...
...
/*ASSIGN THE ID OF TEXTVIEW#3 TO prev_id SO THAT
IN THE NEXT ITERATION TEXTVIEW#1 CAN USE prev_id
*/
prev_id=ID(TEXTVIEW#2);
/*ADD TEXTVIEWS CREATED IN THIS ITERATION*/
container.addView(TEXTVIEW#1);
container.addView(TEXTVIEW#2);
container.addView(TEXTVIEW#3);
container.addView(TEXTVIEW#4);
}
It is due to the fact that you are having a RelativeLayout with height as WRAP_CONTENT, and adding a view doesn't refresh the whole container at that time.. so as you answered you can add a line to measure the dimensions explicitly or invalidate the view to recreate it completely.
In any case LinearLayout would be better to opt-for as it will automatically arrange the children in horizontal or vertical manner and you can even add the new view in any place other than last position and it will automatically be updated..
I used to struggle against common issues a year ago, when I was working on a library for dynamically creating layouts from XML files (as Android does not support this). So when you dynamically add views to a RelativeLayout you have to take in mind a few things:
Create the container View (in this case the RelativeLayout)
Create all views without assigning any layout parameters.
Add all child views to the container.
Iterate over the container's children and populate each child's layout parameters. This is needed because when the relational constraints are applied an Excpetion is thrown if the relative View is missing (was not previously added to the container).
This is an example code taken from the project I used to work on. Take in mind that it is just a single part so it contains references to classes that are not defined in the Android API. I am sure it will give you the basic idea of dynamically creating RelativeLayot:
private void setChildren(RelativeLayout layout, T widget,
InflaterContext inflaterContext, Context context,
Factory<Widget, View> factory) {
List<Widget> children = widget.getChildren();
if (Utils.isEmpty(children))) {
return;
}
// 1. create all children
for (Widget child : children) {
View view = factory.create(inflaterContext, context, child);
layout.addView(view);
}
// 2. Set layout parameters. This is done all children are created
// because there are relations between children.
for (Widget child : children) {
try {
View view = ViewIdManager.getInstance().findViewByName(layout, child.getId());
if (view != null) {
populateLayoutParmas(child, view);
}
} catch (IndexNotFoundException e) {
Log.e(LOG_TAG, "Cannot find a related view for " + child.getId(), e);
}
}
}
I have not yet found the answer to why this is happening. But I have found a solution. After adding each row in the loop, call container.measure(RelativeLayout.LayoutParams.WRAP_CONTENT,RelativeLayout.LayoutParams.WRAP_CONTENT);
This seems to solve the problem. But I really think that container.addView() should also be calling measure().
/*ADD TEXTVIEWS CREATED IN THIS ITERATION*/
container.addView(TEXTVIEW#1);
container.addView(TEXTVIEW#2);
container.addView(TEXTVIEW#3);
container.addView(TEXTVIEW#4);
//---------------------------------------------------------------------
container.measure(RelativeLayout.LayoutParams.WRAP_CONTENT,RelativeLayout.LayoutParams.WRAP_CONTENT);
//Declare globally
LinearLayout[] layout;
ImageView[] imageView1;
ImageView[] imageView2;
ImageView[] imageView3;
// Initialize your layout. It would be RelativeLayout too. Just reference to it.
LinearLayout ll = (LinearLayout) findViewById(R.id.mylinear);
// set listview row size as your demand
layout = new LinearLayout[200];
imageView1 = new ImageView[200];
imageView2 = new ImageView[200];
imageView3 = new ImageView[200];
for (int i = 0; i < 200; i++) {
layout[i] = new LinearLayout(this);
layout[i].setBackgroundResource(R.drawable.book_shelf);
// layout[i].setLayoutParams(new
// LinearLayout.LayoutParams(android.widget.LinearLayout.LayoutParams.FILL_PARENT,
// 120));
layout[i].setLayoutParams(new LinearLayout.LayoutParams(
android.widget.LinearLayout.LayoutParams.FILL_PARENT, 220));
imageView1[i] = new ImageView(this);
imageView2[i] = new ImageView(this);
imageView3[i] = new ImageView(this);
imageView1[i].setLayoutParams(new LinearLayout.LayoutParams(0, 200,
0.33f));
imageView1[i].setPadding(0, 20, 0, 0);
imageView1[i].setImageResource(R.drawable.bibid_one_bankim);
imageView2[i].setLayoutParams(new LinearLayout.LayoutParams(0, 200,
0.33f));
imageView2[i].setPadding(0, 20, 0, 0);
imageView2[i].setImageResource(R.drawable.bibid_two_bankim);
imageView3[i].setLayoutParams(new LinearLayout.LayoutParams(0, 200,
0.33f));
imageView3[i].setImageResource(R.drawable.dena_pawna);
imageView3[i].setPadding(0, 20, 0, 0);
layout[i].setId(i);
layout[i].setClickable(true);
final int j = i;
layout[i].addView(imageView1[i]);
layout[i].addView(imageView2[i]);
layout[i].addView(imageView3[i]);
ll.addView(layout[i]);
}
}
Try adding your views in vertical Linear Layout.
Following link might help you
http://www.myandroidsolutions.com/2012/06/19/android-layoutinflater-turorial/
Inflate your layout in for loop.
I'm developing for Android API v11. There is a large RelativeLayout area (like a canvas) that should be filled with a number of buttons, programmatically. Each button represents a video, and I've extended the android.widget.Button class with my VideoViewButton.
Here's what I'm doing now in the main activity:
private void addButtonForVideo(int videoId) {
Log.d(TAG, "Adding button for video " + videoId);
VideoButtonView button = new VideoButtonView(this, videoId);
RelativeLayout layout = (RelativeLayout) findViewById(R.id.layout_napping);
layout.addView(button, params);
mVideoButtons.add(button);
}
Here, mVideoButtons just contains all buttons so I can reference them later.
The buttons themselves are however placed at the top left of the RelativeLayout, one over the other. What I need to do is place each button to the right of the previous one, so they fill up the screen.
I've tried this, where I check if the video ID is not 0 (meaning, a button already exists). I then get the ID of the previously placed button and say that I want the next button to be placed right of that previous one:
private void addButtonForVideo(int videoId) {
Log.d(TAG, "Adding button for video " + videoId);
VideoButtonView button = new VideoButtonView(this, videoId);
RelativeLayout layout = (RelativeLayout) findViewById(R.id.layout_napping);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT
);
// align to right of previous button
if (videoId != 0) {
VideoButtonView previousButton = mVideoButtons.get(videoId - 1);
params.addRule(RelativeLayout.RIGHT_OF, previousButton.getId());
}
layout.addView(button, params);
mVideoButtons.add(button);
}
However, it's not working — the buttons are still placed on top of each other. How can I get them to show next to the previous one instead?
You need to call setId with the videoId in the constructor of the VideoButtonView for this to work.
Make sure that setId contains a positive number, so for example if videoIds start with 0, use:
public VideoButtonView(Context context, int videoId) {
super(context);
this.setId(videoId + 1);
// other code to set layout
}
I suggest not using the Relative Layout for placing object programmatically. Use Linear Layouts instead and organize the content using LinearLayout.LayoutParams
To do this with LinearLayout, just make sure you set the orientation to HORIZONTAL
private void addButtonForVideo(int videoId) {
Log.d(TAG, "Adding button for video " + videoId);
VideoButtonView button = new VideoButtonView(this, videoId);
LinearLayout layout = (LinearLayout) findViewById(R.id.layout_napping);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCHPARENT,
LinearLayout.LayoutParams.WRAP_CONTENT,
);
LinearLayout buttonWrapper = new LinearLayout();
buttonWrapper.setLayoutParams(params);
buttonWrapper.setOrientation(LinearLayout.HORIZONTAL);
// align to right of previous button
if (videoId != 0) {
VideoButtonView previousButton = mVideoButtons.get(videoId - 1);
}
buttonWrapper.addView(button);
layout.addView(buttonWrapper);
mVideoButtons.add(button);
}
Just remember to place the first button in the ButtonWrapper before placing the second in there.
With a linear layout, the next child will either appear below, or next to the previous child depending on the direction and the orientation given for the layout. here, each button will sit next to each other and the wrapper will extend the full length of the layout it sits in.
Best of luck!
I am getting list of phone companies from web service and i have to set it to textview but the problem is i am not getting alignment as above image.How to achieve it.
From what I understand, you want to add text views one beside the other, but when they overflow (go out of the screen) the next text view should be placed in the next line.
Doing this is not trivial. Implementing something like this (optimally and correctly) requires understanding of how android draws views (onMeasure and onLayout). However if you do not care about efficiency that much (mainly because you are going to do it only for a small portion of the view) then here is my quick hack:
mContainer = (RelativeLayout) findViewById(R.id.container);
// first layout all the text views in a relative layout without any params set.
// this will let the system draw them independent of one another and calculate the
// width of each text view for us.
for (int i = 0; i < 10; i++) {
TextView tv = new TextView(getApplicationContext());
tv.setText("Text View " + i);
tv.setId(i+1);
tv.setPadding(10, 10, 20, 10);
mContainer.addView(tv);
}
// post a runnable on the layout which will do the layout again, but this time
// using the width of the individual text views, it will place them in correct position.
mContainer.post(new Runnable() {
#Override
public void run() {
int totalWidth = mContainer.getWidth();
// loop through each text view, and set its layout params
for (int i = 0; i < 10; i++) {
View child = mContainer.getChildAt(i);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
// this text view can fit in the same row so lets place it relative to the previous one.
if(child.getWidth() < totalWidth) {
if(i > 0) { // i == 0 is in correct position
params.addRule(RelativeLayout.RIGHT_OF, mContainer.getChildAt(i-1).getId());
params.addRule(RelativeLayout.ALIGN_BOTTOM, mContainer.getChildAt(i-1).getId());
}
}
else {
// place it in the next row.
totalWidth = mContainer.getWidth();
params.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
params.addRule(RelativeLayout.BELOW, mContainer.getChildAt(i-1).getId());
}
child.setLayoutParams(params);
totalWidth = totalWidth - child.getWidth();
}
mContainer.requestLayout();
}
});
Basically, I let the system do the layout and measurement for me in the first round(s) of drawing. Then using the widths of each text view now available, I reset the layout params based on the wrapping logic and do the layout again.
Try it with text of different size, it will auto adjust. I would say this solution is pretty hacky but it works. If you are not satisfied with it take a look at this.
use
android:textAlignment="textStart"