realm-mapviewのコードを読んでみた


概要とレポジトリ

Google Map Clusterの Realmと連携したカスタムMapFragmentらしい*1

結論的なこと

  • カスタムMapFragment をそのまま使えるか?
    • 正直微妙。
    • まともに使おうとすると色々と引きずり出さないと駄目
  • Realm/Cluster Utilの実装サンプル例としては良いと思う

現状そのままで使えるという挙動想定

  • カスタムMapFragmentを表示する前に
    • カスタムClusterItem into RealmObject のデータを保存(RealmClusterWrapper)
  • カスタムMapFragmentの表示初期化中
    • findAllで上記のClusterItem情報でMarkerをプロット表示

の流れになると想定

  • カスタムMapFragmentを起点にすると
    • InfoWindow/アイコン等を変更したカスタムMarker等の口 の処理がまったくないため
    • library-projectの形式で導入して、ソースをカスタムする or 参考にして作る
    • カスタムMapFragmentでつかっているクラスだけextendsして使う

あたりがまともな利用法かと

なぜこういう実装になっていると思われるか?

に記載されているように、InfoViewの作りが雑だから。

ココらへんの実装って android.support.v4.app.DialogFragment で

値のBindの実装が出来ない*2のと同じような感じの実装のような感じがするんだよな。。*3

まあ、ポケモンGoを作った Googleの子会社さんのアプリの挙動とか見てれば推察できるわけで

play.google.com

  • Markerピンに関しては、
    • 各オブジェクトに色々と情報をもたせることは可能ですが
  • InfoWindowに関しては
    • Mapに対して1個
    • => これを 集合マーカ用/単一マーカ用 に1個ずつに変更

ぐらいな実装になるかと思います

  • RealmClusterMapFragment.java(追記するとしたら)
getMap().setInfoWindowAdapter(realmClusterManager) //infowindowの関連付け
realmClusterManager.getClusterMarkerCollection().setOnInfoWindowAdapter(new ClusterInfoWindowAdapter());//集合マーカ用
realmClusterManager.getMarkerCollection().setOnInfoWindowAdapter(new ItemInfoWindowAdapter());//単一マーカ用

でこれに対して

  • 値をbindする方法がない
  • Marker個別には設定しない

辺りの状態になっていて、

  • MarkerIdとRealmObjectのId(or Sqliteのindex) とかの関連情報を
    • Applicationクラスとかにもっておいてコンストラクタ経由の参照でアクセスする
    • InfoWindowAdapter::getInfoWindow(Marker marker) が表示時に呼び出されるだけなので
      • 其のタイミングでMarkerの関連情報を引っ張ってきて値を設定する
      • 値を設定済みのカスタムビューをコンストラクタで作る/渡す

あたりになるかな。。

実装で凄いと思った箇所

Genericsのサブクラス(型縛りクラス)を引っ張ってくるコード//☆

はじめてみましわたわ。add(M) とかするときの型縛りとしてのGenerics宣言としてしか認識してなかったもんな。。

未だに Generics型のクラス実装見ると凄く混乱するのですけど(汗

public abstract class RealmClusterMapFragment<M extends RealmObject> extends Fragment {

private Class<M> clazz;//☆

public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        clazz = (Class<M>) getTypeArguments(RealmClusterMapFragment.class, getClass()).get(0);//☆

private void setUpMapIfNeeded() {

        RealmClusterManager<M> realmClusterManager =
                new RealmClusterManager<>(getActivity(), getMap());
        RealmResults<M> realmResults = realm.where(clazz).findAll();//☆
        realmClusterManager.updateRealmResults(
                realmResults,
                getTitleColumnName(),
                getLatitudeColumnName(),
                getLongitudeColumnName());


    //
    // The code below is copied from StackOverflow in order to avoid having to pass in the T as a
    // Class for the Realm query/filtering.
    // http://stackoverflow.com/a/15008017
    //
    /**
     * Get the underlying class for a type, or null if the type is a variable
     * type.
     *
     * @param type the type
     * @return the underlying class
     */

    private static Class<?> getClass(Type type)
    {
        if (type instanceof Class) {
            return (Class) type;
        } else if (type instanceof ParameterizedType) {
            return getClass(((ParameterizedType) type).getRawType());
        } else if (type instanceof GenericArrayType) {
            Type componentType = ((GenericArrayType) type).getGenericComponentType();
            Class<?> componentClass = getClass(componentType);
            if (componentClass != null) {
                return Array.newInstance(componentClass, 0).getClass();
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * Get the actual type arguments a child class has used to extend a generic
     * base class.
     *
     * @param baseClass the base class
     * @param childClass the child class
     * @return a list of the raw classes for the actual type arguments.
     */
    private static <T> List<Class<?>> getTypeArguments(
            Class<T> baseClass, Class<? extends T> childClass)
    {
        Map<Type, Type> resolvedTypes = new HashMap<Type, Type>();
        Type type = childClass;
        // start walking up the inheritance hierarchy until we hit baseClass
        while (!getClass(type).equals(baseClass)) {
            if (type instanceof Class) {
                // there is no useful information for us in raw types, so just keep going.
                type = ((Class) type).getGenericSuperclass();
            } else {
                ParameterizedType parameterizedType = (ParameterizedType) type;
                Class<?> rawType = (Class) parameterizedType.getRawType();

                Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
                TypeVariable<?>[] typeParameters = rawType.getTypeParameters();
                for (int i = 0; i < actualTypeArguments.length; i++) {
                    resolvedTypes.put(typeParameters[i], actualTypeArguments[i]);
                }

                if (!rawType.equals(baseClass)) {
                    type = rawType.getGenericSuperclass();
                }
            }
        }

        // finally, for each actual type argument provided to baseClass, determine (if possible)
        // the raw class for that type argument.
        Type[] actualTypeArguments;
        if (type instanceof Class) {
            actualTypeArguments = ((Class) type).getTypeParameters();
        } else {
            actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
        }
        List<Class<?>> typeArgumentsAsClasses = new ArrayList<Class<?>>();
        // resolve types by chasing down type variables.
        for (Type baseType : actualTypeArguments) {
            while (resolvedTypes.containsKey(baseType)) {
                baseType = resolvedTypes.get(baseType);
            }
            typeArgumentsAsClasses.add(getClass(baseType));
        }
        return typeArgumentsAsClasses;
    }

*1:Realmの公式にも紹介ページ有り

*2:なぜか通常のDialogFragmentで存在しているonBindViewが存在しない。

*3:とりあえず作りました的な。多分G様があまり使ってないから修正されない