创建一个看起来像材料设计指南的 SearchView [英] Creating a SearchView that looks like the material design guidelines

查看:33
本文介绍了创建一个看起来像材料设计指南的 SearchView的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我目前正在学习如何将我的应用程序转换为 Material design,但我现在有点卡住了.我已经添加了工具栏,并让我的导航抽屉覆盖了所有内容.

我现在正在尝试创建一个可扩展的搜索,看起来像 中找到了下面的方法.虽然我稍微修改了一下,让我觉得更有意义:

私有枚举 ActionDrawableState {汉堡,箭头}/*** 修改版,https://stackoverflow.com/a/26836272/1692770
* 我翻转了动画的开始偏移量,因为这对我来说似乎是错误的.<br>* 我还为动画添加了一个侦听器,以便我可以知道主页按钮何时完成旋转.*/私有无效toggleActionBarIcon(最终ActionDrawableState状态,最终ActionBarDrawerToggle切换,布尔动画){如果(动画){浮动开始 = 状态 == ActionDrawableState.BURGER ?1.0f:0f;float end = Math.abs(start - 1);如果 (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() {@覆盖public void onAnimationUpdate(ValueAnimator 动画) {浮动偏移=(浮动)animation.getAnimatedValue();toggle.onDrawerSlide(null, offset);}});offsetAnimator.addListener(new Animator.AnimatorListener() {@覆盖公共无效onAnimationStart(动画师动画){}@覆盖公共无效 onAnimationEnd(动画师动画){工具栏HomeButtonAnimating = false;}@覆盖public void onAnimationCancel(Animator animation) {}@覆盖公共无效onAnimationRepeat(动画师动画){}});工具栏HomeButtonAnimating = true;offsetAnimator.start();}} 别的 {如果(状态== ActionDrawableState.BURGER){toggle.onDrawerClosed(null);} 别的 {toggle.onDrawerOpened(null);}}}

这很有效,我已经设法解决了我在此过程中发现的一些错误.我不认为它是 100%,但它对我来说已经足够了.

如果您想在 XML 而不是 Java 中添加搜索视图,请执行以下操作:

toolbar.xml:

<线性布局android:id="@+id/search_container"android:layout_width="match_parent"android:layout_height="match_parent"机器人:重力=center_vertical"机器人:方向=水平"><编辑文本android:id="@+id/search_view"android:layout_width="0dp"android:layout_height="?attr/actionBarSize"机器人:layout_weight="1"android:background="@android:color/transparent"机器人:重力=center_vertical"机器人:提示=搜索"android:imeOptions="actionSearch"机器人:输入类型=文本"机器人:maxLines =1"机器人:paddingLeft =2dp"机器人:单线=真"android:textColor="#ffffff"android:textColorHint="#b3ffffff"/><图像视图android:id="@+id/search_clear"android:layout_width="wrap_content"android:layout_height="wrap_content"机器人:layout_gravity="中心"机器人:paddingLeft =16dp"机器人: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);//设置搜索容器视图尝试 {//设置光标颜色为白色//https://stackoverflow.com/a/26544231/1692770//https://github.com/android/platform_frameworks_base/blob/kitkat-release/core/java/android/widget/TextView.java#L562-564Field f = TextView.class.getDeclaredField("mCursorDrawableRes");f.setAccessible(true);f.set(toolbarSearchView, R.drawable.edittext_whitecursor);} catch(忽略异常){}//搜索文本改变的监听器toolbarSearchView.addTextChangedListener(new TextWatcher() {@覆盖public void beforeTextChanged(CharSequence s, int start, int count, int after) {}@覆盖public void onTextChanged(CharSequence s, int start, int before, int count) {片段 mainFragment = getFragmentManager().findFragmentById(R.id.container);if (mainFragment != null && mainFragment instanceof MainListFragment) {((MainListFragment) mainFragment).search(s.toString());}}@覆盖公共无效 afterTextChanged(可编辑的){}});//点击清除按钮时清除搜索文本searchClearButton.setOnClickListener(new View.OnClickListener() {@覆盖public void onClick(View v) {toolbarSearchView.setText("");}});//隐藏搜索视图searchContainer.setVisibility(View.GONE);

I'm currently in the process of learning how to convert my app to Material design and I'm a bit stuck right now. I've got the Toolbar added and I have made my navigation drawer overlay all the content.

I'm now trying to create an expandable search that looks like the one in the material guidelines:

This is what I've got right now and I can't figure out how to make it like the above:

This is my menu 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>

That works, I get a menu item that expands to the SearchView and I can filter my list fine. It doesn't look anything like the 1st picture though.

I tried to use MenuItemCompat.setOnActionExpandListener() on R.id.action_search so I could change the home icon to a back arrow, but that doesn't seem to work. Nothing gets fired in the listener. Even if that worked it still wouldn't be very close to the 1st image.

How do I create a SearchView in the new appcompat toolbar that looks like the material guidelines?

解决方案

After a week of puzzling over this. I think I've figured it out.
I'm now using just an EditText inside of the Toolbar. This was suggested to me by oj88 on reddit.

I now have this:


First inside onCreate() of my activity I added the EditText with an image view on the right hand side to the Toolbar like this:

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

This worked, but then I came across an issue where onOptionsItemSelected() wasn't being called when I tapped on the home button. So I wasn't able to cancel the search by pressing the home button. I tried a few different ways of registering the click listener on the home button but they didn't work.

Eventually I found out that the ActionBarDrawerToggle I had was interfering with things, so I removed it. This listener then started working:

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

So I can now cancel the search with the home button, but I can't press the back button to cancel it yet. So I added this to 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;
        }
    }

I created this method to toggle visibility of the EditText and menu item:

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

I needed a way to toggle the home button on the toolbar between the drawer icon and the back button. I eventually found the method below in this SO answer. Though I modified it slightly to made more sense to me:

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

This works, I've managed to work out a few bugs that I found along the way. I don't think it's 100% but it works well enough for me.

EDIT: If you want to add the search view in XML instead of Java do this:

toolbar.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() of your Activity:

    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的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆