Implementing SearchView as per the material design guidelines

前端 未结 7 860
梦谈多话
梦谈多话 2020-12-04 05:04

I have been looking for ways to implement a searchview in the activity toolbar (actionbar) as per the material design guidelines.

On clicking on the search icon, the

7条回答
  •  感动是毒
    2020-12-04 05:19

    I tried several material SearchView libraries, but none of them worked good as the one from the support library, so I decided to redesign it, after a lot of work, I am pleased with the result:

    Here is how you can do it:

    1) Add SearchView item to your menu

    
    

    Notice that I'm declaring actionLayout instead of actionViewClass, I figured that this is the only way to set SearchView theme separately from Toolbar theme.

    search_view_layout.xml:

    
    

    2) Add the custom SearchView theme to your styles, declare SearchView theme in your Toolbar theme as well:

    
    
    
    
    
    

    toolbar_search_view.xml:

    
    
    
    
    
    
    
    
    
        
    
        
        
    
            
    
            
        
    
        
    
            
    
            
        
    
    

    Notice that I added anchor dropdown view under the Toolbar view, so suggestions will get full screen width.

    
    
    
    
    
    
    
    

    search_view_suggestion_row.xml:

    (change suggestion_divider visibility if you want divider between suggestions):

    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

    The suggestions background and the commit icon are custom made, the rest of the icons I used can be found at: https://material.io/icons/

    ic_search_commit.xml:

    
        
    

    search_suggestions_bg.xml:

    
    
        
            
            
        
    
    
        
            
        
    
    
    

    Add following values to your colors.xml (add values-night only if you are using DayNight theme):

    values/colors.xml

    #DE000000
    #61000000
    #8A000000
    #1F000000
    #1F000000
    #8A000000
    @android:color/white
    #757575
    

    values-night/colors.xml:

    #1FFFFFFF
    @android:color/white
    #424242
    

    3) Last part, make the magic happen in code:

    Setup and initialize SearchView in your desired activity

    private MenuItem mSearchItem;
    private Toolbar mToolbar;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        mToolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(mToolbar);
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
    
        mSearchItem = menu.findItem(R.id.m_search);
    
        MenuItemCompat.setOnActionExpandListener(mSearchItem, new MenuItemCompat.OnActionExpandListener() {
            @Override
            public boolean onMenuItemActionCollapse(MenuItem item) {
                // Called when SearchView is collapsing
                if (mSearchItem.isActionViewExpanded()) {
                    animateSearchToolbar(1, false, false);
                }
                return true;
            }
    
            @Override
            public boolean onMenuItemActionExpand(MenuItem item) {
                // Called when SearchView is expanding
                animateSearchToolbar(1, true, true);
                return true;
            }
        });
    
        return true;
    }
    
    public void animateSearchToolbar(int numberOfMenuIcon, boolean containsOverflow, boolean show) {
    
        mToolbar.setBackgroundColor(ContextCompat.getColor(this, android.R.color.white));
        mDrawerLayout.setStatusBarBackgroundColor(ContextCompat.getColor(this, R.color.quantum_grey_600));
    
        if (show) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                int width = mToolbar.getWidth() -
                        (containsOverflow ? getResources().getDimensionPixelSize(R.dimen.abc_action_button_min_width_overflow_material) : 0) -
                        ((getResources().getDimensionPixelSize(R.dimen.abc_action_button_min_width_material) * numberOfMenuIcon) / 2);
                Animator createCircularReveal = ViewAnimationUtils.createCircularReveal(mToolbar,
                        isRtl(getResources()) ? mToolbar.getWidth() - width : width, mToolbar.getHeight() / 2, 0.0f, (float) width);
                createCircularReveal.setDuration(250);
                createCircularReveal.start();
            } else {
                TranslateAnimation translateAnimation = new TranslateAnimation(0.0f, 0.0f, (float) (-mToolbar.getHeight()), 0.0f);
                translateAnimation.setDuration(220);
                mToolbar.clearAnimation();
                mToolbar.startAnimation(translateAnimation);
            }
        } else {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                int width = mToolbar.getWidth() -
                        (containsOverflow ? getResources().getDimensionPixelSize(R.dimen.abc_action_button_min_width_overflow_material) : 0) -
                        ((getResources().getDimensionPixelSize(R.dimen.abc_action_button_min_width_material) * numberOfMenuIcon) / 2);
                Animator createCircularReveal = ViewAnimationUtils.createCircularReveal(mToolbar,
                        isRtl(getResources()) ? mToolbar.getWidth() - width : width, mToolbar.getHeight() / 2, (float) width, 0.0f);
                createCircularReveal.setDuration(250);
                createCircularReveal.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        super.onAnimationEnd(animation);
                        mToolbar.setBackgroundColor(getThemeColor(MainActivity.this, R.attr.colorPrimary));
                        mDrawerLayout.setStatusBarBackgroundColor(getThemeColor(MainActivity.this, R.attr.colorPrimaryDark));
                    }
                });
                createCircularReveal.start();
            } else {
                AlphaAnimation alphaAnimation = new AlphaAnimation(1.0f, 0.0f);
                Animation translateAnimation = new TranslateAnimation(0.0f, 0.0f, 0.0f, (float) (-mToolbar.getHeight()));
                AnimationSet animationSet = new AnimationSet(true);
                animationSet.addAnimation(alphaAnimation);
                animationSet.addAnimation(translateAnimation);
                animationSet.setDuration(220);
                animationSet.setAnimationListener(new Animation.AnimationListener() {
                    @Override
                    public void onAnimationStart(Animation animation) {
    
                    }
    
                    @Override
                    public void onAnimationEnd(Animation animation) {
                        mToolbar.setBackgroundColor(getThemeColor(MainActivity.this, R.attr.colorPrimary));
                    }
    
                    @Override
                    public void onAnimationRepeat(Animation animation) {
    
                    }
                });
                mToolbar.startAnimation(animationSet);
            }
            mDrawerLayout.setStatusBarBackgroundColor(getThemeColor(MainActivity.this, R.attr.colorPrimaryDark));
        }
    }
    
    private boolean isRtl(Resources resources) {
        return resources.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
    }
    
    private static int getThemeColor(Context context, int id) {
        Resources.Theme theme = context.getTheme();
        TypedArray a = theme.obtainStyledAttributes(new int[]{id});
        int result = a.getColor(0, 0);
        a.recycle();
        return result;
    }
    

    Few things to notice about the code:

    1) The animation will adjust it's start point based on your set of number of menu items and if the toolbar has overflow icon, it will detect if layout is LTR or RTL automatically.

    2) I'm using navigation drawer activity, so I set StatusBar color to mDrawerLayout, if you are using regular activity, you can set StatusBar color this way:

    getWindow().setStatusBarColor(ContextCompat.getColor(this, R.color.quantum_grey_600));
    

    3) The circular reveal animation will only work on KitKat and above.

提交回复
热议问题