使用HorizontalScrollView实现侧滑菜单

主要继承 HorizontalScrollView   类 ,在构造方法中设置 菜单的宽,  重写 onMeasure,  onLayout 方法 ,在 onLayout 中设置初始显示到 内容页的 scrollTo ,   一个高级的安卓开发必须熟练掌握 自定义的用法, View的绘制原理。


准备知识:

a. 四舍五入的代码就是把结果加上0.5f再进行强制转换

b.  getDimension()是基于当前DisplayMetrics进行转换,获取指定资源id对应的尺寸。文档里并没说这里返回的就是像素,要注意这个函数的返回值是float,像素肯定是int。   

getDimensionPixelSize()与getDimension()功能类似,不同的是将结果转换为int,并且小数部分四舍五入。

getDimensionPixelOffset()与getDimension()功能类似,不同的是将结果转换为int,并且偏移转换(offset conversion,函数命名中的offset是这个意思)是直接截断小数位,即取整(其实就是把float强制转化为int,注意不是四舍五入哦)。


1、SlidingMenuView 类的实现如下,

package com.example.slidingmenu;

import com.nineoldandroids.view.ViewHelper;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;

public class SlidingMenuView extends HorizontalScrollView {

    /*
     * 1、书写自定义属性的 attr.xml 属性 </br> 2、使用时引入 xmlns命名空间 3、在构造方法中获得我们设置的值
     */

    private LinearLayout mWapper;
    private ViewGroup mMenu;
    private ViewGroup mContent;
    private int mScreenWidth;
    private int mMenuRightPadding;

    private int mMenuWidth;

    private boolean hasMeasured = false;

    private boolean isOpen = false;

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

    public SlidingMenuView(Context context, AttributeSet attrs) {
        this(context, attrs, -1);
    }

    /**
     * 当使用了 自定义属性时, 会调用 此构造方法
     */
    public SlidingMenuView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        // 获取自定义的属性
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
                R.styleable.SlidingMenu, defStyle, 0);
        mMenuRightPadding = a.getDimensionPixelSize(a.getIndex(0),
                (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                        100, context.getResources().getDisplayMetrics()));
        a.recycle();

        WindowManager wm = (WindowManager) context
                .getSystemService(context.WINDOW_SERVICE);
        DisplayMetrics outMetrics = new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(outMetrics);
        mScreenWidth = outMetrics.widthPixels;

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        if (hasMeasured == false) {
            mWapper = (LinearLayout) getChildAt(0);
            mMenu = (ViewGroup) mWapper.getChildAt(0);
            mContent = (ViewGroup) mWapper.getChildAt(1);

            mMenuWidth = mMenu.getLayoutParams().width = mScreenWidth
                    - mMenuRightPadding;
            mContent.getLayoutParams().width = mScreenWidth;
        }

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

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

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
        case MotionEvent.ACTION_UP:
            int scrollX = getScrollX(); // 隐藏 在 左边 的 宽度
            if (scrollX >= mMenuWidth / 2) {
                this.smoothScrollTo(mMenuWidth, 0);
                isOpen = false;
            } else {
                this.smoothScrollTo(0, 0);
                isOpen = true;
            }
            return true;
        default:
            break;
        }

        return super.onTouchEvent(ev);
    }
    
    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        
        // 手指滑动时,   l 的 值 是 从 mMenuWidth  到 0
        Log.e("onScrollChanged", "l="+l + " , oldl="+oldl+ ", mMenuWidth =" +mMenuWidth);
        
        float scaleX = l * 1.0f / mMenuWidth ;    //  1.0  ~ 0.0
        float contentScale = 0.7f+scaleX*0.3f;
        float menuScale = 0.6f + 0.4f *(1- scaleX);
        
        ViewHelper.setTranslationX(mMenu, mMenuWidth * scaleX * 0.95f);
        ViewHelper.setPivotX(mMenu, 0);
        ViewHelper.setPivotY(mMenu, mContent.getHeight()/2);
        ViewHelper.setScaleX(mMenu, menuScale);
        ViewHelper.setScaleY(mMenu, menuScale);
        
        ViewHelper.setPivotX(mContent, 0);
        ViewHelper.setPivotY(mContent, mContent.getHeight()/2);
        ViewHelper.setScaleX(mContent, contentScale);
        ViewHelper.setScaleY(mContent, contentScale);
    }
    

    public void open() {
        if (isOpen == true) {
            return;
        } else {
            this.smoothScrollTo(0, 0);
            isOpen = true;
        }
    }

    public void close() {
        if (isOpen == true) {
            this.smoothScrollTo(mMenuWidth, 0);
            isOpen = false;
        } else {
            return;
        }
    }
    
    public void toggle(){
        if( isOpen )
            close();
        else
            open();
    }

}


2、 xml布局文件中使用的方法如下, 注意 HorizontalScrollView 的子布局里只允许有一个根View

<com.example.slidingmenu.SlidingMenuView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

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

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

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#f0f"
            android:gravity="center" >

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="内容页" />
        </LinearLayout>
    </LinearLayout>

</com.example.slidingmenu.SlidingMenuView>

3、运行效果图

               


4、 总结





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值