Andreas Andreas - 3 years ago 112
Android Question

Constraint layout with child constraint layouts not expanding horizontally

I'm trying to build a rectangular view (spanning the width of the screen) which is split horizontally into three blocks, and I am doing so using a constraint layout. The left and right blocks have a specified width, but the centre block should expand to fit the remaining space. Because I require that the left-most block have a different background colour to the parent, I have grouped its children into another constraint layout. I have given the layout XML below:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_marginTop="8dp"
android:background="#10000000"
xmlns:tools="http://schemas.android.com/tools">

<android.support.constraint.ConstraintLayout
android:id="@+id/abc"
android:layout_width="100dp"
android:layout_height="match_parent"
android:background="#50000000"
android:layout_marginEnd="8dp"
android:padding="2dp"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/jkl"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">

<TextView
android:id="@+id/def"
android:gravity="start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ffffffff"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/ghi"
android:gravity="start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#ffffffff"
android:textSize="14sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/def" />

</android.support.constraint.ConstraintLayout>

<!-- This should expand to fit the remainder of the screen width -->
<android.support.constraint.ConstraintLayout
android:id="@+id/jkl"
android:layout_width="0dp"
android:layout_height="match_parent"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintEnd_toStartOf="@+id/stu"
app:layout_constraintStart_toEndOf="@+id/abc">

<TextView
android:id="@+id/mno"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top|start"
android:textColor="#a0000000"
android:textSize="14sp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/pqr"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top|start"
android:textColor="#a0000000"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/mno" />

</android.support.constraint.ConstraintLayout>

<ImageButton
android:id="@+id/stu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:src="@drawable/x"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/jkl"
app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>


Unfortunately, the middle block does not expand to fit the remaining width of the screen. Does anyone know how to achieve this? Am I doing anything fundamentally wrong here?




Edit:

Based on Ben P.'s response it may be that the XML above is fine, but the layouts in which this layout is contained are at fault. I have therefore provided them below.

This layout XML is the parent of the XML above:

<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/parent_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.Parent">

</android.support.v7.widget.RecyclerView>


This layout XML is the parent of the RecyclerView XML above:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.MainActivity">

<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<include layout="@layout/bottom_navigation" />

</android.support.constraint.ConstraintLayout>





Edit 2:

As requested I've provided the complete code base:

MainActivity.java:

public class MainActivity extends AppCompatActivity {

private FragmentManager mFragmentManager;

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

mFragmentManager = getFragmentManager();

FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, new Foo());
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
}


Foo.java:

public class Foo extends Fragment {
private String[] mDataset;

private OnFragmentInteractionListener mListener;

private RecyclerView mRecyclerView;
private RecyclerView.Adapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;

public Foo() {
// Required empty public constructor
}

/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* @param param1 Parameter 1.
* @param param2 Parameter 2.
* @return A new instance of fragment Foo.
*/
// TODO: Rename and change types and number of parameters
public static Foo newInstance(String param1, String param2) {
Foo fragment = new Foo();
return fragment;
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
initDataset();
}

private void initDataset() {
mDataset = new String[1];
mDataset[0] = "Hello World";
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_foo, container, false);

mRecyclerView = view.findViewById(R.id.foo_view);
mRecyclerView.setHasFixedSize(true);

mLayoutManager = new LinearLayoutManager(getActivity());
mRecyclerView.setLayoutManager(mLayoutManager);

mAdapter = new FooAdapter(mDataset);
mRecyclerView.setAdapter(mAdapter);

return view;
}

// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}

@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}

@Override
public void onDetach() {
super.onDetach();
mListener = null;
}

/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
}


FooAdapter.java:

public class FooAdapter extends RecyclerView.Adapter<FooAdapter.ViewHolder> {

private String[] mDataset;

public static class ViewHolder extends RecyclerView.ViewHolder {
private TextView mFoofoo;
private TextView mBarbar;

public ViewHolder(View itemView) {
super(itemView);

mFoofoo = itemView.findViewById(R.id.foofoo);
mBarbar = itemView.findViewById(R.id.barbar);
}

public TextView getFoofoo() {
return mFoo;
}

public TextView getBarbar() {
return mBar;
}
}

public FooAdapter(String[] mDataset) {
this.mDataset = mDataset;
}

@Override
public FooAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.bar, parent, false);

return new ViewHolder(view);
}

@Override
public void onBindViewHolder(FooAdapter.ViewHolder holder, int position) {
String text = mDataset[position];
holder.getBarbar().setText(text);
holder.getFoofoo().setText(text);
}

@Override
public int getItemCount() {
return mDataset.length;
}
}


activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.MainActivity">

<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#eaff73"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>


fragment.xml:

<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/foo_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#8df78b"
tools:context="com.example.Foo">

</android.support.v7.widget.RecyclerView>


bar.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="104dp"
android:layout_marginTop="4dp"
android:background="#10000000"
xmlns:tools="http://schemas.android.com/tools">

<ImageView
android:id="@+id/backg"
android:background="#50000000"
android:layout_width="104dp"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/foofoo"
android:gravity="start"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:textColor="#ffffffff"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

<ImageButton
android:id="@+id/action"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_width="wrap_content"
android:src="@drawable/pic"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/barbar"
app:layout_constraintTop_toTopOf="parent" />

<TextView
android:id="@+id/barbar"
android:autoSizeMaxTextSize="24sp"
android:autoSizeMinTextSize="14dp"
android:autoSizeStepGranularity="2dp"
android:autoSizeTextType="uniform"
android:background="#ef0505"
android:gravity="top|start"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingStart="8dp"
android:textColor="#a0000000"
android:textSize="24sp"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@id/action"
app:layout_constraintStart_toEndOf="@id/backg"
app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>


Any help is greatly appreciated!

Answer Source

I do not know why this works, but it does.

In bar.xml, replace your outer ConstraintLayout's height with wrap_content (instead of 104dp).

Also in bar.xml, replace your ImageView's height with 104dp (instead of 0dp).

This has the net effect of keeping your layout's design the same... it just has the ConstraintLayout's height defined by the ImageView's height, instead of the other way around.

If I had to guess at why this makes a difference, I'd assume that there's some quirk in the way LinearLayoutManager measures its children, and that when it sees a fixed height it ignores the match_parent width. But that's wild speculation.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download