May I reuse LayoutPrams with ViewGroup.addView? - android

Does ViewGroup.addView clones LayoutParams data into inside or links to it? May I reuse the same instance of LayoutParams with multiple calls to addView() with different views?
There is nothing about it in apidoc.
WOW
The answer is NO (checked experimentally):
public class SymbolPadActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RelativeLayout.LayoutParams labelParams;
/*
* This block to reuse is not working
labelParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
labelParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE);
labelParams.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE);
*/
RelativeLayout mover = new RelativeLayout(this);
TextView textView;
for(int leftMargin = 0; leftMargin<3000; leftMargin += 100) {
for(int topMargin=0; topMargin<800; topMargin += 40) {
// I can't omit these 3 lines
labelParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
labelParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE);
labelParams.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE);
labelParams.leftMargin = leftMargin;
labelParams.topMargin = topMargin;
textView = new TextView(this);
textView.setText("(" + leftMargin + "," + topMargin + ")");
mover.addView(textView, labelParams);
}
}
RelativeLayout.LayoutParams moverParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
moverParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE);
moverParams.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE);
moverParams.leftMargin = 0;
moverParams.topMargin = 0;
RelativeLayout stator = new RelativeLayout(this);
stator.addView(mover, 0, moverParams);
setContentView(stator);
}
}

There is nothing about it in apidoc.
This means you need to make the more conservative choice, no matter what the current implementation is, as the implementation could change.
Hence, you need to assume that it is not safe to reuse an instance of LayoutParams with different Views.
For what it's worth, as far as I can tell, that is true anyway - ViewGroup doesn't make a copy.

This is an old question, but there seems to be an updated answer:
The LayoutParams has a constructor for copying another
Source: http://developer.android.com/reference/android/view/ViewGroup.LayoutParams.html
ViewGroup.LayoutParams(ViewGroup.LayoutParams source)
This would suggest not to re-use it, but perhaps to create 1 layout params object with everything you need, then just call
new LayoutParams(someLayoutParamsToReUse)
In my case, I wanted to set the layout params of a button to be the same as another button. I initially tried:
button.setLayoutParams(button2.getLayoutParams());
This will not work, however this should:
button.setLayoutParams(new LayoutParms(button2.getLayoutParams))'

Related

Android margins not working when the view was added dynamically

I am adding some View to the LinearLayout dynamically:
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
params.setMargins(100, 100, 100, 100);
view.setLayoutParams(params);
parent.addView(view);
However, the margins are not getting applied. The following:
view.invalidate();
view.requestLayout();
parent.invalidate();
parent.requestLayout();
didn't work. However, if I force the activity to recreate (e.g. turn off and on my mobile phone), the margins get applied. Calling activity.recreate() also works, but it's too slow.
How to force layout to recalculate margins? Probably, there's something wrong in the flow? I tried to add my views to the root after creation, add children before and after applying properties, but this didn't work for me.
UPD:
I tried to repeat the bug programmatically and got it with this code:
LinearLayout base = new LinearLayout(context);
LayoutParams params1 = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
base.setLayoutParams(params1);
base.setBackgroundColor(Color.CYAN);
root.addView(base);
LinearLayout another1 = new LinearLayout(context);
LayoutParams params2 = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
another1.setLayoutParams(params2);
another1.setOrientation(LinearLayout.VERTICAL);
another1.setBackgroundColor(Color.BLUE);
base.addView(another1);
TextView tv1 = new TextView(context);
tv1.setText("SOME TEST TEXT 1");
tv1.setTextColor(Color.BLACK);
LayoutParams params4 = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
params4.setMargins(100, 100, 100, 100);
tv1.setLayoutParams(params4);
another1.addView(tv1);
TextView tv2 = new TextView(context);
tv2.setText("SOME TEST TEXT 2");
tv2.setTextColor(Color.BLACK);
LayoutParams params5 = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
params4.setMargins(100, 100, 100, 100);
tv2.setLayoutParams(params5);
another1.addView(tv2);
I expect margins to be applied, but they are not. What is the correct order of initializing such a view?
So, my trouble was in DP to PX converter that just wasn't initialized at that moment. However, to clarify everything, my example above is just an error (I didn't change params4 to params5 when adding the second TextView.
Also while testing everything, I found out that the order of adding layouts and their parameters doesn't matter at all.
You probably added child views to the parent view in onCreate().
In onCreate(), parent view does not have actual size.
If you have to do it in onCreate(), try following code.
final ViewTreeObserver observer = parent.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
LinearLayout.LayoutParams params = new
LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
params.setMargins(100, 100, 100, 100);
TextView view = new TextView(context);
view.setLayoutParams(params);
parent.addView(view);
observer.removeOnGlobalLayoutListener(this);
}
};
Hope this will help!
Try this:
LinearLayout ll = findViewById(R.id.linearLayout);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
layoutParams.setMargins(30,20,30,0);
Hope this will help!

What is the proper way of adding multiple views at runtime without putting too much load on the main thread

I have about 100 views (Buttons,Textviews and ImageButton) in my activity that i create in a for loop. My app runs slow on older versions of android ( 5 and below) and sometimes it does not even load the views.
My question is, what is the proper way of handling such views and loading them without putting too much load on the main thread?
I couldn't use a separate thread since i cant reference UI elements from any other thread than the main one.
Thanks.
for (int i = 0; i < arr.size(); i++) {
String name = arr.get(i).getName();
final int songPath = arr.get(i).getSongPath();
int iconPath = arr.get(i).getIconPath();
RelativeLayout relativeLayout = new RelativeLayout(this);
RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
relativeLayout.setLayoutParams(rlp);
buttons[i] = new ImageButton(this);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 350);
params.setMargins(0, 0, 0, 8);
buttons[i].setLayoutParams(params);
buttons[i].setBackgroundResource(iconPath);
buttons[i].setTag(name + "button");
buttons[i].setId(id++);
TextView emoteName = new TextView(this);
RelativeLayout.LayoutParams textParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
textParams.addRule(RelativeLayout.BELOW, buttons[i].getId());
textParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
textParams.setMargins(20,10,0,0);
emoteName.setText(name.toUpperCase());
emoteName.setId(id++);
emoteName.setTextSize(20f);
emoteName.setLayoutParams(textParams);
Button threedots = new Button(this);
RelativeLayout.LayoutParams threedotsParams = new RelativeLayout.LayoutParams(50, RelativeLayout.LayoutParams.WRAP_CONTENT);
threedotsParams.addRule(RelativeLayout.BELOW, buttons[i].getId());
threedotsParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
threedotsParams.addRule(RelativeLayout.ALIGN_RIGHT,buttons[i].getId());
threedotsParams.setMargins(0,0,0,0);
threedots.setText(getString(R.string.vertical_ellipsis));
threedots.setTextSize(25f);
threedots.setBackgroundColor(Color.TRANSPARENT);
threedots.setLayoutParams(threedotsParams);
final int counterForOnClick = i;
threedots.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ShowDialog(arr.get(counterForOnClick));
}
});
relativeLayout.addView(buttons[i]);
relativeLayout.addView(emoteName);
relativeLayout.addView(threedots);
if (i % 2 == 0) {
leftLayout.addView(relativeLayout);
} else {
rightLayout.addView(relativeLayout);
}
final int index = i;
buttons[i].setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
playEmoteSound(songPath);
}
});
}
As you said - adding all those views is not the best idea.
From your code, I can see that we are talking about the same views with different data.
You will want to use RecyclerView in order to create a list of the same view.
From the documentation:
If your app needs to display a scrolling list of elements based on large data sets (or data that frequently changes), you should use RecyclerView as described on this page.

move button to the next line when there is no

I need to implement menu that can't be GridView or ListView. Items are next to each other and if there is no place, items should be display in next line as is visible on picture
I started implementation that is based on SpannableString and ReplacementSpan, but it supports only basic styling and I have problem with selected state.
What's the best way to implement such menu?
Maybe this will help you
/**
* Copyright 2011 Sherif
* Updated by Karim Varela to handle LinearLayouts with other views on either side.
* #param linearLayout
* #param views : The views to wrap within LinearLayout
* #param context
* #param extraView : An extra view that may be to the right or left of your LinearLayout.
* #author Karim Varela
**/
private void populateViews(LinearLayout linearLayout, View[] views, Context context, View extraView)
{
extraView.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
// kv : May need to replace 'getSherlockActivity()' with 'this' or 'getActivity()'
Display display = getActivity().getWindowManager().getDefaultDisplay();
linearLayout.removeAllViews();
int maxWidth = display.getWidth() - extraView.getMeasuredWidth() - 20;
linearLayout.setOrientation(LinearLayout.VERTICAL);
LinearLayout.LayoutParams params;
LinearLayout newLL = new LinearLayout(context);
newLL.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
newLL.setGravity(Gravity.LEFT);
newLL.setOrientation(LinearLayout.HORIZONTAL);
int widthSoFar = 0;
for (int i = 0; i < views.length; i++)
{
LinearLayout LL = new LinearLayout(context);
LL.setOrientation(LinearLayout.HORIZONTAL);
LL.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM);
LL.setLayoutParams(new ListView.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
views[i].measure(0, 0);
params = new LinearLayout.LayoutParams(views[i].getMeasuredWidth(), LayoutParams.WRAP_CONTENT);
params.setMargins(5, 0, 5, 0);
LL.addView(views[i], params);
LL.measure(0, 0);
widthSoFar += views[i].getMeasuredWidth();
if (widthSoFar >= maxWidth)
{
linearLayout.addView(newLL);
newLL = new LinearLayout(context);
newLL.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
newLL.setOrientation(LinearLayout.HORIZONTAL);
newLL.setGravity(Gravity.LEFT);
params = new LinearLayout.LayoutParams(LL.getMeasuredWidth(), LL.getMeasuredHeight());
newLL.addView(LL, params);
widthSoFar = LL.getMeasuredWidth();
}
else
{
newLL.addView(LL);
}
}
linearLayout.addView(newLL);
}
I finally discovered library called Android flow layout that works well https://github.com/ApmeM/android-flowlayout

Arrange RelativeLayout below another dynamically

I'm trying to dynamically build relative layouts consisting of an image and a textview at the moment. I have tried building a loop to place the next relative layout below the former one, but I can't really make it work. The end result should be something like this, but I guess that if i figure out how to align below the former one, I can also figure out how to place it right_of the former relative layout.
Any suggestions? This is my code so far:
RelativeLayout container1 = (RelativeLayout) rootView
.findViewById(R.id.main_layout);
for (int i = 0; i < pictures.size(); i++) {
RelativeLayout tile = new RelativeLayout(getActivity());
tile.setId(i);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
params.height = height / 3;
params.width = width / 2;
tile.setLayoutParams(params);
ImageButton ibGood = new ImageButton(getActivity());
ibGood.setId(1);
ibGood.setImageResource(pictures.get(0));
ibGood.setAdjustViewBounds(true);
ibGood.setMaxHeight(height / 3 / 5 * 4);
ibGood.setMaxWidth(width);
ibGood.setScaleType(ScaleType.CENTER_CROP);
ibGood.setPadding(5, 5, 5, 5);
tile.addView(ibGood);
RelativeLayout.LayoutParams tvPriceParam = new RelativeLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
tvPriceParam.addRule(RelativeLayout.BELOW, ibGood.getId());
tvPriceParam.addRule(RelativeLayout.ALIGN_LEFT, ibGood.getId());
TextView tvPrice = new TextView(getActivity());
tvPrice.setId(2);
tvPrice.setHeight(tile.getHeight() / 5);
tvPrice.setWidth(tile.getWidth() / 5 * 2);
tvPrice.setPadding(5, 0, 0, 5);
tvPrice.setText(Integer.toString(price.get(0)));
tile.addView(tvPrice, tvPriceParam);
if (counter == 0) {
container1.addView(tile);
} else if (counter % 2 == 0) {
RelativeLayout.LayoutParams lay = new RelativeLayout.LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
lay.addRule(RelativeLayout.BELOW, -1);
tile.setLayoutParams(lay);
container1.addView(tile);
}
counter += 1;
}
you can make it with the custom gridview !! why you are doing this with the relative layout ?
here is the link which you can use to achieve what do you want..
http://www.learn-android-easily.com/2013/09/android-custom-gridview-example.html
http://www.caveofprogramming.com/uncategorized/custom-gridview-with-imageview-and-textview-in-android/
please visit 3 links are there you will get that you want .. with relative layout ..
Thanks,
Madhav

How to make activity with n buttons same size using RelativeLayout?

I need do design programatically one activity with 6 buttons (same size h and w), and all buttons show a fullsize of activity.
I tried do this: RelativeLayout with buttons and modify for tests.
Show one button!!!
`
setContentView(R.layout.main);
ArrayList<Button> buttons = new ArrayList<Button>();
//RelativeLayout bg = (RelativeLayout) findViewById(R.layout.main);
for (int i = 0; i < 10; i++) {
Button newButton = new Button(this);
newButton.setId(100 + i + 1); // ID of zero will not work
newButton.setText("XXXX");
buttons.add(newButton);
// New layout params for each button
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
if (i > 0) {
// using getId() here in case you change how you assign IDs
int id = buttons.get(i - 1).getId();
lp.addRule(RelativeLayout.RIGHT_OF, id);
}
this.addContentView(newButton, lp);
}`
please look this line if ok: this.addContentView(newButton, lp);
Thanks!!!
mateus
It's a bit not clear from your question whether you want all buttons to distribute evenly across the activity space or each button to take over all activity space?
In first case what you need is a LinearLayout with layout_weight for buttons set to 1.
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
lp.weight = 1;
In the second case I think it's better to use a FrameLayout and just let buttons take over all space by setting
FrameLayout.LayoutParams.FILL_PARENT
to both dimensions.

Categories

Resources