I have an AutoCompleteTextView on a TextInputLayout on a ConstraintLayout on a FrameLayout. And i need to add a Button beside it, so i changed the width of the TextInputLayout to 0dp (previously match_parent). But, suddenly the AutoCompleteTextView don't work. It worked before and i checked it, it suddently don't work when the width is 0dp.
The error
java.lang.NullPointerException: Attempt to invoke virtual method 'int java.util.ArrayList.size()' on a null object reference
at com.its.vcss.utils.AutocompleteRetrieval.getCount(AutocompleteRetrieval.java:62)
at com.google.android.material.textfield.MaterialAutoCompleteTextView.measureContentWidth(MaterialAutoCompleteTextView.java:355)
at com.google.android.material.textfield.MaterialAutoCompleteTextView.onMeasure(MaterialAutoCompleteTextView.java:332)
at android.view.View.measure(View.java:27720)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7352)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.view.View.measure(View.java:27720)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7352)
at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1552)
at android.widget.LinearLayout.measureVertical(LinearLayout.java:842)
at android.widget.LinearLayout.onMeasure(LinearLayout.java:721)
at com.google.android.material.textfield.TextInputLayout.onMeasure(TextInputLayout.java:3061)
at android.view.View.measure(View.java:27720)
at androidx.constraintlayout.widget.ConstraintLayout$Measurer.measure(ConstraintLayout.java:811)
at androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.measure(BasicMeasure.java:466)
at androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.measureChildren(BasicMeasure.java:134)
at androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.solverMeasure(BasicMeasure.java:278)
at androidx.constraintlayout.core.widgets.ConstraintWidgetContainer.measure(ConstraintWidgetContainer.java:120)
at androidx.constraintlayout.widget.ConstraintLayout.resolveSystem(ConstraintLayout.java:1594)
at androidx.constraintlayout.widget.ConstraintLayout.onMeasure(ConstraintLayout.java:1708)
at android.view.View.measure(View.java:27720)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7352)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.view.View.measure(View.java:27720)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7352)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
at android.view.View.measure(View.java:27720)
at androidx.constraintlayout.widget.ConstraintLayout$Measurer.measure(ConstraintLayout.java:811)
at androidx.constraintlayout.core.widgets.ConstraintWidgetContainer.measure(ConstraintWidgetContainer.java:632)
at androidx.constraintlayout.core.widgets.analyzer.Direct.verticalSolvingPass(Direct.java:452)
at androidx.constraintlayout.core.widgets.analyzer.Direct.solveVerticalMatchConstraint(Direct.java:750)
at androidx.constraintlayout.core.widgets.analyzer.Direct.verticalSolvingPass(Direct.java:503)
at androidx.constraintlayout.core.widgets.analyzer.Direct.solvingPass(Direct.java:224)
at androidx.constraintlayout.core.widgets.ConstraintWidgetContainer.layout(ConstraintWidgetContainer.java:693)
at androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.solveLinearSystem(BasicMeasure.java:160)
at androidx.constraintlayout.core.widgets.analyzer.BasicMeasure.solverMeasure(BasicMeasure.java:291)
at androidx.constraintlayout.core.widgets.ConstraintWidgetContainer.measure(ConstraintWidgetContainer.java:120)
at androidx.constraintlayout.widget.ConstraintLayout.resolveSystem(ConstraintLayout.java:1594)
at androidx.constraintlayout.widget.ConstraintLayout.onMeasure(ConstraintLayout.java:1708)
at android.view.View.measure(View.java:27720)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7352)
at androidx.coordinatorlayout.widget.CoordinatorLayout.onMeasureChild(CoordinatorLayout.java:760)
at androidx.coordinatorlayout.widget.CoordinatorLayout.onMeasure(CoordinatorLayout.java:833)
at android.view.View.measure(View.java:27720)
at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:7352)
at android.widget.FrameLayout.onMeasure(FrameLayout.java:194)
navigation_layout.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/vcss_light_green">
<fragment
class="com.google.android.gms.maps.SupportMapFragment"
android:id="@+id/map_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:layout="@layout/onboarding_screen"/>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom">
...
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/il_search"
**android:layout_width="0dp"**
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@id/btn_back"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/btn_search"
android:layout_marginTop="10dp"
android:layout_marginStart="14dp"
android:layout_marginEnd="10dp"
app:shapeAppearance="@style/ShapeAppearance.VCSS.SmallComponent"
app:shapeAppearanceOverlay="@style/ShapeAppearance.VCSS.SmallComponent"
app:startIconDrawable="@drawable/ic_round_search"
app:endIconDrawable="@drawable/ic_round_close"
app:endIconTint="@color/black_40"
app:endIconMode="custom"
app:endIconContentDescription="Bersihkan pencarian"
app:hintEnabled="false"
app:boxBackgroundColor="@color/white"
app:expandedHintEnabled="false">
<AutoCompleteTextView
android:id="@+id/edt_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingEnd="16dp"
android:hint="@string/public_facility_search_hint"
android:paddingBottom="0dp"
android:paddingTop="0dp"
android:inputType="textAutoComplete|textCapWords"
android:maxLines="1"
android:imeOptions="actionSearch"
android:dropDownWidth="200dp"
/>
</com.google.android.material.textfield.TextInputLayout>
<Button
style="@style/Theme.VCSS.Button"
android:id="@+id/btn_search"
android:layout_width="48dp"
android:layout_height="0dp"
android:layout_marginEnd="14dp"
app:cornerRadius="24dp"
android:insetTop="0dp"
android:insetBottom="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/il_search"
app:layout_constraintTop_toTopOf="@id/il_search"
app:layout_constraintBottom_toBottomOf="@id/il_search"
app:icon="@drawable/ic_round_search"/>
...
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="bottom"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/il_search"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="160dp">
<include
android:id="@+id/bottom_sheet"
layout="@layout/navigation_information_bottom_sheet" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</FrameLayout>
NavigationFragment.kt
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
...
sharedViewModel.location.observe(viewLifecycleOwner) {
if (it != null) {
Log.d("Location", it.toString())
binding.edtSearch.setAdapter(AutocompleteRetrieval(requireContext(),
android.R.layout.simple_list_item_1, it))
} else {
val location = LatLng(0, 0)
binding.edtSearch.setAdapter(AutocompleteRetrieval(requireContext(),
android.R.layout.simple_list_item_1, location))
}
}
binding.apply {
...
edtSearch.apply {
setAdapter(
AutocompleteRetrieval(
requireContext(),
android.R.layout.simple_list_item_1,
)
)
addTextChangedListener(object : TextWatcher {
...
})
setOnEditorActionListener { _, i, _ ->
...
}
}
...
}
}
AutoCompleteRetrieval.java
public class AutocompleteRetrieval extends ArrayAdapter implements Filterable {
private static Context context;
private LatLng latLng;
private ArrayList resultList;
private static final String PLACES_API_BASE = "https://maps.googleapis.com/maps/api/place/autocomplete/json?radius=10000&type=establishment&language=id";
public AutocompleteRetrieval(@NonNull Context context, int resource, LatLng latLng) {
super(context, resource);
this.context = context;
this.latLng = latLng;
}
public AutocompleteRetrieval(@NonNull Context context, int resource) {
super(context, resource);
}
public ArrayList getResultList() {
return resultList;
}
public void setResultList(ArrayList resultList) {
this.resultList = resultList;
}
@Override
public int getCount() {
return resultList.size();
}
@Override
public String getItem(int index) {
return (String) resultList.get(index);
}
@Override
public Filter getFilter() {
final Filter filter = new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults filterResults = new FilterResults();
if (constraint != null) {
// Retrieve the autocomplete results.
resultList = autocomplete(constraint.toString(), latLng);
// Assign the data to the FilterResults
filterResults.values = resultList;
filterResults.count = resultList.size();
}
return filterResults;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results != null && results.count > 0) {
notifyDataSetChanged();
} else {
notifyDataSetInvalidated();
}
}
};
return filter;
}
public static ArrayList autocomplete(String input, LatLng latLng)
{
ArrayList resultList = null;
HttpURLConnection conn = null;
String location = "&location="+latLng.lat+","+latLng.lng;
StringBuilder jsonResults = new StringBuilder();
try {
String apiKey = BuildConfig.GOOGLE_MAPS_API_KEY;
StringBuilder sb = new StringBuilder(PLACES_API_BASE + location +"&key="+apiKey);
sb.append("&input=").append(URLEncoder.encode(input, "utf8"));
URL url = new URL(sb.toString());
conn = (HttpURLConnection) url.openConnection();
InputStreamReader in = new InputStreamReader(conn.getInputStream());
int read;
char[] buff = new char[1024];
while ((read = in.read(buff)) != -1) {
jsonResults.append(buff, 0, read);
}
} catch (MalformedURLException e) {
Log.e("AutoComp", "Error processing Places API URL", e);
return resultList;
} catch (IOException e) {
Log.e("AutoComp", "Error connecting to Places API", e);
return resultList;
} finally {
if (conn != null) {
conn.disconnect();
}
}
try {
// Create a JSON object hierarchy from the results
JSONObject jsonObj = new JSONObject(jsonResults.toString());
JSONArray predsJsonArray = jsonObj.getJSONArray("predictions");
// Extract the Place descriptions from the results
resultList = new ArrayList(predsJsonArray.length());
for (int i = 0; i < predsJsonArray.length(); i++) {
resultList.add(predsJsonArray.getJSONObject(i).getString("description"));
}
} catch (JSONException e) {
Log.e("LOG_TAG", "Cannot process JSON results", e);
}
return resultList;
}
}
What causes that problem? I think it has something to do with the width since it is what makes it suddently don't work. The error trace also shows some layout methods being called.
Some important thing, although the Error showed NPE on the array which is the result of the autocomplete, i think it is not the problem. I changed nothing but the width of the TextInputLayout from match_parent to 0dp and it crashes with the error. I also tried using fixed width like 128dp for example, and it works.
Really the problem is that
getCount()in yourAdapterclass can accessresultListbefore it's actually assigned, and invokessize()on it instead of null-checking it and returning 0 by default. That's why you're getting theNullPointerExceptionhere.I'm guessing you're only seeing the problem now because instead of using
match_parent(which just reads the parent View's dimensions), you're using0dpwhich invokes the constraints you've set - they weren't being used before. And now you're using constraints, that View is taking part in the constraint resolution process. You can see from your stacktrace that the final error (at the top) was caused by a chain of layout measurement requests that came from theConstraintLayoutlibrary (lower down = earlier in the call chain).I guess it doesn't matter that the
AutoCompleteTextViewismatch_parentso its contents have no bearing on anything - the constraint resolver is asking for measurements, and that ends up calling yourgetCountmethod earlier than you're expecting, when it's not safe to do so. So really it's just revealing that bug in your Adapter code. Handle your potentially-null list correctly and it should fix the issue!