[解決済み] マテリアルデザインガイドラインのようなSearchViewを作成する
質問
現在、自分のアプリを Material デザインに変換する方法を学んでいる最中ですが、少し行き詰っています。ツールバーを追加し、ナビゲーション ドロワーをすべてのコンテンツにオーバーレイするようにしました。
のような拡張可能な検索を作成しようとしているところです。
材料ガイドライン
:
今、こんな感じなんですが、どうやったら上記のようにできるのかがわかりません。
これは私のメニュー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に展開されるメニュー項目が表示され、リストをうまくフィルタリングできるようになりました。しかし、1番目の画像のようには見えません。
を使おうとしたのですが
MenuItemCompat.setOnActionExpandListener()
に
R.id.action_search
で、ホームアイコンを戻る矢印に変更することができましたが、うまくいかないようです。リスナーでは何も実行されません。たとえそれがうまくいったとしても、1枚目の画像にはとても及ばないでしょう。
新しい appcompat ツールバーで、マテリアル ガイドラインのような SearchView を作成するにはどうすればよいですか。
どのように解決するのですか。
1週間ほど悩んだ末に 私はそれを理解したと思います。
私は今、ツールバーの中にある EditText だけを使っています。これは、reddit の oj88 によって提案されたものです。
今、私はこれを持っています。
まずアクティビティのonCreate()内で、右側に画像表示のあるEditTextをこのようにToolbarに追加しています。
// 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);
これはうまくいったのですが、その後、ホームボタンをタップしたときにonOptionsItemSelected()が呼び出されないという問題に出くわしました。そのため、ホームボタンを押しても検索をキャンセルすることができませんでした。ホームボタンのクリックリスナーを登録する方法をいくつか試したのですが、うまくいきませんでした。
最終的には、私が持っていた 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));
}
});
ということで、ホームボタンで検索をキャンセルできるようになりましたが、バックボタンを押してもまだキャンセルできません。そこで、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;
}
}
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);
}
}
私は、ツールバーのホームボタンをドロワーアイコンと戻るボタンの間で切り替える方法が必要でした。私は最終的に、以下のメソッドを このSOの答え . しかし、私はそれが私にとってより理にかなっているように少し修正しました。
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%とは思いませんが、私にとっては十分な働きをしています。
編集: もしJavaの代わりにXMLで検索ビューを追加したい場合は、次のようにしてください。
ツールバー.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>
Activityの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);
関連
-
adb devices OffLine Solution(オフラインソリューション
-
gitlab 設定エラー。リモートリポジトリから読み込めなかったか、ホストキーの検証に失敗しました。
-
Android: インポートモジュールエラー Android リソースのリンクに失敗しました
-
エラーが発生しました。ArrayAdapter は、リソース ID が TextView である必要があります。
-
IllegalStateException。ArrayAdapter は、リソース ID が TextView である必要があります。
-
android bluetooth--Bluetooth on、検索、ペアリング、接続
-
Android基本アプレット
-
アプリの実行エラー。ターゲットデバイスが見つからない問題
-
android studioが新しいプロジェクトを作成しますが、プロジェクトの同期に成功するまでデザインエディタが使用できません。
-
[解決済み] Android マテリアルデザインボタンスタイル
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
NetworkOnMainThreadException
-
android.os の NetworkOnMainThreadException。
-
android:EMSのプロパティ
-
アプリはGoogle検索でインデックスされません Androidmanifestのクソみたいな黄色い警告
-
android block certificate validation CertPathValidatorException: 認証パスのトラストアンカーが見つかりません
-
JVMのエラーに遭遇しました。Java Runtime Environmentによって致命的なエラーが検出されました。
-
AndroidでListViewを使ってカスタムテーブルを描画する
-
超シンプルなアンドロイドのタイムディレイ機能
-
Android Studioのgitの使用とgitの設定パス
-
android.content.ActivityNotFoundException を解決します。Intent問題を処理するActivityが見つからない