Android 自定义view之扇形菜单(中)

本文详细介绍了如何在Android中实现自定义扇形菜单的点击、滑动切换、长按编辑及拖动排序功能。通过在FanMenuView、FanSelectTextLayout和FanRootView中添加相应的方法和事件处理,实现了菜单的动态旋转、滑动切换页面、长按进入编辑模式以及拖动排序。在编辑模式下,用户可以删除项目并自动排序,点击其他区域退出编辑模式。
摘要由CSDN通过智能技术生成

上一篇文章我们介绍了布局和绘制,已经可以显示出基本的效果了,如果没看过的可以先看一下这篇文章Android 自定义view之扇形菜单(上),这次我们来添加各种点击,滑动和拖动排序等功能,先回忆一下需求:


仔细看上图,我们发现大概的事件有几个:1点击三个tab可以滚动切换页面;2滑动可以随手指滚动,超过30度或者速度达到一定值切换到下一项,到达最后一个后再滚动指示器要从另一边出来(注意这里跟点击时候不一样);3 在快捷开关和常用应用中长按可以进入编辑模式,编辑模式可以删除一项同时剩下的自动排序,拖动交换位置,点击其他区域退出编辑模式。大概功能就这些,那我们就来一个一个的实现。


一、点击和滑动切换

点击和滑动切换其实是放在一起的,无非就是触发方式不一样,点击以后也是要执行一个动画,滑动的时候就是多加了一些判断,最后决定是回滚到原来位置还是切换下一项。回忆上一篇文章,其实我们每一个view在绘制的时候都有考虑滚动的情况,举个例子

FanMenuView.java中的drawchild:

    @Override
    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {

        int index = indexOfChild(child);

        float rotateDegree = getCurrentDegreeByChildIndex(index) + mOffsetDegree;
        rotateDegree = isLeft() ? rotateDegree : -rotateDegree;

        canvas.save();
        canvas.rotate(rotateDegree, isLeft() ? 0 : getWidth(), getHeight());
        boolean result = super.drawChild(canvas, child, drawingTime);
        canvas.restore();
        return result;
    }

    public float getCurrentDegreeByChildIndex(int index) {
        int temp = mCurrentPage - index - 1;
        if (temp == -2) {
            temp = 1;
        } else if (temp == 2) {
            temp = -1;
        }

        return temp * CUR_DEGREE;
    }

从代码可以看出来,我们在canvas.rotate的时候旋转角度是通过当前child的位置和offset来决定的,那默认我们的offset是0,如果我们实时增加这个offset值,那么就已经可以做到旋转了,同样的在FanSelectTextLayout中也是如此,我们只要在每个类中增加一个方法,改变这个offset值,然后调用invalidate()函数进行重绘那么就可以做到旋转了,ok那么我们就来添加这个方法。

FanMenuView

    /**
     * 旋转指示器指示的位置
     *
     * @param cur    当前指示的是哪个位置,(0,1,2)
     * @param offset 偏移(-1 -0 - 1)
     */
    public void setRotateView(int cur, float offset) {
        mCurrentPage = cur;
        mOffsetDegree = offset * CUR_DEGREE;

        mFavorite.setDisableTouchEvent(true);
        mRecently.setDisableTouchEvent(true);
        mToolBox.setDisableTouchEvent(true);
        switch (mCurrentPage) {
            case CardState.CARD_STATE_FAVORITE:
                mFavorite.setDisableTouchEvent(false);
                break;
            case CardState.CARD_STATE_RECENTLY:
                mRecently.setDisableTouchEvent(false);
                break;
            case CardState.CARD_STATE_TOOLBOX:
                mToolBox.setDisableTouchEvent(false);
                break;
            default:
                break;
        }
        invalidate();
    }

这里我们添加了一个方法,两个参数,一个是当前选中的哪个项,另一个就是上面说的偏移值,大小从-1到0,0-1的Float值,这样我们在FanRootView中调用这个函数就可以实时改变我们的菜单view的旋转角度了,同理我们添加FanSelectTextLayout和FanSelectTextIndicator,

FanSelectTextLayout

    /**
     * 旋转指示器指示的位置
     * @param cur 当前指示的是哪个位置,(0,1,2)
     * @param offset 偏移(-1 -0 - 1)
     */
    public void setRotateView(int cur, float offset){

        switch (cur){
            case CardState.CARD_STATE_FAVORITE:

                if(offset == 0)
                {
                    mFavorite.setTextColor(mSelectColor);
                    mToolBox.setTextColor(mNormalColor);
                    mRecently.setTextColor(mNormalColor);
                }
                else
                {
                    int color1 = mColorShades.setShade(Math.abs(offset))
                            .setFromColor(mSelectColor)
                            .setToColor(mNormalColor).generate();

                    mFavorite.setTextColor(color1);

                    int color2 = mColorShades.setShade(Math.abs(offset))
                            .setFromColor(mNormalColor)
                            .setToColor(mSelectColor).generate();
                    if(offset > 0)
                    {
                        mToolBox.setTextColor(color2);
                        mRecently.setTextColor(mNormalColor);
                    }
                    else {
                        mRecently.setTextColor(color2);
                        mToolBox.setTextColor(mNormalColor);
                    }
                }
                break;
            case CardState.CARD_STATE_RECENTLY:
                if(offset == 0)
                {
                    mRecently.setTextColor(mSelectColor);
                    mToolBox.setTextColor(mNormalColor);
                    mFavorite.setTextColor(mNormalColor);
                }
                else
                {
                    int color1 = mColorShades.setShade(Math.abs(offset))
                            .setFromColor(mSelectColor)
                            .setToColor(mNormalColor).generate();

                    mRecently.setTextColor(color1);

                    int color2 = mColorShades.setShade(Math.abs(offset))
                            .setFromColor(mNormalColor)
                            .setToColor(mSelectColor).generate();
                    if(offset > 0)
                    {
                        mFavorite.setTextColor(color2);
                        mToolBox.setTextColor(mNormalColor);
                    }
                    else {
                        mToolBox.setTextColor(color2);
                        mFavorite.setTextColor(mNormalColor);
                    }
                }
                break;
            case CardState.CARD_STATE_TOOLBOX:
                if(offset == 0)
                {
                    mToolBox.setTextColor(mSelectColor);
                    mFavorite.setTextColor(mNormalColor);
                    mRecently.setTextColor(mNormalColor);
                }
                else
                {
                    int color1 = mColorShades.setShade(Math.abs(offset))
                            .setFromColor(mSelectColor)
                            .setToColor(mNormalColor).generate();

                    mToolBox.setTextColor(color1);

                    int color2 = mColorShades.setShade(Math.abs(offset))
                            .setFromColor(mNormalColor)
                            .setToColor(mSelectColor).generate();
                    if(offset > 0)
                    {
                        mRecently.setTextColor(color2);
                        mFavorite.setTextColor(mNormalColor);
                    }
                    else {
                        mFavorite.setTextColor(color2);
                        mRecently.setTextColor(mNormalColor);
                    }
                }
                break;
            default:
                break;
        }
    }

这里我们直接改变textview的color,color是通过一个color生成器来生成的颜色,这样就根据offset渐变成了我们选中的颜色,color生成器代码如下:

package com.jeden.fanmenu.util;

import android.graphics.Color;

/**
 * Created by jeden on 2017/3/15.
 */

public class ColorShades {
    private int mFromColor;
    private int mToColor;
    private float mShade;

    public ColorShades setToColor(int toColor)
    {
        this.mToColor = toColor;
        return this;
    }

    public ColorShades setFromColor(int fromColor)
    {

        this.mFromColor = fromColor;
        return this;
    }

    public ColorShades setShade(float shade)
    {
        this.mShade = shade;
        return this;
    }

    public int generate()
    {
        int fromR = Color.red(mFromColor);
        int fromG = Color.green(mFromColor);
        int fromB = Color.blue(mFromColor);

        int toR = Color.red(mToColor);
        int toG = Color.green(mToColor);
        int toB = Color.blue(mToColor);

        int diffR = toR - fromR;
        int diffG = toG - fromG;
        int diffB = toB - fromB;

        int red = fromR + (int)((diffR * mShade));
        int green = fromG + (int)((diffG * mShade));
        int blue = fromB + (int)((diffB * mShade));

        return Color.rgb(red, green, blue);
    }

    public String generateString()
    {
        return String.format("#%06X", 0xFFFFFF & generate());
    }
}


FanSelectTextIndicator

    /**
     * 旋转指示器指示的位置
     *
     * @param cur    当前指示的是哪个位置,(1,2,3)
     * @param offset 偏移(-1 -0 - 1)
     */
    public void setRotateView(int cur, float offset) {

        int tempCur;
        switch (cur) {
            case CardState.CARD_STATE_FAVORITE:
                if (offset < -0.5) {
                    tempCur = CardState.CARD_STATE_RECENTLY;
                    offset += 1;
                    break;
                }
                tempCur = cur;
                break;
            case CardState.CARD_STATE_RECENTLY:
                if (offset > 0.5) {
                    tempCur = CardState.CARD_STATE_FAVORITE;
                    offset -= 1;
                    break;
                }
                tempCur = cur;
                break;
            case CardState.CARD_STATE_TOOLBOX:
                tempCur = cur;
                break;
            default:
                tempCur = cur;
                break;
        }

        tempCur--;
        float offsetDegree = (tempCur + offset) * mFanDegree;
        mStartDegree = isLeft() ? mFanDegree - offsetDegree : -mFanDegree + offsetDegree;
        invalidate();
    }

指示器我们一样也是改变了mStartDegree的角度,然后invalidate以后重新onDraw,就可以改变选择器的位置了。

到这里我们旋转的准备工作已经完成了,下面开始具体实现,首先是加点击事件,这里我们只需要在FanSelectTextLayout中添加点击事件,然后回调到FanRootView中即可

FanSelectTextLayout

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                mInScreenX = event.getX();
                mInScreenY = event.getY();
                mLastTime = System.currentTimeMillis();
                if(isInTheView(event.getX(), event.getY()))
                {
                    getParent().requestDisallowInterceptTouchEvent(true);
                    return true;
                }
            break;
            case MotionEvent.ACTION_MOVE:
            break;
            case MotionEvent.ACTION_UP:
                float upX = event.getX();
                float upY = event.getY();
                long curTime = System.currentTimeMillis();
                if(FanMenuViewTools.getTwoPointDistance(upX, upY, mInScreenX, mInScreenY) < mTouchSlop && curTime - mLastTime < 400)
                {
                    selectTextAndRefresh();
                }
            break;
            default:
            break;
        }
        return super.onTouchEvent(event);
    }

    public void selectTextAndRefresh()
    {
        int index;
        double degree;
        if(isLeft())
        {
            degree = FanMenuViewTools.getDegreeByPoint(mInScreenX, mHeight - mInScreenY);
        }
        else
        {
            degree = FanMenuViewTools.getDegreeByPoint(mWidth - mInScreenX, mHeight - mInScreenY);
        }
        if(degree < 30)
        {
            index = CardState.CARD_STATE_RECENTLY;
        }
        else if(degree > 30 && degree < 60)
        {
            index = CardState.CARD_STATE_TOOLBOX;
        }
        else
        {
            index = CardState.CARD_STATE_FAVORITE;
        }

        if(mStateChangeable != null)
        {
            mStateChangeable.selectCardC
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值