android 仿PathButton控件

最近项目上有一个关于悬浮组件需求需要优化,想好好多方法,一开始是“大块头”悬浮在顶部——位置占据过大,导致显示内容较少;接着是在向下滑动时隐藏部分控件,向上滑动时再做显示,但是又出现闪跳,而且还是占据较大控件;最后想到可以做仿PathButton控件——控件占据小,体验也不错...

好了废话不多说了,希望接下来的内容对需要该控件的你有帮助(可以自定义控件显示位置)

一、首先创建一个类ComposerLayout去继承RelativeLayout,并创建一个初始化的方法:(包括主按钮和子按钮的添加——图片、位置、大小)  

    /**
     * 初始化
     *
     * @param imgResId          子按鈕drawable的图片id容器
     * @param mainButtonBgResId 主按鈕drawable的图片id
     * @param mainCrossResId    主按鈕上面转动十字drawable的图片id
     * @param pCode             控件显示位置代码,例如“右上角”係
ALIGN_PARENT_BOTTOM|ALIGN_PARENT_RIGHT
     * @param radius            半径
     * @param buttonRadius      转主、子按钮半径
     * @param durationMillis    动画时间
     */
    public void init(int[] imgResId, int mainButtonBgResId, int mainCrossResId, byte pCode, int radius, int buttonRadius, final int durationMillis) {
        durationTime = durationMillis;
        // 處理pCode,將自定義嘅位置值改成align值
        int align1 = 12, align2 = 14;
        if (pCode == RIGHTBOTTOM) { // 右下角
            align1 = ALIGN_PARENT_RIGHT;
            align2 = ALIGN_PARENT_BOTTOM;
        } else if (pCode == CENTERBOTTOM) {// 中下
            align1 = CENTER_HORIZONTAL;
            align2 = ALIGN_PARENT_BOTTOM;
        } else if (pCode == LEFTBOTTOM) { // 左下角
            align1 = ALIGN_PARENT_LEFT;
            align2 = ALIGN_PARENT_BOTTOM;
        } else if (pCode == LEFTCENTER) { // 左中
            align1 = ALIGN_PARENT_LEFT;
            align2 = CENTER_VERTICAL;
        } else if (pCode == LEFTTOP) { // 左上角
            align1 = ALIGN_PARENT_LEFT;
            align2 = ALIGN_PARENT_TOP;
        } else if (pCode == CENTERTOP) { // 中上
            align1 = CENTER_HORIZONTAL;
            align2 = ALIGN_PARENT_TOP;
        } else if (pCode == RIGHTTOP) { // 右上角
            align1 = ALIGN_PARENT_RIGHT;
            align2 = ALIGN_PARENT_TOP;
        } else if (pCode == RIGHTCENTER) { // 右中
            align1 = ALIGN_PARENT_RIGHT;
            align2 = CENTER_VERTICAL;
        }
        // 如果細過半徑就整大佢
        RelativeLayout.LayoutParams thislps = (LayoutParams) this.getLayoutParams();
        Bitmap mBottom = BitmapFactory.decodeResource(mContext.getResources(), imgResId[0]);
        if (pCode == CENTERBOTTOM || pCode == CENTERTOP) {
            if (thislps.width != -1 && thislps.width != -2 && thislps.width < (radius + mBottom.getWidth() + radius * 0.1) * 2) {
                thislps.width = (int) ((radius * 1.1 + mBottom.getWidth()) * 2);
            }
        } else {
            if (thislps.width != -1 && thislps.width != -2 && thislps.width < radius + mBottom.getWidth() + radius * 0.1) {
                // -1係FILL_PARENT,-2係WRAP_CONTENT
                thislps.width = (int) (radius * 1.1 + mBottom.getWidth());
            }
        }
        if (pCode == LEFTCENTER || pCode == RIGHTCENTER) {
            if (thislps.height != -1 && thislps.height != -2 && thislps.height < (radius + mBottom.getHeight() + radius * 0.1) * 2) {
                thislps.width = (int) ((radius * 1.1 + mBottom.getHeight()) * 2);
            }
        } else {
            if (thislps.height != -1 && thislps.height != -2 && thislps.height < radius + mBottom.getHeight() + radius * 0.1) {
                thislps.height = (int) (radius * 1.1 + mBottom.getHeight());
            }
        }
        this.setLayoutParams(thislps);
        // 兩個主要層
        RelativeLayout subLayout = new RelativeLayout(mContext);// 包含若干子按鈕嘅層


        RelativeLayout mainLayout = new RelativeLayout(mContext); // 主按扭
        subButtonContainer = new LinearLayout[imgResId.length];
        // N個子按鈕
        for (int i = 0; i < imgResId.length; i++) {
            ImageView img = new ImageView(mContext);// 子按扭圖片


            img.setImageResource(imgResId[i]);
            LinearLayout.LayoutParams subLayoutLps = new LinearLayout.LayoutParams(buttonRadius, buttonRadius);


            img.setLayoutParams(subLayoutLps);
            subButtonContainer[i] = new LinearLayout(mContext);// 子按钮层
            subButtonContainer[i].setId(100 + i);// 子按钮id(随意设置)
            subButtonContainer[i].addView(img);


            RelativeLayout.LayoutParams subLayoutRps = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
            subLayoutRps.alignWithParent = true;
            subLayoutRps.addRule(align1, RelativeLayout.TRUE);
            subLayoutRps.addRule(align2, RelativeLayout.TRUE);
            subButtonContainer[i].setLayoutParams(subLayoutRps);
            subButtonContainer[i].setVisibility(View.INVISIBLE);// 此处不能为GONE
            subLayout.addView(subButtonContainer[i]);
        }
        RelativeLayout.LayoutParams subLayoutLps = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        subLayoutLps.alignWithParent = true;
        subLayoutLps.addRule(align1, RelativeLayout.TRUE);
        subLayoutLps.addRule(align2, RelativeLayout.TRUE);
        subLayout.setLayoutParams(subLayoutLps);


        RelativeLayout.LayoutParams mainLayoutLps = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        mainLayoutLps.alignWithParent = true;
        mainLayoutLps.addRule(align1, RelativeLayout.TRUE);
        mainLayoutLps.addRule(align2, RelativeLayout.TRUE);
        mainLayout.setLayoutParams(mainLayoutLps);
        mainLayout.setBackgroundResource(mainButtonBgResId);
        cross = new ImageView(mContext);
        cross.setImageResource(mainCrossResId);
        RelativeLayout.LayoutParams mainCrossLps = new RelativeLayout.LayoutParams(buttonRadius, buttonRadius);
        mainCrossLps.alignWithParent = true;
        mainCrossLps.addRule(CENTER_IN_PARENT, RelativeLayout.TRUE);
        cross.setLayoutParams(mainCrossLps);
        mainLayout.addView(cross);
        mAnimationsUtils = new MyAnimationsUtils(subLayout, pCode, radius);
        mainLayout.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (areButtonsShowing) {
                    collapse();
                } else {
                    expand();
                }
            }
        });


        this.addView(subLayout);
        this.addView(mainLayout);
        hasInit = true;
    }

二、接着创建动画类MyAnimationsUtils,这边主要是位置的计算:

1、如果位置设在左中或者右中,其子空间位置计算公式为(startAngle为初始角度):

deltaX = Math.sin((offAngle * i + startAngle) * Math.PI / 180) * Radius;

deltaY = Math.cos((offAngle * i + startAngle) * Math.PI / 180) * Radius;

其他位置都是一样的:

deltaY = Math.sin((offAngle * i + startAngle) * Math.PI / 180) * Radius;

deltaX = Math.cos((offAngle * i + startAngle) * Math.PI / 180) * Radius;

2、主按钮的自转动画:

/**
     * MainButton自轉函數(原本就有嘅靜態函數,未實體化都可以調用)
     *
     * @param fromDegrees    從幾多度
     * @param toDegrees      到幾多度
     * @param durationMillis 轉幾耐
     */
    public static Animation getRotateAnimation(float fromDegrees, float toDegrees, int durationMillis) {
        RotateAnimation rotate = new RotateAnimation(fromDegrees, toDegrees, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
        rotate.setDuration(durationMillis);
        rotate.setFillAfter(true);
        return rotate;
    }

三、最后

1、在布局中使用:

<RelativeLayout
    android:id="@+id/rlParent"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- 长宽可以自定义,但是不能过小,会显示不出来 -->
    <!-- align可以自定义,控件显示位置 -->
    <com.mymvp.pathbutton.ComposerLayout
        android:id="@+id/test"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentBottom="true"
        android:layout_centerVertical="true"
        android:padding="8dp"/>
</RelativeLayout>

2、在MainActivity中使用:

a、初始化:

Display display = getWindowManager().getDefaultDisplay();
int width = display.getWidth();
// 引用控件
mComposerLayout = (ComposerLayout) findViewById(R.id.test);
mComposerLayout.init(new int[]{R.drawable.composer_camera, R.drawable.composer_music, R.drawable
.composer_place, R.drawable.composer_sleep, R.drawable.composer_thought, R.drawable.composer_with}, R
.drawable.composer_button, R.drawable.composer_icn_plus, ComposerLayout.CENTERBOTTOM, (int) (width /
4.8), width / 9, 300);

b、添加子按钮点击事件:

OnClickListener onClickListener = new OnClickListener() {
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                String drawableName = "";
                if (v.getId() == 100 + 0) {
                    drawableName = "composer_camera";
                } else if (v.getId() == 100 + 1) {
                    drawableName = "composer_music";
                } else if (v.getId() == 100 + 2) {
                    drawableName = "composer_place";
                } else if (v.getId() == 100 + 3) {
                    drawableName = "composer_sleep";
                } else if (v.getId() == 100 + 4) {
                    drawableName = "composer_thought";
                } else if (v.getId() == 100 + 5) {
                    drawableName = "composer_with";
                }
                Toast.makeText(MainActivity.this, "clickIDrawableName = " + drawableName + ", clickId = " + v.getId() , Toast.LENGTH_SHORT).show();
            }
        };
        mComposerLayout.setButtonsOnClickListener(onClickListener);

ok!

[源码下载地址] 访问密码 cccb

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值