Setting margins programmatically wont let me reassign the value each time - android

I'm trying to set up a layout at runtime that alternates from having a button on the left side of the screen on then the right side. I have an arrayList of button strings and it iterates through, creating a button for each and then applying some styling. Almost all the styling works, except that the margins I'm using to push them to the side of the screen aren't alternating correctly. I'm trying to make the margin push either from the left or the right, but it seems like the buttons are staying within only one column.
Here's the code first of all:
LayoutParams noteStyle = new LayoutParams((int) getResources().getDimension(R.dimen.sticky_note_height),
(int) getResources().getDimension(R.dimen.sticky_note_width));
int margin = (int) getResources().getDimension(R.dimen.margin_huge);
layout = (LinearLayout) findViewById(R.id.note_layout);
int i = 0;
for (String note : notes){
Button btnTag;
if (i % 2 == 0){
btnTag = (Button) getLayoutInflater().inflate(R.layout.sticky_note_right, null);
noteStyle.setMargins(margin,0,0,0);
} else {
btnTag = (Button) getLayoutInflater().inflate(R.layout.sticky_note_left, null);
noteStyle.setMargins(0,0,margin,0);
}
btnTag.setLayoutParams(noteStyle);
btnTag.setText(note);
btnTag.setId(i);
layout.addView(btnTag);
((Button) findViewById(i)).setOnClickListener(this);
i++;
}
And here's a screenshot of how it comes out:

For some reason unknown to me, reusing the LayoutParams can cause goofy results. Instantiating them each time they are needed can help resolve this.
This means putting them inside the for loop, in this situation
for (String note : notes) {
LayoutParams noteStyle = new LayoutParams((int) getResources().getDimension(R.dimen.sticky_note_height),
(int) getResources().getDimension(R.dimen.sticky_note_width));

Related

How to check programatically if all the Views fit inside the screen

I have a layout which is something like this:
LinearLayout (linearLayout)
'--TextView (textView1)
'--ImageView (imageView)
'--TextView (textView2)
textView1 changes its text sometimes and it can be long, so it leaves part of textView2 out of the screen. I want to prevent that, so I want to remove imageView from the layout whenever this happens. imageView may or may not be visible at the time when this is computed (maybe it was removed before when textView1 was edited previously).
This is what I have coded:
void changeText(String veryLongString){
textView1.setText(veryLongString);
int [] loc = new int [2];
textView2.getLocationOnScreen(loc);
int bottom = textView2.getMeasuredHeight() + loc[1];
if (imageView.getVisibility() == View.GONE)
bottom += imageView.getHeight();
if (bottom > linearLayout.getMeasuredHeight()){
imageView.setVisibility(View.GONE);
} else {
imageView.setVisibility(View.VISIBLE);
}
}
But for some reason this doesn't work as expected, because it seems as if changes in the position and height of the Views don't take place immediately. When I call getMeasuredHeight() and getLocationOnScreen() I get the values BEFORE the changes I have just made. The result that I get is that if I set a very large text imageView is not removed, but if I then set a short text, it is removed.
If there any other way to face this problem?
Even though I think that this is not the right approach (you can do all kinds of stuff in your XML so you don't have to meddle with Java code), here is a quick example of what you can do from Java (for example, in your onStart() method)
ViewGroup group = (ViewGroup) findViewById(R.id.myLayout);
int groupHeight = group.getHeight();
for (int i = 0; i < group.getChildCount(); i++) {
groupHeight -= group.getChildAt(i).getHeight();
if (groupHeight < 0) {
// they don't fit in the layout
myImageView.setVisibility(View.GONE);
}
}

Dynamically add view in vertical and horizontal way

I'm trying to create Buttons in LinearLayout dynamically, and I want to add those in vertical and horizontal way.
At first, add a button A in the layout, and if there's enough space between button A and screen edge, add button B to the right of button A (horizontally). Otherwise, add button B below button A (vertically).
My current layout :
<LinearLayout
android:id="#+id/btn_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
</LinearLayout>
in class :
LinearLayout btnLayout = (LinearLayout) findViewById(R.id.btn_layout);
btnLayout.removeAllViewsInLayout();
for(Tag tag : tagList.getChildTags()) {
Button button = new Button(this);
button.setId(tag.getId());
button.setText(tag.getName());
btnLayout.addView(button);
}
In this case, if I set orientation as horizontal, then some of buttons are not showing (cut-off by screen), and if I set as vertical, it looks pretty bad.
Is there any way to solve this problem? Thanks everyone in advance!
You can achieve this but not in a trivial way. I'll explain how I do something similar (in my case, I add TextViews) to TableRows, if they fit.
With this approach you'll have to use a TableLayout and add TableRows to it with your Buttons. So you might replace your "#+id/btn_layout" LinearLayout to be a TableLayout instead.
Firstly, to get the screen's width, use something like this:
final Display display = getWindowManager().getDefaultDisplay();
final Point size = new Point();
display.getSize(size);
final WindowManager.LayoutParams params = getWindow().getAttributes();
// Your screen's width will be stored within your params.width value
You'll use this to know if the current Button still fits the screen's width within the current TableRow or it has to be added to a new one. So now, use something like this to create your buttons:
int currentRowsWidth = 0;
TableLayout tl = (TableLayout) findViewById(R.id.my_table_layout);
TableRow currentRow = new TableRow();
for (Tag tag : tagList.getChildTags()) {
Button button = new Button(this);
button.setId(tag.getId());
button.setText(tag.getName());
// There's where you check whether it still fits the current `TableRow` or not
if (currentRowsWidth + button.getWidth() < params.width) {
currentRowsWidth += button.getWidth();
currentRow.addView(button);
}
else {
// It doesn't fit, add the currentRow to the table and start a new one
tl.add(currentRow);
currentRow = new TableRow();
currentRow.addView(button);
currentRowsWidth = button.getWidth();
}
}
It might happen that once you get out of the loop there are still Buttons to add in the currentView, simply test it:
if (currentRow.getChildCound() > 0)
tl.add(currentRow);
I'm writing this from head, so some things might not compile at first time, but I hope you get the idea.

Add a button next to another one, programmatically

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!

how to set textview dynamically according to numbers of strings

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"

When is the width of an Android View set?

I have a little issue on what sequence things are being called when adding stuff to a RelativeLayout. I have a class extending Activity (name it RelActivity) where I want to create a RelativeLayout and put several custom Views (name it cusView) into that RelativeLayout. The topMargin and leftMargin of a custom View is calculated by using the position of another custom View (i.e. the first custom View has to be positioned directly by setting a number to topMargin and leftMargin). Please note that the Rules of RelativeLayout is not sufficient in this case.
So, over to the problem. In my RelActivity I do this:
Create a RelativeLayout (name it relLayout)
Iterate a cursor with cusViews recieved from a database
For the first cusView -> Set position by topMargin and leftMargin using a LayoutParameter
For the other cusViews -> calculate their topMargin and leftMargin by using one of the other cusViews and a LayoutParameter
Set RelActivity's contentView to relLayout
What happens is that all cusViews but the first one are squeezed in the top left corner because both leftMargin and topMargin are always calculated to be zero. This happens because I use the width of the cusViews to calculate the topMargin and leftMargin, and the width of the cusView has not given a value yet.
Is the width first calculated in the cusView's overrided method onSizeChanged()? Is the onSizeChanged() method get called first when the layout is presented on the screen? If so, how do I work around this issue? Do I have to calculate the positionings after onSizeChanged() is done?
Edit: Here is a minimum working example:
Here is my onCreate in RelActivity:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
relLayout = new RelativeLayout(this);
cusViews = new ArrayList<CusView>();
listParams = new ArrayList<RelativeLayout.LayoutParams>();
readDBandSetLayout();
setContentView(relLayout);
}
There is too much information in the readDBandSetLayout() method to present it all here. below are the most important details. If I create the LayoutParams in the following way it works fine, the cusViews are listed downwards and rightwards of eachother:
queryCursor = customApplication.customData.query( number); //Fetches cursor
for ( int i = 0; i < numberOfRows; i++ ){
if ( i == 0 ){
LayoutParams p = new LayoutParams(this.getResources().getDimensionPixelSize(R.dimen.small), this.getResources().getDimensionPixelSize(R.dimen.small));
p.topMargin = 50;
p.leftMargin = 50;
listParams.add(p);
}
else{
LayoutParams p = new LayoutParams(this.getResources().getDimensionPixelSize(R.dimen.large),this.getResources().getDimensionPixelSize(R.dimen.large));
p.addRule(RelativeLayout.BELOW, cusViews.get(i-1).getId());
p.addRule(RelativeLayout.RIGHT_OF, cusViews.get(i-1).getId());
listParams.add(p);
}
relLayout.addView(cusViews.get(i), listParams.get(i));
}
However, what I want to do in the else statement is something like:
else{
LayoutParams p = new LayoutParams(this.getResources().getDimensionPixelSize(R.dimen.large),this.getResources().getDimensionPixelSize(R.dimen.large));
//Here I want to calculate cusView2Topmargin and cusView2Leftmargin based on the widths of the first or previosly positioned cusViews. But here the widths are 0 since they haven't been calculated yet.
p.topMargin = cusView2Topmargin; //Always zero
p.leftMargin = cusView2Leftmargin; //Always zero
listParams.add(p);
}
So the problem lies in that the widths of the cusViews are zero at the point I need them to calculate the layout parameters topMargin and leftMargin.
Unfortunately I cannot use the RelativeLayout's Rules for what I want to achieve. If there were some way to create rules like RelativeLayout.RIGHT_OF and RelativeLayout.BELOW I could do it like that. Is this possible?
Its not very clear what your goal is for this layout. It might well be possible to use a simple LinearLayout to get what you want.
If you want to size these from a database lookup then try simply adding each of the views, using addView() first, storing a reference to each, then go back and sett the margins to place them in the proper positions.

Categories

Resources