I created a custom style:
<style name="Static">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginEnd">5dp</item>
</style>
Then I extended anko with a static function:
inline fun ViewManager.static(theme: Int = R.style.Static, init: TextView.() -> Unit) = ankoView(::TextView, theme, init)
When I use this in my layout:
static { text = resources.getString(R.string.name) }
The marginEnd value is ignored.
If I add a margin manually in anko:
static { text = resources.getString(R.string.name) }.lparams { marginEnd = dip(5) }
The margin is fine.
Do you guys know what is happening that anko is ignoring my margin value or any other way to define a predefined margin for a extended view anko function?
This is not Anko problem, this is how Android works:
If you are specifying layout_margin in a custom style, this style must be explicitly applied to each individual view that you wish to have the specified margin (as seen in the code sample below). Including this style in a theme and applying it to your application or an activity will not work.
This is because attributes which begin with layout_ are LayoutParams, or as in this example its MarginLayoutParams. Each ViewGroup have it's own LayoutParams implementation. And so layout_margin is not just general attribute that can be applied anywhere. It must be applied within the context of a ViewGroup that specifically defines it as a valid argument.
Look here for more.
As #John pointed in his answer, using a style is not an option to define layout params.
So, I developed a function to use in the applyRecursively that iterates over the views and apply the layouts that I want to apply.
The solution:
I wanted to define matchParent for width and height and a margin of 16dp for a TableView, so I created a new class that extends TableLayout
class TableViewFrame(context: Context) : TableLayout(context)
and then in the function when the view is a TableViewFrame I apply my layouts
fun applyTemplateViewLayouts(view: View) {
when(view) {
is TableViewFrame -> {
when(view.layoutParams) {
is LinearLayout.LayoutParams -> {
view.layoutParams.height = matchParent
view.layoutParams.width = matchParent
(view.layoutParams as LinearLayout.LayoutParams).margin = view.dip(16)
}
}
}
}
}
To use the function, in the view definition, I just pass it in the applyRecursively:
verticalLayout {
tableViewFrame {
tableRow {
...
}
}
}
}.applyRecursively { view -> applyTemplateViewLayouts(view) }
I wrote an article at medium with a more detailed explanation: https://medium.com/#jonathanrafaelzanella/using-android-styles-with-anko-e3d5341dd5b4
Related
I am using VerticalGridFragment to display items in a grid-like layout
I don't need to show search or title and I want the rows to start from top of the screen without any margins. Any help?
I found a way to do it by overriding the VerticalGridPresenter of the VerticalGridFragment then getting the VerticalGridView, and setting top padding to a smaller value.
In the CustomVerticalGridPresenter class (that extends VerticalGridPresenter), override this method:
#Override
protected void initializeGridViewHolder(ViewHolder vh) {
super.initializeGridViewHolder(vh);
gridView = vh.getGridView();
int top= 20;//this is the new value for top padding
int bottom = gridView.getPaddingBottom();
int right = gridView.getPaddingRight();
int left = gridView.getPaddingLeft();
gridView.setPadding(left,top,right,bottom);
}
Then in the VerticalGridFragment, assign the new CustomVerticalGridPresenter as following:
CustomVerticalGridPresenter gridPresenter = new CustomVerticalGridPresenter();
gridPresenter.setNumberOfColumns(NUM_COLUMNS);
setGridPresenter(gridPresenter);
You need create new class with name CustomVerticalGridPresenter and put followig code in it.
public class CustomVerticalGridPresenter extends VerticalGridPresenter {
VerticalGridView gridView;
CustomVerticalGridPresenter(int zoom, boolean val){
super(zoom, val);
}
#Override
protected void initializeGridViewHolder(ViewHolder vh) {
super.initializeGridViewHolder(vh);
gridView = vh.getGridView();
int top = 20;//this is the new value for top padding
int bottom = gridView.getPaddingBottom();
int right = gridView.getPaddingRight();
int left = gridView.getPaddingLeft();
gridView.setPadding(left,top,right,bottom);
}
}
Then in verticalgridfragment class use
CustomVerticalGridPresenter videoGridPresenter = new
CustomVerticalGridPresenter(ZOOM_FACTOR, false);
instead of
VerticalGridPresentervideoGridPresenter = new VerticalGridPresenter(ZOOM_FACTOR, false);
Another way over this problem is to set the windowAlignment attributes of the VerticalGridView. Most notably ...
verticalGridView.windowAlignment = BaseGridView.WINDOW_ALIGN_LOW_EDGE //this will remove the top margin
verticalGridView.windowAlignmentOffset = 0
verticalGridView.windowAlignmentOffsetPercent = 25f //find correct values for your case
First of follow this steps to move the rows up which is by default given margins by Lean back Library.
1. Go to you SDK.
2. Inside SDK -> extras -> android -> support -> v17 -> leanback -> res -> values.
3. Inside values folder copy the dimens.xml inside your current project and copy to your projects res -> values folder.
Now you have the file name dimens.xml inside your values folder.
Now open the dimens.xml file which you have copied.
Change the value for property defined below. By default it will given 168dp around that. So make it less to decrease the top margin as i have given below.
<dimen name="lb_browse_rows_margin_top">35dp</dimen>
Now you can be able to split your rows up exact below of top section of your Browse Fragment.
The best way is to create a theme and set it for your Activity / Fragment.
In themes.xml:
<style name="AppTheme.Leanback.NoTitle" parent="Theme.Leanback.Browse">
<item name="browseRowsMarginTop">#dimen/lb_browse_rows_margin_top_none</item>>
</style>
in dimens.xml
<dimen name="lb_browse_rows_margin_top_none">32dp</dimen>
I want to be able to to be able to set padding values if a boolean is true. The problem is that Android studio cannot parse the layout because it thinks 2dp is a decimal with a value of 2 and then doesn't know what to do with the p. how do I format this so that it understands i mean 2 density pixels.
Data layout:
<data class=".ItemBinding">
<variable name="isGroupType" type="Boolean"/>
</data>
View layout(whats important):
<android.support.v7.widget.AppCompatImageView
android:layout_width="64dp"
android:layout_height="64dp"
android:paddingBottom='#{isGroupType ? 2dp : 0dp}'
android:paddingTop='#{isGroupType ? 8dp : 0dp}'
android:paddingRight='#{isGroupType ? 2dp : 0dp}'
android:paddingLeft='#{isGroupType ? 2dp : 0dp}'/>
Store padding value in dimen.xml and use it. Please keep habit to write binding string with " " (double quotes)
android:paddingBottom="#{isGroupType ? #dimen/padding_normal : #dimen/padding_null}"
and so on for other paddings also.
For anyone looking to set margins via DataBinding, you'll have to use BindingAdapter as well:
#BindingAdapter("layoutMarginBottom")
fun setLayoutMarginBottom(view: View, dimen: Float) {
val layoutParams = view.layoutParams as MarginLayoutParams
layoutParams.bottomMargin = dimen.toInt()
view.layoutParams = layoutParams
}
And your xml property will look like this:
app:layoutMarginBottom="#{someCondition ? #dimen/zero_dp : #dimen/twenty_dp}"
Just as a heads-up this does not work with layout_margin's :(
Not sure why, but think it's due to the parent layout needs to be remeasured..
#Ravi's answer is correct.
But for more flexibility you can also try this:
#BindingAdapter({"padding", "shouldAdd"})
public static void setPadding(AppCompatImageView imageView, boolean shouldAdd, int padding){
if (shouldAdd){
imageView.setPadding(padding, padding, padding, padding);
}
}
Then:
<android.support.v7.widget.AppCompatImageView
android:layout_width="64dp"
android:layout_height="64dp"
shouldAdd="#{isGroupType}"
padding="#{10}"/>
#Ravi's answer is good, but it's working only for padding.
If You want to simply add margin, create empty view e.g TextView with padding.
Use a blank view
<View
android:layout_width = "8dp"
android:layout_height = "8dp"
>
Now constraint this view to the layout you want to control the visibility of .
Now disappear this view depending on the condition. would work the same as a margin
You can use logic and ternary statements in xml-binding, but you really shouldn't. It will come back to haunt you when you're looking the usual places you have logic and don't see what's going on.
BindingAdapter for all your margin needs:
fun bindingSetMargins(view: View, start: Float?, top: Float?, end: Float?, bottom: Float?) {
view.layoutParams = (view.layoutParams as ViewGroup.MarginLayoutParams).apply {
start?.toInt()?.let { leftMargin = it }
top?.toInt()?.let { topMargin = it }
end?.toInt()?.let { rightMargin = it }
bottom?.toInt()?.let { bottomMargin = it }
}
}
I have a navigation drawer with many items so the user needs to scroll up and down in order to see all the items.
I would like to reduce the margins between individual menu items, so that all items fit within a standard screen with no need to scroll.
Is there a way to control the margins in between the menu items?
Answer here helped me to reduce space at least between groups. I used the following dimen
<dimen tools:override="true" name="design_navigation_separator_vertical_padding">1dp</dimen>
We can create a drawable and set it as the NavigationView's itemBackground attribute. I will explain below:
If we walk through the NavigationMenuAdapter, we will see there are four types of items:
private static final int VIEW_TYPE_NORMAL = 0;
private static final int VIEW_TYPE_SUBHEADER = 1;
private static final int VIEW_TYPE_SEPARATOR = 2;
private static final int VIEW_TYPE_HEADER = 3;
What we want to work with is VIEW_TYPE_NORMAL. The attributes exposed to developers can be found in the below code:
case VIEW_TYPE_NORMAL:
{
NavigationMenuItemView itemView = (NavigationMenuItemView) holder.itemView;
itemView.setIconTintList(iconTintList);
if (textAppearanceSet) {
itemView.setTextAppearance(textAppearance);
}
if (textColor != null) {
itemView.setTextColor(textColor);
}
ViewCompat.setBackground(
itemView,
itemBackground != null ? itemBackground.getConstantState().newDrawable() : null);
NavigationMenuTextItem item = (NavigationMenuTextItem) items.get(position);
itemView.setNeedsEmptyIcon(item.needsEmptyIcon);
itemView.setHorizontalPadding(itemHorizontalPadding);
itemView.setIconPadding(itemIconPadding);
if (hasCustomItemIconSize) {
itemView.setIconSize(itemIconSize);
}
itemView.setMaxLines(itemMaxLines);
itemView.initialize(item.getMenuItem(), 0);
break;
}
Unfortunately,there is no interface for us to add margins between the NavigationMenuItemView. However, it allows us to set a background. So we can set a customer drawable to the NavigationView with a specific height. We only set a height in that drawable, like:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="rectangle">
<size android:height="60dp"/>
</shape>
Then apply this drawable to the NavigationView in the layout.xml, like:
<com.google.android.material.navigation.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:itemBackground="#drawable/bk_menu_item"/>
I understand it is not a perfect solution, but it seems the only solution working in my case.
Paste this in styles.xml
<style name="NavigationTheme" parent="AppTheme">
<item name="android:textSize">16sp</item>
<item name="android:layout_marginBottom">02dp</item>
</style>
In navigation drawer put this in each item:
android:theme="#style/NavigationTheme"
add below code in dimens.xml
<dimen tools:override="true" name="design_navigation_icon_padding">16dp</dimen>
I need to position a TextView the way its baseline is 20dp from the bottom of the container.
How can I achieve this?
The layout with bottom margin or padding produces the same result.
I would like to make the text 'sit' on the purple line.
When I write 'sit' I mean, the 'wert' should touch the line, not 'q...y'.
The padding / margin is equal to the purple square size:
If you still need it, I wrote custom method, to not create lots of custom views. It works for me with TextView:
public static void applyExistingBotMarginFromBaseline(View view) {
final int baseline = view.getBaseline();
final int height = view.getHeight();
final ViewGroup.MarginLayoutParams marginLayoutParams;
try {
marginLayoutParams = ((ViewGroup.MarginLayoutParams) view.getLayoutParams());
} catch (ClassCastException e) {
throw new IllegalArgumentException("Applying margins on a view with wrong layout params.");
}
final int baselineMarginValue = baseline + marginLayoutParams.bottomMargin;
marginLayoutParams.bottomMargin = baselineMarginValue - height;
view.setLayoutParams(marginLayoutParams);
}
You can apply it when view is measured already, so like this:
final TextView title = (TextView) findViewById(R.id.title);
title.post(new Runnable() {
#Override public void run() {
Utils.applyExistingBotMarginFromBaseline(title);
}
});
Also you can use databinding framework and write your own custom BindingAdapter with a bit customized method, to use it from xml.
Your problem is not the padding/margin referenced to the parent, I think is about your font, I recommend you to change the fontFamily:"yourStyle"
even worst you have to re-difine your own font style which is explained here Custom fonts and XML layouts (Android) or Set specific font in a styles.xml
I've been creating apps without much XML, creating views programmatically. I'd like to switch to XML. So I wrote an XML file for a RelativeLayout, and I need to inflate it into an existing class (a subclass of RelativeLayout, of course) that has all the implementation logic.
How do I inflate into "this" in the constructor?
By the way, what's really the advantage of XML? When I create views in the code, I scale fonts and images and also move views around depending on the screen's size, orientation, aspect ratio, etc. With XML approach, I'd have to create a separate XML for all possible configurations...
Constructor code:
public OrderEditControl()
{
super(LmcActivity.W.getApplicationContext());
Resources res = LmcActivity.W.getResources();
setBackgroundColor(Color.TRANSPARENT);
headers = res.getStringArray(R.array.item_list_columns);
widths = new int[headers.length];
createLabels();
createButtons();
LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
lp.addRule(ALIGN_PARENT_TOP);
lp.addRule(RIGHT_OF, labels[LabelType.CUSTOMER.ordinal()].getId());
lp.addRule(LEFT_OF, buttons[ButtonType.FIND_CUSTOMER.ordinal()].getId());
customerView = new TextView(LmcActivity.W.getApplicationContext());
customerView.setTextColor(Color.BLACK);
customerView.setId(400);
customerView.setTypeface(Typeface.DEFAULT_BOLD);
customerView.setGravity(Gravity.CENTER_VERTICAL);
addView(customerView, lp);
lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp.addRule(ALIGN_TOP, labels[LabelType.SHIP_TYPE.ordinal()].getId());
lp.addRule(ALIGN_BOTTOM, labels[LabelType.SHIP_TYPE.ordinal()].getId());
lp.addRule(RIGHT_OF, labels[LabelType.SHIP_TYPE.ordinal()].getId());
shipSpinner = new Spinner(LmcActivity.W);
shipSpinner.setId(401);
shipSpinner.setAdapter(shipAdapter);
shipSpinner.setOnItemSelectedListener(this);
addView(shipSpinner, lp);
deliveryView = new EditText(LmcActivity.W.getApplicationContext());
deliveryView.setGravity(Gravity.CENTER_VERTICAL);
deliveryView.setSingleLine();
deliveryView.setId(402);
addView(deliveryView);
lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
lp.addRule(RIGHT_OF, labels[LabelType.COMMENTS.ordinal()].getId());
lp.addRule(LEFT_OF, buttons[ButtonType.ITEMS.ordinal()].getId());
lp.addRule(ALIGN_TOP, labels[LabelType.COMMENTS.ordinal()].getId());
commentView = new EditText(LmcActivity.W.getApplicationContext());
commentView.setGravity(Gravity.TOP);
commentView.setId(403);
addView(commentView, lp);
lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
lp.addRule(BELOW, commentView.getId());
itemList = new ListView(LmcActivity.W.getApplicationContext());
itemList.addHeaderView(createRow(null, null), null, false);
itemList.setOnItemClickListener(this);
itemList.setAdapter(itemAdapter);
itemList.setCacheColorHint(0);
itemList.setBackgroundColor(Color.TRANSPARENT);
itemList.setId(404);
addView(itemList, lp);
lays[0] = new LayParm(false);
lays[1] = new LayParm(true);
}
/** create the view's buttons */
private void createButtons()
{
for (int i = 0; i < N_BUT; ++i)
{
Button but = i == ButtonType.ITEMS.ordinal() ?
new TextGlassButton(2.4f, LmcActivity.W.getResources().getString(R.string.items), Color.WHITE) :
new EffGlassButton(1.2f, butEffects[i]);
but.setId(BUT_ID + i);
but.setOnClickListener(this);
buttons[i] = but;
if (i == ButtonType.DATE.ordinal())
addView(but);
else
{
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
if (i < 2)
lp.addRule(ALIGN_PARENT_TOP);
else
lp.addRule(BELOW, BUT_ID + i - 2);
if (i % 2 == 0)
lp.addRule(ALIGN_PARENT_RIGHT);
else
lp.addRule(LEFT_OF, BUT_ID + i - 1);
addView(but, lp);
}
}
}
/** create text labels */
private void createLabels()
{
Paint paint = AFDraw.W.textPaint;
paint.setTextSize(Universe.TEXT_SIZE);
paint.setTypeface(LmcActivity.W.defaultTypeface);
String[] titles = LmcActivity.W.getResources().getStringArray(R.array.order_labels);
for (int i = 0; i < titles.length; ++i)
{
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
lp.addRule(ALIGN_PARENT_LEFT);
if (i == 0)
lp.addRule(ALIGN_PARENT_TOP);
else
lp.addRule(BELOW, LABEL_ID + i - 1);
TextView tv = new TextView(LmcActivity.W.getApplicationContext());
tv.setText(titles[i]);
tv.setTextColor(Color.BLACK);
tv.setId(LABEL_ID + i);
tv.setTypeface(LmcActivity.W.defaultTypeface);
tv.setGravity(Gravity.CENTER_VERTICAL);
labels[i] = tv;
addView(tv, lp);
labelWidth = Math.max(labelWidth, paint.measureText(titles[i]));
}
labelWidth += Universe.TEXT_SIZE * 0.5f;
dateWidth = paint.measureText("00/00/00") + Universe.TEXT_SIZE * 1.5f;
}
#scriptocalypse is generally right, but subclassing some layouts and inflating custom layout to this class helps to separate different abstractions. There are so many bad tutorials, in which everything is done in the Activity. I see that the world's new comming programmers will code only crap looking applications.
With custom layout you can do in Activity only such a thing:
medicineView.putMedicine(medicineList);
instead of all crappy adapter creations and looking for views...
Firstly you should create some view for your custom View:
<RelativeLayout ...>
<!-- You put all your views here -->
</RelativeLayout>
Secondly if you are sattisfied with your view, you should change the root to merge tag:
<merge ...>
<!-- You put all your views here -->
</merge>
This is very important. We begin design with RelativeLayout tags in order to IDE know how to draw layouts, and how to do completions. But if we leave it as it is, we will end up in two nested RelativeLayouts it will be something like that in the end:
<RelativeLayout ...> <!-- That is your class -->
<RelativeLayout ...> <!-- This is inflated from layout -->
<!-- You put all your views here -->
</RelativeLayout>
</RelativeLayout>
If you change your layout to "merge" then it will look like this:
<RelativeLayout ...> <!-- That is your class -->
<merge...> <!-- This is inflated from layout -->
<!-- You put all your views here -->
</merge>
</RelativeLayout>
and will be merged to its root:
<RelativeLayout ...> <!-- That is your class, merged with layout -->
<!-- You put all your views here -->
</RelativeLayout>
At the end you must subclass demanded View or ViewGroup:
public class CustomView extends RelativeLayout {
public CustomView(Context context) {
super(context);
initialize();
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize();
}
public CustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initialize();
}
private void initialize() {
LayoutInflater inflater = LayoutInflater.from(getContext());
inflater.inflate(R.id.your_layout, this, true);
// find your views, set handlers etc.
}
}
Usage
Just like #scriptocalypse already said. In another layout you use this like that:
<SomeLayout>
<com.foo.CustomView>
</SomeLayout>
First, to answer your main question:
you would not want to inflate an XML RelativeLayout into your RelativeLayout class. You'd extend RelativeLayout and then declare an instance of your RelativeLayout in an XML file, like so:
// com.foo.MyRelativeLayout.java
public class MyRelativeLayout extends RelativeLayout{
/**
* Implement MyRelativeLayout
*/
}
and...
// layout_example.xml
<?xml version="1.0" encoding="utf-8"?>
<com.foo.MyRelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- Put More Views in here... -->
<TextView
android:id="#+id/customer_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/customer_name_placeholder" />
<!-- and on... -->
</com.foo.MyRelativeLayout>
But more to the point, if you're using the XML to lay out your file, you don't need any of those instantiations or .addRule() method invocations inside your MyRelativeLayout file because you've done it declaratively in XML instead.
To answer your second question of "Why do you want to use XML anyway?"
There are many reasons. Maybe these apply to you, and maybe they don't, but they're ones that I can think of fairly easily that have been relevant in my work.
You don't actually have to create a new layout file for every separate screen size or use case. For the most part, a single layout file will suffice for most screens. You might find that you will have size/resolution/orientation specific dimens.xml or style.xml files, but unless you want a dramatically different arrangement for your different possibilities then the layouts themselves don't repeat themselves too often.
You can use a visual editor. This is important if you're working in teams, and your teammates don't like to or want to use only Java to lay out their screens. While I and others gladly create View and Layout subclasses to fit our needs, I know of literally nobody who prefers to use Java as their primary layout language. Finding people who will work with you (or a job where everyone else uses the XML tools) could be challenging.
If you're creating tools for other people to use (like the above-mentioned folks who prefer XML) you can actually give them custom attributes to work with, that make positioning and layout more powerful. These attributes could be hard-coded in the XML, or they could be references to any of the other Android resources (drawable/string/color/integer/boolean/etc...). As a contrived example, but one based on your code, you could give your users the ability to specify a number of buttons to create rather than rely on the N_BUT variable. You could give it a default value, but offer users a way to change it in XML.
Here is an example:
// somelayout.xml
<?xml version="1.0" encoding="utf-8"?>
<com.foo.MyRelativeLayout
xmlns:param="http://schemas.android.com/apk/res-auto"
style="#style/MyRelativeLayoutStyle"
param:numberOfButtons="3">
</com.foo.MyRelativeLayout>
and in a different file...
//attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyRelativeLayout">
<attr name="numberOfButtons" format="reference|integer" />
</declare-styleable>
</resources>
and in your MyRelativeLayout, you access those attributes from the AttributeSet in its constructor (the one called by Android when it uses XML to create a layout).
Using the style="#style/foo" syntax can allow you to create "classes" of styles that can apply to all kinds of views without actually making a View subclass. Let's say you know that you always want to have a set of parameters that hold true for all your Button elements but don't want to subclass Button.
For example:
// styles.xml
<style name="BaseButton">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:focusable">true</item>
<item name="android:clickable">true</item>
<item name="android:background">#drawable/bg_common_button</item>
<item name="android:textColor">#color/white</item>
<item name="android:textSize">#dimens/base_button_text_size</item>
<!-- ^^ that dimen value could vary from screen size to screen size, but the style likely won't -->
</style>
// button_layout.xml
<Button
android:id="#+id/styled_button"
style="#style/BaseButton" /> <!-- and you're done -->
// some_other_layout.xml
<LinearLayout
style="#style/BaseLinearLayout">
<Button style="#style/BaseButton" android:text="Button1" />
<Button style="#style/BaseButton" android:text="Button2" />
<Button style="#style/BaseButton" android:text="Button3" />
</LinearLayout>
If you would like to instantiate that button using code, then you can use the LayoutInflater to inflate that specific button's layout and use that wherever you want. In fact, you can create all manner of components in XML and then inflate them at runtime.
LayoutInflater inflater = LayoutInflater.from(YourActivity.this);
Button theInflatedButton = inflater.inflate(R.layout.button_layout.xml, null);
Of course, the canonical example is ListViews and the items that you wish to populate them. You'd create a listview item layout xml and then inflate that whenever your Adapter is in need of a new convertView instance.