仿QQ5.0侧滑菜单——自定义属性

在上一篇文章中,尝试了仿QQ5.0的侧滑菜单,但是,菜单展开时距离屏幕右侧的宽度(mMenuRightPadding)是在SlidingMenu.java中设定,现在,就来尝试把它设置成自定义属性,可以直接在布局文件中直接设定。

有关于自定义属性,可以参考:http://wujiandong.iteye.com/blog/1184921


第一步:书写xml文件(在res/values下建立,文件名可自定义),用于存放自定义的属性
在这里,可以把名字名设置为attr.xml,简单明了,使人一看就知道是有关于属性的。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="rightPadding" format="dimension" ></attr>

    <declare-styleable name="SlidingMenu">
        <attr name="rightPadding" ></attr>
    </declare-styleable>
</resources>

format=”dimension” 中的dimension表示支持dp、sp等格式(如果只需要int类型,可将dimension改成int即可)

declare-styleable是给自定义控件添加自定义属性用的,相当于一个包含自定义属性元素的集合,在这里,只添加了一个元素


第二步:在布局文件中的根布局下定义命名空间,再进行使用
xmls:自定义空间名=”http://schemas.android.com/apk/res/+当前应用的包名”定义命名空间
接着,就可以像使用android:layout_width=”match_parent”(android为命名空间)一样使用自己的定义的属性了

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:test="http://schemas.android.com/apk/res/com.example.sideslipfollowqq5_0"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <!-- SlidingMenu继承自HorizontalScrollView -->
    <com.example.sideslipfollowqq5_0.SlidingMenu 
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        test:rightPadding="150dp" > <!-- 使用自定义的属性,在这里设置为150dp-->

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

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

           <LinearLayout
               android:layout_width="match_parent"
               android:layout_height="match_parent" 
               android:background="@drawable/qq" >
           </LinearLayout>

        </LinearLayout>

    </com.example.sideslipfollowqq5_0.SlidingMenu>

</RelativeLayout>

注意:
如果仿照attr.xml在values下写一个attr2.xml如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="rightPadding2" format="dimension" ></attr>

    <declare-styleable name="SlidingMenu2">
        <attr name="rightPadding2" ></attr>
    </declare-styleable>
</resources>

那么在布局文件中,可以依托上一个自定义的命名空间(test)来使用该属性,当然,也可以新定义一个命名空间,比如test2,然后test:rightPadding2="150dp"


第三步:在SlidingMenu.java的构造方法(含3个形参的)中获得在布局文件中设置的自定义属性的值,同时还要改写含1个形参以及含2个形参的构造函数

public class SlidingMenu extends HorizontalScrollView {
    private LinearLayout mWapper;//activity_main.xml中com.example.sideslipfollowqq5_0.SlidingMenu布局
    private ViewGroup mMenu;//activity_main.xml中include的布局
    private ViewGroup mContent;//activity_main.xml中第二个LinearLayout布局

    private int mScreenWidth;//屏幕的宽度

    private int mMenuRightPadding;//菜单完全显示时与屏幕右侧的距离(在SlidingMenu中设置成一个固定值)

    private boolean isOnce=true;//因为onMeasure又可能会多次调用(影响效率),这里用于判断onMeasure是否是第一次调用

    private int mMenuWidth;//代表mMenu的宽度

//  /**
//   * 未使用自定义属性时调用
//   * @param context
//   * @param attrs
//   */
//  public SlidingMenu(Context context, AttributeSet attrs) {
//      super(context, attrs);
//      
//      //获取屏幕的宽度
//      WindowManager wm=(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
//      DisplayMetrics outMetrics=new DisplayMetrics();
//      wm.getDefaultDisplay().getMetrics(outMetrics);//getMetrics将会为传入的outMetrics赋值
//      mScreenWidth=outMetrics.widthPixels;
//      
//      //将50dp转化成相应的px
//      //因为dp是相对于分辨率的,而px就是一个像素点,是固定大小的
//      //为了在不同分辨率的屏幕上显示的效果相同,应该将mMenuRightPadding依据不同的分辨率而变换
//      mMenuRightPadding=(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics());
//  }

    public SlidingMenu(Context context) {
        this(context,null);//改写构造参数,调用含2个形参的构造函数
    }

    public SlidingMenu(Context context, AttributeSet attrs) {
        this(context, attrs,0);//改写构造参数,调用含3个形参的构造函数
    }

    /**
     * 当自定义了属性时且使用时,会调用该方法
     * @param context
     * @param attrs
     * @param defStyle
     */
    public SlidingMenu(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);//注意,这里显示的调用父类的构造函数应该放在第一行,因为如果不这样,系统或自动调用一个父类的无参构造函数,而这是不存在的

        //获取自己定义的属性,使用TypeArray类之后要用recycle释放
        TypedArray a=context.getTheme().obtainStyledAttributes(attrs, R.styleable.SlidingMenu, defStyle, 0);//获得attr.xml中<declare-styleable></declare-styleable>下<attr></attr>组成的数组
        int n=a.getIndexCount();//Return the number of indices in the array that actually have data.即获得数组中元素的个数
        for(int i=0;i<n;i++) {
            int attr = a.getIndex(i);//Return an index in the array that has data.即得到数组中第i个具有数据的元素的索引(下标识从0开始)
            switch (attr) {
            case R.styleable.SlidingMenu_rightPadding:  
                //SlidingMenu_rightPadding:"_"前面的为attr.xml中<declare-styleable></declare-styleable>的name,"_"后面的为<attr></attr>的name
                //所以SlidingMenu_rightPadding表示名字为"SlidingMenu"的<declare-styleable></declare-styleable>中名字为"rightPadding"的<attr></attr>

                //getDimensionPixelSize的第一个参数为attr,因为attr同时具有rightPadding设置的值的索引,传入该方法后会根据索引获取rightPadding设置的值
                //getDimensionPixelSize的第二个参数是设置一个默认的值,这里是为了给mMenuRightPadding设置了默认的值50以后
                mMenuRightPadding=a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics()));
                break;
            default:
                break;
            }
        }

        a.recycle();//释放资源

        //获取屏幕的宽度
        WindowManager wm=(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics outMetrics=new DisplayMetrics();
        wm.getDefaultDisplay().getMetrics(outMetrics);//getMetrics将会为传入的outMetrics赋值
        mScreenWidth=outMetrics.widthPixels;

        //给mMenuRightPadding设置了默认的值50以后,这一步就不需要了
        //将50dp转化成相应的px
        //因为dp是相对于分辨率的,而px就是一个像素点,是固定大小的
        //为了在不同分辨率的屏幕上显示的效果相同,应该将mMenuRightPadding依据不同的分辨率而变换
        //mMenuRightPadding=(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, context.getResources().getDisplayMetrics());
    }

    /**
     * 设置子View的宽和高以及自己的宽和高
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (isOnce) {
            mWapper = (LinearLayout) getChildAt(0);// 得到activity_main.xml中SlidingMenu布局下的第一个元素,即activity_main.xml中第一个LinearLayout
            mMenu = (ViewGroup) mWapper.getChildAt(0);
            mContent = (ViewGroup) mWapper.getChildAt(1);

            mMenuWidth=mMenu.getLayoutParams().width = mScreenWidth - mMenuRightPadding;// 设置mMenu的宽度
            mContent.getLayoutParams().width = mScreenWidth;
            isOnce = false;
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    /**
     * 通过设置偏移量,将menu隐藏
     */
    @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) {
        int action=ev.getAction();//通过action判断用户的操作,是按下、抬起、还是在移动
        switch (action) {
        case MotionEvent.ACTION_UP:
            int scrollX=(int) getScrollX();//得到未在屏幕显示出的内容的左侧区域宽度(即左侧隐藏的宽度),动态变化的
            if(scrollX>=mMenuWidth/2) {
                this.smoothScrollTo(mMenuWidth, 0);
                //this.scrollTo(mMenuWidth, 0);
                //如果内容隐藏的区域>=菜单的一半,隐藏菜单
                //smoothScrollTo方法与scroolTo相似,只不过scroolTo是瞬间完成,而该方法有一个缓冲的效果
            }
            else {
                this.smoothScrollTo(0, 0);
            }
            return true;
        default:
            break;
        }
        return super.onTouchEvent(ev);
    }
}

getDimension
获取某个dimen的值,如果是dp或sp的单位,将其乘以density,如果是px,则不乘 返回float

getDimensionPixelOffset
获取某个dimen的值,如果是dp或sp的单位,将其乘以density,如果是px,则不乘 返回int

getDimensionPixelSize
则不管写的是dp还是sp还是px,都会乘以denstiy.

在类TypedArray和类Resources中都有这三个函数,功能类似,TypedArray中的函数是获取自定义属性的,Resources中的函数是获取android预置属性的


最后,就可以运行看看效果了

源码:http://download.csdn.net/download/qq_22804827/9288579

1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值