寫在前面
最近由于項(xiàng)目需求谓着,需要有一個(gè)類似蘋果的滑動(dòng)解鎖控件,抱著萬事不求人的學(xué)習(xí)態(tài)度屉栓,這種時(shí)候肯定是要自己肝出一個(gè)這樣的控件了友多,以下帶來實(shí)現(xiàn)的思路與具體過程
一域滥、實(shí)現(xiàn)的思路
個(gè)人覺得自定義View肯定要整理自己的實(shí)現(xiàn)思路蜈抓,只有思路清晰明確了沟使,才能夠順利的實(shí)現(xiàn)自己所需的功能,以下淺淡一下拾酝,實(shí)現(xiàn)滑動(dòng)解鎖的具體思路:
1蒿囤、滑動(dòng)解鎖方面蟋软,肻定要有一個(gè)按鈕與文本,按鈕只需要實(shí)現(xiàn)點(diǎn)擊滑過的功能凄敢,所以這邊我繼承了TextView免去文本的操作,其實(shí)繼承Button也是可以的譬重,看個(gè)人的選擇
2、實(shí)現(xiàn)文本之后滩援,還要有按鈕玩徊,想了下利用drawCircle實(shí)現(xiàn)不難恩袱,外邊的弧可以用drawRoundRect實(shí)現(xiàn)畔塔,剩下的就是控制文本把敢、按鈕還有弧之間的距離了
3、實(shí)現(xiàn)之后上述功能后屈藐,就是要做點(diǎn)擊操作了联逻,這邊可以重寫onTouchEvent检痰,來實(shí)現(xiàn)這個(gè)滑動(dòng)的過程
思路整理完之后好像沒什么難度铅歼,開始擼代碼
二椎椰、先上成品圖
按鈕以圖片形式
[圖片上傳失敗...(image-532ee6-1597629394228)]
按鈕默認(rèn)模式
[圖片上傳失敗...(image-8b2262-1597629394228)]
三厦幅、自定義屬性
<declare-styleable name="ButtonSliding">
<!-- 弧的顏色-->
<attr name="boundColor" format="color"></attr>
<!-- 文本的顏色-->
<attr name="mtextColor" format="color"></attr>
<!-- 按鈕是選擇圖片還是默認(rèn)的顏色按鈕形式,默認(rèn)是顏色慨飘,設(shè)置true之后需設(shè)置btnSrc-->
<attr name="typeColor" format="boolean"/>
<!-- 按鈕的圖片或者顏色确憨,只有在typeColor設(shè)置為true時(shí)有效-->
<attr name="btnSrc" format="reference|color"></attr>
</declare-styleable>
四、使用
<com.example.test.Widget.ButtonSliding
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="滑動(dòng)進(jìn)入會(huì)議"
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"
/>
五瓤的、具體實(shí)現(xiàn)
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;
/**
* 滑動(dòng)進(jìn)入
*
* @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("資源設(shè)置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));
}
/**
* 測量處理,默認(rèn)情況
*/
@Override
protected 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);
//如果寬高設(shè)置為wrap_content時(shí)休弃,剛默認(rèn)為300
if (widthMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.AT_MOST) {
height = 200;
width = height * 4;
}
//如果寬高不一致時(shí),則以高為標(biāo)準(zhǔn)
if (width != height) {
width = height * 4;
}
setMeasuredDimension(width, height);
}
@Override
protected 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();
// 計(jì)算縮放比例.
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// 取得想要縮放的matrix參數(shù).
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
// 得到新的圖片.
Bitmap newBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true);
return newBitmap;
}
/**
* 創(chuàng)建新的圓角圖片
* @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);
}
/**
* 中心點(diǎn)
*/
private int y;
private int x;
/**
* 按鈕中心點(diǎn)
*/
private int btnX;
private int btnY;
/**
* 按鈕與背景弧的間隙
*/
private int btnBound = 10;
/**
* 背景的圖片
*/
private int bitmapRadus;
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
//數(shù)值太多選擇在測量時(shí)初始化桥帆,在構(gòu)造函數(shù)時(shí)有些數(shù)值拿不到,draw刷新時(shí)又會(huì)被重新賦值,所以選擇在此初始化
//初始化中心點(diǎn)
x = (right - left) / 2;
y = (bottom - top) / 2;
//初始化中心點(diǎn)
btnRadus = (y + y) / 2 - btnBound;
//按鈕的X,Y
btnX = btnRadus + btnBound;
btnY = y;
//按鈕圖片的X,Y
btnBitmapX = 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);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int X = (int)event.getX();
switch (event.getAction()) {
//手指移動(dòng)時(shí)
case MotionEvent.ACTION_MOVE:
if(isColorSrc){
btnX = (X) ;
//到達(dá)右端一定距離時(shí)調(diào)用接口
if (btnX >= x + x - btnBound - btnRadus - 5) {
buttonSildingEvent.onOver();
}
//如果超出控件范圍則不調(diào)用刷新
if (btnX >= x + x - btnBound - btnRadus || btnX <= btnRadus + btnBound)
break;
//如果在控件范圍則不調(diào)用刷新
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;
//手指抬起時(shí)
case MotionEvent.ACTION_UP:
if(isColorSrc){
btnX = (X);
//如果超出控件邊界,則給定默認(rèn)值
if (btnX > x + x - btnRadus)
btnX = x + x - btnRadus;
//如果超出控件邊界,則給定默認(rèn)值
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() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if(isColorSrc){
//X的值,Y不用管
btnX = (int) animation.getAnimatedValue();
}else {
//圖片是由Rect決定繪制圖片的位置,所以要賦值X與X2
btnBitmapX2 = (int) animation.getAnimatedValue();
btnBitmapX = (int) animation.getAnimatedValue() - bitmapRadus;
if(btnBitmapX <= 0){
btnBitmapX = btnBound;
btnBitmapX2 = btnRadus + btnRadus + btnBound;
}
}
//刷新
invalidate();
}
});
}
private ButtonSildingEvent buttonSildingEvent;
/**
* 設(shè)置完成的監(jiān)聽
* @param buttonSilding
*/
public void setOnListener(ButtonSildingEvent buttonSilding) {
this.buttonSildingEvent = buttonSilding;
}
/**
* 按鈕滑動(dòng)到右端的接口
*/
public interface ButtonSildingEvent {
void onOver();
}
}
CSDN地址:Android自定義View-滑動(dòng)解鎖按鈕