Android——SideMenu侧滑菜单的实现

  最近有些忙,有一段时间没有写博客了,今天跟大家分享下SideMenu侧滑菜单的实现,自定义的Animation效果,给别人看起来就感觉好屌的样子,哈哈!不多说,先看看实现的效果。
  
  这里写图片描述
  
总体来说也并不复杂,下面给大家看下整个项目的结构:
  结构图
  
以下是实现的源码及详细步骤:
  新建android studio项目我这边就不讲述了,说一下android studio关联library项目,很简单,我贴几张图片大家就知道了,File->
 这里写图片描述

然后
 这里写图片描述

  这样就可以了。
  
首先看下ViewAnimationUtils类
  

package com.lai.library.utils;

import android.app.Activity;
import android.os.Handler;
import android.support.v4.widget.DrawerLayout;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.widget.ImageView;

import com.lai.library.animations.FlipAnimation;
import com.lai.library.interfaces.Resourceble;
import com.lai.library.interfaces.ScreenShortable;

import java.util.ArrayList;
import java.util.List;

import static com.lai.sidemenu.R.layout;

/**
 * Created by henry on 2016/5/11.
 * 自定义的动画效果
 */
public class ViewAnimationUtils<T extends Resourceble> {
    /**
     * 切换隐藏与显示的效果
     */
    private final int ANIMATION_DURATION = 10;
    public static final int CIRCULAR_REVEAL_ANIMATION_DURATION = 20;

    private Activity activity;
    private List<T> list;
    private List<View> viewList = new ArrayList<View>();  //View集合
    private ScreenShortable screenShortable;//获取图片
    private DrawerLayout drawerLayout;
    private ViewAnimatorListener viewAnimatorListener;//实现定义接口

    /**
     * 构造器
     */
    public ViewAnimationUtils(Activity activity, List<T> list, ScreenShortable screenShortable, DrawerLayout drawerLayout, ViewAnimatorListener viewAnimatorListener) {
        this.activity = activity;
        this.list = list;
        this.screenShortable = screenShortable;
        this.drawerLayout = drawerLayout;
        this.viewAnimatorListener = viewAnimatorListener;
    }

    /**
     * 显示菜单的数量
     */
    public void showMenuCount() {
        setViewsClickable(false);//刚显示个数时view是出于false的状态
        viewList.clear();//清空组件
        double size = list.size();
        for (int i = 0; i < size; i++) {
            View viewMenu = activity.getLayoutInflater().inflate(layout.menu_list_item, null);
            final int finalI = i;
            /**
             * view的点击事件
             */
            viewMenu.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    int[] localtion = {0, 0};//用数组坐标记录位置
                    v.getLocationOnScreen(localtion);//垂直
                    switchItem(list.get(finalI), localtion[1] + v.getHeight() / 2);     //点击后调用switchItem隐藏
                }
            });
            //得到资源并设置
            ((ImageView) viewMenu.findViewById(com.lai.sidemenu.R.id.menu_item_image)).setImageResource(list.get(i).getImageRes());
            viewMenu.setVisibility(View.GONE);
            viewMenu.setEnabled(false);
            viewList.add(viewMenu);//添加list中
            viewAnimatorListener.addViewToContainer(viewMenu);//添加到动画接口中

            final double position = i;
            final double delay = 3 * ANIMATION_DURATION * (position / size);
            /**
             * 耗时操作
             */
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (position < viewList.size()) {
                        animateView((int) position);
                    }
                    if (position == viewList.size() - 1) {
                        screenShortable.takeScreenShort();
                        setViewsClickable(true);
                    }
                }
            }, (long) delay);


        }

    }

    /**
     * 设置View
     */
    private void animateView(int position) {
        final View view = viewList.get(position);
        view.setVisibility(View.VISIBLE);//可见
        FlipAnimation rotation = new FlipAnimation(90, 0, 0.0f, view.getHeight() / 2.0f);//设置动画效果
        rotation.setDuration(ANIMATION_DURATION);
        rotation.setFillAfter(true);
        rotation.setInterpolator(new AccelerateInterpolator());
        //动画监听
        rotation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
            }

            @Override
            public void onAnimationEnd(Animation animation) {
                view.clearAnimation();//结束后清除动画效果
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }
        });
    }


    /**
     * 点击的Item
     */
    private void switchItem(Resourceble slideMenuItem, int topPosition) {
        this.screenShortable = viewAnimatorListener.onSwitch(slideMenuItem, screenShortable, topPosition);
        //点击后隐藏viewList
        hideMenuContent();
    }

    /**
     * 隐藏menu
     */
    private void hideMenuContent() {
        setViewsClickable(false);
        double size = list.size();
        //隐藏全部
        for (int i = list.size(); i >= 0; i--) {
            final double position = i;//记录在viewList中点击的position作为头部,添加动画效果
            final double delay = 3 * ANIMATION_DURATION * (position / size);    //线程时间(其实这一步没必要,线程的时间可以直接写死)
            //耗时操作应放在handler里面操作
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (position < viewList.size()) {
                        //设置隐藏时的动画
                        animateHideView((int) position);
                    }
                }
            }, (long) delay);
        }
    }

    /**
     * 设置view隐藏时的动画
     */
    private void animateHideView(final int position) {
        final View view = viewList.get(position);//得到点击的view
        //0表示正在处于的状态为0°,90为操作后的度数,0.0f表示起始精确位置,centerY为view高度的二分之一
        FlipAnimation rotation = new FlipAnimation(0, 90, 0.0f, view.getHeight() / 2.0f);//设置动画
        rotation.setDuration(ANIMATION_DURATION);   //设置动画时间
        rotation.setFillAfter(true);
        rotation.setInterpolator(new AccelerateInterpolator());
        rotation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
            }

            /**
             * 结束时
             * @param animation
             */
            @Override
            public void onAnimationEnd(Animation animation) {
                view.clearAnimation();
                view.setVisibility(View.INVISIBLE);//隐藏view
                if (position == viewList.size() - 1) //防止越界
                {
                    viewAnimatorListener.enableHomeButton();
                    drawerLayout.closeDrawers();//关闭drawers
                }
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }
        });
        view.startAnimation(rotation);//开始动画
    }


    /**
     * 设置控件是否可用
     */
    private void setViewsClickable(boolean clickable) {
        //点击以后view不可再重新点击
        viewAnimatorListener.disableHomeButton();
        /**
         * 点击第几个view
         */
        for (View view : viewList) {
            view.setEnabled(clickable);
        }

    }

    /**
     * 定义接口
     */
    public interface ViewAnimatorListener {
        //点击MenuItem
        public ScreenShortable onSwitch(Resourceble slideMenuItem, ScreenShortable screenShortable, int position);

        public void disableHomeButton();

        public void enableHomeButton();

        public void addViewToContainer(View view);
    }
}

  上面的注释写的非常详细了,一目了然。

下面是FlipAnimation类,自定义的动画效果,代码量很少,更容易理解!

package com.lai.library.animations;

/**
 * Created by henry on 2016/5/11.
 */

import android.graphics.Camera;
import android.graphics.Matrix;
import android.view.animation.Animation;
import android.view.animation.Transformation;

/**
 * 自定义动画效果
 */
public class FlipAnimation extends Animation {
    private final float mFromDegrees;//起始的度数
    private final float mToDegrees;//要到达的度数
    private final float mCenterX;// 水平方向X轴的位置
    private final float mCenterY;//Y轴的位置
    private Camera mCamera;


    /**
     * 构造器
     *
     * @param fromDegrees
     * @param toDegrees
     * @param centerX
     * @param centerY
     */
    public FlipAnimation(float fromDegrees, float toDegrees, float centerX, float centerY) {
        this.mFromDegrees = fromDegrees;
        this.mToDegrees = toDegrees;
        this.mCenterX = centerX;
        this.mCenterY = centerY;
    }

    /**
     * 初始化屏幕宽度创建Camera
     *
     * @param width
     * @param height
     * @param parentWidth
     * @param parentHeight
     */
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        mCamera = new Camera();
    }

    /**
     * 动画效果
     */
    public void applyTransformation(float interpolatedTime, Transformation transformation) {
        final float fromDegrees = mFromDegrees;
        float degrees = fromDegrees + ((mToDegrees - fromDegrees) * interpolatedTime);//总度数

        /**
         * 获取值
         */
        final float centerX = mCenterX;
        final float centerY = mCenterY;
        final Camera camera = mCamera;

        /**
         * 对图片进行处理
         * Translate           平移变换
         Rotate                旋转变换
         Scale                  缩放变换
         Skew                  错切变换
         */
        final Matrix matrix = transformation.getMatrix();
        camera.save();  //保存当前状态 ,与restore是成对出现的
        camera.rotateY(degrees);//Y轴旋转
        camera.getMatrix(matrix);//得到设置后的matrix

        camera.restore();//回复当前状态

        /**
         * 下面两句标识以动画中心点为中央部分
         */
        matrix.preTranslate(-centerX, -centerY);
        matrix.postTranslate(centerX, centerY);
    }


}

还有几个要实现的接口就不贴出来了,到最后面可以自己去下载源码。

  写完关联的library以后,接下来就是我们实际操作的页面了。我们只需要在MainActivity里面嵌套一个Fragment就行了,然后再实现完ViewAnimationUtils的四个接口,马上开始。
  第一步写一个Fragment类

package com.lai.sidemenu.fragment;

import android.app.Fragment;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import com.lai.library.interfaces.ScreenShortable;
import com.lai.sidemenu.R;

/**
 * Created by henry on 2016/5/11.
 */
public class ContentFragment extends Fragment implements ScreenShortable {
    /**
     * 点击图片需要切换的选项value
     */
    public static final String CLOSE = "关闭";
    public static final String BUILDING = "创建";
    public static final String BOOK = "书本";
    public static final String PAINT = "交点";
    public static final String CASE = "案例";
    public static final String SHOP = "商品";
    public static final String PARTY = "派对";
    public static final String MOVIE = "电影";

    private View rootView;
    private ImageView mImageView;
    private int res;
    private Bitmap bitmap;

    public static ContentFragment newInstance(int resId) {
        ContentFragment contentFragment = new ContentFragment();
        Bundle bundle = new Bundle();
        bundle.putInt(Integer.class.getName(), resId);
        contentFragment.setArguments(bundle);//设置Arguments
        return contentFragment;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        this.rootView = view.findViewById(R.id.container);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        res = getArguments().getInt(Integer.class.getName());//获取Arguments的值
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_main, null);
        mImageView = (ImageView) view.findViewById(R.id.image_content);
        mImageView.setClickable(true);
        mImageView.setFocusable(true);
        mImageView.setImageResource(res);
        return view;
    }

    /**
     * 设置bitmap
     * 耗时操作
     */
    @Override
    public void takeScreenShort() {
        Thread thread=new Thread(){
            @Override
            public void run() {
                Bitmap bitmap=Bitmap.createBitmap(rootView.getWidth(),rootView.getHeight(),Bitmap.Config.ARGB_8888);
                Canvas canvas=new Canvas(bitmap);
                rootView.draw(canvas);
                ContentFragment.this.bitmap=bitmap;
            }
        };
        thread.start();
    }

    @Override
    public Bitmap getBitmap() {
        return bitmap;
    }
}

在MainActivity中主要就是为Fragment页面添加菜单项,一些点击的操作,见下面代码:

package com.lai.sidemenu;


import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.LinearLayout;

import com.lai.library.interfaces.Resourceble;
import com.lai.library.interfaces.ScreenShortable;
import com.lai.library.model.SlideMenuitem;
import com.lai.library.utils.ViewAnimationUtils;
import com.lai.sidemenu.fragment.ContentFragment;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends ActionBarActivity implements ViewAnimationUtils.ViewAnimatorListener {
    private ContentFragment contentFragment;
    private DrawerLayout drawerLayout;
    private ActionBarDrawerToggle drawerToggle;
    private List<SlideMenuitem> list = new ArrayList<SlideMenuitem>();
    private ViewAnimationUtils viewAnimationUtils;
    private int res = R.drawable.content_music;
    private LinearLayout linearLayout;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        contentFragment = ContentFragment.newInstance(R.drawable.content_music);
        getFragmentManager().beginTransaction().replace(R.id.container_frame, contentFragment).commit();
        drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        drawerLayout.setScrimColor(Color.TRANSPARENT);
        linearLayout = (LinearLayout) findViewById(R.id.left_drawer);
        linearLayout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                drawerLayout.closeDrawers();
            }
        });
        setActionBar();
        createMenuList();
        viewAnimationUtils = new ViewAnimationUtils<>(this, list, contentFragment, drawerLayout, this);

    }

    /**
     * 设置ActionBar一些基础操作
     */
    private void setActionBar() {
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        getSupportActionBar().setHomeButtonEnabled(true);
        getSupportActionBar().setDisplayHomeAsUpEnabled(true);
        drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close) {
            public void onDrawerClosed(View view) {
                super.onDrawerClosed(view);
                linearLayout.removeAllViews();
                linearLayout.invalidate();
            }

            @Override
            public void onDrawerSlide(View drawerView, float slideOffset) {
                super.onDrawerSlide(drawerView, slideOffset);
                if (slideOffset > 0.6 && linearLayout.getChildCount() == 0) {
                    viewAnimationUtils.showMenuCount();
                }
            }

            @Override
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
            }
        };
        drawerLayout.setDrawerListener(drawerToggle);
    }

    /**
     * 创建menu菜单
     */
    private void createMenuList() {
        SlideMenuitem menuitemOne = new SlideMenuitem(ContentFragment.CLOSE, R.drawable.icn_close);
        list.add(menuitemOne);
        SlideMenuitem menuitemTwo = new SlideMenuitem(ContentFragment.BUILDING, R.drawable.icn_1);
        list.add(menuitemTwo);
        SlideMenuitem menuitemthree = new SlideMenuitem(ContentFragment.BOOK, R.drawable.icn_2);
        list.add(menuitemthree);
        SlideMenuitem menuitemFour = new SlideMenuitem(ContentFragment.CASE, R.drawable.icn_3);
        list.add(menuitemFour);
        SlideMenuitem menuitemFive = new SlideMenuitem(ContentFragment.MOVIE, R.drawable.icn_4);
        list.add(menuitemFive);
        SlideMenuitem menuitemSix = new SlideMenuitem(ContentFragment.PAINT, R.drawable.icn_5);
        list.add(menuitemSix);
        SlideMenuitem menuitemSeven = new SlideMenuitem(ContentFragment.SHOP, R.drawable.icn_6);
        list.add(menuitemSeven);
        SlideMenuitem menuitemEight = new SlideMenuitem(ContentFragment.PARTY, R.drawable.icn_7);
        list.add(menuitemEight);

    }

    /**
     * 开始状态
     *
     * @param savedInstanceState
     * @param persistentState
     */
    @Override
    public void onPostCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
        super.onPostCreate(savedInstanceState, persistentState);
        drawerToggle.syncState();
    }

    /**
     * 配置drawerToggle
     *
     * @param newConfig
     */
    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        drawerToggle.onConfigurationChanged(newConfig);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (drawerToggle.onOptionsItemSelected(item)) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public ScreenShortable onSwitch(Resourceble slideMenuItem, ScreenShortable screenShortable, int position) {
        switch (slideMenuItem.getName()) {
            case ContentFragment.CLOSE:
                return screenShortable;
            default:
                return replaceFragment(screenShortable, position);
        }
    }

    /**
     * 切换图片操作
     *
     * @param screenShortable
     * @param position
     * @return
     */
    private ScreenShortable replaceFragment(ScreenShortable screenShortable, int position) {
        this.res = this.res == R.drawable.content_music ? R.drawable.content_films : R.drawable.content_music;
        View view = findViewById(R.id.container_frame);
        int finalRadius = Math.max(view.getWidth(), view.getHeight());
        io.codetail.animation.SupportAnimator animator = io.codetail.animation.ViewAnimationUtils.createCircularReveal(view, 0, position, 0, finalRadius);
        animator.setInterpolator(new AccelerateDecelerateInterpolator());
        animator.setDuration(ViewAnimationUtils.CIRCULAR_REVEAL_ANIMATION_DURATION);

        findViewById(R.id.content_overlay).setBackgroundDrawable(new BitmapDrawable(getResources(), screenShortable.getBitmap()));
        animator.start();//开始动画
        ContentFragment contentFragment = ContentFragment.newInstance(this.res);
        getFragmentManager().beginTransaction().replace(R.id.content_frame, contentFragment).commit();
        return contentFragment;
    }

    /**
     * 设置button不可用
     */
    @Override
    public void disableHomeButton() {
        getSupportActionBar().setHomeButtonEnabled(false);
    }

    @Override
    public void enableHomeButton() {
        getSupportActionBar().setHomeButtonEnabled(true);
        drawerLayout.closeDrawers();
    }

    @Override
    public void addViewToContainer(View view) {
        linearLayout.addView(view);
    }


}

主要的代码已经贴出来了,如果感兴趣的话可以从下面下载项目。

项目源码

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
实现带搜索框的侧边菜单可以分为以下几个步骤: 1. 安装依赖 需要使用到 react-navigation、react-native-vector-icons 和 react-native-elements 这三个库,可以使用 npm 或 yarn 进行安装。 2. 创建侧边菜单和搜索框 使用 react-navigation 中的 DrawerNavigator 创建侧边菜单,然后在 DrawerNavigator 的 contentComponent 中添加搜索框。可以使用 react-native-elements 中的 SearchBar 组件来实现搜索框。 3. 处理搜索功能 获取搜索框中的输入值后,可以将其传递给侧边菜单中的列表组件进行筛选。可以使用 FlatList 组件来渲染列表,并在其 renderItem 属性中根据搜索关键字来筛选列表项。 代码实现如下: ```javascript import React, { useState } from 'react'; import { StyleSheet, View, Text } from 'react-native'; import { createDrawerNavigator } from 'react-navigation-drawer'; import { SearchBar } from 'react-native-elements'; import Icon from 'react-native-vector-icons/FontAwesome'; const data = [ { name: 'Apple', price: '$1.99' }, { name: 'Banana', price: '$0.99' }, { name: 'Cherry', price: '$2.99' }, { name: 'Durian', price: '$5.99' }, { name: 'Elderberry', price: '$3.99' }, ]; const SideMenu = ({ navigation }) => { const [search, setSearch] = useState(''); const filteredData = search ? data.filter(item => item.name.includes(search)) : data; return ( <View style={styles.container}> <SearchBar placeholder="Search" onChangeText={setSearch} value={search} containerStyle={styles.searchBar} inputContainerStyle={styles.inputContainer} searchIcon={<Icon name="search" size={20} color="gray" />} /> {filteredData.map((item, index) => ( <Text key={index} style={styles.item}> {item.name} ({item.price}) </Text> ))} </View> ); }; const DrawerNavigator = createDrawerNavigator( { Home: { screen: SideMenu, navigationOptions: { title: 'Side Menu', drawerIcon: <Icon name="bars" size={20} color="gray" />, }, }, }, { drawerWidth: 200, contentComponent: SideMenu, } ); const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#fff', paddingVertical: 20, paddingHorizontal: 10, }, searchBar: { backgroundColor: '#fff', borderBottomWidth: 0, borderTopWidth: 0, }, inputContainer: { backgroundColor: '#f2f2f2', }, item: { fontSize: 16, paddingVertical: 10, borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: 'gray', }, }); export default DrawerNavigator; ``` 在上面的代码中,我们先定义了一个 data 数组作为列表数据源,然后使用 useState 定义了一个 search 状态来保存搜索框中的输入值,然后根据 search 的值对列表数据进行筛选。在 SideMenu 组件中,我们使用 SearchBar 组件来实现搜索框,并使用 FlatList 组件来渲染列表。在 FlatList 的 renderItem 属性中,我们对每个列表项进行渲染,并根据 search 的值来筛选列表项。 最后,我们使用 createDrawerNavigator 创建了一个侧边菜单,并将 SideMenu 组件作为 contentComponent 属性传递给 DrawerNavigator,在 SideMenu 组件中,我们渲染了一个带搜索框的列表。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值