Is there any way I can get around having to add the layout_width and layout_height parameters to my custom views? I know there are a few built in android views that you don't have to supply those attributes for.
It's not a View's responsibility to decide whether or not it can/should provide these attributes. The parent ViewGroup dictates whether these attributes are mandatory or not. TableRow for instance makes them optional. Other layouts (LinearLayout, FrameLayout, etc.) require these params.
When would you want to not use the height and width parameters? I'm not sure but I think that would cause them to not even show up on the layout?
Look here for reference http://developer.android.com/guide/topics/ui/declaring-layout.html#layout-params
From the same Reference Dave has suggested.
All view groups include a width and height (layout_width and
layout_height), and each view is required to define them. Many
LayoutParams also include optional margins and borders.
So it looks like, you have to.
If reducing common and redundant attributes is what you want, then you should try styling.
Developer guide here.
The problem is that ViewGroup.LayoutParams.setBaseAttributes() uses the strict getLayoutDimension(int, String).
You need to extend whichever LayoutParams you need and override setBaseAttributes.
Inside you can either manually set width and height or use the more lenient getLayoutDimension(int, int). Finally, you'll have to override in your layout class that you are using your own LayoutParams.
#Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
public static class LayoutParams extends FrameLayout.LayoutParams {
public LayoutParams(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
width = a.getLayoutDimension(widthAttr, WRAP_CONTENT);
height = a.getLayoutDimension(heightAttr, WRAP_CONTENT);
}
}
Related
I want to create a custom ImageView with fixed height and width.
It's easy to do with XML, like
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"/>
But how to do it programmatically?
class customView : ImageView {
// code to achieve fixed height and width
}
You will want to use the AppCompat version of what you want to use, such as the AppCompatImageView as layoutParams would then be supported. One thing I also saw wrong: you must specify the context and all of the included attributes in the constructor of the class:
(context : Context, attrs : AttributeSet)
These will be auto assigned by the OS so nothing really needs to be done with them, but they must be there. Here is what I have for you:
class CustomImageView(context: Context,
attrs : AttributeSet) : AppCompatImageView(context, attrs) {
init {
applyDefaults()
}
private fun applyDefaults(){
val height = 130
val width = 300
layoutParams = ViewGroup.LayoutParams(width, height)
}
}
Get the current layout parameters by getting them from the base class, which would be AppCompatImageView. Then set the layout parameters to be the height and width you want as an integer.
I have one imageView and I am trying to give it minus margin top as much as its height / 2. I can do it at programmatically but i wondred is it possible at xml also andorid published percentrelative layout . I don't know how to to do it or possible?
--Edit: As #aga suggests, there seems to be a way to achieve it via the Percent Support Library--
If you want to use this type of imageView more often throughout your application you could extend imageview and put your margin-code inside of it's onMeasure:
public class HalfMarginImageView extends ImageView {
public HalfMarginImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
((ViewGroup.MarginLayoutParams) getLayoutParams()).topMargin = -getMeasuredHeight() / 2;
}
}
for this to work the view must be part of a ViewGroup.Also make sure you use the constructor with AttributeSet, oltherwise you can't create the View from xml. Yo then just include a CustomView in your layout xml, select HalfMarginImageView and use it as normal imageView.
I am building a custom View that contains two standard Views. I have a default style for each contained View, and a custom attribute that lets the user specify a custom style for each contained View. I can get the default vs. custom styles just fine, and pass the right style id as the third parameter of each contained View's constructor. What I am having a hard time doing is generating a ViewGroup.LayoutParams for these contained Views, based on the android:layout_height and android:layout_width in the appropriate style.
It seems like I need to use the ViewGroup.LayoutParams(Context, AttributeSet) constructor, and the AttributeSet docs say that I should get an AttributeSet via
XmlPullParser parser = resources.getXml(myResouce);
AttributeSet attributes = Xml.asAttributeSet(parser);
... but that throws a Resources$NotFoundException with a warning from frameworks/base/libs/utils/ResourceTypes.cpp that Requesting resource %p failed because it is complex.
Hence, my questions, in decreasing order of specificity:
Is there a way to get an XmlPullParser that works with "complex" elements?
Is there some other way to get an AttributeSet that corresponds to a <style> element?
Is there some other way to construct a LayoutParameters that will pay attention to the layout_height and layout_width values in a given style?
static ViewGroup.LayoutParams layoutFromStyle(Context context,
int style) {
TypedArray t = context.getTheme().obtainStyledAttributes(
null,
new int[] { android.R.attr.layout_width,
android.R.attr.layout_height }, style, style);
try {
int w = t
.getLayoutDimension(0, ViewGroup.LayoutParams.WRAP_CONTENT);
int h = t
.getLayoutDimension(1, ViewGroup.LayoutParams.WRAP_CONTENT);
return new ViewGroup.LayoutParams(w, h);
} finally {
t.recycle();
}
}
I have a custom Button and I want to set its dimensions in code (as opposed to in xml), so that users can customize the dimensions. The seemingly obvious way to this is:
public class MyButton extends Button
{
public MyButton(Context context, AttributeSet attrs)
{
super(context, attrs);
int buttonSize = getSize();
setLayoutParams(new LinearLayout.LayoutParams(buttonSize, buttonSize));
}
However, this fails to be generic because it only works if the Button's parent is a LinearLayout. Instead, I tried this:
#Override
protected void onMeasure(int specw, int spech)
{
int spec = MeasureSpec.makeMeasureSpec(getButtonSize()), MeasureSpec.EXACTLY);
super.onMeasure(spec, spec);
}
...which seems to work well. Is anyone aware of any shortcomings to this? or aware of a better way to generically set widget dimensions in code?
Doing it from onMeasure() is a good way to do it (even though your code doesn't work since you're not using the measure spec you've created.) You could also override onFinishInflate() and call getLayoutParams() and change the width and height fields.
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.