Подтвердить что ты не робот

Режим Lite в Google Maps вызывает зависание в RecyclerView

У меня есть RecyclerView, который является вертикальным списком прокрутки элементов. Каждый элемент списка содержит карту Google Maps V2 MapView в режиме Lite. Я использую эту новую функцию, которая возвращает растровые изображения вместо полномасштабной карты в качестве замены Google Static Maps API.

MapView требует, чтобы вы вызывали onCreate(), onResume(), onPause(), onDestroy() и т.д. из родительского метода Activity/Fragment. Где подходящее место для вызова из RecyclerView.Adapter и/или RecyclerView.ViewHolder?

Как я могу очистить переработанные MapViews, чтобы память не протекала, сохраняя свободный список?

Google говорит, что режим Lite можно использовать в списках:

... 'lite mode map option, идеально подходит для ситуаций, когда вы хотите предоставить несколько меньших карт или карту, которая настолько мала, что значимое взаимодействие нецелесообразно, например миниатюра в списке.

ListItem.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent">

    <com.google.android.gms.maps.MapView
        android:id="@+id/mapImageView"
        xmlns:map="http://schemas.android.com/apk/res-auto"
        android:layout_width="80dp"
        android:layout_height="100dp"
        map:liteMode="true"
        map:mapType="normal"
        map:cameraZoom="15"/>

<!-- ... -->

</RelativeLayout>

RecyclerView.Adapter и ViewHolder

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

    private final Context mContext;

    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        MapView map;

        public ViewHolder(View view) {
            super(view);
            map = (MapView) view.findViewById(R.id.mapImageView);
            // Should this be created here?
            map.onCreate(null);
            map.onResume();
        }
    }

    public NearbyStopsAdapter(Context c) {
        this.mContext = c;
    }

    @Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int position) {
        View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.list_item_nearby_stop, viewGroup, false);
        return new ViewHolder(itemView);
    }

    @Override public void onBindViewHolder(ViewHolder holder, int position) {
        //Call Async Map here?
        holder.map.getMapAsync(this);
    }

    @Override public void onViewRecycled(ViewHolder holder) {
        // Cleanup MapView here?
//        if (holder.map != null) {
//            holder.map.onPause();
//            holder.map.onDestroy();
//        }
    }

    @Override public void onViewAttachedToWindow(ViewHolder holder) {
        // Setup MapView here?
//            holder.map.onCreate(null);
//            holder.map.onResume();
    }

    @Override public void onViewDetachedFromWindow(ViewHolder holder) {
        // Cleanup MapView here?
//        if (holder.map != null) {
//            holder.map.onPause();
//            holder.map.onDestroy();
//        }
    }

    // ...
}

Logcat:

I/Google Maps Android API﹕ Google Play services package version: 659943
W/Google Maps Android API﹕ Map Loaded callback is not supported in Lite Mode
W/Google Maps Android API﹕ Buildings are not supported in Lite Mode
W/Google Maps Android API﹕ Indoor is not supported in Lite Mode
W/Google Maps Android API﹕ Toggling gestures is not supported in Lite Mode
W/Google Maps Android API﹕ Toggling gestures is not supported in Lite Mode
W/Google Maps Android API﹕ Toggling gestures is not supported in Lite Mode
W/Google Maps Android API﹕ Toggling gestures is not supported in Lite Mode

Обновление: (3 февраля 2016 г.) Google выпустила образец кода для использования Lite Maps в ListView. Смотрите здесь

4b9b3361

Ответ 1

Решение следующим образом:

  • Внедрить OnMapReadyCallback в класс ViewHolder.
  • В onMapReady вызовите MapsInitializer.initialize, чтобы функции gaurantee могли использоваться перед получением карты.

Используйте этот класс для инициализации API Android Google Maps, если перед использованием карты необходимо использовать функции. Его нужно вызывать, потому что некоторые классы, такие как BitmapDescriptorFactory и CameraUpdateFactory, должны быть инициализированы.

  1. Перечислить карту из onViewRecycled.


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


       @Override 
       public void onBindViewHolder(ViewHolder holder, int position)  
       {
          //get 'location' by 'position' from data list
          //get GoogleMap
          GoogleMap thisMap = holder.gMap;
          //then move map to 'location'
          if(thisMap != null) 
             //move map to the 'location' 
             thisMap.moveCamera(...);          
       }


       //Recycling GoogleMap for list item
       @Override 
       public void onViewRecycled(ViewHolder holder) 
       {
          // Cleanup MapView here?
          if (holder.gMap != null) 
          {
              holder.gMap.clear();
              holder.gMap.setMapType(GoogleMap.MAP_TYPE_NONE);
          }
       }



       public class ViewHolder extends RecyclerView.ViewHolder implements OnMapReadyCallback { 

           GoogleMap gMap; 
           MapView map;
            ... ... 

           public ViewHolder(View view) {
              super(view);
              map = (MapView) view.findViewById(R.id.mapImageView);

              if (map != null) 
              {
                 map.onCreate(null);
                 map.onResume();
                 map.getMapAsync(this);
              }

          }


          @Override
          public void onMapReady(GoogleMap googleMap) {
              //initialize the Google Maps Android API if features need to be used before obtaining a map 
              MapsInitializer.initialize(getApplicationContext());
              gMap = googleMap;

              //you can move map here to item specific 'location'
              int pos = getPosition();
              //get 'location' by 'pos' from data list  
              //then move to 'location'
              gMap.moveCamera(...);

                  ... ...
         }

       }
    } 

Ответ 3

У вас должен быть отдельный класс Holder класса. Класс адаптера RecyclerView будет иметь только onCreateViewHolder() и onBindViewHolder().

Ваш файл макета должен выглядеть примерно так:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".MyActivity">

    <view
    <com.google.android.gms.maps.MapView
    android:id="@+id/mapImageView"
    xmlns:map="http://schemas.android.com/apk/res-auto"
    android:layout_width="80dp"
    android:layout_height="100dp"
    map:liteMode="true"
    map:mapType="normal"
    map:cameraZoom="15" />

</RelativeLayout>

И onCreate(), onDestroy() будут вызваны в классе Activity, как обычно.

Пожалуйста, следуйте этому учебнику, чтобы получить полный обзор.

Ответ 4

Google говорит:

При использовании API в полностью интерактивном режиме пользователи класса MapView должны перенаправить все методы жизненного цикла активности на соответствующие методы в классе MapView. Примеры методов жизненного цикла включают onCreate(), onDestroy(), onResume() и onPause().

При использовании класса MapView в режиме Lite события жизненного цикла пересылки являются необязательными, за исключением следующих ситуаций:

Обязательно вызывать onCreate(), иначе карта не появится. Если вы хотите показать точку "Мое местоположение" на вашей карте режима Lite и использовать источник местоположения по умолчанию, вам необходимо вызвать onResume() и onPause(), поскольку источник местоположения будет обновляться только между этими вызовами. Если вы используете свой собственный источник местоположения, нет необходимости вызывать эти два метода.

Итак, в режиме Lite вам не нужно беспокоиться о onDestroy(), onResume() и onPause()

Ответ 5

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

@Override 
public void onViewRecycled(ViewHolder holder) 
{
  // Cleanup MapView here?
  if (holder.gMap != null) 
  {
      holder.gMap.clear();
      holder.gMap.setMapType(GoogleMap.MAP_TYPE_NONE);
  }
}

Вы можете попробовать, если код выше не работает и в вашем случае.