Android SlidingMenu 史上最简单实现方式

1.#概述#

目前的开发中,有很项目中都会要求使用侧滑菜单,当然现在在CSDN上面也是有很多篇文章都是讲述侧滑菜单的实现尤其是弘扬大神的文章更是有很多(弘扬大神博客地址:点这里);此文的绝大部分想法都是学习于弘扬大神,只是进行简单的整理便于使用,那么在这里就给大家讲述一篇比较简单实现侧滑的案例,希望可以帮到想要用的人。

2.#效果展示#
(1).QQ效果
仿QQ5.0效果
(2) 抽屉效果
抽屉式效果
(3)普通效果
普通效果
3.#代码展示区#

(1). 左边菜单left_menu.xml;

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <ListView
        android:dividerHeight="1dp"
        android:scrollbars="none"
        android:id="@+id/menu_list" 
        android:divider="#b2b2b2"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
</RelativeLayout>

(2) 左边菜单区域menu_item.xml;

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="56dp" >

        <ImageView
            android:id="@+id/id_item_img"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:padding="16dp"
            />

        <TextView
            android:id="@+id/id_item_name"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toRightOf="@+id/id_item_img"
            android:paddingBottom="16dp"
            android:paddingLeft="2dp"
            android:paddingTop="16dp"
            android:textSize="16sp" />
    </RelativeLayout>

</LinearLayout>

(3)内容区域 content_main.xml,这个也比较简单只是为了相应的演示功能。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    android:background="#197dec"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:onClick="toggle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="切换菜单"
        />
    <TextView
        android:textColor="#fff"
        android:textSize="36sp"
        android:gravity="center"
        android:text="内容区域"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />
</RelativeLayout>

(4)下面是主配置文件 main.xml,因为在这里有使用自定义的属性,因此必须要添加命名空间在老版本中是xmlns:yourcustonname=”http://schemas.android.com/apk/应用的namespace” 新版本中是xmlns:yourcustonname=”http://schemas.android.com/apk/res-auto”

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:sliding="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <!--
        普通模式
        <enum name="ordinary" value="0"/>
        抽屉模式
        <enum name="drawer" value="1"/>
        仿QQ模式
        <enum name="imitationQQ" value="2"/>
        这个是设置滑动完成后,菜单区域的右边距
        sliding:mMenuRightPadding="100dp"
    -->
    <cn.hxy.com.menu.HSlideMenu
        android:id="@+id/sliding_menu"
        android:background="#ffffff"
        sliding:mType="ordinary"
        sliding:mMenuRightPadding="100dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:orientation="horizontal"
            android:layout_width="wrap_content"
            android:layout_height="match_parent">

            <include layout="@layout/left_menu"/>

            <include layout="@layout/content_main"/>

        </LinearLayout>

    </cn.hxy.com.menu.HSlideMenu>

</LinearLayout>

(5),自定义的类 HSlideMenu.java代码展示。注释添加的比较详细,有不懂得的问题可以留言。

package cn.hxy.com.menu;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import com.nineoldandroids.view.ViewHelper;
import cn.hxy.com.R;
import cn.hxy.com.utils.ConvertUtils;
import cn.hxy.com.utils.ScreenUtil;

/**
 * Created by admin on 2016/1/11.
 */
public class HSlideMenu extends HorizontalScrollView
{
    // 普通菜单
    private static final int TYPE_ORDINARY = 0;
    // 抽屉式菜单
    private static final int TYPE_DRAWER = 1;
    // 仿 5.0 QQ菜单
    private static final int TYPE_IMITATION = 2;
    /**
     * 标识当前的菜单样式选项
     */
    private enum Type
    {
        TYPE_ORDINARY,TYPE_DRAWER,TYPE_IMITATION
    }
    /**
     * HorizontalScrollView 主内容区域唯一控件
     */
    private ViewGroup mWapper;
    /**
     * 左边菜单
     */
    private ViewGroup mMenu;
    /**
     * 主菜单menu的宽度
     */
    private int mMenuWidth;
    /**
     * 菜单距离右边的距离
     */
    private int mMenuRightPadding = 70;
    /**
     * 内容区域
     */
    private ViewGroup mContent;
    /**
     * 定义初始化方法只进行一次
     */
    private boolean isFirst;
    /**
     * 定义屏幕的大小
     */
    private int[] mScreenSize;
    /**
     * 设置默认的菜单样式为
     * 抽屉式菜单
     */
    private Type mType = Type.TYPE_DRAWER;
    /**
     * 标识当前菜单界面是否打开
     */
    private boolean isMenuOpen = false;

    public HSlideMenu(Context context)
    {
        this(context,null);
    }

    public HSlideMenu(Context context, AttributeSet attrs)
    {
        this(context, attrs,0);
    }

    public HSlideMenu(Context context, AttributeSet attrs, int defStyleAttr)
    {
        super(context, attrs, defStyleAttr);
        init(context,attrs,defStyleAttr);
    }
    /**
     * 初始化参数,并获取自定义参数
     * @param context
     */
    private void init(Context context, AttributeSet attrs, int defStyleAttr)
    {
        TypedArray mCustomParams = context.
                obtainStyledAttributes(attrs, R.styleable.HSlideMenu,defStyleAttr,0);
        int count = mCustomParams.getIndexCount();
        for (int i = 0; i < count; i++)
        {
            int attr = mCustomParams.getIndex(i);
            if (attr == R.styleable.HSlideMenu_mMenuRightPadding)
            {
                mMenuRightPadding =
                        mCustomParams.getDimensionPixelSize(attr,
                                ConvertUtils.dp2px(context, 70));

            }
            else if (attr == R.styleable.HSlideMenu_mType)
            {
                int mTypeValue = mCustomParams.getInteger(attr, TYPE_DRAWER);
                switch (mTypeValue)
                {
                    case TYPE_ORDINARY:
                        mType = Type.TYPE_ORDINARY;
                        break;
                    case TYPE_DRAWER:
                        mType = Type.TYPE_DRAWER;
                        break;
                    case TYPE_IMITATION:
                        mType = Type.TYPE_IMITATION;
                        break;
                }

            }
        }
        mCustomParams.recycle();
        mScreenSize = ScreenUtil.getScreenSize(context);
    }
    /**
     * 初始化控件 以及计算控件的宽高
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        if(!isFirst)
        {
            mWapper = (ViewGroup) this.getChildAt(0);
            mMenu = (ViewGroup) mWapper.getChildAt(0);
            mContent = (ViewGroup) mWapper.getChildAt(1);
            mContent.setOnClickListener(new OnClickListener()
            {
                @Override
                public void onClick(View v)
                {
                    if (isMenuOpen)
                    {
                        closeMenu();
                    }
                }
            });
            /**
             * 设置菜单的宽度
             */
            mMenu.getLayoutParams().width = mMenuWidth = mScreenSize[0] - mMenuRightPadding;
            /**
             * 设置朱内容区域宽度
             */
            mContent.getLayoutParams().width = mScreenSize[0];
            isFirst = true;
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    /**
     * 设置menu控件初始隐藏
     * @param changed
     * @param l
     * @param t
     * @param r
     * @param b
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b)
    {
        super.onLayout(changed, l, t, r, b);
        /**
         * 设置默认显示内容区域
         */
        if (changed)
        {
            this.scrollTo(mMenuWidth,0);
        }
    }

    /**
     * 用户滑动时对菜单进行隐藏和展示
     * @param ev
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev)
    {
        int action = ev.getAction();
        switch (action)
        {
            case MotionEvent.ACTION_UP:
                /**
                 * 用户拖动时候动态获取
                 * 隐藏在屏幕左边的距离的值
                 */
                int mScrollX = getScrollX();
                if (mScrollX >= mMenuWidth / 2)
                {
                    /**
                     * 当隐藏的距离大于等于菜单界面的一半时隐藏菜单
                     */
                    this.smoothScrollTo(mMenuWidth,0);
                    isMenuOpen = false;
                }
                else
                {
                    this.smoothScrollTo(0,0);
                    isMenuOpen = true;
                }
                return true;
        }
        return super.onTouchEvent(ev);
    }

    /**
     * 关闭菜单
     */
    private void closeMenu()
    {
        if (!isMenuOpen)
        {
            return;
        }
        /**
         * 做关闭菜单的操作
         */
        this.smoothScrollTo(mMenuWidth,0);
        isMenuOpen = false;
    }

    /**
     * 打开菜单
     */
    private void openMenu()
    {
        if (isMenuOpen)
        {
            return;
        }
        /**
         * 做打开菜单的操作
         */
        this.smoothScrollTo(0,0);
        isMenuOpen = true;
    }

    /**
     * 切换菜单
     */
    public void toggle()
    {
        if (isMenuOpen)
        {
            closeMenu();
        }
        else
        {
            openMenu();
        }
    }

    /**
     * 设置控件的动画效果
     * @param l
     * @param t
     * @param oldl
     * @param oldt
     */
    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt)
    {
        super.onScrollChanged(l, t, oldl, oldt);
        /**
         * scale 为隐藏在屏幕左边的值和菜单界面的比例
         * 取值范围为 1.0 ~~~ 0.0
         */
        float scale = 1.0f * l / mMenuWidth;
        /**
         * 此处的 l 值即表示隐藏在屏幕左边的值
         */
        switch (mType)
        {
            case TYPE_ORDINARY:
                /**
                 * 普通的 不做任何操作
                 */
                break;
            case TYPE_DRAWER:
                /**
                 * 抽屉菜单
                 * 1.用户在拖动的时候,菜单其实已经在当前内容区域的后面
                 */
                ViewHelper.setTranslationX(mMenu,mMenuWidth * scale);
                break;
            case TYPE_IMITATION:
                /**
                 * mMenuWidth * scale 默认为mMenuWidth
                 * 意思为 用户在滑动的时候菜单界面已经移动到内容区域下方
                 * mMenuWidth * scale * 0.7f
                 * 意思为  在内容区域界面的下方已经有课70% 另外30% 还隐藏在屏幕左边
                 */
                ViewHelper.setTranslationX(mMenu,mMenuWidth * scale * 0.7f);
                /**
                 * 仿QQ
                 * 1.内容区域宽和高分别缩小至屏幕的80%
                 * (缩放中心点默认为mContent的中心点 --  这样会导致内容区域缩放异常
                 * 因此 在缩放之前需要设置缩放中心点为 (0,screenHeight / 2))
                 * 2.左侧菜单区域有0.6 ~~ 1.0 透明度效果
                 * 3.左侧菜单 0.7 ~~ 1.0 缩放效果
                 */
                ViewHelper.setPivotX(mContent,0);
                ViewHelper.setPivotY(mContent,mScreenSize[1] / 2);
                /**
                 * 这里需要计算
                 * 1.内容区域缩放的范围为 1.0 ~~ 0.75
                 * mContentScale = 0.75f + 0.25f * scale
                 * 2.菜单区域透明度取值范围为  0.6 ~~ 1.0
                 * mMenuAlpha = 0.6f + 0.4f *(1 - scale)
                 * 3.菜单区域缩放动画范围为  0.7 ~~ 1.0
                 * mMenuScale = 0.7f + 0.3f * (1 - scale)
                 * mMenuScale = 1.0f - 0.3f * scale
                 */
                float mContentScale = 0.75f + 0.25f * scale;
                float mMenuAlpha = 0.6f + 0.4f *(1 - scale);
                float mMenuScale = 1.0f - 0.3f * scale;
                ViewHelper.setScaleX(mContent,mContentScale);
                ViewHelper.setScaleY(mContent,mContentScale);
                ViewHelper.setAlpha(mMenu,mMenuAlpha);
                ViewHelper.setScaleX(mMenu,mMenuScale);
                ViewHelper.setScaleY(mMenu,mMenuScale);
                break;
        }
    }
}

(5) 自定义的属性attr.xml,

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

    <attr name="mMenuRightPadding" format="dimension"/>
    <attr name="mType" format="integer">
        <enum name="ordinary" value="0"/>
        <enum name="drawer" value="1"/>
        <enum name="imitationQQ" value="2"/>
    </attr>

    <declare-styleable name="HSlideMenu">
        <attr name="mMenuRightPadding"/>
        <attr name="mType"/>
    </declare-styleable>

</resources>

(6.)工具类 ScreenUtils代码。

package cn.hxy.com.utils;

import android.content.Context;
import android.util.DisplayMetrics;
import android.view.WindowManager;

/**
 * Created by admin on 2016/1/11.
 */
public class ScreenUtil
{
    /**
     * 获取手机屏幕宽高
     * @param mContext
     * @return
     */
    public static int[] getScreenSize(Context mContext)
    {
        WindowManager mManager = (WindowManager)
                mContext.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics mOutMetrics = new DisplayMetrics();
        mManager.getDefaultDisplay().getMetrics(mOutMetrics);

        return new int[]{mOutMetrics.widthPixels,mOutMetrics.heightPixels};
    }

}

(7) ConvertUtils.xml源码展示:

package cn.hxy.com.utils;

import android.content.Context;
import android.util.TypedValue;

/**
 * 参数转换实体类
 * @author Master
 */
public class ConvertUtils 
{
    /**
     * 将dp值转化为像素px值
     * @param mContext 
     * @param convertValue
     * @return px像素值
     */
    public static int dp2px(Context mContext , float convertValue)
    {
        final float density = mContext.getResources().getDisplayMetrics().density;
        return (int) (convertValue * density + 0.5f);
    }

    /**
     * 系统的方法将dp值转换为px像素值
     * @param convertValue
     * @param mContext
     * @return
     */
    public static int dp2px(float convertValue,Context mContext) 
    {
        return (int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP
                ,convertValue
                ,mContext.getResources().getDisplayMetrics());
    }

    /**
     * 将px像素值转换成dp值
     * @param mContext
     * @param convertValue
     * @return
     */
    public static int px2dp(Context mContext , float convertValue)
    {
        final float density = mContext.getResources().getDisplayMetrics().density;
        return (int) (convertValue / density + 0.5f);
    }

    /**
     * 将px像素值转换为sp字体大小
     * @param mContext
     * @param convertValue
     * @return
     */
    public static int px2sp(Context mContext,float convertValue)
    {
        final float fontScale = mContext.getResources().getDisplayMetrics().scaledDensity;
        return (int) (convertValue / fontScale + 0.5f);
    }

    /**
     * 将sp字体大小转换为px像素值 
     * @param mContext
     * @param convertValue
     * @return
     */
    public static int sp2px(Context mContext , float convertValue) 
    {
        final float fontScale = mContext.getResources().getDisplayMetrics().scaledDensity;
        return (int) (convertValue * fontScale + 0.5f);
    }

    /**
     * 系统方法将sp字体大小值转换为px像素值
     * @param convertValue
     * @param mContext
     * @return
     */
    public static int sp2px(float convertValue,Context mContext) 
    {
        return (int) TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_SP
                ,convertValue
                ,mContext.getResources().getDisplayMetrics());
    }

}

(8) MainActivity代码。

package com.example.slidingmenu;

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

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;


public class MainActivity extends Activity {

    private HSlideMenu sliding_menu;

    private ListView menu_list;
    private List<MenuItem> menuItems;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
    {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.main);

        initMenuItem();

        sliding_menu = (HSlideMenu) findViewById(R.id.sliding_menu);
        menu_list = (ListView) findViewById(R.id.menu_list);
        menu_list.setAdapter(new BaseAdapter() 
        {
            @Override
            public View getView(int position, View convertView, ViewGroup parent)
            {
                ViewHolder viewHolder = null;
                MenuItem menuItem = getItem(position);
                if (convertView == null)
                {
                    convertView = getLayoutInflater().inflate(R.layout.menu_item, parent,false);
                    viewHolder = new ViewHolder();
                    viewHolder.itemImg = (ImageView) convertView.findViewById(R.id.id_item_img);
                    viewHolder.itemName = (TextView) convertView.findViewById(R.id.id_item_name);
                    convertView.setTag(viewHolder);
                }
                else
                {
                    viewHolder = (ViewHolder) convertView.getTag();
                }
                viewHolder.itemName.setText(menuItem.getItemName());
                viewHolder.itemImg.setImageResource(menuItem.getItemImg());
                return convertView;
            }

            @Override
            public long getItemId(int position) {
                return position;
            }

            @Override
            public MenuItem getItem(int position) {
                return menuItems.get(position);
            }

            @Override
            public int getCount() {
                return menuItems.size();
            }
            class ViewHolder
            {
                ImageView itemImg;
                TextView itemName;
            }
        });
    }

    private void initMenuItem()
    {
        menuItems = new ArrayList<MenuItem>();
        for (int i = 0; i < 15; i++)
        {
            menuItems.add(new MenuItem(R.drawable.btn_about_us,"Item " + i));
        }
    }

    public void toggle(View view)
    {
        sliding_menu.toggle();
    }
}

到此本篇博客就结束了。愿大家越来越棒。
在这里感谢弘扬大神。谢谢。

三种效果展示下载链接

源码下载

若有问题欢迎留言。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值