Android 很有意思的控件:粘性控件
发布日期:2021-06-30 22:37:33 浏览次数:3 分类:技术文章

本文共 8754 字,大约阅读时间需要 29 分钟。

一、效果如图:

示例图:

这里写图片描述
应用程序图:
这里写图片描述

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

有需求者请加qq:136137465,非诚勿扰
(java 架构师全套教程,共760G, 让你从零到架构师,每月轻松拿3万)
01.高级架构师四十二个阶段高
02.Java高级系统培训架构课程148课时
03.Java高级互联网架构师课程
04.Java互联网架构Netty、Nio、Mina等-视频教程
05.Java高级架构设计2016整理-视频教程
06.架构师基础、高级片
07.Java架构师必修linux运维系列课程
08.Java高级系统培训架构课程116课时
(送:hadoop系列教程,java设计模式与数据结构, Spring Cloud微服务, SpringBoot入门)
——————————————————————–

二、分析:

1、应用的地方:如未读数据的清除等

2、这个控件要实现哪些功能呢?
1)拖拽超出范围时,断开了,此时我们松手,图标消失
2)拖拽超出范围时,断开了,此时我们把图标移动回去,图标恢复原样
3)拖拽没有超出范围时,此时我们松手,图标弹回去

3、如何实现:

1)我们先画个两个静态的圆圈,一个大的,一个小的
这里写图片描述
2)绘制中间连接的部分:
这里写图片描述
3)把静态的数值变成变量
4)不断地修改变量,重绘界面,就能动起来

三、实现简单的

1、创建自定义view

/** * @描述 粘性控件 * @项目名称 App_imooc * @包名 com.android.imooc.goo * @类名 GooView * @author chenlin * @date 2015年6月2日 下午8:40:53 * @version 1.0 */public class GooView extends View {
public GooView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public GooView(Context context) { this(context, null); } public GooView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mPaint.setColor(Color.RED); } @Override protected void onDraw(Canvas canvas) { }}

2、在onDraw里绘制个圆

private Paint mPaint;private float mCircleCenter = 150f;private float mRadius = 14f;//直径private void init() {    mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);    mPaint.setColor(Color.RED);}@Overrideprotected void onDraw(Canvas canvas) {    canvas.drawCircle(mCircleCenter, mCircleCenter, mRadius, mPaint);}

3、生成主页

/** * @描述         主页 * @项目名称      App_imooc * @包名         com.android.imooc.goo * @类名         GooActivity * @author      chenlin * @date        2015年6月2日 下午8:57:16 * @version     1.0 */public class GooActivity extends Activity {
private GooView mGooView; @Override protected void onCreate(Bundle savedInstanceState) { requestWindowFeature(Window.FEATURE_NO_TITLE); super.onCreate(savedInstanceState); mGooView = new GooView(this); setContentView(mGooView); }}

4、开始画拖拽的大圆

private float mMoveCircleCenter = 70f;private float mMoveRadius = 20f;//直径canvas.drawCircle(mMoveCircleCenter, mMoveCircleCenter, mMoveRadius, mPaint);

5、画拖拽的填充部分,这是重点,绘制使用drawPath

canvas.drawPath(path, mPaint);

但如何才能实现有弯曲的填充物呢?使用了path,

只要一个控制点,如图:
这里写图片描述
有两个控制点:如图:
这里写图片描述

在这里,我们先绘制一个这样的图,

p1到p2绘制一条贝塞尔曲线
p2到p3绘制直线
p3到p4绘制贝塞尔曲线
p4到p1绘制直线
如图:
这里写图片描述

代码实现:

// 画连接部分

Path path = new Path();// 50, 250 p2path.moveTo(250f, 250f);// 150f, 300f填充物的中间点// 50f, 250f p1path.quadTo(150f, 300f, 50f, 250f);// p3path.lineTo(50f, 350f);// p4path.quadTo(150f, 300f, 250f, 350f);//关闭后,会回到最开始的地方,形成封闭的图形path.close();canvas.drawPath(path, mPaint);

这里写图片描述

四、把静态点动态化

好了,现在我们会基本的绘制了,开始把这些点转为动态的吧!

如图:
这里写图片描述

1、把上的圆的点全部转化成float类型的点

PointF staticPointF = new PointF(mStaicCircleCenter, mStaicCircleCenter);// 画小圆canvas.drawCircle(staticPointF.x, staticPointF.y, mStaicRadius, mPaint);PointF movewPointF = new PointF(mMoveCircleCenter, mMoveCircleCenter);// 画移动的大圆canvas.drawCircle(movewPointF.x, movewPointF.y, mMoveRadius, mPaint);

2、转化Path上面的点

我们分别使用两个集合存储静态的点与动态点

//存储静态的两个点PointF[] staticPointFs = new PointF[] { new PointF(250f, 250f), new PointF(250f, 350f) };//存储移动的两个点PointF[] moviePointFs = new PointF[] { new PointF(50f, 250f), new PointF(50f, 350f) };//控制点PointF controlPointF = new PointF(150f, 300f);// 画连接部分Path path = new Path();// 50, 250 p2path.moveTo(staticPointFs[0].x, staticPointFs[0].y);// 150f, 300f填充物的中间点// 50f, 250f p1path.quadTo(controlPointF.x, controlPointF.y, moviePointFs[0].x, moviePointFs[0].y);// p3path.lineTo(moviePointFs[1].x, moviePointFs[1].y);// p4path.quadTo(controlPointF.x, controlPointF.y, staticPointFs[1].x, staticPointFs[1].y);// 关闭后,会回到最开始的地方,形成封闭的图形path.close();canvas.drawPath(path, mPaint);

如图:

这里写图片描述

3、接下来我们就是要把下面的曲型移动到上面两个圆圈之间,形成一个整体

在这里有,两个圆圈的中心点的坐标是已知的,半径也是知道的,现在就剩下求4个点的坐标了
第一个移动点的坐标如何求,看图
这里写图片描述
movePointfs[0].x = movePointf.x + mMoveRadius. cos(a);
movePointfs[0].y = movePointf.y - mMoveRadius. sin(a);

4、开始求得各个点

// 1、获得偏移量float yOffset = mStaticCenter.y - mMovewCenter.y;float xOffset = mStaticCenter.x - mMovewCenter.x;// 2、有了偏移量就可以求出两点斜率了Double lineK = 0.0;if (xOffset != 0f) {    lineK = (double) (yOffset / xOffset);}// 3、通过工具求得两个点的集合mMovewPointFs = GeometryUtil.getIntersectionPoints(mMovewCenter, mMoveRadius, lineK);mStaticPointFs = GeometryUtil.getIntersectionPoints(mStaticCenter, mStaicRadius, lineK);// 4、通过公式求得控制点mControlPointF = GeometryUtil.getMiddlePoint(mStaticCenter, mMovewCenter);

5、得到如图效果:

这里写图片描述

五、开始实现拖动

1、复写onTouchEvent事件,记得在view里要返回true;

@Override    public boolean onTouchEvent(MotionEvent event) {        float downX = 0.0f;        float downY = 0.0f;        switch (event.getAction()) {        case MotionEvent.ACTION_DOWN:            // 得到按下的坐标            downX = event.getRawX();            downY = event.getRawY();            // 更新移动的坐标            updateMoveCenter(downX, downY);            break;        case MotionEvent.ACTION_MOVE:            // 得到按下的坐标            downX = event.getRawX();            downY = event.getRawY();            // 更新移动的坐标            updateMoveCenter(downX, downY);            break;        case MotionEvent.ACTION_UP:        case MotionEvent.ACTION_CANCEL:            break;        }        return true;    }    /**     * 更新移动的点     *      * @param downX     * @param downY     */    public void updateMoveCenter(float downX, float downY) {        mMovewCenter.set(downX, downY);        invalidate();    }

2、此时看,我们的手总是离大圆圈有一点距离,这个距离就是状态栏的高度,如何才能让手指在中间呢?

方法1:此时必须先得到状态栏的高度

/**     * 获取状态栏高度     *      * @param v     * @return     */    public static int getStatusBarHeight(View v) {        if (v == null) {            return 0;        }        Rect frame = new Rect();        v.getWindowVisibleDisplayFrame(frame);        return frame.top;    }

如果画布进行了平移或旋转动画,我们必须在平移钱,保存,在绘制完成后恢复

canvas.save();canvas.translate(0, -mBarHeight);canvas.restore();

方法2:不使用getRawX,而是使用getX,这样的话就不存在状态栏的问题了

downX = event.getX();downY = event.getY();// 更新移动的坐标updateMoveCenter(downX, downY);

3、当大圆圈移动到一定距离时,会断开,此时如果松手了,消失

怎么才能得到两个圆心的距离呢?我们以前学过勾股定理
就是:(y1-y2)的平方 + (x1-x2)的平方的和然后开根号

/**     * As meaning of method name. 获得两点之间的距离     *      * @param p0     * @param p1     * @return     */    public static float getDistanceBetween2Points(PointF p0, PointF p1) {        float distance = (float) Math.sqrt(Math.pow(p0.y - p1.y, 2) + Math.pow(p0.x - p1.x, 2));        return distance;    }

4、当移动时,小圆圈的半径会随着拉动的距离变化,如何实现

// 获取固定圆半径(根据两圆圆心距离)    private float getTempStickRadius() {        float distance = GeometryUtil.getDistanceBetween2Points(mMovewCenter, mStaticCenter);        // if(distance> farestDistance){
// distance = farestDistance; // } distance = Math.min(distance, mMaxDistance); // 0.0f -> 1.0f float percent = distance / mMaxDistance; // percent , 100% -> 20% return ValueUtil.evalute(percent, mStaicRadius, mStaicRadius * 0.2f); }

然后在 onDraw里把得到的值设置到

float tempStaticRadius = getTempStickRadius();canvas.drawCircle(mStaticCenter.x, mStaticCenter.y, tempStaticRadius, mPaint);

5、现在实现到了一定距离后,断开,在onTouchevent的move事件里判断

//当超过最大值时断开float distance = GeometryUtil.getDistanceBetween2Points(mMovewCenter, mStaticCenter);if (distance > mMaxDistance) {    isOutRange = true;    invalidate();}

然后在onDraw里判断

if (!isOutRange) {    //如果没有超出范围才绘制}

6、超出范围后,是否消失的实现在MotionEvent.ACTION_UP里实现,这里有三种可能,上面已经说了

if (isOutRange) {//1)拖拽超出范围时,断开了,此时我们松手,图标消失// 当超过最大值时断开distance = GeometryUtil.getDistanceBetween2Points(mMovewCenter, mStaticCenter);if (distance > mMaxDistance) {    isDisappear = true;    invalidate();}else {    //2)拖拽超出范围时,断开了,此时我们把图标移动回去,图标恢复原样    //就是把移动的圆圈设置到原来的静态圆圈里    updateMoveCenter(mStaticCenter.x, mStaticCenter.y);}}else {//3)拖拽没有超出范围时,此时我们松手,图标弹回去//得到固定的点final PointF tempMovePointF = new PointF(mMovewCenter.x, mMovewCenter.y);ValueAnimator vAnim = ValueAnimator.ofFloat(1.0f);vAnim.addUpdateListener(new AnimatorUpdateListener() {    @Override    public void onAnimationUpdate(ValueAnimator animation) {        float percent = animation.getAnimatedFraction();        //得到两点的        PointF pointF = GeometryUtil.getPointByPercent(tempMovePointF, mStaticCenter, percent);        updateMoveCenter(pointF.x, pointF.y);    }});vAnim.setInterpolator(new OvershootInterpolator(4));vAnim.setDuration(500);vAnim.start();}

到此,这个控件基本完成了,

六、源码下载

链接: 密码:rniy

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

(java 架构师全套教程,共760G, 让你从零到架构师,每月轻松拿3万)
有需求者请进站查看,非诚勿扰

https://item.taobao.com/item.htm?spm=686.1000925.0.0.4a155084hc8wek&id=555888526201

01.高级架构师四十二个阶段高

02.Java高级系统培训架构课程148课时
03.Java高级互联网架构师课程
04.Java互联网架构Netty、Nio、Mina等-视频教程
05.Java高级架构设计2016整理-视频教程
06.架构师基础、高级片
07.Java架构师必修linux运维系列课程
08.Java高级系统培训架构课程116课时
(送:hadoop系列教程,java设计模式与数据结构, Spring Cloud微服务, SpringBoot入门)
——————————————————————–

转载地址:https://lovoo.blog.csdn.net/article/details/51570143 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Android 实现简单的分页
下一篇:Android 使用EventBus实现菜单

发表评论

最新留言

感谢大佬
[***.8.128.20]2024年04月27日 10时58分21秒