Android自定义View——从零开始实现可展开收起的水平菜单栏

本文介绍了如何在Android中从零开始实现一个可展开收起的水平菜单栏控件,通过继承ViewGroup(RelativeLayout)进行二次开发。内容包括设置背景、绘制按钮、添加动画和点击事件、菜单动画以及测量子View位置的详细步骤。提供源码链接供读者参考。
摘要由CSDN通过智能技术生成

版权声明:本文为博主原创文章,未经博主允许不得转载。
系列教程:Android开发之从零开始系列
源码:github.com/AnliaLee/ExpandMenu,欢迎star

大家要是看到有错误的地方或者有啥好的建议,欢迎留言评论

前言:最近项目里要实现一个 可展开收起的水平菜单栏控件,刚接到需求时想着用自定义View自己来绘制,发现要实现 圆角、阴影、菜单滑动等效果非常复杂且耗时间。好在这些效果 Android原生代码中都已经有非常成熟的解决方案,我们只需要去继承它们进行 二次开发就行。本期将教大家如何继承 ViewGroup(RelativeLayout)实现自定义菜单栏

本篇只着重于思路和实现步骤,里面用到的一些知识原理不会非常细地拿来讲,如果有不清楚的api或方法可以在网上搜下相应的资料,肯定有大神讲得非常清楚的,我这就不献丑了。本着认真负责的精神我会把相关知识的博文链接也贴出来(其实就是懒不想写那么多哈哈),大家可以自行传送。为了照顾第一次阅读系列博客的小伙伴,本篇会出现一些在之前系列博客就讲过的内容,看过的童鞋自行跳过该段即可

国际惯例,先上效果图

目录
  • 为菜单栏设置背景
  • 绘制菜单栏按钮
  • 设置按钮动画与点击事件
  • 设置菜单展开收起动画
  • 测量菜单栏子View的位置

为菜单栏设置背景

自定义ViewGroup自定义View的绘制过程有所不同,View可以直接在自己的onDraw方法中绘制所需要的效果,而ViewGroup会先测量子View的大小位置(onLayout),然后再进行绘制,如果子View或background为空,则不会调用draw方法绘制。当然我们可以调用setWillNotDraw(false)ViewGroup可以在子View或background为空的情况下进行绘制,但我们会为ViewGroup设置一个默认背景,所以可以省去这句代码

设置背景很简单,因为要实现圆角、描边等效果,所以我们选择使用GradientDrawable来定制背景,然后调用setBackground方法设置背景。创建HorizontalExpandMenu,继承自RelativeLayout,同时自定义Attrs属性

public class HorizontalExpandMenu extends RelativeLayout {
   
    private Context mContext;
    private AttributeSet mAttrs;

    private int defaultWidth;//默认宽度
    private int defaultHeight;//默认长度
    private int viewWidth;
    private int viewHeight;

    private int menuBackColor;//菜单栏背景色
    private float menuStrokeSize;//菜单栏边框线的size
    private int menuStrokeColor;//菜单栏边框线的颜色
    private float menuCornerRadius;//菜单栏圆角半径

    public HorizontalExpandMenu(Context context) {
        super(context);
        this.mContext = context;
        init();
    }

    public HorizontalExpandMenu(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
        this.mAttrs = attrs;
        init();
    }

    private void init(){
        TypedArray typedArray = mContext.obtainStyledAttributes(mAttrs, R.styleable.HorizontalExpandMenu);

        defaultWidth = DpOrPxUtils.dip2px(mContext,200);
        defaultHeight = DpOrPxUtils.dip2px(mContext,40);

        menuBackColor = typedArray.getColor(R.styleable.HorizontalExpandMenu_back_color,Color.WHITE);
        menuStrokeSize = typedArray.getDimension(R.styleable.HorizontalExpandMenu_stroke_size,1);
        menuStrokeColor = typedArray.getColor(R.styleable.HorizontalExpandMenu_stroke_color,Color.GRAY);
        menuCornerRadius = typedArray.getDimension(R.styleable.HorizontalExpandMenu_corner_radius,DpOrPxUtils.dip2px(mContext,20));
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int height = measureSize(defaultHeight, heightMeasureSpec);
        int width = measureSize(defaultWidth, widthMeasureSpec);
        viewHeight = height;
        viewWidth = width;
        setMeasuredDimension(viewWidth,viewHeight);

        //布局代码中如果没有设置background属性则在此处添加一个背景
        if(getBackground()==null){
            setMenuBackground();
        }
    }

    private int measureSize(int defaultSize, int measureSpec) {
        int result = defaultSize;
        int specMode = View.MeasureSpec.getMode(measureSpec);
        int specSize = View.MeasureSpec.getSize(measureSpec);

        if (specMode == View.MeasureSpec.EXACTLY) {
            result = specSize;
        } else if (specMode == View.MeasureSpec.AT_MOST) {
            result = Math.min(result, specSize);
        }
        return result;
    }

    /**
     * 设置菜单背景,如果要显示阴影,需在onLayout之前调用
     */
    private void setMenuBackground(){
        GradientDrawable gd = new GradientDrawable();
        gd.setColor(menuBackColor);
        gd.setStroke((int)menuStrokeSize, menuStrokeColor);
        gd.setCornerRadius(menuCornerRadius);
        setBackground(gd);
    }
}

attrs属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--注意这里的name要和自定义View的名称一致,不然在xml布局中无法引用-->
    <declare-styleable name="HorizontalExpandMenu">
        <attr name="back_color" format="color"></attr>
        <attr name="stroke_size" format="dimension"></attr>
        <attr name="stroke_color" format="color"></attr>
        <attr name="corner_radius" format="dimension"></attr>
    </declare-styleable>
</resources>

在布局文件中使用

<com.anlia.expandmenu.widget.HorizontalExpandMenu
    android:id="@+id/expandMenu1"
    android:layout_width="match_parent"
    android:layout_height="40dp"
    android:layout_alignParentBottom="true"
    android:layout_marginBottom="20dp"
    android:layout_marginLeft="15dp"
    android:layout_marginRight="15dp">
</com.anlia.expandmenu.widget.HorizontalExpandMenu>

效果如图


绘制菜单栏按钮

我们要绘制菜单栏两边的按钮,首先是要为按钮“圈地”(测量位置和大小)。设置按钮区域为正方形,位于左侧或右侧(根据开发者设置而定)边长和菜单栏ViewGroup的高相等。按钮中的加号可以使用Path进行绘制(当然也可以用矢量图位图),代码如下

public class HorizontalExpandMenu extends RelativeLayout {
   
    //省略部分代码...
    private float buttonIco
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值