7
val margin = 8
ConstraintSet().apply {
  connect(anId, ConstraintSet.START, anotherId, ConstraintSet.START, margin)
}

Is margin applied as pixels or density-dependent pixels?

Various articles scattered around the web and apocryphal knowledge seem not to agree on one or the other. I'm looking for official documentation or source evidence.

Ola Ström
  • 4,136
  • 5
  • 22
  • 41
nukeforum
  • 1,284
  • 3
  • 16
  • 38

4 Answers4

4

The margin parameter should be supplied in pixels.

I ended up tracing the ConstraintSet.applyTo(ConstraintLayout) down to ConstraintSet.applyToInternal(ConstraintLayout) and found that here, each child of a ConstraintLayout has its LayoutParams copied and passed to ConstraintSet.Constraint.applyTo(LayoutParams). Which then copies all the parameters (without modification) from the ConstraintSet.Constraint into the LayoutParams and then assigns the modified LayoutParams back to the child. I think this is concrete evidence that the supplied margin parameters should follow the LayoutParams rules.

in ConstraintSet.java

    this.applyToInternal(constraintLayout);
    constraintLayout.setConstraintSet((ConstraintSet)null); }

at applyToInternal(ConstraintLayout) declaration

    int count = constraintLayout.getChildCount();

    //...

    LayoutParams param;
    for(int i = 0; i < count; ++i) {
        View view = constraintLayout.getChildAt(i);
        //...
        param = (LayoutParams)view.getLayoutParams();
        constraint.applyTo(param);
        view.setLayoutParams(param);
        //...
    }

    //...

at applyTo(LayoutParams) declaration

    //...
    param.leftMargin = this.leftMargin;
    param.rightMargin = this.rightMargin;
    param.topMargin = this.topMargin;
    param.bottomMargin = this.bottomMargin;
    //...
nukeforum
  • 1,284
  • 3
  • 16
  • 38
  • I'll be leaving this open just in case there's any other input. Otherwise, I'll accept my answer tomorrow. – nukeforum May 22 '19 at 22:51
2

The margin, padding, constraint ... APIs all work in pixels. If you have DPs, you have to convert those to pixels first.

Francesc
  • 25,014
  • 10
  • 66
  • 84
2

In general, Java/Kotlin work with px. There's no official documentation that I can find (or anything in the source code of ConstraintSet or Constraint) that says this outright.

If you look at the source code for ViewGroup.MarginLayoutParams, however, you will find this code:

public MarginLayoutParams(Context c, AttributeSet attrs) {
    ...
    int margin = a.getDimensionPixelSize(
                com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1);
    if (margin >= 0) {
        leftMargin = margin;
        topMargin = margin;
        rightMargin= margin;
        bottomMargin = margin;
    }
    ...
}

It should be plain to see (from the call to getDimensionPixelSize()) that the units for margin here are px. I think it is reasonable to say that MarginLayoutParams is the "original" source of truth for margins, and I would generally expect things that emulate this behavior (like ConstraintSet) to follow the same pattern.

Note that ConstraintLayout.LayoutParams is a subclass of ViewGroup.MarginLayoutParams.

Ben P.
  • 52,661
  • 6
  • 95
  • 123
  • I think you've arrived at the correct answer, however the `LayoutParams` of `ConstraintLayout` don't actually dictate anything about the functionality of `connect`. – nukeforum May 22 '19 at 22:03
  • Sure, but it would be very strange for two different pieces of one component to work two different ways. Considering that source code and experimentation show that it uses `px`, I'm not really sure what else would convince you. – Ben P. May 22 '19 at 22:21
  • I actually ended up tracing the `ConstraintSet.applyTo(ConstraintLayout)` down to `ConstraintSet.applyToInternal(ConstraintLayout)` and found that here, each child of a `ConstraintLayout` has its `LayoutParams` copied and passed to `ConstraintSet.Constraint.applyTo(LayoutParams)`. Which then copies all the parameters (without modification) from the `ConstraintSet.Constraint` into the `LayoutParams` and then assigns the modified `LayoutParams` back to the child. I think this is concrete evidence that the supplied `margin` parameters should follow the `LayoutParams` rules. – nukeforum May 22 '19 at 22:36
1

It needs to be a pixel. Use below function to convert dp to px.

public int dpToPx(int dp, Context context) {

        float density = context.getResources()
                .getDisplayMetrics()
                .density;
        return Math.round((float) dp * density);
}
Sajith
  • 713
  • 6
  • 21
  • Can you please link to the documentation that states this? – nukeforum May 22 '19 at 21:26
  • As outlined in the chapter entitled Designing an Android User Interface using the Graphical Layout Tool, when setting sizes and positions in user interface layouts it is better to use density independent pixels (dp) rather than pixels (px). In order to set a position using dp it is necessary to convert a dp value to a px value at runtime, taking into consideration the density of the device display . – Sajith May 22 '19 at 21:35
  • That tutorial explicitly indicates that the margin supplied should be dp and not pixels. Which is the opposite of what you're saying in your answer – nukeforum May 22 '19 at 21:38
  • In order to set a position using dp it is necessary to convert a dp value to a px value at runtime – Sajith May 22 '19 at 21:45