0

I am trying to extract a custom InfoWindowAdapter into a separate class file. I started with an inner class which works perfect:

public class FoobarMapActivity extends SherlockFragmentActivity {

    protected GoogleMap mMap;
    private final GoogleMap.InfoWindowAdapter mInfoWindowAdapter;

    public FoobarMapActivity() {
        mInfoWindowAdapter = new GoogleMap.InfoWindowAdapter() {

            @Override
            public View getInfoWindow(Marker marker) {
                View window = getLayoutInflater().inflate(R.layout.custom_info_window, null);
                render(marker, window);
                return window;
            }

            @Override
            public View getInfoContents(Marker marker) {
                return null;
            }

            private void render(Marker marker, View view) {
                ((ImageView) view.findViewById(R.id.badge)).setImageResource(0);
                TextView titleTextView = ((TextView) view.findViewById(R.id.title));
                TextView snippetTextView = ((TextView) view.findViewById(R.id.snippet));
                titleTextView.setText(StringHelper.setTextOrEmpty(marker.getTitle()));
                snippetTextView.setText(StringHelper.setTextOrEmpty(marker.getSnippet()));
            }

        };
    }

But as soon as I put the same code into a separate class I run into a NullPointerException when I inflate the layout.

Caused by: java.lang.NullPointerException
  at android.view.LayoutInflater.from(LayoutInflater.java:210)
  at com.example.foobar.activities.CustomInfoWindowAdapter.<init>(CustomInfoWindowAdapter.java:20)
  at com.example.foobar.activities.FoobarMapActivity.<init>(FoobarMapActivity.java:107)
  at java.lang.Class.newInstanceImpl(Native Method)
  at java.lang.Class.newInstance(Class.java:1319)
  at android.app.Instrumentation.newActivity(Instrumentation.java:1023)
  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1887)

Here is the custom class for the InfoWindowAdapter:

package com.example.foobar.activities;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.Marker;

import com.example.foobar.R;
import com.example.foobar.utils.StringHelper;

public class CustomInfoWindowAdapter implements GoogleMap.InfoWindowAdapter {

    protected View mWindow;

    public CustomInfoWindowAdapter(Context context) {
        LayoutInflater inflater = LayoutInflater.from(context);
        mWindow = inflater.inflate(R.layout.custom_info_window, null);
    }

    @Override
    public View getInfoWindow(Marker marker) {
        render(marker, mWindow);
        return mWindow;


    @Override
    public View getInfoContents(Marker marker) {
        return null;
    }

    private void render(Marker marker, View view) {
        ((ImageView) view.findViewById(R.id.badge)).setImageResource(0);
        TextView titleTextView = ((TextView) view.findViewById(R.id.title));
        TextView snippetTextView = ((TextView) view.findViewById(R.id.snippet));
        titleTextView.setText(StringHelper.setTextOrEmpty(marker.getTitle()));
        snippetTextView.setText(StringHelper.setTextOrEmpty(marker.getSnippet()));
    }

}

Here I instatiate the custom adapter:

public class FoobarMapActivity extends SherlockFragmentActivity {

    private final GoogleMap.InfoWindowAdapter mInfoWindowAdapter;

    public FoobarMapActivity() {
        mInfoWindowAdapter = new CustomInfoWindowAdapter(getBaseContext()); // line 107
    }
JJD
  • 50,076
  • 60
  • 203
  • 339

3 Answers3

2

Please move this line:

mInfoWindowAdapter = new CustomInfoWindowAdapter(getBaseContext());

into onCreate and replace getBaseContext() with this.

You are doing something against the platform. onCreate is your constructor and you should not use Java constructor when working with Activities.

MaciejGórski
  • 22,187
  • 7
  • 70
  • 94
  • 2
    Indeed, please remove your constructor, and use onCreate instead. Dianne Hackborn (from the Android team) said: "Simple answer: if you are implementing a component like Activity, Service, and ContentProvider, don't implement a constructor. (That is just use the default empty constructor). Whatever you think you want to do in the constructor... you very like don't, and should do in onCreate() instead." - https://groups.google.com/forum/#!topic/android-developers/70VaBROqcxg – BoD Jun 22 '13 at 15:03
  • What would be your suggestion to ensure the instance is constructed once only then? – JJD Jun 25 '13 at 11:59
  • 1
    @JJD `onCreate` is called only once just after constructor is invoked. – MaciejGórski Jun 25 '13 at 12:59
0

I think you have NullReference in render function Try to add null checking:

private void render(Marker marker, View view) {
        if (view == null || marker == null)
          return;
        ((ImageView) view.findViewById(R.id.badge)).setImageResource(0);
        TextView titleTextView = ((TextView) view.findViewById(R.id.title));
        TextView snippetTextView = ((TextView) view.findViewById(R.id.snippet));
        if (titleTextView != null)
          titleTextView.setText(StringHelper.setTextOrEmpty(marker.getTitle()));
        if (snippetTextView!= null)
          snippetTextView.setText(StringHelper.setTextOrEmpty(marker.getSnippet()));
    }

EDITED:

Try to get LayoutInflater another way:

mInflater = (LayoutInflater) context.getSystemService( Context.LAYOUT_INFLATER_SERVICE);

and also check that context is not null.

EDITED2:

Try

mInfoWindowAdapter = new CustomInfoWindowAdapter(this);
Dimmerg
  • 2,113
  • 1
  • 13
  • 14
  • Please read the stacktrace. The error occurs when the layout is inflated in the constructor of `CustomInfoWindowAdapter`. – JJD Jun 22 '13 at 12:03
  • The same exception is thrown when I use `context.getSystemService()` to retrieve the inflater. – JJD Jun 22 '13 at 12:14
  • So, we have another question - why context passed in constructor is null :) Can you provide code, where you create and set this adapter for map? – Dimmerg Jun 22 '13 at 12:16
  • Pass `this` to the adapter causes an `IllegalStateException: System services not available to Activities before onCreate()`. I tried both variation to retrieve the inflater. I cannot move the instantiation info `onCreate` since I declared it to be final. – JJD Jun 22 '13 at 12:34
  • Why did you mark adapter as final? Is it possible to make it standart class variable and move adapter constructor into onCreate? – Dimmerg Jun 22 '13 at 13:17
  • I declare the adapter as `final` to avoid recreating it every once in a while. – JJD Jun 22 '13 at 13:20
  • I think you have more problems with this solution, because your adapter anyway will create info view again on each call. – Dimmerg Jun 22 '13 at 13:26
  • I can avoid this by moving `mInflater.inflate(R.layout.custom_info_window, null);` into the constructor. – JJD Jun 22 '13 at 13:48
  • Do you mean to inflate view into activity and pass it into adapter, than adapter will return this view? – Dimmerg Jun 22 '13 at 13:51
  • So, you are really avoid recreating, but your problem still not solved? – Dimmerg Jun 22 '13 at 14:12
0

Try to use getApplicationContext() or your Activity's context instead of getBaseContext(). Context context = this; make context globle to your activity.

JJD
  • 50,076
  • 60
  • 203
  • 339
Vijju
  • 3,458
  • 1
  • 22
  • 20
  • Passing `getApplicationContext()` also causes a `NullPointerException. This is also against [how you should use context](http://www.doubleencore.com/2013/06/context/?utm_source=Android+Weekly&utm_campaign=78ad4cb95e-Android_Weekly_64&utm_medium=email&utm_term=0_4eb677ad19-78ad4cb95e-330180005). – JJD Jun 22 '13 at 12:54