版权声明:本文为博主原创文章,未经博主允许不得转载。
系列教程: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