Working with the XML file was easy as I could specify the parameters as
<android:layout_width="fill_parent" android:layout_height="wrap_content">
But I am confused while specifying it through code. For each view I specify the parameters using
view.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.FILL_PARENT));
I see that I have an option of specifying it as relative layout, frame layout etc.
As of now I am using linear layout for all views such as images, text and also gridview. Should the view parameters be defined based on the layout of the parent element? Or is it OK to specify it as linear layout even if the view is a child of, say, a framelayout? Sorry, but I couldn't find out the difference.
All layout classes (LinearLayout, RelativeLayout, etc.) extend ViewGroup.
The ViewGroup class has two static inner classes: LayoutParams and MarginLayoutParams. And ViewGroup.MarginLayoutParams actually extends ViewGroup.LayoutParams.
Sometimes layout classes need extra layout information to be associated with child view. For this they define their internal static LayoutParams class. For example, LinearLayout has:
public class LinearLayout extends ViewGroup {
...
public static class LayoutParams extends ViewGroup.MarginLayoutParams {
...
}
}
Same thing for RelativeLayout:
public class RelativeLayout extends ViewGroup {
...
public static class LayoutParams extends ViewGroup.MarginLayoutParams {
...
}
}
But LinearLayout.LayoutParams and RelativeLayout.LayoutParams are completely different independent classes. They store different additional information about child views.
For example, LinearLayout.LayoutParams can associate weight value with each view, while RelativeLayout.LayoutParams can't. Same thing with RelativeLayout.LayoutParams: it can associate values like above, below, alightWithParent with each view. And LinearLayout.LayoutParams simply don't have these capability.
So in general, you have to use LayoutParams from enclosing layout to make your view correctly positioned and rendered. But note that all LayoutParams have same parent class ViewGroup.LayoutParams. And if you only use functionality that is defined in that class (like in your case WRAP_CONTENT and FILL_PARENT) you can get working code, even though wrong LayoutParams class was used to specify layout params.
Depending on how many views you want to change the layouts on, I think it's better to create a helper method and pass whatever views you want to change to the method along with the height and width values you want them to change to:
public void setWidthHeight(View v, int width, int height){
LayoutParams lp;
lp = v.getLayoutParams();
lp.width = width;
lp.height = height;
v.setLayoutParams(lp);
}
Remember that setting the width and height here are not going to match the same values in your xml, i.e., android:layout_width="32dp" is not the same as lp.width = 32;
Also, the LayoutParams type variable called lp should be of the type returned by your view... Check what type is returned by the view and import that type in your import statements.
Related
I have made class called ProgressButton that extended RelativeLayout.Now in main xml i added this class:
<com.tazik.progressbutton.ProgressButton
android:id="#+id/pb_button"
android:layout_width="200dp"
android:layout_height="wrap_content"/>
As you can see i added android:layout_width="200dp", now in ProgressButton class i want to get this size to create a button with this size:
public class ProgressButton extends RelativeLayout {
private AppCompatButton button;
public ProgressButton(Context context) {
super(context);
initView();
}
private void initView() {
initButton();
}
private void initButton() {
button = new AppCompatButton(getContext());
LayoutParams button_params = new LayoutParams(????, ViewGroup.LayoutParams.WRAP_CONTENT);
button_params.addRule(RelativeLayout.CENTER_IN_PARENT,RelativeLayout.TRUE);
button.setLayoutParams(button_params);
button.setText("click");
addView(button);
}
I want to create button exactly to size of relativeLayout, so how can i get layout_width in my custom view to set button_params width?
now in ProgressButton class i want to get this size to create a button with this size
As #MikeM. suggested in a comment. It could be as easy as giving that child view a width of MATCH_PARENT. See below...
LayoutParams button_params = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
With that in place you don't need to worry about the actual size because MATCH_PARENT will stretch your child view to occupy the whole parent's width...obviosuly respecting margins and paddings.
However, if you do need to know the parent's width, you should query that in onMeasure. I strongly suggest you to stay away from onMeasure whenever possible because it is a bit complex and it might take a lot of your development time.
Either way, in onMeasure you can know what measurements the parent view wants to give to its child views, this is based on the space available to render inside the parent and the layout params specified...
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int childWidth = 0;
if(widthSpecMode == MeasureSpec.AT_MOST){
//The parent doesn't want the child to exceed "childWidth", it doesn't care if it smaller than that, just not bigger/wider
childWidth = MeasureSpec.getSize(widthMeasureSpec);
}
else if(widthSpecMode == MeasureSpec.EXACTLY){
//The parent wants the child to be exactly "childWidth"
childWidth = MeasureSpec.getSize(widthMeasureSpec);
}
else {
//The parent doesn't know yet what its children's width will be, probably
//because it's still taking measurements
}
//IMPORTANT!!! set your desired measurements (width and height) or call the base class's onMeasure method. Do one or the other, NOT BOTH
setMeasuredDimension(dimens, dimens);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
Add a few Log.d calls inside onMeasure for a better understanding of what's happening. Be aware that this method will be called multiple times.
Again, this is an unnecessary overkill for your case scenario. Setting MATCH_PARENT to the button should produce the results you want
I'd like to place a view on top of an existing view. The view I'm targeting is inside a LinearLayout, which resides in a FrameLayout.
I'm thinking there's a way to do this with RelativeLayout because I already have it partially working. I'd like to align the new view to the bottom-left or top-left (as the origin) and then offset X and Y to some precise value that I specify.
How can this be achieved?
Here's the idea:
public static void placeTextRelativeToBottomLeftOfViewAtXY(final FrameLayout layout, View component, int x, int y, String text) {
final TextView textView = new TextView(getContext());
textView.setId((int)System.currentTimeMillis());
final RelativeLayout relativeLayout = new RelativeLayout(getContext());
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.MATCH_PARENT);
params.setMargins(x, y, 0,0);
params.addRule(RelativeLayout.LEFT_OF, component.getId());
relativeLayout.setLayoutParams(params);
relativeLayout.setBackgroundColor(Color.TRANSPARENT);
textView.setText("+500 points!");
textView.bringToFront();
relativeLayout.addView(textView, params);
layout.addView(relativeLayout);
}
Based on the additional information in comments, even if it is possible to overlap a different layouts inside a FrameLayout, those layouts will only be able to position their own children.
A RelativeLayout won't be able to position one of its child views relative to a view in a different sibling or parent Layout.
The way to go would be to flattern the heierarchy of Layouts, setting the root layout to a RelativeLayout or a ConstraintLayout.
ConstraintLayout is more flexible in terms of positioning views, but it is also more difficult to learn.
Here I am leaving an alternative to be used with RelativeLayout as the root view. The important items to look at are the setting of the LayoutParams which is sometimes a bit confussing.
The LayoutParams are set on the child view, but the class used depends on the parent view.
Also take in mind that to keep margins display independent you need to convert dp into pixels (for the sake of simplicity I haven't done that, but there are examples of how to do this here in SO).
It also uses View.generteViewId() go get an id for a view created dynamically.
To make it simple I included the reference View in the xml, but i could have also been created dynamically.
Layout
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/rlContainer"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/tvCenterText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Texto estatico"
android:layout_centerInParent="true"/>
</RelativeLayout>
Main Activity
public class DynamicViewsActivity extends AppCompatActivity {
RelativeLayout rlContainer;
TextView centerText;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dynamicviews);
rlContainer = findViewById(R.id.rlContainer);
centerText = findViewById(R.id.tvCenterText);
placeTextRelativeToBottomLeftOfViewAtXY(rlContainer, centerText, 100,10, "Hola");
}
public void placeTextRelativeToBottomLeftOfViewAtXY(final RelativeLayout layout, View component, int x, int y, String text) {
final TextView textView = new TextView(this);
textView.setId(View.generateViewId());
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
params.setMargins(x, y, x,y);
params.addRule(RelativeLayout.LEFT_OF, component.getId());
params.addRule(RelativeLayout.ALIGN_BASELINE, component.getId());
textView.setLayoutParams(params);
textView.setText(text);
layout.addView(textView);
}
}
I have a statically defined relative layout with height set to wrap_content.
I add several children to it dynamically. This appears just fine.
I now have to add a few more children and line them up above previously added children. This sort of works. (Note: I can only do this once those previously added children have actually been added, as my new children depend on their width.)
I can only see the changes if I change layout height to say 50dp vs wrap_content.
I tried calling invalidate(), postInvalidate() and requestLayout() on the holding layout, but that didn't work. What am I not doing?
public void layOutExtras(CustomView section) {
if (section.isUnderlined()) {
int labelOrientation = section.getLabelOrientation();
View line = createLine(labelOrientation, section.getMeasuredWidth(), section.getId());
addView(line);
}
}
private View createLine(int orientation, int width, int viewId) {
RelativeLayout.LayoutParams params = new LayoutParams(width, 4);
params.addRule(ABOVE, viewId);
params.addRule(ALIGN_RIGHT, viewId);
View line = new ImageView(context);
line.setBackgroundColor(Color.RED);
line.setLayoutParams(params);
return line;
}
Sounds like the issue I had a few days ago. The issue is probably due to the height not getting set right. Try setting the minimum height of both the new view and the container you're modifying.
i have created a custom view
CameraView extends View{
.....
}
in OnCreate methode i have define as
cameraView = new CameraView(this);
LayoutParams layoutParamsCamera
= new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
this.addContentView(cameraView, layoutParamsCamera);
but this use full screen i need to use as
marginBootom= 20dp or paddingBoototm= 20dp
so how i can add this parameter to LayoutParams??
i need help
& sorry for my bad English
Use MarginLayoutParams instead of whatever flavor of LayoutParams you're now using to set view layout margins programmatically.
I want to add a view inside a FrameLayout programmatically and to place it in a specific point within the layout with a specific width and height. Does FrameLayout support this? If not, should I use an intermediate ViewGroup to achieve this?
int x; // Can be negative?
int y; // Can be negative?
int width;
int height;
View v = new View(context);
// v.setLayoutParams(?); // What do I put here?
frameLayout.addView(v);
My initial idea was to add an AbsoluteLayout to the FrameLayout and place the view inside the AbsoluteLayout. Unfortunately I just found out that AbsoluteLayout is deprecated.
Any pointers will be much appreciated. Thanks.
The following example (working code) shows how to place a view (EditText) inside of a FrameLayout. Also it shows how to set the position of the EditText using the setPadding setter of the FrameLayout (everytime the user clicks on the FrameLayout, the position of the EditText is set to the position of the click):
public class TextToolTestActivity extends Activity{
FrameLayout frmLayout;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
frmLayout = (FrameLayout)findViewById(R.id.frameLayout1);
frmLayout.setFocusable(true);
EditText et = new EditText(this);
frmLayout.addView(et,100,100);
frmLayout.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
Log.i("TESTING","touch x,y == " + event.getX() + "," + event.getY() );
frmLayout.setPadding(Math.round(event.getX()),Math.round(event.getY()) , 0, 0);
return true;
}
});
}
}
main.xml
<?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="fill_parent">
<FrameLayout
android:id="#+id/frameLayout1"
android:layout_height="fill_parent" android:layout_width="fill_parent">
</FrameLayout>
</LinearLayout>
You can also add a margin around the newly added view to position it inside the FrameLayout.
FrameLayout frameLayout = (FrameLayout) findViewById(R.id.main); // or some other R.id.xxx
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.setMargins(0, metrics.heightPixels - 20, 0, 0);
View v = new View(context);
v.setLayoutParams(params);
frameLayout.addView(v);
This will position the FrameLayout 20 pixels from the bottom of the screen.
Edit: completed the example so it stands by itself. And oh, yes it does work.
It's true that with FrameLayout all children are pegged to the top left of the screen, but you still have some control with setting their padding. If you set different padding values to different children, they will show up at different places in the FrameLayout.
From the link Quinn1000 provided:
You can add multiple children to a FrameLayout, but all children are pegged to the top left of the screen.
This means you can't put your View at a specific point inside the FrameLayout (except you want it to be at the top left corner :-)).
If you need the absolute positioning of the View, try the AbsoluteLayout:
A layout that lets you specify exact locations (x/y coordinates) of its children. Absolute layouts are less flexible and harder to maintain than other types of layouts without absolute positioning.
As for setting the width and height of the View, also like Quinn1000 said, you supply the v.setLayoutParams() method a LayoutParams object, depending on the container you chose (AbsoluteLayout, LinearLayout, etc.)
The thread here on stackOverflow at
How do you setLayoutParams() for an ImageView?
covers it somewhat.
For instance:
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(30, 30);
yourImageView.setLayoutParams(layoutParams);
implies that you need to be defining a LinearLayout.LayoutParams (or in your case a FrameLayout.layoutParams) object to pass to the setLayoutParams method of your v object.
At
http://developer.android.com/reference/android/widget/FrameLayout.html
it almost makes it looks like you could ask your v to:
generateDefaultLayoutParams () via this method if you have not defined the parameters specifically.
But it's late, and those links are making my eyes bleed a little. Let me know if they nhelp any :-)