Créer Une Animation De Superposition D'ImageView Google Map

je suis en train de faire mon image de superposition pour faire des choses suivantes:

  • onClick / onDrag de carte, afficher une image constante au milieu de la carte qui est un pin
  • onTouchUp, changez l'image du marqueur en Marqueur de chargement et une fois les données chargement modifier le chargement de l'image à une nouvelle image avec du texte.

Voici une solution assez similaire à mon problème:

animatedMarker

Ce que j'ai fait jusqu'à présent ?

  • placé une imageview sur ma carte google au milieu , et a obtenu loacation de l'imageView à l'aide de mMap.getCameraPosition().target qui donne des coordonnées approximatives de position moyenne en utilisant setOnCameraChanged , je sais que c'est déconseillé, bien mieux la solution?.
  • reste de la partie Je ne peux pas comprendre, bien que j'ai lu plusieurs questions disant qu'ils l'ont mis en œuvre enveloppant framelayout sur fragment de Google map, et en appliquant onTouch sur cela, mais ne peut pas trouver une réponse authentique pour celui-là aussi.

mon xml de fragment, où tout ce qui se passe ressemble à ceci :

<RelativeLayout 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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".fragments.MovableMapFragment">

    <fragment
        android:id="@+id/map_frag_fmm"
        android:name="com.google.android.gms.maps.SupportMapFragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_above="@+id/tablayout_global_tabs"
        android:layout_alignParentTop="true" />
<! -- long xml ahead -->

Quelqu'un sait comment réaliser ce genre de comportement ?

12
demandé sur MrUpsidown 2016-10-02 22:51:07

3 réponses

Android Custom Pin

la meilleure solution serait d'utiliser RxJava / RxAndroid et de faire des tâches de fond avec, mais je ne peux pas poster une solution en utilisant rx sans voir votre code et il y a déjà un exemple de @user27799 aussi @Manas Chaudhari a fourni une grande explication de la logique (ci-dessous).

//bunch of imports
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;

public class MapsActivity extends AppCompatActivity implements OnMapReadyCallback
{

  private GoogleMap mGoogleMap;
  private FrameLayout frameLayout, circleFrameLayout;
  private ProgressBar progress;
  private TextView textView;
  private int circleRadius;
  private boolean isMoving = false;
  private SupportMapFragment mapFragment;

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_maps);
    initViews();
  }

  private void initViews() {

    frameLayout = (FrameLayout) findViewById(R.id.map_container);

    circleFrameLayout = (FrameLayout) frameLayout.findViewById(R.id.pin_view_circle);
    textView = (TextView) circleFrameLayout.findViewById(R.id.textView);
    progress = (ProgressBar) circleFrameLayout.findViewById(R.id.profile_loader);

    mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
    mapFragment.getMapAsync(this);

}

  private void moveMapCamera() {
    if (mGoogleMap == null) {
        return;
    }

    CameraUpdate center = CameraUpdateFactory
            .newLatLng(new LatLng(40.76793169992044, -73.98180484771729));
    CameraUpdate zoom = CameraUpdateFactory.zoomTo(15);

    mGoogleMap.moveCamera(center);
    mGoogleMap.animateCamera(zoom);
}

  @Override
  public void onMapReady(GoogleMap googleMap) {
    mGoogleMap = googleMap;

    mGoogleMap.setOnCameraMoveStartedListener(new GoogleMap.OnCameraMoveStartedListener() {

        /* User starts moving map - change your pin's image here 
        */
        @Override
        public void onCameraMoveStarted(int i) {
            isMoving = true;
            textView.setVisibility(View.GONE);
            progress.setVisibility(View.GONE);
            Drawable mDrawable;
            if (Build.VERSION.SDK_INT >= 21)
                mDrawable = getApplicationContext().getResources().getDrawable(R.drawable.circle_background_moving, null);
            else
                mDrawable = getApplicationContext().getResources().getDrawable(R.drawable.circle_background_moving);

            circleFrameLayout.setBackground(mDrawable);
            resizeLayout(false);
        }
    });

    /*User stoped moving map -> retrieve location and do your background task */
    mGoogleMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() {
        @Override
        public void onCameraIdle() {

            isMoving = false;
            textView.setVisibility(View.INVISIBLE);
            progress.setVisibility(View.VISIBLE);
            resizeLayout(true);

            /* this is just an example that simulates data loading
               you shoud substitute it with your logic
            */
            new Handler().postDelayed(new Runnable() {
                public void run() {

                    Drawable mDrawable;
                    if (Build.VERSION.SDK_INT >= 21)
                        mDrawable = getApplicationContext().getResources().getDrawable(R.drawable.circle_background, null);
                    else
                        mDrawable = getApplicationContext().getResources().getDrawable(R.drawable.circle_background);

                    if (!isMoving) {
                        circleFrameLayout.setBackground(mDrawable);
                        textView.setVisibility(View.VISIBLE);
                        progress.setVisibility(View.GONE);
                    }
                }
            }, 1500);
        }
    });

    MapsInitializer.initialize(this);
    moveMapCamera();
  }

  //resing circle pin
  private void resizeLayout(boolean backToNormalSize){
    FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) circleFrameLayout.getLayoutParams();

    ViewTreeObserver vto = circleFrameLayout.getViewTreeObserver();
    vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            circleFrameLayout.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            circleRadius = circleFrameLayout.getMeasuredWidth();
        }
    });

    if (backToNormalSize) {
        params.width = WRAP_CONTENT;
        params.height = WRAP_CONTENT;
        params.topMargin = 0;

    } else {
        params.topMargin = (int) (circleRadius * 0.3);
        params.height = circleRadius - circleRadius / 3;
        params.width = circleRadius - circleRadius / 3;
    }

    circleFrameLayout.setLayoutParams(params);
  }

}

fichier de mise en page avec pin au centre de l'écran:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:id="@+id/map_container"
         android:layout_width="match_parent"
         android:layout_height="match_parent">

<fragment xmlns:tools="http://schemas.android.com/tools"
          android:id="@+id/map"
          android:name="com.google.android.gms.maps.SupportMapFragment"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          tools:context="com.example.mapstest.MapsActivity" />

<FrameLayout
    android:id="@+id/pin_view_line"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:layout_marginTop="@dimen/line_top_margin"
    android:background="@drawable/line_background"/>

<FrameLayout
    android:id="@+id/pin_view_circle"
    android:layout_gravity="center"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/circle_background">

    <TextView
        android:id="@+id/textView"
        android:layout_margin="@dimen/inner_circle_margin"
        android:layout_width="@dimen/inner_circle_radius"
        android:layout_height="@dimen/inner_circle_radius"
        android:layout_gravity="top|center_horizontal"
        android:gravity="center"
        android:text="12 min"
        android:textSize="@dimen/text_size"
        android:textColor="@android:color/white"/>

    <ProgressBar
        android:id="@+id/profile_loader"
        android:layout_margin="@dimen/inner_circle_margin"
        android:layout_width="@dimen/inner_circle_radius"
        android:layout_height="@dimen/inner_circle_radius"
        android:indeterminate="true"
        android:layout_gravity="top|center_horizontal"
        android:visibility="gone"
        android:contentDescription="@null"/>

</FrameLayout>

Encerclez l'arrière-plan lorsque ce n'est pas le cas déménagement:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
   android:shape="oval">    
<solid android:color="@android:color/holo_green_dark"/>
<stroke
    android:width="@dimen/stroke_width"
    android:color="@android:color/black"/>
<size
    android:width="@dimen/circle_radius"
    android:height="@dimen/circle_radius"/>
</shape>

Cercle d'arrière-plan lorsque la carte est en mouvement:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
   android:shape="oval">    
<solid android:color="@android:color/black"/>
<size
    android:width="@dimen/circle_radius"
    android:height="@dimen/circle_radius"/>
</shape>

Ligne de fond de fichiers:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
   android:shape="rectangle">    
<solid android:color="@android:color/black"/>
<size
    android:width="@dimen/line_width"
    android:height="@dimen/line_height"/>
</shape>

Dimensions:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <dimen name="circle_radius">48dp</dimen>
  <dimen name="line_width">4dp</dimen>
  <dimen name="line_height">40dp</dimen>
  <dimen name="stroke_width">4dp</dimen>
  <dimen name="text_size">10sp</dimen>
  <dimen name="inner_circle_radius">40dp</dimen>
  <dimen name="inner_circle_margin">4dp</dimen>
  <dimen name="line_top_margin">20dp</dimen>
</resources>

j'espère que cela vous aide, mais si vous avez des questions n'hésitez pas à poser ou si vous voir les zones que vous amélioreriez - modifier ma solution.

Cheers.

5
répondu Fedor Tsyganov 2016-10-11 19:11:49

vous mettre en page dans FrameLayout et dessiner une image sur la carte. Quand GoogleMap est prêt (mon code utilise RxJava):

Observable.<Void>create(subscriber -> {
        mMap.setOnCameraIdleListener(() -> subscriber.onNext(null));
    })
            .debounce(1, TimeUnit.SECONDS)
            .subscribeOn(AndroidSchedulers.mainThread())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(aVoid -> {
                LatLng newPos = mMap.getProjection().fromScreenLocation(new Point(mBinding.googleMap.getWidth() / 2, mBinding.googleMap.getHeight() / 2));

                makeServerRequest(newPos);
            });
4
répondu Nickolay Savchenko 2016-10-10 17:47:43

Mise en page

les repères sont conçus pour être positionnés à des coordonnées précises sur la carte. Ainsi, ils se déplacent quand vous changez la carte.

comme votre NIP est toujours au milieu de la carte, il n'est pas approprié de le créer en utilisant Marker. Au lieu de cela, vous pouvez le placer en dehors du fragment de carte au centre du conteneur de carte. Votre XML devrait donc ressembler à ceci:

<RelativeLayout>
  <fragment 
    android:id="@+id/map_fragment" />

  <RelativeLayout
    android:id="@+id/pin_view"
    android:centerInParent="true" />
</RelativeLayout>

Vous pouvez ajouter des enfants à pin_view pour mettre en place des animations selon vos exigence.

États Pin

basé sur le gif fourni par vous, il peut y avoir trois états pour le NIP.

  • Vide
  • Chargement
  • Chargé d'

Auditeurs

vous pouvez déclencher les animations en paramétrant ces écouteurs:

  • setOnCameraMoveStartedListener
    • valeur Précédente doit être invalidé. Changer l'état de pin en Empty
    • Annuler toutes les demandes en cours
  • setOnCameraIdleListener
    • obtenir les coordonnées en utilisant map.getCameraPosition()
    • Modifier pin de l'état de "Chargement"
    • Déclencher un appel réseau pour récupérer des données. Dans le rappel
      • changer l'état du pin en 'Loaded' et afficher la valeur Notez que le callback ne sera pas exécuté, si l'utilisateur bouge avant que l'api ne soit terminée, car nous annulerons la requête dès que l'utilisateur déplacer. Donc, ce scénario sera également pris en charge
1
répondu Manas Chaudhari 2016-10-10 16:27:40