Android自定义View-滑动解锁按钮

  • 写在前面
    • 一、实现的思路
    • 二、先上成品图
    • 三、自定义属性
    • 四、使用
    • 五、具体实现

写在前面

最近由于项目需求,需要有一个类似苹果的滑动解锁控件,抱着万事不求人的学习态度,这种时候肯定是要自己肝出一个这样的控件了,以下带来实现的思路与具体过程

一、实现的思路

个人觉得自定义View肯定要整理自己的实现思路,只有思路清晰明确了,才能够顺利的实现自己所需的功能,以下浅淡一下,实现滑动解锁的具体思路:

1、滑动解锁方面,肻定要有一个按钮与文本,按钮只需要实现点击滑过的功能,所以这边我继承了TextView免去文本的操作,其实继承Button也是可以的,看个人的选择
2、实现文本之后,还要有按钮,想了下利用drawCircle实现不难,外边的弧可以用drawRoundRect实现,剩下的就是控制文本、按钮还有弧之间的距离了
3、实现之后上述功能后,就是要做点击操作了,这边可以重写onTouchEvent,来实现这个滑动的过程

思路整理完之后好像没什么难度,开始撸代码

二、先上成品图

按钮以图片形式

按钮默认模式

三、自定义属性

    <declare-styleable name="ButtonSliding"><!--        弧的颜色--><attr name="boundColor" format="color"></attr><!--        文本的颜色--><attr name="mtextColor" format="color"></attr><!--        按钮是选择图片还是默认的颜色按钮形式,默认是颜色,设置true之后需设置btnSrc--><attr name="typeColor" format="boolean"/><!--        按钮的图片或者颜色,只有在typeColor设置为true时有效--><attr name="btnSrc" format="reference|color"></attr></declare-styleable>

四、使用

    <com.example.test.Widget.ButtonSlidingandroid:id="@+id/tv_name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="滑动进入会议"android:textSize="24sp"android:visibility="visible"app:boundColor="@color/colorSkyBlue"app:mtextColor="#FFC107"app:typeColor="true"app:btnSrc="@mipmap/icon"app:layout_constraintBottom_toTopOf="@+id/btnTest"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"/>

五、具体实现

package com.example.test.Widget;import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.animation.BounceInterpolator;import com.example.test.R;/*** 滑动进入** @author chen*/
public class ButtonSliding extends androidx.appcompat.widget.AppCompatTextView {/*** 文本颜色画笔*/private Paint paintText;/*** 背景弧画笔*/private Paint paintRect;/*** 按钮画笔*/private Paint paintCircle;private Context context;/*** 背景弧颜色*/private int boundColor = Color.RED;/*** 文本弧颜色*/private int mTextColor = Color.BLACK;private Bitmap bitmap;/*** 按钮背景*/private int drawableId;private boolean isColorSrc = true;public ButtonSliding(Context context, AttributeSet attrs) {super(context, attrs);this.context = context;TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ButtonSliding);boundColor = typedArray.getColor(R.styleable.ButtonSliding_boundColor,boundColor);mTextColor = typedArray.getColor(R.styleable.ButtonSliding_boundColor,mTextColor);drawableId = typedArray.getResourceId(R.styleable.ButtonSliding_btnSrc, 0);isColorSrc = typedArray.getBoolean(R.styleable.ButtonSliding_typeColor, isColorSrc);if (isColorSrc) {} else {if(drawableId == 0)throw new IllegalArgumentException("资源设置null");bitmap = BitmapFactory.decodeResource(getResources(), drawableId);}typedArray.recycle();init();}/*** 初始化画笔*/private void init() {paintText = new Paint();paintText.setDither(true);paintText.setAntiAlias(true);paintText.setStyle(Paint.Style.FILL);paintText.setTextSize(getTextSize());paintText.setColor(mTextColor);paintRect = new Paint();paintRect.setDither(true);paintRect.setAntiAlias(true);paintRect.setColor(boundColor);paintRect.setStyle(Paint.Style.STROKE);paintRect.setStrokeWidth(pxToDp(2));paintCircle = new Paint();paintCircle.setDither(true);paintCircle.setAntiAlias(true);paintCircle.setStyle(Paint.Style.FILL);paintCircle.setStrokeWidth(pxToDp(2));}/*** 测量处理,默认情况*/@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//获取宽高的模式int widthMode = MeasureSpec.getMode(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);//指定宽高的大小int width = MeasureSpec.getSize(widthMeasureSpec);int height = MeasureSpec.getSize(heightMeasureSpec);//如果宽高设置为wrap_content时,刚默认为300if (widthMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.AT_MOST) {height = 200;width = height * 4;}//如果宽高不一致时,则以高为标准if (width != height) {width = height * 4;}setMeasuredDimension(width, height);}@Overrideprotected void onDraw(Canvas canvas) {drawCircle(canvas);drawText(canvas);
//        drawButton(canvas);if (isColorSrc) {drawButton(canvas);} else {drawBitmap(canvas);}}/*** 按钮X(left)与Y(top)的值*/private int btnBitmapX;private int btnBitmapY;/*** 按钮X(right)与Y(bottom)的值*/private int btnBitmapX2;private int btnBitmapY2;/*** 绘制图片的按钮* @param canvas*/private void drawBitmap(Canvas canvas) {RectF rectF = new RectF(btnBitmapX, btnBitmapY, btnBitmapX2, btnBitmapY2);Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());canvas.drawBitmap(bitmap, rect, rectF, paintCircle);}/*** 缩放图片* @param bitmap* @param newWidth* @param newHeight* @return*/public Bitmap getNewBitmap(Bitmap bitmap, int newWidth, int newHeight) {// 获得图片的宽高.int width = bitmap.getWidth();int height = bitmap.getHeight();// 计算缩放比例.float scaleWidth = ((float) newWidth) / width;float scaleHeight = ((float) newHeight) / height;// 取得想要缩放的matrix参数.Matrix matrix = new Matrix();matrix.postScale(scaleWidth, scaleHeight);// 得到新的图片.Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);return newBitmap;}/*** 创建新的圆角图片* @param bitmap* @param px* @return*/public  Bitmap makeRoundCorner(Bitmap bitmap, int px){int width = bitmap.getWidth();int height = bitmap.getHeight();Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);Canvas canvas = new Canvas(output);int color = 0xff424242;Paint paint = new Paint();Rect rect = new Rect(0, 0, width, height);RectF rectF = new RectF(rect);paint.setAntiAlias(true);canvas.drawARGB(0, 0, 0, 0);paint.setColor(color);canvas.drawRoundRect(rectF, px, px, paint);paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));canvas.drawBitmap(bitmap, rect, rect, paint);return output;}/*** 绘制按钮** @param canvas*/private void drawButton(Canvas canvas) {canvas.drawCircle(btnX, btnY, btnRadus, paintCircle);}/*** 绘制背景弧** @param canvas*/private void drawCircle(Canvas canvas) {RectF rectF = new RectF(x - x, y - y, x + x, y + y);canvas.drawRoundRect(rectF, 100, 100, paintRect);}private float pxToDp(int value) {return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, context.getResources().getDisplayMetrics());}/*** 按钮半径*/private int btnRadus;/*** 绘制文本** @param canvas*/private void drawText(Canvas canvas) {String text = (String) getText();//获取文本的宽高Rect rect = new Rect();paintText.getTextBounds(text.trim(), 0, text.trim().length(), rect);int dx = getWidth() / 2 - rect.width() / 2 + btnRadus;Paint.FontMetrics fontMetrics = paintText.getFontMetrics();//基线float baseline = getHeight() / 2 + (fontMetrics.top - fontMetrics.bottom) / 2 - fontMetrics.top;//绘制文本canvas.drawText(text, dx, baseline, paintText);}/*** 中心点*/private int y;private int x;/*** 按钮中心点*/private int btnX;private int btnY;/*** 按钮与背景弧的间隙*/private int btnBound = 10;/*** 背景的图片*/private int bitmapRadus;@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {//数值太多选择在测量时初始化,在构造函数时有些数值拿不到,draw刷新时又会被重新赋值,所以选择在此初始化//初始化中心点x = (right - left) / 2;y = (bottom - top) / 2;//初始化中心点btnRadus = (y + y) / 2 - btnBound;//按钮的X,YbtnX = btnRadus + btnBound;btnY = y;//按钮图片的X,YbtnBitmapX = btnBound;btnBitmapY = btnBound;btnBitmapX2 = btnRadus + btnRadus + btnBound;btnBitmapY2 = btnRadus + btnRadus + btnBound;//按钮图片与弧的间隙bitmapRadus = btnBitmapX2 - btnBitmapX;if(!isColorSrc){bitmap = getNewBitmap(bitmap, btnRadus + btnRadus, btnRadus + btnRadus);bitmap = makeRoundCorner(bitmap,100);}super.onLayout(changed, left, top, right, bottom);}@Overridepublic boolean onTouchEvent(MotionEvent event) {int X = (int)event.getX();switch (event.getAction()) {//手指移动时case MotionEvent.ACTION_MOVE:if(isColorSrc){btnX = (X) ;//到达右端一定距离时调用接口if (btnX >= x + x - btnBound - btnRadus - 5) {buttonSildingEvent.onOver();}//如果超出控件范围则不调用刷新if (btnX >= x + x - btnBound - btnRadus || btnX <= btnRadus + btnBound)break;//如果在控件范围则不调用刷新if (btnX <= x + x - btnRadus - btnBound && btnX >= btnRadus + btnBound)postInvalidate();}else {btnBitmapX2 = (X);//同上if (btnBitmapX2 >= x + x  || btnBitmapX2 <= btnRadus + btnBound) {break;}//同上if (btnBitmapX2 >= x + x - btnBound - btnRadus - 5) {buttonSildingEvent.onOver();}//同上if (btnBitmapX2 <= x + x - btnRadus - btnBound && btnBitmapX2 >= btnRadus + btnBound)postInvalidate();}break;//手指抬起时case MotionEvent.ACTION_UP:if(isColorSrc){btnX = (X);//如果超出控件边界,则给定默认值if (btnX > x + x - btnRadus)btnX = x + x - btnRadus;//如果超出控件边界,则给定默认值if (btnX < btnRadus + btnBound)btnX = btnRadus + btnBound;reset(btnX);}else {btnBitmapX2 = (X);btnBitmapX = (X) - bitmapRadus;//同上if (btnBitmapX2 > x + x - btnRadus)btnBitmapX2 = x + x - btnRadus;//同上if (btnBitmapX2 < btnRadus + btnBound)btnBitmapX2 = btnRadus + btnBound;reset(btnBitmapX2);}break;}return true;}/*** 松手回弹*/private void reset(int start) {ValueAnimator valueAnimator = ValueAnimator.ofInt(start, btnRadus + btnBound);valueAnimator.setDuration(1000);valueAnimator.setInterpolator(new BounceInterpolator());valueAnimator.start();valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {if(isColorSrc){//X的值,Y不用管btnX = (int) animation.getAnimatedValue();}else {//图片是由Rect决定绘制图片的位置,所以要赋值X与X2btnBitmapX2 = (int) animation.getAnimatedValue();btnBitmapX = (int) animation.getAnimatedValue() - bitmapRadus;if(btnBitmapX <= 0){btnBitmapX = btnBound;btnBitmapX2 = btnRadus + btnRadus + btnBound;}}//刷新invalidate();}});}private ButtonSildingEvent buttonSildingEvent;/*** 设置完成的监听* @param buttonSilding*/public void setOnListener(ButtonSildingEvent buttonSilding) {this.buttonSildingEvent = buttonSilding;}/*** 按钮滑动到右端的接口*/public interface ButtonSildingEvent {void onOver();}
}

简书地址:Android自定义View-滑动解锁按钮

Android自定义View-滑动解锁按钮相关推荐

  1. Android 自定义View 滑动解锁

    自定义view来绘制一个类似滑动解锁的button,注释都在,效果如下 代码在下面,替换一下资源,可以直接使用,如果需要画的是圆角的话,需要把下面两行注释的DrawLine的注释打开,然后把两行dra ...

  2. android设置自定义按钮,Android自定义View之元素按钮

    Android自定义View之元素按钮 之前在dribbble看到的三个元素的按钮,参考了设计的创意,添加了自己定义的动画效果来实现.先看效果 效果图 分别是水火电三个元素的按钮实现.其中电的实现最简 ...

  3. android自定义View: 九宫格解锁

    本系列自定义View全部采用kt 系统:mac android studio: 4.1.3 kotlin version1.5.0 gradle: gradle-6.5-bin.zip 废话不多说,先 ...

  4. android 按钮添加动画,Android 自定义View画出按钮加载动画~

    声明:原创作品转载请注明出处http://www.jianshu.com/p/a0ffbc04c089 看了太多别人的文章,我觉得我也应该写点什么了~一是巩固自己的知识,二是共同学习,敢说我做的不好我 ...

  5. android 自定义view: 蛛网/雷达图(三)

    本系列自定义View全部采用kt 系统mac android studio: 4.1.3 kotlin version1.5.0 gradle: gradle-6.5-bin.zip 本篇效果: 蛛网 ...

  6. android 自定义view滚动条,Android自定义View实现等级滑动条的实例

    Android自定义View实现等级滑动条的实例 实现效果图: 思路: 首先绘制直线,然后等分直线绘制点: 绘制点的时候把X值存到集合中. 然后绘制背景图片,以及图片上的数字. 点击事件down的时候 ...

  7. Android 自定义View(四)实现股票自选列表滑动效果

    一.前言 Android 开发过程中自定义 View 真的是无处不在,随随便便一个 UI 效果,都会用到自定义 View.前面三篇文章已经讲过自定义 View 的一些案例效果,相关类和 API,还有事 ...

  8. android自定义view之九宫格解锁

    android自定义view之九宫格解锁 更多细节请看源码 https://github.com/que123567/lockview 1. 定义一个类作为九宫格的格子 包含坐标和索引(用来记录密码) ...

  9. android自定义弧度按钮,Android 自定义View 绘制六边形设置按钮

    今天逛酷安的时候,发现酷安的设置按钮(截图的右上角),是一个六边形 + 中心圆的图标,所以又是一个自定义View练习对象了.画圆很简单,知道半径即可,而重点就在画出六边形. 酷安截图.png 最终效果 ...

最新文章

  1. tensorflow入门教程和底层机制简单解说——本质就是图计算,自动寻找依赖,想想spark机制就明白了...
  2. 进阶指令——df指令(作用:查看磁盘的空间)、free指令(作用:查看内存使用情况)、head指令(作用:查看一个文件的前n行,如果不指定n,则默认显示前10行)、tail指令、less指令
  3. 多线程 python layer_在Caffe中加Python Layer的方法
  4. spring logback mysql_logback 日志输出格式
  5. 小师妹学JavaIO之:目录还是文件
  6. javascript常用技巧
  7. Spring MVC:MySQL和Hibernate的安全性
  8. CSS块元素水平垂直居中的实现技巧
  9. TBXML常用API
  10. 教程视图Android教程(十三)-- Activity间的切换
  11. unslider.js 实现移动web轮播
  12. CSS详解(一)——CSS基本原理
  13. 【问底】许鹏:使用Spark+Cassandra打造高性能数据分析平台(一)
  14. android 网页存储,90%的人不懂将网页永久保存在手机里的方法
  15. 项目Beta冲刺(5/7)(追光的人)(2019.5.27)
  16. [0CTF 2016]piapiapia 1
  17. 抢先看,2023年前瞻版Java八股文面试题,面试应该是够用了(吊打面试官)
  18. 微信小程序例子——获取用户登录信息
  19. 计算机自我鉴定高中生200字,高中生毕业自我鉴定200字
  20. 该怎么去学UI设计?UI设计学习路线分享

热门文章

  1. 容器挂载volume出现“Permission denied”的问题定位解决
  2. 3月13日云栖精选夜读:通过阿里云容器服务深度学习解决方案上手Caffe+多GPU训练
  3. linux主内网dns辅助外网dns,linux 主DNS和辅助DNS的配置
  4. 微信公众号对接后台开发的一些问题总结
  5. 期权Greek之vega【python复现】
  6. mac下chrome全屏地址栏不见的解决方法
  7. Android自定义A_Z字母排序ListView,悬停Listview
  8. Monkey测试(一)
  9. IO线程模型Reactor模型
  10. vagrant 说明