创建一个看起来像材质设计指南的 SearchView

我目前正在学习如何将我的应用程序转换为材料设计的过程中,我现在有点卡住了。我已经添加了工具栏,并且我已经让我的导航抽屉覆盖了所有的内容。我现在尝试创建一个可扩展的搜索,它看起来像 物料指引中的那个: enter image description here

这就是我现在所拥有的,我不知道如何让它像上面那样:
My search

这是我的菜单 xml:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_search"
android:icon="@android:drawable/ic_menu_search"
android:title="Search"
app:showAsAction="always"
app:actionViewClass="android.support.v7.widget.SearchView" />
</menu>

这样就可以了,我得到一个扩展到 SearchView 的菜单项,我可以很好地过滤我的列表。它看起来一点也不像第一张照片。

我尝试在 R.id.action_search上使用 MenuItemCompat.setOnActionExpandListener(),这样我就可以将主图标改为反向箭头,但这似乎不起作用。没有什么会在听众中被激发。即使成功了,它仍然不会非常接近第一个图像。< br > < br > 如何在新的应用程序工具栏中创建类似材料指南的 SearchView?

145077 次浏览

你问题中的第一个截图不是一个公共部件。支持 SearchView (android.support.v7.widget.SearchView)模仿 Android 5.0棒棒糖的 SearchView (android.widget.SearchView)。你的第二个截图被其他材料设计的应用程序使用,比如 Google Play。

第一个截图中的 SearchView 用于 Drive、 YouTube 和其他封闭源代码的 Google Apps。幸运的是,它也在 Android 5.0拨号器中使用。您可以尝试对视图进行后端移植,但是它使用了一些5.0 API。

你想看的类有:

SearchEditTextLayout 动物工具方言活动来理解如何使用视图。

祝你好运。

经过一个星期的思考,我想我明白了。
我现在只在工具栏中使用 EditText,这是 reddit 上的 oj88建议的。

我现在有了这个:
New SearchView

首先在我的活动 onCreate ()中,我在工具栏的右边添加了一个带图片视图的 EditText,如下所示:

    // 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 = obtainStyledAttributes(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.toString());
}
}


@Override
public void afterTextChanged(Editable s) {
// https://stackoverflow.com/a/6438918/1692770
if (s.toString().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);

这个方法起作用了,但是当我点击 home 按钮时,遇到了 onOptionsItemSelected ()没有被调用的问题。所以我没法按 Home 键取消搜索。我尝试了几种不同的方法来注册主页按钮上的点击监听器,但是都不起作用。最后,我发现我的 ActionBarDrawerToggle 干扰了一些事情,所以我把它删除了。然后这个听众开始工作:

    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));
}
});

所以我现在可以用 home 按钮取消搜索,但是我还不能按后退按钮取消搜索。所以我把这个添加到 onBackPress () :

    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;
}
}

我创建了这个方法来切换 EditText 和菜单项的可见性:

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);
}
}

我需要一种方法来切换工具栏上的主页按钮之间的抽屉图标和后退按钮。我最终在 这么回答中找到了下面的方法。不过我稍微修改了一下,让它对我来说更有意义:

private enum ActionDrawableState {
BURGER, ARROW
}


/**
* Modified version of this, https://stackoverflow.com/a/26836272/1692770<br>
* I flipped the start offset around for the animations because it seemed like it was the wrong way around to me.<br>
* 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);
}
}
}

这个工作,我已经设法解决了一些错误,我发现了一路上。我不认为这是100% ,但它工作的足够好,为我。

编辑: 如果你想用 XML 而不是 Java 来添加搜索视图,可以这样做:

Xml:

<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/toolbar"
contentInsetLeft="72dp"
contentInsetStart="72dp"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:elevation="4dp"
android:minHeight="?attr/actionBarSize"
app:contentInsetLeft="72dp"
app:contentInsetStart="72dp"
app:popupTheme="@style/ActionBarPopupThemeOverlay"
app:theme="@style/ActionBarThemeOverlay">


<LinearLayout
android:id="@+id/search_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal">


<EditText
android:id="@+id/search_view"
android:layout_width="0dp"
android:layout_height="?attr/actionBarSize"
android:layout_weight="1"
android:background="@android:color/transparent"
android:gravity="center_vertical"
android:hint="Search"
android:imeOptions="actionSearch"
android:inputType="text"
android:maxLines="1"
android:paddingLeft="2dp"
android:singleLine="true"
android:textColor="#ffffff"
android:textColorHint="#b3ffffff" />


<ImageView
android:id="@+id/search_clear"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:src="@drawable/ic_close_white_24dp" />
</LinearLayout>
</android.support.v7.widget.Toolbar>

OnCreate ()你的活动:

    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.toString());
}
}


@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);

要实现所需的 SearchView 外观,可以使用样式。

首先,您需要为您的 SearchView 创建 style,它应该像这样:

<style name="CustomSearchView" parent="Widget.AppCompat.SearchView">
<item name="searchIcon">@null</item>
<item name="queryBackground">@null</item>
</style>

您可以在 这个文章的“ SearchView”部分中找到的所有属性列表。

其次,您需要为您的 Toolbar创建一个 style,它被用作 ActionBar:

<style name="ToolbarSearchView" parent="Base.ThemeOverlay.AppCompat.Dark.ActionBar">
<item name="searchViewStyle">@style/CustomSearchView</item>
</style>

最后,您需要这样更新您的 Toolbar 主题属性:

<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:theme="@style/ToolbarSearchView" />

结果:

enter image description here

注意: 您需要直接更改 Toolbar主题属性。如果你只是更新你的主题 searchViewStyle属性,它不会影响你的 Toolbar

如果您使用的是 android.support.v7库,那么实际上做到这一点非常容易。

第一步

声明菜单项

<item android:id="@+id/action_search"
android:title="Search"
android:icon="@drawable/abc_ic_search_api_mtrl_alpha"
app:showAsAction="ifRoom|collapseActionView"
app:actionViewClass="android.support.v7.widget.SearchView" />

第二步

扩展 AppCompatActivity并在 onCreateOptionsMenu中设置 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);
// Retrieve 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;
}


...


}

结果

enter image description here

enter image description here

下面将创建一个与 Gmail 中相同的 SearchView,并将其添加到给定的工具栏中。您只需实现自己的“ ViewUtil.ConvertDpToPixel”方法。

private SearchView createMaterialSearchView(Toolbar toolbar, String 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;
}

下面是我的尝试:

步骤1: 创建一个名为 SearchViewStyle的样式

<style name="SearchViewStyle" parent="Widget.AppCompat.SearchView">
<!-- Gets rid of the search icon -->
<item name="searchIcon">@drawable/search</item>
<!-- Gets rid of the "underline" in the text -->
<item name="queryBackground">@null</item>
<!-- Gets rid of the search icon when the SearchView is expanded -->
<item name="searchHintIcon">@null</item>
<!-- The hint text that appears when the user has not typed anything -->
<item name="queryHint">@string/search_hint</item>
</style>

步骤2: 创建一个名为 simple_search_view_item.xml的布局

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.SearchView
android:layout_gravity="end"
android:layout_height="wrap_content"
android:layout_width="match_parent"
style="@style/SearchViewStyle"
xmlns:android="http://schemas.android.com/apk/res/android" />

步骤3: 为此搜索视图创建菜单项

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
app:actionLayout="@layout/simple_search_view_item"
android:title="@string/search"
android:icon="@drawable/search"
app:showAsAction="always" />
</menu>

第四步: 充气菜单

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

结果:

enter image description here

我唯一做不到的就是让它填满了 Toolbar的整个宽度。如果有人能帮我,那就太好了。

我知道这是一个旧的线程,但仍然张贴图书馆我刚刚。希望这可以帮助某人。

Https://github.com/shahroz16/material-searchview

Meterial Search View

另一种达到预期效果的方法是使用这个 资料检索视图库。它可以自动处理搜索历史,也可以向视图提供搜索建议。

示例: (它用葡萄牙语显示,但也用英语和意大利语显示)。

Sample

设置

在使用这个库之前,必须在应用程序模块的 br.com.mauker包中实现一个名为 MsvAuthority的类,它应该有一个名为 CONTENT_AUTHORITY的公共静态 String 变量。给它您想要的值和 别忘了在您的清单文件中添加相同的名称。库将使用此文件设置 ContentProvider 权限。

例如:

MsvAuthority.java

package br.com.mauker;


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

AndroidManifest.xml

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


<application ... >
<provider
android:name="br.com.mauker.materialsearchview.db.HistoryProvider"
android:authorities="br.com.mauker.materialsearchview.searchhistorydatabase"
android:exported="false"
android:protectionLevel="signature"
android:syncable="true"/>
</application>


</manifest>

用法

要使用它,请添加依赖项:

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

然后,在 Activity布局文件中添加以下内容:

<br.com.mauker.materialsearchview.MaterialSearchView
android:id="@+id/search_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

之后,您只需使用 getViewById()获取 MaterialSearchView引用,并使用 MaterialSearchView#openSearch()MaterialSearchView#closeSearch()打开或关闭它。

附注: 不仅可以从 Toolbar上打开和关闭视图,还可以从 Toolbar上打开和关闭视图。您可以使用基本上来自任何 ButtonopenSearch()方法,例如浮动操作按钮。

// 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();
}
});

您也可以使用后退按钮关闭视图,执行以下操作:

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

有关如何使用库的更多信息,请参见 看看 github 页面