Android - Prevent text truncation in SearchView suggestions?
Asked Answered
R

3

7

The suggestions that appear in the default ListView beneath my SearchView contain text that is truncated. I would like the text to be displayed in its entirety (on multiple lines if necessary).

I have come up with two possible ways to solve this but, with no examples to be found on the net, I was hoping someone on here may be able to help...

Approach #1 / Q1: How can I directly access and modify the appearance of the TextViews that hold the SUGGEST_COLUMN_TEXT_1 and SUGGEST_COLUMN_TEXT_1 text?

Approach #2 / Q2: Alternatively, SearchView has a setSuggestionsAdapter(CursorAdapter adapter) method which looks like it may be (more?) suitable than approach #1. Although I have read up on CursorAdapters and have one already implemented in my app, I'm not sure how I would configure one for the SearchView (esp. in terms of accessing the cursor), so could anyone help me out with some general guidance or a skeleton example?

Here is the existing code from my SearchViewFragment class:

public class SearchViewFragment extends Fragment {

public SearchViewFragment() {
}

@Override
public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
    Bundle savedInstanceState) {

    // Inflate the layout for this fragment
    View fragmentView =  inflater.inflate(R.layout.fragment_search_view, container, false);

    // Use the Search Manager to find the SearchableInfo related to this Activity
    SearchManager searchManager = (SearchManager)getActivity().getSystemService(Context.SEARCH_SERVICE);
    SearchableInfo searchableInfo = searchManager.getSearchableInfo(getActivity().getComponentName());

    // Bind the Activity's SearchableInfo to the Search View
    SearchView searchView = (SearchView)fragmentView.findViewById(R.id.searchView);
    searchView.setSearchableInfo(searchableInfo);
    searchView.setIconifiedByDefault(false);
    searchView.setSubmitButtonEnabled(true);
    //searchView.setQueryRefinementEnabled(true);

    return fragmentView;
}

}

Update: SOLVED!

Thanks to the accepted answer, I have created this code which is pretty clean and does the job well...

public class SearchViewFragment extends Fragment {

    private static final String LOG_TAG = SearchViewFragment.class.getSimpleName();

    public SearchViewFragment() {
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        setRetainInstance(true); //todo - not working - Remember search term
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {

        // Inflate the layout for this fragment
        View fragmentView =  inflater.inflate(R.layout.fragment_search_view, container, false);

        // Use the Search Manager to find the SearchableInfo related to this Activity
        SearchManager searchManager = (SearchManager)getActivity().getSystemService(Context.SEARCH_SERVICE);
        SearchableInfo searchableInfo = searchManager.getSearchableInfo(getActivity().getComponentName());

        // Bind the Activity's SearchableInfo to the Search View
        final SearchView searchView = (SearchView)fragmentView.findViewById(R.id.searchView);
        searchView.setSearchableInfo(searchableInfo);
        searchView.setIconifiedByDefault(false);
        searchView.setSubmitButtonEnabled(true);
        //searchView.setQueryRefinementEnabled(true);

        searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String s) {
               //DO whatever you want here on text submit in the search View
                Log.d(LOG_TAG, "onQueryTextSubmit(" + s + ")");

                return true;
            }
            @Override
            public boolean onQueryTextChange(String textChange) {

                Log.d(LOG_TAG, "onQueryTextChange(" + textChange + ")");

                ContentResolver cr = getActivity().getContentResolver();

                Uri uri = DbContentProvider.CONTENT_URI_REAL_PRODUCTS;
                String[] projection = DbContentProvider.getProjectionIn(DbContentProvider.REAL_PRODUCTS_SUGGEST);
                String selection = DbContentProvider.getSelection(false);
                String[] selectionArgs = {Utilities.formatQueryString(textChange)};
                String sortOrder = null;
                Cursor cursor = cr.query(uri, projection, selection, selectionArgs, sortOrder);
                Log.d(LOG_TAG, "Setting setSuggestionsAdapter. cursor: " + cursor);
                searchView.setSuggestionsAdapter(new SearchSuggestionsAdapter(getActivity(), cursor));

                return true;

            }
        });

        return fragmentView;
    }

    private static class SearchSuggestionsAdapter extends SimpleCursorAdapter {

        private static final String[] mVisible = {SearchManager.SUGGEST_COLUMN_TEXT_1, SearchManager.SUGGEST_COLUMN_TEXT_2};
        private static final int[] mViewIds = {R.id.product_name, R.id.product_shelf};

        public SearchSuggestionsAdapter(Context context, Cursor cursor) {

            super(context, R.layout.search_view_suggestions, cursor, mVisible, mViewIds, 0);

        }
        /*
        @Override
        public void bindView(View view, Context context, Cursor cursor) {

            Log.d(LOG_TAG, "bindView(" + view + ", " + context + ", " + cursor + ")");
            super.bindView(view, context, cursor);

        }

        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {

            Log.d(LOG_TAG, "newView(" + context + ", " + cursor + ", " + parent + ")");
            return super.newView(context, cursor, parent);

        }
        */

    }

}

And here is my search_view_suggestions.xml...

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin" >

    <TextView
        android:id="@+id/product_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Product Name placeholder"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <TextView
        android:id="@+id/product_shelf"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Product Shelf placeholder" />

</LinearLayout>

...the result is no text truncation. :-)

Rifleman answered 27/12, 2014 at 22:17 Comment(3)
are you saying that you have a solution that works but text truncation is still the issue?Ave
My code shows the suggestions, but they are truncated. I need a solution to overcome the truncation problem.Rifleman
Where is the data coming from for the suggestions? The documentation for SearchableInfo says that it should be used for searching across different apps. If you have the cursor adapter up and running and displaying suggestions then it's simple to change the layout used in getView. Perhaps you're currently using a default android.R.layout... oneDistraught
A
5

It seems like you want a custom layout for your search view results. I will try to outline some clear steps below:

To make the search view to work we need to have a searchable.xml in the res/xml folder and a content provider at all times.

searchable.xml example:

<searchable xmlns:android="http://schemas.android.com/apk/res/android"
    android:label="@string/app_name"
    android:searchSuggestAuthority="com.example.searchProvider" ></searchable>

The label and searchSuggestAuthority are required. The searchSuggestAuthority should point to the location of the content provider

Add the intent-filter meta-data to the activity in the manifest. Example:

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

In the activity/fragment, get your searchView object and pass it the custom adapter for displaying the custom layout.

searchView.setSearchableInfo(manager.getSearchableInfo(getActivity().getComponentName()));
        final SearchView.OnQueryTextListener queryTextListener = new SearchView.OnQueryTextListener() {
            @Override
            public boolean onQueryTextSubmit(String s) {
               //DO whatever you want here on text submit in the search View

                return true;
            }
 @Override
            public boolean onQueryTextChange(String textChange) {


                searchView.setSuggestionsAdapter(new ExampleAdapter(context,yourData));

            }
        };

Make sure the custom adapter extends the cursorAdapter.

public class ExampleAdapter extends CursorAdapter {

    private Cursor cursor;

    private TextView text;

    public ExampleAdapter(Context context, Cursor cursor) {

        super(context, cursor);

        this.cursor= cursor;

    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {

        text.setText((cursor.getString(cursor.getColumnIndex(YOUR_STRING_KEY));));

    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {

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

        View view = inflater.inflate(R.layout.item, parent, false);

        text = (TextView) view.findViewById(R.id.text);

        return view;

    }

}

In the above code R.layout.item refers to your custom xml file that you can use to inflate in the searchview results.

Make sure you are using a content provider though. The searchView doesn't work without one. It doesn't matter even if you are only caching the data on a temporary basis.

Hope this helps!

Ave answered 27/1, 2015 at 21:12 Comment(2)
Thanks for this. Your code contains one or two typos and some deprecated code, but it certainly pointed me in the right direction - mainly searchView.setSuggestionsAdapter(). I'll update my question with my working code shortly. Cheers! :-)Rifleman
worked with searchView quite some time back so not too updated with the deprecated methods on that.. Sorry about that. And let me know what typos are there, i can certainly update the answer. CheersAve
O
2

user2511882's answer is perfect, but that constructor is deprecated.

Deprecated :
public ExampleAdapter(Context context, Cursor cursor) {
    super(context, cursor);
    this.cursor= cursor;
}

you can use the below one instead of that.

public ExampleAdapter(Context context, Cursor c, int flags) {
    super(context, c, flags);
}

and then call it like this.

searchView.setSuggestionsAdapter(new ExampleAdapter(context,YOUR_DATA,CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER));

https://developer.android.com/reference/android/widget/CursorAdapter.html

Overtime answered 31/7, 2017 at 5:20 Comment(0)
B
0

I think you will need to create a CustomAdapter that access your own layout to do this. It is surprising this hasn't been made easier to modify. SuggestionsAdapter isn't public so you can't extend it. It's not pretty but the easiest solution will be to copy SuggestionsAdapter.java into your own project and point it to a layout in your own res/layouts. I would again copy the version from the Android source code and modify it to your needs.

The Android 5 version is even worse for modifying so this is the KitKat version: http://grepcode.com/file_/repository.grepcode.com/java/ext/com.google.android/android/4.4.4_r1/android/widget/SuggestionsAdapter.java/?v=source

Change this line to point to your layout file:

super(context,
        com.android.internal.R.layout.search_dropdown_item_icons_2line, // make it you layout xml (see below)
        null,   // no initial cursor
        true);  // auto-requery

http://grepcode.com/file_/repository.grepcode.com/java/ext/com.google.android/android/4.4.4_r1/frameworks/base/core/res/res/layout/search_dropdown_item_icons_2line.xml/?v=source

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:paddingStart="@dimen/dropdownitem_text_padding_left"
    android:paddingEnd="4dip"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" > <!-- changed -->

Also in the layout file:

<TextView android:id="@android:id/text2"
    style="?android:attr/dropDownItemStyle"
    android:textAppearance="?android:attr/textAppearanceSearchResultSubtitle"
    <!-- android:singleLine="true" --> <!-- remove this line -->
    android:layout_width="match_parent"
    android:layout_height="wrap_content" <!-- changed -->

user2511882's answer is probably better than this quick hack.

Boyes answered 27/1, 2015 at 21:24 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.