Фрагмент автозаполнения в Android Place: невозможно установить текст

Google недавно обновил свой Places SDK для Android, поэтому теперь я тоже обновляю свой код. Я пытаюсь использовать AutocompleteSupportFragment чтобы позволить пользователю установить свой адрес.

Это мой код:

mAddressEditText = (AutocompleteSupportFragment) getSupportFragmentManager().findFragmentById(R.id.address);
mAddressEditText.setPlaceFields(Arrays.asList(Place.Field.ADDRESS, Place.Field.LAT_LNG));
mAddressEditText.setHint("Address");
mAddressEditText.setText("Test1");                      // Works fine at the beginning, disappears after selecting a place and shows only the hint
mAddressEditText.setOnPlaceSelectedListener(new PlaceSelectionListener() {
    @Override
    public void onPlaceSelected(Place place) {
        Log.d(TAG, "Place Selected");
        // Other Stuff
        mAddressEditText.setText("Test2");              // Doesn't Work, all I can see is the hint
        mAddressEditText.setText(place.getAddress());   // Doesn't Work, all I can see is the hint
    }

    @Override
    public void onError(Status status) {
        Log.e(TAG, "An error occurred: " + status);
        invalidAddressDialog.show();
    }
});

В предыдущем SDK фрагмент автоматически устанавливал текст на выбранный адрес. Это не работает в новом SDK (не уверен, намеренно это или нет). Поэтому я пытаюсь установить его вручную. Как вы можете видеть в комментариях в моем коде, используя setText отлично работает вне слушателей. Внутри слушателя нет.

Я что-то не так делаю или это ошибка?

10 ответов

setText создает для меня ту же проблему - я думаю, что это ошибка. Однако я нашел немного работы с подсказкой. В вашем onPlaceSelected вы можете поставить следующее:

Джава

EditText etPlace = (EditText) autocompleteFragment.getView().findViewById(R.id.places_autocomplete_search_input);
etPlace.setHint(place.getAddress())

Котлин

val etPlace = autocompleteFragment.view?.findViewById(R.id.places_autocomplete_search_input) as EditText
etPlace.hint = place.address

Это код, который я использую, и он работает отлично.

Внесите некоторые изменения в build.gradle (уровень приложения)

Добавьте это в build.gradle:

android{
   ...
   ext {
        googlePlayServicesVersion = "15.0.1"
   }
}

Добавьте эти зависимости:

dependencies {
    ...
    //Also if you're using any firebase dependencies make sure that the are up to date
    implementation 'com.google.android.gms:play-services-places:16.0.0'
    implementation 'com.google.android.libraries.places:places:1.1.0'
}
apply plugin: 'com.google.gms.google-services'

В формате XML:

<fragment
                android:id="@+id/autocomplete_fragment"
                android:name="com.google.android.libraries.places.widget.AutocompleteSupportFragment"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />

Код в Activity:

private void initGooglePlacesApi() {
        // Initialize Places.
        Places.initialize(getApplicationContext(), "YOUR_API_KEY");
        // Create a new Places client instance.
        PlacesClient placesClient = Places.createClient(getApplicationContext());

        // Initialize the AutocompleteSupportFragment.
        AutocompleteSupportFragment autocompleteFragment = (AutocompleteSupportFragment)
                getSupportFragmentManager().findFragmentById(R.id.autocomplete_fragment);

        autocompleteFragment.setHint(getString(R.string.select_location_search_bar));
//        autocompleteFragment.setLocationRestriction(RectangularBounds.newInstance(
//                new LatLng(34.7006096, 19.2477876),
//                new LatLng(41.7488862, 29.7296986))); //Greece bounds
        autocompleteFragment.setCountry("gr");


        // Specify the types of place data to return.
        autocompleteFragment.setPlaceFields(Arrays.asList(Place.Field.ADDRESS, Place.Field.ADDRESS_COMPONENTS));
        autocompleteFragment.setTypeFilter(TypeFilter.ADDRESS);


        // Set up a PlaceSelectionListener to handle the response.
        autocompleteFragment.setOnPlaceSelectedListener(new PlaceSelectionListener() {
            @Override
            public void onPlaceSelected(Place place) {
                if(place.getAddressComponents().asList().get(0).getTypes().get(0).equalsIgnoreCase("route")){
                    binding.textViewLocation.setText(place.getAddress()); //Works well
                    location = place.getAddress();

                }else{ //If user does not choose a specific place.
                    AndroidUtils.vibratePhone(getApplication(), 200);
                    TastyToast.makeText(getApplicationContext(),
                            getString(R.string.choose_an_address), TastyToast.DEFAULT, TastyToast.CONFUSING);
                }

                Log.i(TAG, "Place: " + place.getAddressComponents().asList().get(0).getTypes().get(0) + ", " + place.getId() + ", " + place.getAddress());
            }

            @Override
            public void onError(Status status) {
                Log.i(TAG, "An error occurred: " + status);
            }
        });
    }

Я нашел довольно простое решение... просто немного задержите момент, когда вы устанавливаете текст в EditText. Итак, в вашем PlaceSelectionListener просто сделайте это следующим образом:

Handler().postDelayed({
    mAddressEditText.setText(place.getAddress());
}, 300)

PS: это код kotlin, но он почти похож на Java

Установив «NAME» в setPlaceFields, выбранный адрес автоматически отображается во фрагменте:

      AutocompleteSupportFragment autocompleteFragment = (AutocompleteSupportFragment)
    getSupportFragmentManager().findFragmentById(R.id.autocomplete_fragment);


// Set "Place.Field.NAME" as below here to show the selected item //
autocompleteFragment.setPlaceFields(Arrays.asList(Place.Field.ID, **Place.Field.NAME**,Place.Field.ADDRESS,Place.Field.LAT_LNG));


autocompleteFragment.setOnPlaceSelectedListener(new PlaceSelectionListener() {
    @Override
    public void onPlaceSelected(@NonNull Place place) {
        // TODO: Get info about the selected place. }
 
    @Override
    public void onError(@NonNull Status status) {
        // TODO: Handle the error. }
});

Решение от Матиаса работает, но находится в котлине. Ниже представлена ​​такая же реализация на Java.

 @Override
 public void onPlaceSelected(Place place) {


                        String name =  place.getName()+", "+place.getAddress();
                      
                        new Handler().postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                autocompleteFragmentDestination.setText(name);
                            }
                        },300);

                    }

РЕДАКТИРОВАТЬ: вот мой обновленный ответ

# When you are using AutocompleteSupportFragment or AutocompleteActivity
# in Fragments, do this:
public class YourFragment extends Fragment {
/.../
@Override
public void onActivityResult (int requestCode,int resultCode,
 @Nullable Intent data){
 # AUTOCOMPLETE_REQUEST_CODE is just a unique constant, define it
 if (requestCode == AUTOCOMPLETE_REQUEST_CODE) {
   if (resultCode == AutocompleteActivity.RESULT_OK) {
    Place place = Autocomplete.getPlaceFromIntent(data);
    // when resultcode is RESULT_OK
    mAddressEditText.setText(place.getName());
    // Notice this line, update your editText up here
    }else if (resultCode == AutocompleteActivity.RESULT_ERROR) {
    Status status = Autocomplete.getStatusFromIntent(data);
    // Handle error
    } else if (resultCode == AutocompleteActivity.RESULT_CANCELED) {
    // Handle results if canceled
    }
  super.onActivityResult(requestCode, resultCode, data);
 }
}
/.../
}
# If you are extending AppCompatActivity, you might want to do this
# ONLY when you are already doing something in onActivityResult
public class YourActivity extends AppCompatActivity{
/.../
@Override
public void onActivityResult (int requestCode,int resultCode,@Nullable Intent data){
 # your logic here.
 /.../ 
  # if you are already overriding onActivityResult, 
  # do not forget to put this line
  super.onActivityResult(requestCode, resultCode, data);
 }
/.../
}

У меня тоже была проблема. Оказывается, вы должны переопределить это и реализовать его в любом случае, будь то с помощью AutocompleteSupportFragment или же AutocompleteActivity если вы работаете во фрагментах.

Если вы используете AppCompatActivity вам не нужно это реализовывать, но если вы уже переигрываете onActivityResult чтобы что-то сделать, не забудьте вызвать базовый метод super.onActivityResult

Простой. Используйте Spannable строку, чтобы установить цвет!

          val autocompleteFragment = childFragmentManager.findFragmentById(R.id.location_filter_autocomplete) as AutocompleteSupportFragment
    val text = SpannableString("Enter a location")
    text.setSpan(ForegroundColorSpan(Color.BLACK), 0, text.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
    autocompleteFragment.setHint(text)

Сделайте то же самое внутри onPlaceSelectedListener()

Я считаю, что это ошибка, потому что не имеет смысла так работать. То, что работает, это установить другие тексты автозаполнения, но не свой собственный. Это должно быть ошибкой.

Я попробовал решение CacheMeOutside, но оно вообще не сработало. Итак, я решил попробовать решение Матиаса, и оно сработало, потому что текст фактически устанавливается, а затем по какой-то причине сразу же удаляется. Небольшая задержка исправляет. Задержка может составлять всего 1 миллисекунду.

Если мое решение вам не подходит, вы можете попробовать поэкспериментировать с задержкой. Также кажется, что он не останавливает рендеринг представления, поэтому вы можете установить любое время, когда захотите.

private lateinit var autocomplete: AutocompleteSupportFragment

        override fun onPlaceSelected(place: Place) {
            Timer("SetAddress", false).schedule(1) {
                autocomplete.setText(place.address)
            }
        }

Фрагмент кода на Kotlin. Если ваш код написан на Java, просто найдите какой-нибудь инструмент, чтобы отложить выполнение кода на некоторое время.

Получить ссылку на AutoCompleteFragment, а затем установить текст для фрагмента автозаполнения, например

autoCompleteFragment.setText("Address")

для справки вы можете посмотреть документацию

https://developers.google.com/android/reference/com/google/android/gms/location/places/ui/PlaceAutocompleteFragment

Другие вопросы по тегам