Create Android views and widgets programmatically

What if you can’t hard code the UI in advance?

Yat Man, Wong
4 min readJun 1, 2020

Why not just inflate the appropriate xml to display the UI?

Because xml is not flexible.
Imagine your need to display a button that may have border, shadow, rounded corners, or any combination of them. The border may be dashed, the width and color are also decided by the server.

Because of the uncertainty, you couldn’t possibility create a bunch of xml files for all the possible combination. The most logical way is to create the button dynamically.

if (containBorder){
button.add(border);
}
if (containShadow){
button.add(shadow);
}

Creating view programmatically

First create a button object inside onCreate(). We can set its width and height to some fix value.

Button button = new Button(this);
button.setId(ViewCompat.generateViewId());
ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(900, 600); // width, height
button.setLayoutParams(layoutParams);

To get the border, round corners etc, we can create a GradientDrawable object.

public GradientDrawable createBorder(String backgroundColor,
String borderColor,
Float borderRadius,
boolean dashed, int width){


GradientDrawable border = new GradientDrawable();
border.setShape(GradientDrawable.RECTANGLE);

if (backgroundColor != null){
border.setColor(Color.parseColor(backgroundColor));
}

if (borderRadius != null){
border.setCornerRadius(borderRadius);
}

if (dashed){
border.setStroke(width, Color.parseColor(borderColor), 100,20);
}else{
border.setStroke(width, Color.parseColor(borderColor));
}


return border;
}

Then set it as the button’s background.

GradientDrawable border = createBorder("#ffa500", "#6ED2CF", 30f, true, 20)
button.setBackground(border);

This is GradientDrawable’s background effect, this way we can control the background color, corner radius, dashed, border width, border color in the code. (If you also want shadow, you need something more advance.)

Adding a view into a viewgroup

Viewgroup is a view that can contain other view. Like our typical constraint layout, relative layout, linear layout.

So lets create a constraint layout and set its width and height to fill the screen.

final ConstraintLayout layout = new ConstraintLayout(this);ConstraintLayout.LayoutParams layoutParamsparent = new ConstraintLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);layout.setLayoutParams(layoutParamsparent);

Then add the button into the constraint layout.

// add to parent view
layout.addView(button);

Setting constraint programmatically

We can set the constraint using ConstraintSet, this is equivalent to xml’s

app:layout_constraintTop_toTopOf="parent"

We bind this button’s top to the parent’s top, remember this constraint is apply to the parent view.

ConstraintSet constraint = new ConstraintSet();constraint.clone(layout);constraint.connect(button.getId(), ConstraintSet.LEFT,
ConstraintSet.PARENT_ID, ConstraintSet.LEFT);
constraint.connect(button.getId(), ConstraintSet.TOP,
ConstraintSet.PARENT_ID, ConstraintSet.TOP);
constraint.connect(button.getId(), ConstraintSet.BOTTOM,
ConstraintSet.PARENT_ID, ConstraintSet.BOTTOM);
constraint.connect(button.getId(), ConstraintSet.RIGHT,
ConstraintSet.PARENT_ID, ConstraintSet.RIGHT);
constraint.applyTo(layout);

NOTICE

1. View MUST have valid id to use view.getId()

2. ConstraintSet MUST clone the layout before connect

3. We MUST set constraint AFTER add view to parent

When Constraint fail to take effect

Display the layout

s̶e̶t̶C̶o̶n̶t̶e̶n̶t̶V̶i̶e̶w̶(̶R̶.̶l̶a̶y̶o̶u̶t̶.̶a̶c̶t̶i̶v̶i̶t̶y̶_̶m̶a̶i̶n̶_̶l̶a̶y̶o̶u̶t̶)̶;̶

If we pass in a view object to setContentView(), it will set the activity content to the explicit view.

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(layout);
}

Result

Now our code don’t need to change and the button will look difference base on the properties.

button.setBackground(createBorder("#A3DAFF", "#22576D", 60f, false, 10));
Round corners + solid border? Check
button.setBackground(createBorder("#FEC9D2", "#22576D", 60f, false, 0));
Round corners + no border? Check
button.setBackground(createBorder("#ffffff", "#719861", 0f, true, 30));
No round corner + dashed border? Check

We can use the same concept to create more complicated UI like putting images into recycler list, putting editTexts into scroll view, and more.

All views are, essentially, Java objects that we can modify. Anything we can describe in xml, we can create it in code.

Complete Source Code

public class MainActivity extends AppCompatActivity {


public GradientDrawable createBorder(String backgroundColor,
String borderColor,
Float borderRadius,
boolean dashed, int width){

//use a GradientDrawable with only one color set, to make it a solid color
GradientDrawable border = new GradientDrawable();
border.setShape(GradientDrawable.RECTANGLE);

if (backgroundColor != null){
border.setColor(Color.parseColor(backgroundColor)); // background color
}

if (borderRadius != null){
border.setCornerRadius(borderRadius);
}

if (dashed){
border.setStroke(width, Color.parseColor(borderColor), 100,40);
}else{
border.setStroke(width, Color.parseColor(borderColor));
}

return border;
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);



final ConstraintLayout layout = new ConstraintLayout(this);
layout.setId(ViewCompat.generateViewId());
ConstraintLayout.LayoutParams layoutParamsparent = new ConstraintLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
layout.setLayoutParams(layoutParamsparent);


Button button = new Button(this);
button.setId(ViewCompat.generateViewId());


button.setBackground(createBorder("#FEC9D2", "#22576D", 60f, false, 0));
button.setText("CLICK ME!");
button.setTextSize(15f);
button.setTextColor(Color.parseColor("#ffffff"));


ConstraintLayout.LayoutParams layoutParams = new ConstraintLayout.LayoutParams(600, 600);
button.setLayoutParams(layoutParams);



// add to parent view
layout.addView(button);


// NOTICE you MUST set constraint AFTER add view to parent
ConstraintSet constraint = new ConstraintSet();
constraint.clone(layout); // MUST clone

constraint.connect(button.getId(), ConstraintSet.LEFT,
layout.getId(), ConstraintSet.LEFT);
constraint.connect(button.getId(), ConstraintSet.TOP,
layout.getId(), ConstraintSet.TOP);
constraint.connect(button.getId(), ConstraintSet.BOTTOM,
layout.getId(), ConstraintSet.BOTTOM);
constraint.connect(button.getId(), ConstraintSet.RIGHT,
layout.getId(), ConstraintSet.RIGHT);


constraint.applyTo(layout);


setContentView(layout);

}
}

--

--

Yat Man, Wong

Android developer, problem solver, real man in training