史上最炫酷的控件

本来打算年前发布的,但是开了一波车,没刹住。年后回来公司APP又改版,累坏宝宝了,改的差不多了,就赶紧抽空把这个炫酷的控件分享给大家。

控件的四大特点

  1. 可旋转:支持普通旋转和惯性旋转,主要是对触摸事件的分析,本控件没有用GestureDetector,条件纯手工,这也方便学习,这也是控件发布的主要原因
  2. 可拖拽:支持长按拖拽,当然这也是触摸事件,我觉得比较精妙的地方是对数据的处理
  3. 炫酷的动画:大多数动画在该控件都有涉及,例如拖拽结束,显示,隐藏和布局改动时
  4. 灵活性:相信大家都用过RecyclerView,用过的都说好,哈哈,我第一次想到它是因为LayoutManager这个类,大家可以随意改动自己菜单的布局,多炫酷啊!可惜后来写着写着,就忘了,有空我会想想怎么实现,当然大家都可以提意见,剩下的还剩它的Adapter,你也可以根据type提供不同的view,但是我去掉了它的回收机制,我想这个菜单应该不需要大量的回收吧(当然有些地方还是需要做的,确实做得不够好),除了用适配器,用户还可以直接在xml中添加子view,但两者不兼容

那它到底长什么样呢?看看炫酷的马赛克吧!

介绍下控件的主要结构吧

控件的第一个子view作为菜单的中心点,所以

int childCount = getChildCount();
if (childCount <= 0) {
    throw new RuntimeException("child count must be more than 1");
}

控件会根据用户对宽高设置的模式,进行相应的测量和布局

if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {
    edge = width >= height ? height : width;
} else {
......
measureChild(child, widthMeasureSpec, heightMeasureSpec);
......
int m = MeasureSpec.makeMeasureSpec((int) (edge * (i == 0 ? 0.8f : 0.6f) * 1.0f / 3), MeasureSpec.EXACTLY);
child.measure(m, m);

大致介绍一下,只有宽高都精确地情况下才支持用户提供的数据,否则会获取子view的宽高,取最大的3者与屏幕宽高相比,如若超过屏幕,则取屏幕宽高小者,反之则取宽高和大者,得到边长,菜单中间边长取边长的0.8,其它孩子取0.6。

布局我就不介绍了,根据角度排布,暂时只有圆形布局这么一项,我会思考加入LayoutManager这个玩意的。

关于触摸事件,介绍真的是三天三夜都介绍不完,长话短说,重写了dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent,从中并没有用GestureDetector,既然是学习,我就没用了,越是好的API,越是让人不想去学习,因为太方便了,现在这版本触摸事件没有考虑cancel这个条件,下个版本会加入的。

关于适配器方面只提供以下3个刷新方法,因为我想菜单的数量并不是太多,太多实在太丑了,但我并没有做数量的限制,这个由用户自己决定。

public final void notifyDataSetChanged() {
    mObservable.notifyChanged();
}
public final void notifyItemInserted(int position) {
    mObservable.notifyItemInserted(position);
}
public final void notifyItemRemoved(int position) {
    mObservable.notifyItemRemoved(position);
}

关于布局动画我只去掉了在拖拽的时候,如果用户觉得其他时候,动画感觉不适可以提出建议,或者动画由用户决定,这也是不错的。

关于向外曝露的接口

public interface OnItemClickListener {
    void onItemClick(View view, int position);
}
public interface OnItemDragListener {
    void onDragStart(View view, int position);
    void onDragMove(View view, float rawX, float rawY, int position);
    void onDragEnd(View view, int position);
}
public interface OnItemFlingListener {
    void onFlingStart();
    void onFlingEnd();
}

为什么提供了OnItemClickListener,是因为我在onInterceptTouchEvent方法中,返回了true。如果用户一定需要孩子的触摸事件,就要考虑完整的逻辑哦。

属性提供

<attr name="centerLayout" /> //菜单中间布局
<attr name="isDrag" />  //可拖拽
<attr name="isFling" />  //可惯性滑动

接近尾声了,还有许多我并没有列出来,感兴趣的可以自己去发掘秘密,控件还做得不好,我希望正在阅读的你把它分享给身边喜爱自定义控件的朋友,大家共同学习,共同进步!还有一句就是大家不要在意标题,那只是一个噱头 - -!

Gradle依赖

compile 'com.crazysunj:coolmenu:1.0.0'

Github地址:https://github.com/crazysunj/Android-CoolMenu

博客地址:http://crazysunj.com/