热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

Android高仿QQ小红点功能

这篇文章主要介绍了Android高仿QQ小红点功能的相关资料,需要的朋友可以参考下

先给大家展示下效果图:

代码已上传至Github:高仿QQ小红点,如对您有帮助,欢迎star~感谢

绘制贝塞尔曲线:

主要是当在一定范围内拖拽时算出固定圆和拖拽圆的外切直线以及对应的切点,就可以通过path.quadTo()来绘制二阶贝塞尔曲线了~

整体思路:

1、当小红点静止时,什么都不做,只需要给自定义小红点QQBezierView(extends TextView)添加一个.9文件当背景即可

2、当滑动时,通过getRootView()获得顶级根View,然后new一个DragView ( extends View ) 来绘制各种状态时的小红点,并且通过getRootView().addView()的方式把DragView 加进去,这样DragView 就可以实现全屏滑动了

实现过程:

自定义QQBezierView ( extends TextView ) 并复写onTouchEvent来处理各种情况,代码如下:

@Override
public boolean onTouchEvent(MotionEvent event) {
  //获得根View
  View rootView = getRootView();
  //获得触摸位置在全屏所在位置
  float mRawX = event.getRawX();
  float mRawY = event.getRawY();
  switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
      //请求父View不拦截
      getParent().requestDisallowInterceptTouchEvent(true);
      //获得当前View在屏幕上的位置
      int[] cLocation = new int[2];
      getLocationOnScreen(cLocation);
      if (rootView instanceof ViewGroup) {
        //初始化拖拽时显示的View
        dragView = new DragView(getContext());
        //设置固定圆的圆心坐标
        dragView.setStickyPoint(cLocation[0] + mWidth / 2, cLocation[1] + mHeight / 2, mRawX, mRawY);
        //获得缓存的bitmap,滑动时直接通过drawBitmap绘制出来
        setDrawingCacheEnabled(true);
        Bitmap bitmap = getDrawingCache();
        if (bitmap != null) {
          dragView.setCacheBitmap(bitmap);
          //将DragView添加到RootView中,这样就可以全屏滑动了
          ((ViewGroup) rootView).addView(dragView);
          setVisibility(INVISIBLE);
        }
      }
      break;
    case MotionEvent.ACTION_MOVE:
      //请求父View不拦截
      getParent().requestDisallowInterceptTouchEvent(true);
      if (dragView != null) {
        //更新DragView的位置
        dragView.setDragViewLocation(mRawX, mRawY);
      }
      break;
    case MotionEvent.ACTION_UP:
      getParent().requestDisallowInterceptTouchEvent(false);
      if (dragView != null) {
        //手抬起时来判断各种情况
        dragView.setDragUp();
      }
      break;
  }
  return true;
}

上面代码注释已经很详细了,总结一下就是通过内部拦截法来请求父View是否拦截分发事件,并通过event.getRawX()和event.getRawY()来不断更新DragView的位置,那么DragView都做了哪些事呢,接下来就看一下DragView,DragView是QQBezierView 的一个内部View类:

 private int mState;//当前红点的状态
 private static final int STATE_INIT = 0;//默认静止状态
 private static final int STATE_DRAG = 1;//拖拽状态
 private static final int STATE_MOVE = 2;//移动状态
 private static final int STATE_DISMISS = 3;//消失状态

首先声明了小红点的四种状态,静止状态,拖拽状态,移动状态和消失状态。

在QQBezierView 的onTouchEvent的DOWN事件中调用了setStickyPoint()方法:

/**
 * 设置固定圆的圆心和半径
 * @param stickyX 固定圆的X坐标
 * @param stickyY 固定圆的Y坐标
 */
 public void setStickyPoint(float stickyX, float stickyY, float touchX, float touchY) {
   //分别设置固定圆和拖拽圆的坐标
   stickyPointF.set(stickyX, stickyY);
   dragPointF.set(touchX, touchY);
   //通过两个圆点算出圆心距,也是拖拽的距离
   dragDistance = MathUtil.getTwoPointDistance(dragPointF, stickyPointF);
   if (dragDistance <= maxDistance) {
     //如果拖拽距离小于规定最大距离,则固定的圆应该越来越小,这样看着才符合实际
     stickRadius = (int) (defaultRadius - dragDistance / 10) <10 &#63; 10 : (int) (defaultRadius - dragDistance / 10);
     mState = STATE_DRAG;
   } else {
     mState = STATE_INIT;
   }
 }

接着,在QQBezierView 的onTouchEvent的MOVE事件中调用了setDragViewLocation()方法:

 /**
 * 设置拖拽的坐标位置
 *
 * @param touchX 拖拽时的X坐标
 * @param touchY 拖拽时的Y坐标
 */
 public void setDragViewLocation(float touchX, float touchY) {
   dragPointF.set(touchX, touchY);
   //随时更改圆心距
   dragDistance = MathUtil.getTwoPointDistance(dragPointF, stickyPointF);
   if (mState == STATE_DRAG) {
    if (isInsideRange()) {
       stickRadius = (int) (defaultRadius - dragDistance / 10) <10 &#63; 10 : (int) (defaultRadius - dragDistance / 10);
     } else {
       mState = STATE_MOVE;
       if (onDragListener != null) {
         onDragListener.onMove();
       }
     }
   }
   invalidate();
 }

最后在QQBezierView 的onTouchEvent的UP事件中调用了setDragUp()方法:

public void setDragUp() {
  if (mState == STATE_DRAG && isInsideRange()) {
    //拖拽状态且在范围之内
    startResetAnimator();
   } else if (mState == STATE_MOVE) {
     if (isInsideRange()) {
      //在范围之内 需要RESET
      startResetAnimator();
    } else {
      //在范围之外 消失动画
      mState = STATE_DISMISS;
      startExplodeAnim();
    }
  }
}

最后来看下DragView的onDraw方法,拖拽时的贝塞尔曲线以及拖拽滑动时的状态都是通过onDraw实现的:

@Override
 protected void onDraw(Canvas canvas) {
   if (isInsideRange() && mState == STATE_DRAG) {
     mPaint.setColor(Color.RED);
     //绘制固定的小圆
     canvas.drawCircle(stickyPointF.x, stickyPointF.y, stickRadius, mPaint);
     //首先获得两圆心之间的斜率
     Float linK = MathUtil.getLineSlope(dragPointF, stickyPointF);
     //然后通过两个圆心和半径、斜率来获得外切线的切点
     PointF[] stickyPoints = MathUtil.getIntersectionPoints(stickyPointF, stickRadius, linK);
     dragRadius = (int) Math.min(mWidth, mHeight) / 2;
     PointF[] dragPoints = MathUtil.getIntersectionPoints(dragPointF, dragRadius, linK);
     mPaint.setColor(Color.RED);
     //二阶贝塞尔曲线的控制点取得两圆心的中点
     cOntrolPoint= MathUtil.getMiddlePoint(dragPointF, stickyPointF);
     //绘制贝塞尔曲线
     mPath.reset();
     mPath.moveTo(stickyPoints[0].x, stickyPoints[0].y);
     mPath.quadTo(controlPoint.x, controlPoint.y, dragPoints[0].x, dragPoints[0].y);
     mPath.lineTo(dragPoints[1].x, dragPoints[1].y);
     mPath.quadTo(controlPoint.x, controlPoint.y, stickyPoints[1].x, stickyPoints[1].y);
     mPath.lineTo(stickyPoints[0].x, stickyPoints[0].y);
     canvas.drawPath(mPath, mPaint);
   }
   if (mCacheBitmap != null && mState != STATE_DISMISS) {
     //绘制缓存的Bitmap
     canvas.drawBitmap(mCacheBitmap, dragPointF.x - mWidth / 2,
            dragPointF.y - mHeight / 2, mPaint);
   }
   if (mState == STATE_DISMISS && explodeIndex 

PS:最开始使用的是 Android:clipChildren=”false” 这个属性,如果父View只是一个普通的ViewGroup(如LinearLayout、RelativeLayout等),此时在父View中设置android:clipChildren=”false”后,子View就可以超出自己的范围,在ViewGroup中也可以滑动了,此时也没问题;但是当是RecycleView时,只要ItemView设置了background属性,滑动时的DragView就会显示在background的下面了,好蛋疼~如有知其原因的还望不吝赐教~

最后再贴下源码下载地址:Android高仿QQ小红点

以上所述是小编给大家介绍的Android高仿QQ小红点功能,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!


推荐阅读
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文讲述了如何通过代码在Android中更改Recycler视图项的背景颜色。通过在onBindViewHolder方法中设置条件判断,可以实现根据条件改变背景颜色的效果。同时,还介绍了如何修改底部边框颜色以及提供了RecyclerView Fragment layout.xml和项目布局文件的示例代码。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
author-avatar
梦回大唐2502907957
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有