文档管理中心

一个可拖动item的九宫格自定义View

 


一、写在前面的话

也不知道给这个取个啥名,就随便取了一个,主要功能就如上图显示的那样,待选区view可以拖拽进九宫格中,九宫格可以按照顺时针轮播。


二、分析需要的元素

1.待选区的item

选中底色有改变,item可以拖动到九宫格中,在即将进入九宫格的时候有一个吸附的效果。


2.九宫格

点击中间的开始按钮顺时针轮播九宫格


3.拖动的View

在底部待选栏中的item选中的时候绘制,模拟从待选栏拖入九宫格的效果。


三、动画效果

1、拖动的view(思路)

在手指触碰到备选栏中的item一定时间后(500ms左右),在手指的位置绘制一个选中的item并且跟随手指的位置移动。九宫格的轮播位置,在拖动的view进入到其范围(手指位置的坐标与九宫格item中心的直线距离小于九宫格item的内切圆半径)之后,在九宫格中绘制一个拖动的item,手指的位置取消绘制,模拟吸附效果。


2、九宫格的轮播动画

九宫格对于的位置为


123

456

789

轮播的时候顺序为12369874,在对应的位置颜色变为高亮色,再在该位置画上一个稍小的,圆角一致的矩形。(时至今日我都觉得这个方式有点蠢,等后面再优化了)


四、开始画(绘制)

1、准备工作

首先准备三个List去存放九宫格中item的位置,待选栏view的位置,待选栏view中的图片的位置。


 

    private List<RectF> mItemRectFList;

 

    private List<RectF> mSmallRectFList;

 

    private List<Bitmap> mBitmapList;

 private void initItemRectf() {

        if (mItemRectFList == null) {

            mItemRectFList = new ArrayList<>();

        }

        mItemRectFList.clear();

        int fristX = (int) (mWidth * 0.2);

        //第一排

        mItemRectFList.add(new RectF(fristX + mItemRectMagin, top + mItemRectMagin,

                fristX + mItemRectMagin + mItemRectA, top + mItemRectMagin + mItemRectA));

 

        mItemRectFList.add(new RectF(fristX + 2 * mItemRectMagin + mItemRectA, top + mItemRectMagin,

                fristX + 2 * mItemRectMagin + 2 * mItemRectA, top + mItemRectMagin + mItemRectA));

 

        mItemRectFList.add(new RectF(fristX + 3 * mItemRectMagin + 2 * mItemRectA, top + mItemRectMagin,

                fristX + 3 * mItemRectMagin + 3 * mItemRectA, top + mItemRectMagin + mItemRectA));

        //第二排

        mItemRectFList.add(new RectF(fristX + mItemRectMagin, top + 2 * mItemRectMagin + mItemRectA,

                fristX + mItemRectMagin + mItemRectA, top + 2 * mItemRectMagin + 2 * mItemRectA));

        mItemRectFList.add(new RectF(fristX + 2 * mItemRectMagin + mItemRectA, top + 2 * mItemRectMagin + mItemRectA,

                fristX + 2 * mItemRectMagin + 2 * mItemRectA, top + 2 * mItemRectMagin + 2 * mItemRectA));

        mItemRectFList.add(new RectF(fristX + 3 * mItemRectMagin + 2 * mItemRectA, top + 2 * mItemRectMagin + mItemRectA,

                fristX + 3 * mItemRectMagin + 3 * mItemRectA, top + 2 * mItemRectMagin + 2 * mItemRectA));

        //第三排

        mItemRectFList.add(new RectF(fristX + mItemRectMagin, top + mItemRectMagin * 3 + mItemRectA * 2,

                fristX + mItemRectMagin + mItemRectA, top + mItemRectMagin * 3 + mItemRectA * 3));

 

        mItemRectFList.add(new RectF(fristX + 2 * mItemRectMagin + mItemRectA, top + mItemRectMagin * 3 + mItemRectA * 2,

                fristX + 2 * mItemRectMagin + 2 * mItemRectA, top + mItemRectMagin * 3 + mItemRectA * 3));

 

        mItemRectFList.add(new RectF(fristX + 3 * mItemRectMagin + 2 * mItemRectA, top + mItemRectMagin * 3 + mItemRectA * 2,

                fristX + 3 * mItemRectMagin + 3 * mItemRectA, top + mItemRectMagin * 3 + mItemRectA * 3));

 

    }

对九宫格item的位置、大小等信息进行初始化


private void initSmallRectf() {

        if (mSmallRectFList == null) {

            mSmallRectFList = new ArrayList<>();

        }

        mSmallRectFList.clear();

        for (int i = 0; i < 8; i++) {

                if (i<5) {

                    mSmallRectFList.add(new RectF((i + 1) * mSmallRectMagin +  mSmallRectA / 2 + i * mSmallRectA, mSmallRectTop,

                            (i + 1) * mSmallRectMagin + (i + 1) * mSmallRectA +  mSmallRectA / 2, mSmallRectTop + mSmallRectA));

                }else {

                    mSmallRectFList.add(new RectF((i + 1) * mSmallRectMagin +  mSmallRectA*3/2  + i * mSmallRectA, mSmallRectTop,

                            (i + 1) * mSmallRectMagin + (i + 1) * mSmallRectA +  mSmallRectA*3/2 , mSmallRectTop + mSmallRectA));

                }

            }

 

    }

待选栏的item的位置、大小进行初始化。这里我一排显示5个,所以对大于5位置上进行了一些许的微调。


        Bitmap bitmap1 = BitmapFactory.decodeResource(getResources(), R.mipmap.food1);

...

图片的初始化。


private void initPath() {

        int centerX =  mSmallRectMagin;

        int centerY = (int) mSmallRectFList.get(0).centerY();

        mLeftPath = new Path();

        mLeftPath.moveTo(centerX+20,centerY+20);

        mLeftPath.lineTo(centerX,centerY);

        mLeftPath.lineTo(centerX+20,centerY-20);

        mLeftPath.close();

 

        mRightPath = new Path();

        mRightPath.moveTo(mWidth-mSmallRectMagin-20,centerY+20);

        mRightPath.lineTo(mWidth-mSmallRectMagin,centerY);

        mRightPath.lineTo(mWidth-mSmallRectMagin-20,centerY-20);

        mRightPath.close();

 

    }

待选栏的两个小三角,用path画一下,找不到适合的图片了。


2、绘制(onDraw)

下面的顺序就是在onDraw中的绘制顺序。


for (int i = 0; i < mItemRectFList.size(); i++) {

            if (i == 4) {

                canvas.drawRoundRect(mItemRectFList.get(i), 20, 20, mRectFItemSelectPaint);

                //画图片

                if (isPaly) {

                    //画图片

                    canvas.drawBitmap(mBitmapStop, mSrcPlay, mdesPlay, mTextPaint);

                } else {

                    canvas.drawBitmap(mBitmapStart, mSrcPlay, mdesPlay, mTextPaint);

 

                }

 

            } else {

                canvas.drawRoundRect(mItemRectFList.get(i), 20, 20, mRectFItemPaint);

            }

 

        }

画九宫格,位置4上为九宫格的中间那格,根据轮播状态画上不同的图片。


for (int i = 0; i < mSmallRectFList.size(); i++) {

            canvas.drawCircle(mSmallRectFList.get(i).centerX() + offset, mSmallRectFList.get(i).centerY(), mSmallRectA / 2, mRectFItemPaint);

            //画图片

            Rect des = new Rect((int) mSmallRectFList.get(i).left + offset + mSmallRectA / 4, (int) mSmallRectFList.get(i).top + mSmallRectA / 4

                    , (int) mSmallRectFList.get(i).right + offset - mSmallRectA / 4, (int) mSmallRectFList.get(i).bottom - mSmallRectA / 4);

            canvas.drawBitmap(mBitmapList.get(i), mSrcItem, des, mRectFItemPaint);

            des = null;

            //画text

            canvas.drawText(modeTextArray[i], mSmallRectFList.get(i).centerX() + offset - mTextSize, (float) (mSmallRectFList.get(i).bottom + 1.5 * mTextSize), mTextPaint);

            if (mDragIndex != -1 && mDragIndex == i && isDrag) {

                mRectFItemSelectPaint.setAlpha(128);

                canvas.drawCircle(mSmallRectFList.get(i).centerX() + offset, mSmallRectFList.get(i).centerY(), mSmallRectA / 2, mRectFItemSelectPaint);

                //画图片

                Rect desAlpha = new Rect((int) mSmallRectFList.get(i).left + offset + mSmallRectA / 4, (int) mSmallRectFList.get(i).top + mSmallRectA / 4

                        , (int) mSmallRectFList.get(i).right + offset - mSmallRectA / 4, (int) mSmallRectFList.get(i).bottom - mSmallRectA / 4);

                canvas.drawBitmap(mBitmapList.get(i), mSrcItem, desAlpha, mRectFItemPaint);

                mRectFItemSelectPaint.setAlpha(255);

 

            }

        }

 //画箭头

        if (offset < 0) {

            canvas.drawPath(mLeftPath, mTitlePaint);

            canvas.drawPath(mRightPath, mTextPaint);

 

        }else {

            canvas.drawPath(mLeftPath, mTextPaint);

            canvas.drawPath(mRightPath, mTitlePaint);

 

        }

待选栏的绘制,因为有不同的两页,所以小箭头的颜色有些许的区别。


 //画选中效果

        canvas.drawRoundRect(mItemRectFList.get(selectIndex), 20, 20, mRectFSelectPaint);

        for (int z = 0; z < seletIndexArray.length; z++) {

            if (seletIndexArray[z] != -1) {

                canvas.drawCircle(mItemRectFList.get(z).centerX(), mItemRectFList.get(z).centerY(), (float) (mItemRectA / 2.2), mRectFItemSelectPaint);

                Rect des = new Rect((int) mItemRectFList.get(z).left + mItemRectA / 4, (int) mItemRectFList.get(z).top + mItemRectA / 4

                        , (int) mItemRectFList.get(z).right - mItemRectA / 4, (int) mItemRectFList.get(z).bottom - mItemRectA / 4);

                canvas.drawBitmap(mBitmapList.get(seletIndexArray[z]), mSrcItem, des, mRectFItemSelectPaint);

            }

        }

绘制选中效果以及从待选栏拖入的item。


   //画拖动中的view

        if (isDrag && mDragX != 0) {

            canvas.drawCircle(mDragX, mDragY, (float) (mItemRectA / 2.2), mRectFItemSelectPaint);

            matrix.setTranslate(mDragX - mBitmapList.get(mDragIndex).getWidth() / 2, mDragY - mBitmapList.get(mDragIndex).getHeight() / 2);

            Rect des = new Rect(mDragX - mBitmapList.get(mDragIndex).getWidth() / 2, mDragY - mBitmapList.get(mDragIndex).getHeight() / 2

                    , mDragX + mBitmapList.get(mDragIndex).getWidth() / 2, mDragY + mBitmapList.get(mDragIndex).getHeight() / 2);

            canvas.drawBitmap(mBitmapList.get(mDragIndex), mSrcItem, des, mRectFItemSelectPaint);

            des = null;

        }

绘制拖动中的item


五、触摸事件处理

1、判断触摸的有效范围

  //判断点击的是不是九宫格中的item

    private int checkItem(int x, int y) {

        int r = (int) ((mItemRectFList.get(0).right - mItemRectFList.get(0).left) / 2);

        for (int i = 0; i < mItemRectFList.size(); i++) {

 

            if (checkPoint(x, y, mItemRectFList.get(i).centerX(), mItemRectFList.get(i).centerY(), r)) {

                return i;

            }

        }

        return -1;

    }

判断触摸的是不是九宫格中的item


//判断点击的是不是待选栏的item

    private int checkSmallItem(int x, int y) {

        Log.d(TAG, "x=" + x + ",y=" + y);

        int r = (int) ((mSmallRectFList.get(0).right - mSmallRectFList.get(0).left) / 2);

        x = x - offset;

        Log.d(TAG, "x2=" + x);

 

        for (int i = 0; i < mSmallRectFList.size(); i++) {

            if (checkPoint(x, y, mSmallRectFList.get(i).centerX(), mSmallRectFList.get(i).centerY(), r)) {

                Log.d(TAG, "index=" + i);

                return i;

            }

        }

 

        return -1;

    }

判断触摸的是不是待选栏


 //计算两点间的距离

    private boolean checkPoint(float x1, float y1, float x2, float y2, int r) {

        double c;

        double i = Math.pow((x1 - x2), 2.0);

        double j = Math.pow((y1 - y2), 2.0);

        c = Math.sqrt(i + j);

        return c < r;

 

    }

两点间的距离公式


  public interface OnExpLoreViewEventListener {

        void onPlayIndexChange(int index);

 

        void onCenterClick();

 

    }


六、总结

总之,这种复杂一点的控件还是需要一边做一边优化。由于代码写的有点乱,整理后再贴github的链接了,就这样。

————————————————

版权声明:本文为CSDN博主「橙熟的橙」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/qwe749082787/article/details/104005508


点赞
1
收藏
1
回复
1
分享
举报
浏览379 发布于2020-02-19 06:51未知归属地
全部评论
最多点赞
最新发布
最早发布
写回答
新增插入模板功能
一键使用模板,快速填写内容,轻松发帖~
知道了
  • 为了保障您的信息安全,请勿上传您的敏感个人信息(如您的密码等信息)和您的敏感资产信息(如关键源代码、签名私钥、调试安装包、业务日志等信息),且您需自行承担由此产生的信息泄露等安全风险。
  • 如您发布的内容为转载内容,请注明内容来源。

我要发帖子

了解社区公约,与您携手共创和谐专业的开发者社区。