I am developing an Android app that I am pushing out to testers using Fabric/Crashlytics.
I have thoroughly tested my app on 2 physical devices myself:
- Samsung Galaxy Tab Pro running 4.4.2 (API 19)
- LG G3 running 5.0.1 (API 21)
I have recently pushed out a version of my app to a single tester. Her device is:
- Samsung Galaxy S4 running 5.0.1 (API 21)
I have a login/register Activity
/ Fragment
series that she is trying to use. I am getting a crash report through Crashlytics, which contains the following information:
Here's an excerpt from the log:
Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.company.app/com.company.app.LoginActivity}: android.view.InflateException: Binary XML file line #70: Error inflating class fragment
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2693)
... other lines
Caused by android.view.InflateException: Binary XML file line #70: Error inflating class fragment
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:770)
... other lines
at android.support.v7.app.AppCompatDelegateImplV7.setContentView(AppCompatDelegateImplV7.java:256)
at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:109)
at com.company.app.LoginActivity.onCreate(LoginActivity.java:25)
at android.app.Activity.performCreate(Activity.java:6289)
... other lines
Caused by java.lang.IllegalStateException: Fragment com.company.app.LoginFragment did not create a view.
at android.app.FragmentManagerImpl.onCreateView(FragmentManager.java:2160)
at android.app.Activity.onCreateView(Activity.java:5610)
at android.support.v4.app.BaseFragmentActivityHoneycomb.onCreateView(BaseFragmentActivityHoneycomb.java:34)
at android.support.v4.app.FragmentActivity.onCreateView(FragmentActivity.java:79)
... other lines
at android.support.v7.app.AppCompatDelegateImplV7.setContentView(AppCompatDelegateImplV7.java:256)
at android.support.v7.app.AppCompatActivity.setContentView(AppCompatActivity.java:109)
at com.company.app.LoginActivity.onCreate(LoginActivity.java:25)
at android.app.Activity.performCreate(Activity.java:6289)
... other lines
Here is a condensed version of my LoginActivity and LoginFragment files:
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout>
<LinearLayout>
<android.support.v7.widget.Toolbar>
<LinearLayout>
<TextView />
<ImageButton />
</LinearLayout>
</android.support.v7.widget.Toolbar>
<FrameLayout>
<fragment
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.company.app.LoginFragment"
tools:layout="@layout/fragment_login" />
<View />
</FrameLayout>
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
LoginActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// This next line causes the error
setContentView(R.layout.activity_login);
ImageButton infoButton = (ImageButton)findViewById(R.id.info_button);
infoButton.setColorFilter(ContextCompat.getColor(this, R.color.colorPrimary), PorterDuff.Mode.SRC_ATOP);
infoButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mUserCreationFragment != null) {
mUserCreationFragment.showInfo();
} else if (mEnterCodesFragment != null) {
mEnterCodesFragment.showInfo();
} else if (mLoginFragment != null) {
mLoginFragment.showInfo();
}
}
});
mToolbar = (Toolbar)findViewById(R.id.toolbar);
mTitleLabel = (TextView)findViewById(R.id.title_label);
mTitleLabel.setText(getString(R.string.login_string));
setSupportActionBar(mToolbar);
}
fragment_login.xml
<com.company.app.SlidingFrameLayout>
<ScrollView>
<LinearLayout>
<TextView />
<Button />
<android.support.design.widget.TextInputLayout>
<EditText />
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout>
<EditText />
</android.support.design.widget.TextInputLayout>
<Button />
<Button />
</LinearLayout>
</ScrollView>
</com.company.app.SlidingFrameLayout>
Line 70 in fragment_login.xml
Line 69 <android.support.design.widget.TextInputLayout
Line 70 android:layout_height="wrap_content"
Line 71 android:layout_width="match_parent"
Line 72 >
Line 73 <EditText
Line 74 android:id="@+id/password"
Line 75 android:hint="@string/prompt_password"
Line 76 android:imeOptions="actionDone"
Line 77 android:inputType="textPassword"
Line 78 android:layout_height="wrap_content"
Line 79 android:layout_width="match_parent"
Line 80 android:maxLines="1"
Line 71 android:singleLine="true"
Line 82 android:textSize="16sp"
Line 83
Line 84 />
Line 85
Line 86 </android.support.design.widget.TextInputLayout>
LoginFragment.java
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_login, container, false);
mPasswordView = (EditText)rootView.findViewById(R.id.password);
Button registerButton = (Button)rootView.findViewById(R.id.register_button);
TextView registerLabel = (TextView)rootView.findViewById(R.id.register_label);
mSignInButton = (Button)rootView.findViewById(R.id.sign_in_button);
mUserNameView = (EditText)rootView.findViewById(R.id.user_name);
LoginActivity activity = (LoginActivity)getActivity();
activity.setLoginFragment(this);
checkIfComplete();
mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
if (id == EditorInfo.IME_ACTION_DONE || id == EditorInfo.IME_NULL) {
if (mComplete) {
beginLogin();
return true;
}
}
return false;
}
});
mPasswordView.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
checkIfComplete();
}
@Override
public void afterTextChanged(Editable s) {}
});
registerButton.setBackgroundColor(ContextCompat.getColor(getActivity(), R.color.colorPrimaryDark));
registerButton.setTextColor(ContextCompat.getColor(getActivity(), R.color.colorTextEnabled));
registerButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EnterCodesFragment fragment = new EnterCodesFragment();
getFragmentManager()
.beginTransaction()
.setCustomAnimations(R.animator.slide_in_from_right, R.animator.slide_out_to_left, R.animator.slide_in_from_left, R.animator.slide_out_to_right)
.replace(R.id.container, fragment, getString(R.string.basic_information_title_string))
.addToBackStack(getString(R.string.enter_codes_fragment_title))
.commit();
}
});
registerLabel.setTextColor(ContextCompat.getColor(getActivity(), R.color.colorTextDisabled));
mSignInButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
beginLogin();
}
});
Button forgotPasswordButton = (Button)rootView.findViewById(R.id.forgot_password_button);
forgotPasswordButton.setPaintFlags(forgotPasswordButton.getPaintFlags() | Paint.UNDERLINE_TEXT_FLAG);
forgotPasswordButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final View view = View.inflate(getActivity(), R.layout.reset_password_dialog, null);
final EightyPercentDialog dialog = new EightyPercentDialog(getActivity());
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(view);
final EditText emailEditText = (EditText)view.findViewById(R.id.email_edit_text);
Button cancelButton = (Button)view.findViewById(R.id.cancel_button);
Button okButton = (Button)view.findViewById(R.id.ok_button);
cancelButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dialog.dismiss();
}
});
okButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String email = emailEditText.getText().toString();
dialog.dismiss();
sendPasswordResetEmail(email);
}
});
dialog.show();
}
});
mUserNameView.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
checkIfComplete();
}
@Override
public void afterTextChanged(Editable s) {}
});
return rootView;
}
I've looked at several other posts on here related to InflateException
, but none of them seem to be applicable to my situation.
For instance:
- This question and answer seems to be highlighting a problem with package naming, which isn't an issue in my project.
- This question and answer is dealing with an issue where the order of lines of code is incorrect, which isn't an issue in my project.
- I had a look at this question in an effort to determine whether my issue might be related to using an
AppCompatActivity
, but so far I have not seen anything there that would be problematic.
So, basically, what's confusing me is that I do not have this problem at all on either of my devices, and I don't know Android well enough to see anything on her system (also a Samsung phone running 5.0.1, like mine) that would be problematic. Is there something I'm missing? How can I track down what the actual problem is?