Créer un SearchView qui ressemble aux directives de conception de matériel

Je suis actuellement en train d’apprendre comment convertir mon application en Material Design et je suis un peu coincé en ce moment. J’ai ajouté la barre d’outils et j’ai fait en sorte que mon tiroir de navigation recouvre tout le contenu.

J’essaie maintenant de créer une recherche extensible qui ressemble à celle des directives matérielles : entrer la description de l'image ici

C’est ce que j’ai en ce moment et je n’arrive pas à comprendre comment faire comme ci-dessus:
Ma recherche

Ceci est mon menu xml:

    

Cela fonctionne, j’obtiens un élément de menu qui s’étend à SearchView et je peux filtrer ma liste très bien. Cela ne ressemble en rien à la 1ère photo.

J’ai essayé d’utiliser MenuItemCompat.setOnActionExpandListener() sur R.id.action_search afin que je puisse changer l’icône de la maison en une flèche arrière, mais cela ne semble pas fonctionner. Rien ne se déclenche dans l’auditeur. Même si cela fonctionnait, il ne serait toujours pas très proche de la 1ère image.

Comment créer un SearchView dans la nouvelle barre d’outils appcompat qui ressemble aux instructions matérielles?

C’est en fait assez facile à faire si vous utilisez la bibliothèque android.support.v7 .

Étape 1

Déclarez un élément de menu

  

Étape 2

Étendez AppCompatActivity et dans la configuration onCreateOptionsMenu du SearchView.

 import android.support.v7.widget.SearchView; ... public class YourActivity extends AppCompatActivity { ... @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_home, menu); // Resortingeve the SearchView and plug it into SearchManager final SearchView searchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.action_search)); SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE); searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName())); return true; } ... } 

Résultat

entrer la description de l'image ici

entrer la description de l'image ici

Après une semaine de perplexité à ce sujet. Je pense que je l’ai compris.
J’utilise maintenant juste un EditText à l’intérieur de la barre d’outils. Cela m’a été suggéré par oj88 sur reddit.

J’ai maintenant ceci:
Nouvelle rechercheView

D’abord dans onCreate () de mon activité J’ai ajouté le EditText avec une vue d’image sur le côté droit de la barre d’outils comme ceci:

  // Setup search container view searchContainer = new LinearLayout(this); Toolbar.LayoutParams containerParams = new Toolbar.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); containerParams.gravity = Gravity.CENTER_VERTICAL; searchContainer.setLayoutParams(containerParams); // Setup search view toolbarSearchView = new EditText(this); // Set width / height / gravity int[] textSizeAttr = new int[]{android.R.attr.actionBarSize}; int indexOfAttrTextSize = 0; TypedArray a = obtainStyledAtsortingbutes(new TypedValue().data, textSizeAttr); int actionBarHeight = a.getDimensionPixelSize(indexOfAttrTextSize, -1); a.recycle(); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, actionBarHeight); params.gravity = Gravity.CENTER_VERTICAL; params.weight = 1; toolbarSearchView.setLayoutParams(params); // Setup display toolbarSearchView.setBackgroundColor(Color.TRANSPARENT); toolbarSearchView.setPadding(2, 0, 0, 0); toolbarSearchView.setTextColor(Color.WHITE); toolbarSearchView.setGravity(Gravity.CENTER_VERTICAL); toolbarSearchView.setSingleLine(true); toolbarSearchView.setImeActionLabel("Search", EditorInfo.IME_ACTION_UNSPECIFIED); toolbarSearchView.setHint("Search"); toolbarSearchView.setHintTextColor(Color.parseColor("#b3ffffff")); try { // Set cursor colour to white // https://stackoverflow.com/a/26544231/1692770 // https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/widget/TextView.java#L562-564 Field f = TextView.class.getDeclaredField("mCursorDrawableRes"); f.setAccessible(true); f.set(toolbarSearchView, R.drawable.edittext_whitecursor); } catch (Exception ignored) { } // Search text changed listener toolbarSearchView.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { Fragment mainFragment = getFragmentManager().findFragmentById(R.id.container); if (mainFragment != null && mainFragment instanceof MainListFragment) { ((MainListFragment) mainFragment).search(s.toSsortingng()); } } @Override public void afterTextChanged(Editable s) { // https://stackoverflow.com/a/6438918/1692770 if (s.toSsortingng().length() < = 0) { toolbarSearchView.setHintTextColor(Color.parseColor("#b3ffffff")); } } }); ((LinearLayout) searchContainer).addView(toolbarSearchView); // Setup the clear button searchClearButton = new ImageView(this); Resources r = getResources(); int px = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16, r.getDisplayMetrics()); LinearLayout.LayoutParams clearParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); clearParams.gravity = Gravity.CENTER; searchClearButton.setLayoutParams(clearParams); searchClearButton.setImageResource(R.drawable.ic_close_white_24dp); // TODO: Get this image from here: https://github.com/google/material-design-icons searchClearButton.setPadding(px, 0, px, 0); searchClearButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { toolbarSearchView.setText(""); } }); ((LinearLayout) searchContainer).addView(searchClearButton); // Add search view to toolbar and hide it searchContainer.setVisibility(View.GONE); toolbar.addView(searchContainer); 

Cela a fonctionné, mais je suis tombé sur un problème où onOptionsItemSelected () n'était pas appelé lorsque je tapais sur le bouton d'accueil. Je n'ai donc pas pu annuler la recherche en appuyant sur le bouton d'accueil. J'ai essayé de différentes manières d'enregistrer l'auditeur de clic sur le bouton d'accueil, mais cela n'a pas fonctionné.

Finalement, j'ai découvert que le ActionBarDrawerToggle que j'avais était interférer avec les choses, alors je l'ai enlevé. Cet auditeur a alors commencé à travailler:

  toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // toolbarHomeButtonAnimating is a boolean that is initialized as false. It's used to stop the user pressing the home button while it is animating and breaking things. if (!toolbarHomeButtonAnimating) { // Here you'll want to check if you have a search query set, if you don't then hide the search box. // My main fragment handles this stuff, so I call its methods. FragmentManager fragmentManager = getFragmentManager(); final Fragment fragment = fragmentManager.findFragmentById(R.id.container); if (fragment != null && fragment instanceof MainListFragment) { if (((MainListFragment) fragment).hasSearchQuery() || searchContainer.getVisibility() == View.VISIBLE) { displaySearchView(false); return; } } } if (mDrawerLayout.isDrawerOpen(findViewById(R.id.navigation_drawer))) mDrawerLayout.closeDrawer(findViewById(R.id.navigation_drawer)); else mDrawerLayout.openDrawer(findViewById(R.id.navigation_drawer)); } }); 

Je peux donc maintenant annuler la recherche avec le bouton d'accueil, mais je ne peux pas encore appuyer sur le bouton Retour pour l'annuler. J'ai donc ajouté ceci à onBackPressed ():

  FragmentManager fragmentManager = getFragmentManager(); final Fragment mainFragment = fragmentManager.findFragmentById(R.id.container); if (mainFragment != null && mainFragment instanceof MainListFragment) { if (((MainListFragment) mainFragment).hasSearchQuery() || searchContainer.getVisibility() == View.VISIBLE) { displaySearchView(false); return; } } 

J'ai créé cette méthode pour basculer la visibilité du texte EditText et de l'élément de menu:

 public void displaySearchView(boolean visible) { if (visible) { // Stops user from being able to open drawer while searching mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); // Hide search button, display EditText menu.findItem(R.id.action_search).setVisible(false); searchContainer.setVisibility(View.VISIBLE); // Animate the home icon to the back arrow toggleActionBarIcon(ActionDrawableState.ARROW, mDrawerToggle, true); // Shift focus to the search EditText toolbarSearchView.requestFocus(); // Pop up the soft keyboard new Handler().postDelayed(new Runnable() { public void run() { toolbarSearchView.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_DOWN, 0, 0, 0)); toolbarSearchView.dispatchTouchEvent(MotionEvent.obtain(SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, 0, 0, 0)); } }, 200); } else { // Allows user to open drawer again mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED); // Hide the EditText and put the search button back on the Toolbar. // This sometimes fails when it isn't postDelayed(), don't know why. toolbarSearchView.postDelayed(new Runnable() { @Override public void run() { toolbarSearchView.setText(""); searchContainer.setVisibility(View.GONE); menu.findItem(R.id.action_search).setVisible(true); } }, 200); // Turn the home button back into a drawer icon toggleActionBarIcon(ActionDrawableState.BURGER, mDrawerToggle, true); // Hide the keyboard because the search box has been hidden InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(toolbarSearchView.getWindowToken(), 0); } } 

J'avais besoin d'un moyen pour basculer le bouton d'accueil sur la barre d'outils entre l'icône du tiroir et le bouton Retour. J'ai finalement trouvé la méthode ci-dessous dans cette réponse SO . Bien que je l'ai légèrement modifié pour avoir plus de sens pour moi:

 private enum ActionDrawableState { BURGER, ARROW } /** * Modified version of this, https://stackoverflow.com/a/26836272/1692770
* I flipped the start offset around for the animations because it seemed like it was the wrong way around to me.
* I also added a listener to the animation so I can find out when the home button has finished rotating. */ private void toggleActionBarIcon(final ActionDrawableState state, final ActionBarDrawerToggle toggle, boolean animate) { if (animate) { float start = state == ActionDrawableState.BURGER ? 1.0f : 0f; float end = Math.abs(start - 1); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { ValueAnimator offsetAnimator = ValueAnimator.ofFloat(start, end); offsetAnimator.setDuration(300); offsetAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); offsetAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float offset = (Float) animation.getAnimatedValue(); toggle.onDrawerSlide(null, offset); } }); offsetAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { toolbarHomeButtonAnimating = false; } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); toolbarHomeButtonAnimating = true; offsetAnimator.start(); } } else { if (state == ActionDrawableState.BURGER) { toggle.onDrawerClosed(null); } else { toggle.onDrawerOpened(null); } } }

Cela fonctionne, j'ai réussi à résoudre quelques bugs que j'ai trouvés en chemin. Je ne pense pas que ce soit à 100% mais ça marche assez bien pour moi.

EDIT: Si vous souhaitez append la vue de recherche en XML au lieu de Java, procédez comme suit:

toolbar.xml:

       

onCreate () de votre activité:

  searchContainer = findViewById(R.id.search_container); toolbarSearchView = (EditText) findViewById(R.id.search_view); searchClearButton = (ImageView) findViewById(R.id.search_clear); // Setup search container view try { // Set cursor colour to white // https://stackoverflow.com/a/26544231/1692770 // https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/widget/TextView.java#L562-564 Field f = TextView.class.getDeclaredField("mCursorDrawableRes"); f.setAccessible(true); f.set(toolbarSearchView, R.drawable.edittext_whitecursor); } catch (Exception ignored) { } // Search text changed listener toolbarSearchView.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { Fragment mainFragment = getFragmentManager().findFragmentById(R.id.container); if (mainFragment != null && mainFragment instanceof MainListFragment) { ((MainListFragment) mainFragment).search(s.toSsortingng()); } } @Override public void afterTextChanged(Editable s) { } }); // Clear search text when clear button is tapped searchClearButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { toolbarSearchView.setText(""); } }); // Hide the search view searchContainer.setVisibility(View.GONE); 

Je sais qu’il s’agit d’un ancien sujet mais que je poste toujours la bibliothèque que je viens de créer. J’espère que cela pourrait aider quelqu’un.

https://github.com/Shahroz16/material-searchview

Affichage de la mesure

La première capture d’écran de votre question n’est pas un widget public. Le support SearchView ( android.support.v7.widget.SearchView ) imite Android 5.0 Lollipop SearchView ( android.widget.SearchView ). Votre deuxième capture d’écran est utilisée par d’autres applications conçues comme Google Play.

Le SearchView de votre première capture d’écran est utilisé dans Drive, YouTube et d’autres applications Google fermées. Heureusement, il est également utilisé dans Android 5.0 Dialer . Vous pouvez essayer de sauvegarder la vue, mais elle utilise des API 5.0.

Les classes que vous voudrez examiner sont:

SearchEditTextLayout , AnimUtils et DialtactsActivity pour comprendre comment utiliser View. Vous aurez également besoin de ressources de ContactsCommon .

Bonne chance.

Voici ma tentative de le faire:

Étape 1: créez un style nommé SearchViewStyle

  

Étape 2: Créer une mise en page nommée simple_search_view_item.xml

 < ?xml version="1.0" encoding="utf-8"?>  

Étape 3: Créez un élément de menu pour cette vue de recherche

 < ?xml version="1.0" encoding="utf-8"?>    

Étape 4: Gonflez le menu

 @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.menu_searchable_activity, menu); return true; } 

Résultat:

entrer la description de l'image ici

La seule chose que je n’ai pas réussi à faire, c’est de faire remplir toute la largeur de la Toolbar . Si quelqu’un pouvait m’aider à faire cela, alors ce serait en or.

Pour obtenir l’apparence souhaitée de SearchView, vous pouvez utiliser des styles.

Tout d’abord, vous devez créer un style pour votre SearchView, qui devrait ressembler à ceci:

  

Toute la liste des atsortingbuts que vous pouvez trouver dans cet article, sous la section “SearchView”.

Deuxièmement, vous devez créer un style pour votre Toolbar , qui est utilisé comme ActionBar:

  

Et enfin, vous devez mettre à jour votre atsortingbut de thème Toolbar de cette façon:

  

Résultat:

entrer la description de l'image ici

REMARQUE: vous devez modifier directement l’atsortingbut de thème de la Toolbar . Si vous ne faites que mettre à jour votre atsortingbut principal searchViewStyle cela n’affectera pas votre Toolbar .

Vous pouvez également obtenir l’effet souhaité en utilisant cette bibliothèque de vues Recherche de matériaux . Il gère automatiquement l’historique des recherches et il est également possible de fournir des suggestions de recherche à la vue.

Sample: (il est montré en portugais, mais il fonctionne aussi en anglais et en italien).

Échantillon

Installer

Avant de pouvoir utiliser cette lib, vous devez implémenter une classe nommée MsvAuthority dans le package br.com.mauker de votre module d’application, et elle doit avoir une variable de chaîne statique publique appelée CONTENT_AUTHORITY . Donnez-lui la valeur que vous voulez et n’oubliez pas d’append le même nom sur votre fichier manifeste. La lib utilisera ce fichier pour définir l’autorité du fournisseur de contenu.

Exemple:

MsvAuthority.java

 package br.com.mauker; public class MsvAuthority { public static final Ssortingng CONTENT_AUTHORITY = "br.com.mauker.materialsearchview.searchhistorydatabase"; } 

AndroidManifest.xml

 < ?xml version="1.0" encoding="utf-8"?>      

Usage

Pour l’utiliser, ajoutez la dépendance:

 comstack 'br.com.mauker.materialsearchview:materialsearchview:1.2.0' 

Et puis, dans votre fichier de disposition d’ Activity , ajoutez ce qui suit:

 

Après cela, vous aurez juste besoin d’obtenir la référence MaterialSearchView en utilisant getViewById() , et de l’ouvrir ou de la fermer en utilisant MaterialSearchView#openSearch() et MaterialSearchView#closeSearch() .

PS: il est possible d’ouvrir et de fermer la vue non seulement à partir de la Toolbar . Vous pouvez utiliser la méthode openSearch() partir de n’importe quel Button , tel qu’un bouton d’action flottant.

 // Inside onCreate() MaterialSearchView searchView = (MaterialSearchView) findViewById(R.id.search_view); Button bt = (Button) findViewById(R.id.button); bt.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { searchView.openSearch(); } }); 

Vous pouvez également fermer la vue à l’aide du bouton Précédent, en procédant comme suit:

 @Override public void onBackPressed() { if (searchView.isOpen()) { // Close the search on the back button press. searchView.closeSearch(); } else { super.onBackPressed(); } } 

Pour plus d’informations sur l’utilisation de la lib, consultez la page github .

Ce qui suit va créer un SearchView identique à celui de Gmail et l’append à la barre d’outils donnée. Vous devrez simplement implémenter votre propre méthode “ViewUtil.convertDpToPixel”.

 private SearchView createMaterialSearchView(Toolbar toolbar, Ssortingng hintText) { setSupportActionBar(toolbar); ActionBar actionBar = getSupportActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); actionBar.setDisplayShowCustomEnabled(true); actionBar.setDisplayShowTitleEnabled(false); SearchView searchView = new SearchView(this); searchView.setIconifiedByDefault(false); searchView.setMaxWidth(Integer.MAX_VALUE); searchView.setMinimumHeight(Integer.MAX_VALUE); searchView.setQueryHint(hintText); int rightMarginFrame = 0; View frame = searchView.findViewById(getResources().getIdentifier("android:id/search_edit_frame", null, null)); if (frame != null) { LinearLayout.LayoutParams frameParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); rightMarginFrame = ((LinearLayout.LayoutParams) frame.getLayoutParams()).rightMargin; frameParams.setMargins(0, 0, 0, 0); frame.setLayoutParams(frameParams); } View plate = searchView.findViewById(getResources().getIdentifier("android:id/search_plate", null, null)); if (plate != null) { plate.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); plate.setPadding(0, 0, rightMarginFrame, 0); plate.setBackgroundColor(Color.TRANSPARENT); } int autoCompleteId = getResources().getIdentifier("android:id/search_src_text", null, null); if (searchView.findViewById(autoCompleteId) != null) { EditText autoComplete = (EditText) searchView.findViewById(autoCompleteId); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, (int) ViewUtil.convertDpToPixel(36)); params.weight = 1; params.gravity = Gravity.CENTER_VERTICAL; params.leftMargin = rightMarginFrame; autoComplete.setLayoutParams(params); autoComplete.setTextSize(16f); } int searchMagId = getResources().getIdentifier("android:id/search_mag_icon", null, null); if (searchView.findViewById(searchMagId) != null) { ImageView v = (ImageView) searchView.findViewById(searchMagId); v.setImageDrawable(null); v.setPadding(0, 0, 0, 0); LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); params.setMargins(0, 0, 0, 0); v.setLayoutParams(params); } toolbar.setTitle(null); toolbar.setContentInsetsAbsolute(0, 0); toolbar.addView(searchView); return searchView; }