ClarkXP
ClarkXP

Reputation: 1233

SuggestionsAdapter NullPointerException when I'm writing in SearchView

When I'm writing on search edittext, at moment to show suggestions my app show a FC. Before worked smoothly.

I have delegated the functions in the map's controller class. In my Activity have the next methods:

private void handleIntent(Intent intent) {
    if (intent.getAction() == null) {
        return;
    }
    if (intent.getAction().equals(Intent.ACTION_SEARCH)) {
        ((DayRentController) mapFragment.getMapController()).doSearch(intent.getStringExtra(SearchManager.QUERY));
    } else if (intent.getAction().equals(Intent.ACTION_VIEW)) {
        ((DayRentController) mapFragment.getMapController()).getPlace(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
    }
}

@Override
protected void onNewIntent(Intent intent) {
    super.onNewIntent(intent);
    setIntent(intent);
    handleIntent(intent);
}

@Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
    return ((DayRentController) mapFragment.getMapController()).onCreateLoader(i, bundle);
}
@Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
    ((DayRentController) mapFragment.getMapController()).onLoadFinished(cursorLoader, cursor);
}

@Override
public void onLoaderReset(Loader<Cursor> cursorLoader) {
    ((DayRentController) mapFragment.getMapController()).onLoaderReset(cursorLoader);
}

in DayRentController:

@Override
public android.support.v4.content.Loader onCreateLoader(int id, Bundle bundle) {
    CursorLoader cLoader = null;

    if (id == 0)
        cLoader = new CursorLoader(getActivity().getBaseContext(), PlaceProvider.SEARCH_URI, null, null, new String[]{bundle.getString("query")}, null);
    else if (id == 1)
        cLoader = new CursorLoader(getActivity().getBaseContext(), PlaceProvider.DETAILS_URI, null, null, new String[]{bundle.getString("query")}, null);
    return cLoader;
}

@Override
public void onLoadFinished(android.support.v4.content.Loader loader, Cursor o) {
    showLocations(o);
}

@Override
public void onLoaderReset(android.support.v4.content.Loader loader) {

}

public void doSearch(String query) {
    Bundle data = new Bundle();
    data.putString("query", query);
    getActivity().getSupportLoaderManager().restartLoader(0, data, this);
}

public void getPlace(String query) {
    Bundle data = new Bundle();
    data.putString("query", query);
    getActivity().getSupportLoaderManager().restartLoader(1, data, this);
}

private void showLocations(Cursor c) {
    //Add markers to map
}

In Manifest:

    <activity
        android:name=".BuscapakingActivity"
        android:configChanges="screenLayout|screenSize|orientation"
        android:label="@string/title_activity_buscapaking"
        android:launchMode="singleTop" >
        <intent-filter>
            <action android:name="android.intent.action.SEARCH" />
        </intent-filter>

        <!-- Points to searchable activity -->
        <meta-data
            android:name="android.app.default_searchable"
            android:value=".MainActivity" />

        <!-- Points to searchable meta data -->
        <meta-data
            android:name="android.app.searchable"
            android:resource="@xml/searchable" />
    </activity>

My Content provider:

package com.buscaparking.client.utils.geocoding;

import android.app.SearchManager;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.util.Log;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;

public class PlaceProvider extends ContentProvider {

    public static final String AUTHORITY = "com.buscaparking.client.PlaceProvider";

    public static final Uri SEARCH_URI = Uri.parse("content://"+AUTHORITY+"/search");

    public static final Uri DETAILS_URI = Uri.parse("content://"+AUTHORITY+"/details");

    private static final int SEARCH = 1;    
    private static final int SUGGESTIONS = 2;
    private static final int DETAILS = 3;

    // Obtain browser key from https://code.google.com/apis/console
    String mKey = "key="+Constants.MYKEY;

    // Defines a set of uris allowed with this content provider
    private static final UriMatcher mUriMatcher = buildUriMatcher();    

    private static UriMatcher buildUriMatcher() {

        UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

        // URI for "Go" button
        uriMatcher.addURI(AUTHORITY, "search", SEARCH );

        // URI for suggestions in Search Dialog
        uriMatcher.addURI(AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY,SUGGESTIONS);

        // URI for Details
        uriMatcher.addURI(AUTHORITY, "details",DETAILS);


        return uriMatcher;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        Cursor c = null;


        PlaceJSONParser parser = new PlaceJSONParser();
        PlaceDetailsJSONParser detailsParser = new PlaceDetailsJSONParser();

        String jsonString = "";
        String jsonPlaceDetails = "";

        List<HashMap<String, String>> list = null;
        List<HashMap<String, String>> detailsList = null;

        MatrixCursor mCursor = null;

        switch(mUriMatcher.match(uri)){
        case SEARCH:
            // Defining a cursor object with columns description, lat and lng
            mCursor = new MatrixCursor(new String[] { "description","lat","lng" });

            // Create a parser object to parse places in JSON format
            parser = new PlaceJSONParser();

            // Create a parser object to parse place details in JSON format
            detailsParser = new PlaceDetailsJSONParser();

            // Get Places from Google Places API
            jsonString = getPlaces(selectionArgs); 
            try {
                // Parse the places ( JSON => List )
                list  = parser.parse(new JSONObject(jsonString));

                // Finding latitude and longitude for each places using Google Places Details API
                for(int i=0;i<list.size();i++){
                    HashMap<String, String> hMap = (HashMap<String, String>) list.get(i);

                    detailsParser =new PlaceDetailsJSONParser();

                    // Get Place details
                    jsonPlaceDetails  = getPlaceDetails(hMap.get("reference"));

                    // Parse the details ( JSON => List )
                    detailsList = detailsParser.parse(new JSONObject(jsonPlaceDetails));

                    // Creating cursor object with places
                    for(int j=0;j<detailsList.size();j++){
                        HashMap<String, String> hMapDetails = detailsList.get(j);

                        // Adding place details to cursor
                        mCursor.addRow(new String[]{ hMap.get("description") , hMapDetails.get("lat") , hMapDetails.get("lng") });
                    }

                }               
            } catch (JSONException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            c = mCursor;
            break;

        case SUGGESTIONS :          

            // Defining a cursor object with columns id, SUGGEST_COLUMN_TEXT_1, SUGGEST_COLUMN_INTENT_EXTRA_DATA
            mCursor = new MatrixCursor(new String[] { "_id", SearchManager.SUGGEST_COLUMN_TEXT_1, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA }  );

            // Creating a parser object to parse places in JSON format
            parser = new PlaceJSONParser();

            // Get Places from Google Places API
            jsonString = getPlaces(selectionArgs);      

            try {
                // Parse the places ( JSON => List )
                list  = parser.parse(new JSONObject(jsonString));

                // Creating cursor object with places
                for(int i=0;i<list.size();i++){
                    HashMap<String, String> hMap = (HashMap<String, String>) list.get(i);

                    // Adding place details to cursor
                    mCursor.addRow(new String[] { Integer.toString(i), hMap.get("description"), hMap.get("reference") });               
                }               
            } catch (JSONException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            c = mCursor;
            break;

        case DETAILS :
            // Defining a cursor object with columns description, lat and lng
            mCursor = new MatrixCursor(new String[] { "description","lat","lng" });

            detailsParser = new PlaceDetailsJSONParser();
            jsonPlaceDetails  = getPlaceDetails(selectionArgs[0]);
            try {
                detailsList = detailsParser.parse(new JSONObject(jsonPlaceDetails));
            } catch (JSONException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            for(int j=0;j<detailsList.size();j++){
                HashMap<String, String> hMapDetails = detailsList.get(j);               
                mCursor.addRow(new String[]{ hMapDetails.get("formatted_address") , hMapDetails.get("lat") , hMapDetails.get("lng") });
            }
            c = mCursor;
            break;


        }       

        return c;
    }


    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // TODO Auto-generated method stub
        return 0;
    }



    @Override
    public String getType(Uri uri) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean onCreate() {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
        // TODO Auto-generated method stub
        return 0;
    }

    /** A method to download json data from url */
    private String downloadUrl(String strUrl) throws IOException{
        String data = "";
        InputStream iStream = null;
        HttpURLConnection urlConnection = null;
        try{
                URL url = new URL(strUrl);

                // Creating an http connection to communicate with url 
                urlConnection = (HttpURLConnection) url.openConnection();

                // Connecting to url 
                urlConnection.connect();

                // Reading data from url 
                iStream = urlConnection.getInputStream();

                BufferedReader br = new BufferedReader(new InputStreamReader(iStream));

                StringBuffer sb  = new StringBuffer();

                String line = "";
                while( ( line = br.readLine())  != null){
                        sb.append(line);
                }

                data = sb.toString();

                br.close();

        }catch(Exception e){
                Log.d("Exception while downloading url", e.toString());
        }finally{
                iStream.close();
                urlConnection.disconnect();
        }
        return data;
    }


    private String getPlaceDetailsUrl(String ref){


        // reference of place
        String reference = "reference="+ref;

        // Sensor enabled
        String sensor = "sensor=false";

        // Building the parameters to the web service
        String parameters = reference+"&"+sensor+"&"+mKey;

        // Output format
        String output = "json";

        // Building the url to the web service
        String url = "https://maps.googleapis.com/maps/api/place/details/"+output+"?"+parameters;

        return url;
    }

    private String getPlacesUrl(String qry){    


        try {
            qry = "input=" + URLEncoder.encode(qry, "utf-8");
        } catch (UnsupportedEncodingException e1) {
            e1.printStackTrace();
        }

        // Sensor enabled
        String sensor = "sensor=false";


        // place type to be searched
        String types = "types=geocode";

        // Building the parameters to the web service
        String parameters = qry+"&"+types+"&"+sensor+"&"+mKey;

        // Output format
        String output = "json";    


        // Building the url to the web service
        String url = "https://maps.googleapis.com/maps/api/place/autocomplete/"+output+"?"+parameters;        

        return url;

    }


    private String getPlaces(String[] params){
        // For storing data from web service
        String data = "";
        String url = getPlacesUrl(params[0]);
        try{
            // Fetching the data from web service in background
            data = downloadUrl(url);
        }catch(Exception e){
                Log.d("Background Task",e.toString());
        }
        return data;        
    }

    private String getPlaceDetails(String reference){
        String data = "";
        String url = getPlaceDetailsUrl(reference);
        try {
            data = downloadUrl(url);
        } catch (IOException e) {
            e.printStackTrace();
        }       
        return data;
    }   
}

My Row Layout:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:paddingLeft="@dimen/abc_dropdownitem_text_padding_left"
    android:paddingRight="4dip"
    android:layout_width="match_parent"
    android:layout_height="48dp" >

    <TextView android:id="@android:id/text1"
        style="?android:attr/dropDownItemStyle"
        android:textAppearance="?attr/textAppearanceSearchResultTitle"
        android:singleLine="true"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_toRightOf="@android:id/icon1"
        android:layout_toLeftOf="@android:id/icon2"
        android:layout_above="@android:id/text2"
        android:textColor="#fff"
        android:background="@color/theme_default_primary_dark"/>

</RelativeLayout>

My Searchable XML:

<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/app_name"
    android:hint="Buscar"
    android:searchSettingsDescription="Buscar Lugar"
    android:searchSuggestAuthority="com.buscaparking.client.PlaceProvider"
    android:searchSuggestIntentAction="android.intent.action.VIEW"    
    android:searchSuggestSelection=" ?"
    android:searchSuggestThreshold="2"/>

My logcat:

java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.ImageView.setImageResource(int)' on a null object reference
        at android.support.v7.widget.SuggestionsAdapter.newView(SuggestionsAdapter.java:249)
        at android.support.v7.widget.SuggestionsAdapter.getView(SuggestionsAdapter.java:453)
        at android.widget.AbsListView.obtainView(AbsListView.java:2346)
        at android.widget.ListPopupWindow$DropDownListView.obtainView(ListPopupWindow.java:1684)
        at android.widget.ListView.measureHeightOfChildren(ListView.java:1270)
        at android.widget.ListPopupWindow.buildDropDown(ListPopupWindow.java:1181)
        at android.widget.ListPopupWindow.show(ListPopupWindow.java:568)
        at android.widget.AutoCompleteTextView.showDropDown(AutoCompleteTextView.java:1099)
        at android.widget.AutoCompleteTextView.updateDropDownForFilter(AutoCompleteTextView.java:974)
        at android.widget.AutoCompleteTextView.onFilterComplete(AutoCompleteTextView.java:956)
        at android.widget.Filter$ResultsHandler.handleMessage(Filter.java:285)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:135)
        at android.app.ActivityThread.main(ActivityThread.java:5255)
        at java.lang.reflect.Method.invoke(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:372)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:898)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:693)

Upvotes: 1

Views: 810

Answers (1)

ClarkXP
ClarkXP

Reputation: 1233

Finally I found the problem. SearchView depend of 'buttonGravity' Toolbar's attribute. In Android Support Library v21.0.2, this attribute was deleted. Some days ago, I changed the versions of android libraries and it showed this attribute as conflicting, then I remove this. But I never thought this would be related to SearchView.

I restored previous version of appcompat-v7 (21.0.0) and works. Also I had to exclude appcompat-v7 from the dependencies tree.

If I found an updated solution, I will update this answer.

Upvotes: 1

Related Questions