I'm doing on a questionnaire which means that one fragment is representing one question. A question contains some fields which can be of different types, e.g. text field, rated field...
These fields are rendered as rows in ListView in the fragment. They are implemented as compound views because I need doing some other works with fields.
The problem is when I rendering the rows by the custom adapter, the InflateException exception is thrown:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: cz.rusna.clinical_questionnaire, PID: 9141
android.view.InflateException: <merge /> can be used only with a valid ViewGroup root and attachToRoot=true
at android.view.LayoutInflater.inflate(LayoutInflater.java:475)
at android.view.LayoutInflater.inflate(LayoutInflater.java:414)
at cz.rusna.clinical_questionnaire.views.adapters.TestRunnerAdapter.getView(TestRunnerAdapter.java:80)
at android.widget.AbsListView.obtainView(AbsListView.java:2347)
at android.widget.ListView.makeAndAddView(ListView.java:1864)
at android.widget.ListView.fillDown(ListView.java:698)
at android.widget.ListView.fillFromTop(ListView.java:759)
at android.widget.ListView.layoutChildren(ListView.java:1673)
at android.widget.AbsListView.onLayout(AbsListView.java:2151)
at android.view.View.layout(View.java:15671)
at android.view.ViewGroup.layout(ViewGroup.java:5038)
at android.support.constraint.ConstraintLayout.onLayout(ConstraintLayout.java:1915)
at android.view.View.layout(View.java:15671)
at android.view.ViewGroup.layout(ViewGroup.java:5038)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:579)
at android.widget.FrameLayout.onLayout(FrameLayout.java:514)
at android.view.View.layout(View.java:15671)
at android.view.ViewGroup.layout(ViewGroup.java:5038)
at android.support.v7.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:443)
at android.view.View.layout(View.java:15671)
at android.view.ViewGroup.layout(ViewGroup.java:5038)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:579)
at android.widget.FrameLayout.onLayout(FrameLayout.java:514)
at android.view.View.layout(View.java:15671)
at android.view.ViewGroup.layout(ViewGroup.java:5038)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1703)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1557)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1466)
at android.view.View.layout(View.java:15671)
at android.view.ViewGroup.layout(ViewGroup.java:5038)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:579)
at android.widget.FrameLayout.onLayout(FrameLayout.java:514)
at android.view.View.layout(View.java:15671)
at android.view.ViewGroup.layout(ViewGroup.java:5038)
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2086)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1843)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1061)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5885)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767)
at android.view.Choreographer.doCallbacks(Choreographer.java:580)
at android.view.Choreographer.doFrame(Choreographer.java:550)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
The activity xms looks like:
<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:id="@+id/activity_runner_test"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".views.activities.TestRunnerActivity">
<FrameLayout
android:id="@+id/test_runner_frame_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.constraint.ConstraintLayout>
The fragment layout looks like:
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/fragment_test_runner"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".views.fragments.TestRunnerFragment">
</ListView>
The fragment onCreateView looks like:
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
ListView listView = (ListView) inflater.inflate(R.layout.fragment_test_runner, container, false);
listView.setAdapter(new TestRunnerAdapter(mCurrent.getQuestionFields(), getContext()));
return listView;
}
The adapter code looks like:
public class TestRunnerAdapter extends BaseAdapter {
private List<QuestionField> mQuestionFields;
private Context mContext;
public TestRunnerAdapter(List<QuestionField> fields, Context context) {
mQuestionFields = fields;
mContext = context;
}
@Override
public int getCount() {
return mQuestionFields.size();
}
@Override
public QuestionField getItem(int position) {
return mQuestionFields.get(position);
}
@Override
public int getItemViewType(int position) {
QuestionField questionField = mQuestionFields.get(position);
if (questionField.getFieldType() == FieldType.TEXT) {
return 0;
} else if (questionField.getFieldType() == FieldType.RATED) {
return 1;
} else {
return 2;
}
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public int getViewTypeCount() {
return 3;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = convertView;
int type = getItemViewType(position);
QuestionField questionField = mQuestionFields.get(position);
if (view == null) {
LayoutInflater inflater = LayoutInflater.from(mContext);
if (type == 0) {
view = inflater.inflate(R.layout.text_field, parent, false);
TextFieldData textFieldData = (TextFieldData) questionField.getFieldData();
((TextFieldView)view).setTextField(textFieldData.getText());
} else if (type == 1) {
view = inflater.inflate(R.layout.rated_field, parent, false);
RatedFieldData ratedFieldData = (RatedFieldData) questionField.getFieldData();
((RatedFieldView)view).setRatedField(ratedFieldData);
} else {
view = inflater.inflate(R.layout.unrated_field, parent, false);
}
}
return view;
}
}
The example of a compound view.
public class TextFieldView extends LinearLayout {
private TextView mTextView;
public TextFieldView(Context context) {
this(context, null);
}
public TextFieldView(Context context, AttributeSet attributes) {
this(context, attributes, 0);
}
public TextFieldView(Context context, AttributeSet attributes, int defStyleAttr) {
super(context, attributes, defStyleAttr);
init(context);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public TextFieldView(Context context, AttributeSet attributes, int defStyleAttr, int defStyleRes) {
super(context, attributes, defStyleAttr, defStyleRes);
init(context);
}
private void init(Context context) {
LayoutInflater.from(context).inflate(R.layout.text_field, this);
mTextView = (TextView) findViewById(R.id.text);
}
public void setTextField(String text) {
mTextView.setText(text);
}
}
And its xml:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="18dp"
android:gravity="center_vertical" />
</merge>
The code is failing in the getView method in the adapter class when it rendering these views on the line:
view = inflater.inflate(R.layout.text_field, parent, false);