您好,登錄后才能下訂單哦!
前兩天在熱搜上看到即刻app暫停服務了,正好之前同事問我說實踐中仿寫即刻的點贊你有思路嗎,你不實現(xiàn)一下?看到熱搜后,我突然記起來這件事,于是準備開始:實現(xiàn)其實并不難,用到了位移,縮放,漸變動畫和自定義View的基礎(chǔ)用法,剛好加深對自定義View的理解。
喜歡的小伙伴歡迎關(guān)注,我會定期分享Android知識點及解析,還會不斷更新的BATJ面試專題,歡迎大家前來探討交流,如有好的文章也歡迎投稿。
把即刻app下載后,以解壓包的方式解壓,發(fā)現(xiàn)點贊效果有三張圖,一張是沒有點贊的小手圖片,一張是點贊后的紅色小手圖片,最后一張是點贊后,點贊手指上的四點如下圖:
先仔細看上面即刻的點贊效果圖,點贊后:灰色小手縮小了一下,并消失變成紅色小手,紅色小手放大了一些,并且手指上有四點出現(xiàn),下面描述四點圖像或者高亮都是指它。另外中間有一圈淡紅色的圓形擴散放大效果,右邊的數(shù)是一個一個字符第跳動,并不是整個數(shù)一起在跳動中被新的數(shù)替換掉,好像是數(shù)字輪表。如上面:3往上移并漸漸消失,4從下面出來并漸漸清晰出現(xiàn)。取消點贊后:高亮的四點消失,紅色小手變成灰色小手,字符往下移,整個效果和點贊效果相反,下面準備用一個View來完成以上的實現(xiàn)。
最外面的藍色矩形就是這個自定義View的區(qū)域,里面有五個矩形,最上面的綠色矩形就是手指頭距離本View頂部的范圍,設(shè)定10px,下面綠色矩形也是小手底部距離本View底部的范圍,設(shè)定10px。最左邊的紅色矩形是小手最左邊距離本View左邊緣的范圍,設(shè)定10px,中間的紅色矩形是小手距離數(shù)字文本顯示的范圍,距離10px,最右邊的紅色矩形是數(shù)字文本距離本View右邊緣的范圍,設(shè)定10px。
這樣整個View的寬和高算出來了:
View的寬度 = 小手圖像的寬度 + 數(shù)字文本寬度 + 30px
View的高度 = 小手圖像的高度 (因為手指高度比數(shù)字文本高度高)+ 20px
下面確定小手上的四點位置,因為在Android上有坐標系這個概念,畫出一張圖只要確定左上角就可以了。
上圖綠色矩形就是素材點贊后小手上的四點圖像區(qū)域,綠色的點就是要我們要算出來,X坐標10px + 幾px,10px是手指最左邊距離坐標系Y軸的距離,為什么還要加幾px呢,因為四點圖像并不是和小手右邊對齊,而是往里一點,所以還有加多4 - 5px。Y坐標其實不能非常準確算出來,我是用整個View的高度減去小手的高度再除2減去高亮四點圖像的高度再加上15-17px的距離。
四點圖像的左上角坐標:
X = (15px)
Y = (整個圖像高度 - 小手圖像高度)/ 2 - 高亮圖像的高度 + 17px
小手的和手指上的四點位置繪制了,下面繪制數(shù)字文本,因為數(shù)字文本比較特殊,打算將×××轉(zhuǎn)換為String類型,再通過字符數(shù)組將數(shù)字一個一個繪制出來。
image
因為繪制文本內(nèi)容都是通過drawText(String text,float x,float y,Paint paint)這個方法實現(xiàn)的,方法的參數(shù)很簡單,text是文本內(nèi)容,x和y分別是文字的坐標,但是這個坐標并不是文字的左上角和描繪圖像有區(qū)別的,先說x參數(shù),x參數(shù)其實也并不是文字內(nèi)容的左邊位置,而是比文字內(nèi)容的左邊再往左一點點,因為在絕大多數(shù)字符,它們的寬度是要略微大于實際顯示的寬度,也就是字符的左右兩邊會留一部分空隙,其實就是文字與文字之間,文字與邊框之間的間隔。y參數(shù),y參數(shù)是指文字的基線,就是用一條線讓所有文字互相對齊的基準線,不同的語言文字,每個字符的高度和上下位置都是不一樣的,讓不同的文字并排顯示的時候整體看起來很整齊,這個對齊不是頂部對齊或者底部對齊,而是重心對齊。這里先講到這,具體詳細自行網(wǎng)上查找。
因為數(shù)字字符是一個一個繪制的。
textX = 小手的寬度 + 20px + 右邊字符的的寬度
textY = View的高度 + 文字區(qū)域的高度的一半下面實際用法不同,因為坐標原點不在左上角
畫完一個字符后,下一個字符的x坐標加上上一個字符的寬度即可,高度是不變的,所以不用管。
點贊過程和取消點贊再結(jié)合動畫實現(xiàn)就可以了。
上面只是粗略的把圖像元素定位了,下面具體實現(xiàn):
在values下的attrs文件下添加屬性集合如下:
<?xml?version="1.0"?encoding="utf-8"?><resources>
????<!--name為聲明的屬性集合,可以隨意取,最好是和自定義View一樣的名稱,這樣方便管理-->
????<declare-styleable?name="JiKeLikeView">
????????<!--?聲明屬性,名稱為like_number,取值是×××-->
????????<attr?name="like_number"?format="integer"/>
????</declare-styleable></resources>
因為點贊只涉及到數(shù)字,所以聲明和定義×××即可。
新建一個類繼承View,并在構(gòu)造函數(shù)中,讀取attrs文件下配置屬性:
public?JiKeLikeView(Context?context)?{????????this(context,?null);
????}????public?JiKeLikeView(Context?context,?@Nullable?AttributeSet?attrs)?{????????this(context,?attrs,?0);
????}????public?JiKeLikeView(Context?context,?@Nullable?AttributeSet?attrs,?int?defStyleAttr)?{????????super(context,?attrs,?defStyleAttr);????????//獲取attrs文件下配置屬性
????????TypedArray?typedArray?=?context.obtainStyledAttributes(attrs,?R.styleable.JiKeLikeView);????????//點贊數(shù)量?第一個參數(shù)就是屬性集合里面的屬性?固定格式R.styleable+自定義屬性名字
????????//第二個參數(shù),如果沒有設(shè)置這個屬性,則會取設(shè)置的默認值
????????likeNumber?=?typedArray.getInt(R.styleable.JiKeLikeView_like_number,?1999);????????//記得把TypedArray對象回收
????????typedArray.recycle();
????????init();
????}
init方法是初始化一些畫筆,文本顯示范圍
private?void?init()?{????????//創(chuàng)建文本顯示范圍
????????textRounds?=?new?Rect();????????//點贊數(shù)暫時8位
????????widths?=?new?float[8];????????//Paint.ANTI_ALIAS_FLAG?屬性是位圖抗鋸齒
????????//bitmapPaint是圖像畫筆
????????bitmapPaint?=?new?Paint(Paint.ANTI_ALIAS_FLAG);????????//這是繪制原來數(shù)字的畫筆?加入沒點贊之前是45?那么點贊后就是46?點贊是46?那么沒點贊就是45
????????textPaint?=?new?Paint(Paint.ANTI_ALIAS_FLAG);
????????oldTextPaint?=?new?Paint(Paint.ANTI_ALIAS_FLAG);????????//文字顏色大小配置?顏色灰色?字體大小為14
????????textPaint.setColor(Color.GRAY);
????????textPaint.setTextSize(SystemUtil.sp2px(getContext(),?14));
????????oldTextPaint.setColor(Color.GRAY);
????????oldTextPaint.setTextSize(SystemUtil.sp2px(getContext(),?14));????????//圓畫筆初始化?Paint.Style.STROKE只繪制圖形輪廓
????????circlePaint?=?new?Paint(Paint.ANTI_ALIAS_FLAG);
????????circlePaint.setColor(Color.RED);
????????circlePaint.setStyle(Paint.Style.STROKE);????????//設(shè)置輪廓寬度
????????circlePaint.setStrokeWidth(SystemUtil.dp2px(getContext(),?2));????????//設(shè)置模糊效果?第一個參數(shù)是模糊半徑,越大越模糊,第二個參數(shù)是陰影的橫向偏移距離,正值向下偏移?負值向上偏移
????????//第三個參數(shù)是縱向偏移距離,正值向下偏移,負值向上偏移?第四個參數(shù)是畫筆的顏色
????????circlePaint.setShadowLayer(SystemUtil.dp2px(getContext(),?1),?SystemUtil.dp2px(getContext(),?1),?SystemUtil.dp2px(getContext(),?1),?Color.RED);
????}
在onAttachedToWindow方法上創(chuàng)建Bitmap對象
????/**
?????*?這個方法是在Activity?resume的時候被調(diào)用的,Activity對應的window被添加的時候
?????*?每個view只會調(diào)用一次,可以做一些初始化操作
?????*/
????@Override
????protected?void?onAttachedToWindow()?{????????super.onAttachedToWindow();
????????Resources?resources?=?getResources();????????//構(gòu)造Bitmap對象,通過BitmapFactory工廠類的static?Bitmap?decodeResource根據(jù)給定的資源id解析成位圖
????????unLikeBitmap?=?BitmapFactory.decodeResource(resources,?R.drawable.ic_message_unlike);
????????likeBitmap?=?BitmapFactory.decodeResource(resources,?R.drawable.ic_message_like);
????????shiningBitmap?=?BitmapFactory.decodeResource(resources,?R.drawable.ic_message_like_shining);
????}
至于為什么要在這個方法構(gòu)建而不寫在init方法,上面代碼附帶了解釋。
另外要在onDetachedFromWindow方法回收bitmap
/**
?????*?和onAttachedToWindow對應,在destroy?view的時候調(diào)用
?????*/
????@Override
????protected?void?onDetachedFromWindow()?{????????super.onDetachedFromWindow();????????//回收bitmap
????????unLikeBitmap.recycle();
????????likeBitmap.recycle();
????????shiningBitmap.recycle();
????}
構(gòu)造了三個Bitmap對象,上面分析很清楚了,一個是小手上的四點,一個是點贊小手,最后一個是沒點贊的小手。
????/**
?????*?測量寬高
?????*?這兩個參數(shù)是由父視圖經(jīng)過計算后傳遞給子視圖
?????*?@param?widthMeasureSpec?寬度
?????*?@param?heightMeasureSpec?高度
?????*/
????@Override
????protected?void?onMeasure(int?widthMeasureSpec,?int?heightMeasureSpec)?{????????//MeasureSpec值由specMode和specSize共同組成,onMeasure兩個參數(shù)的作用根據(jù)specMode的不同,有所區(qū)別。
????????//當specMode為EXACTLY時,子視圖的大小會根據(jù)specSize的大小來設(shè)置,對于布局參數(shù)中的match_parent或者精確大小值
????????//當specMode為AT_MOST時,這兩個參數(shù)只表示了子視圖當前可以使用的最大空間大小,而子視圖的實際大小不一定是specSize。所以我們自定義View時,重寫onMeasure方法主要是在AT_MOST模式時,為子視圖設(shè)置一個默認的大小,對于布局參數(shù)wrap_content。
????????//高度默認是bitmap的高度加上下margin各10dp
????????heightMeasureSpec?=?MeasureSpec.makeMeasureSpec(unLikeBitmap.getHeight()?+?SystemUtil.dp2px(getContext(),?20),?MeasureSpec.EXACTLY);????????//寬度默認是bitmap的寬度加左右margin各10dp和文字寬度和文字右側(cè)10dp?likeNumber是文本數(shù)字
????????String?textnum?=?String.valueOf(likeNumber);????????//得到文本的寬度
????????float?textWidth?=?textPaint.measureText(textnum,?0,?textnum.length());????????//計算整個View的寬度?小手寬度?+?文本寬度?+?30px
????????widthMeasureSpec?=?MeasureSpec.makeMeasureSpec(((int)?(unLikeBitmap.getWidth()?+?textWidth?+?SystemUtil.dp2px(getContext(),?30))),?MeasureSpec.EXACTLY);????????super.onMeasure(widthMeasureSpec,?heightMeasureSpec);
????}
至于上面為什么用MeasureSpec.EXACTLY,上面已經(jīng)解釋很清楚了。
????????super.onDraw(canvas);????????//獲取正個View的高度
????????int?height?=?getHeight();????????//取中心
????????int?centerY?=?height?/?2;????????//小手根據(jù)有沒有點贊進行改變
????????Bitmap?handBitmap?=?isLike???likeBitmap?:?unLikeBitmap;????????//得到圖像寬度
????????int?handBitmapWidth?=?handBitmap.getWidth();????????//得到圖像高度
????????int?handBitmapHeight?=?handBitmap.getHeight();????????//畫小手
????????int?handTop?=?(height?-?handBitmapHeight)?/?2;????????//先保存畫布的狀態(tài)
????????canvas.save();????????//根據(jù)bitmap中心進行縮放
????????canvas.scale(handScale,?handScale,?handBitmapWidth?/?2,?centerY);????????//畫bitmap小手,第一個是參數(shù)對應的bitmap,第二個參數(shù)是左上角坐標,第三個參數(shù)上頂部坐標,第四個是畫筆
????????canvas.drawBitmap(handBitmap,?SystemUtil.dp2px(getContext(),?10),?handTop,?bitmapPaint);????????//讀取之前沒有縮放畫布的狀態(tài)
????????canvas.restore();
這里解釋一下為什么用到canvas.save()和canvas.restore()呢,因為整個點贊效果是有動畫效果的,對畫布進行縮放,如果不保存畫布之前的狀態(tài),縮放后繼續(xù)繪制其他圖像效果并不是你想要的。
//畫上面四點閃亮
????????//先確定頂部
????????int?shiningTop?=?handTop?-?shiningBitmap.getHeight()?+?SystemUtil.dp2px(getContext(),?17);????????//根據(jù)隱藏系數(shù)設(shè)置點亮的透明度
????????bitmapPaint.setAlpha((int)?(255?*?shiningAlpha));????????//保存畫布狀態(tài)
????????canvas.save();????????//畫布根據(jù)點亮的縮放系數(shù)進行縮放
????????canvas.scale(shiningScale,?shiningScale,?handBitmapWidth?/?2,?handTop);????????//畫出點亮的bitmap
????????canvas.drawBitmap(shiningBitmap,?SystemUtil.dp2px(getContext(),?15),?shiningTop,?bitmapPaint);????????//恢復畫筆之前的狀態(tài)
????????canvas.restore();????????//并且恢復畫筆bitmapPaint透明度
????????bitmapPaint.setAlpha(255);
注意只是用了bitmapPaint.setAlpha()方法設(shè)置這四點是否顯示和消失,設(shè)置上這四點都是存在畫布上的,點贊后設(shè)置setAlpha(255)出現(xiàn),否則根據(jù)透明度來進行顯示,有個變化的趨勢。
這里分兩種大情況,一種是不同位數(shù)的數(shù)字變化,另外一種是同位數(shù)數(shù)字變化
????//畫文字
????????String?textValue?=?String.valueOf(likeNumber);????????//如果點贊了,之前的數(shù)值就是點贊數(shù)-1,如果取消點贊,那么之前數(shù)值(對比點贊后)就是現(xiàn)在顯示的
????????String?textCancelValue;????????if?(isLike)?{
????????????textCancelValue?=?String.valueOf(likeNumber?-?1);
????????}?else?{????????????if?(isFirst)?{
????????????????textCancelValue?=?String.valueOf(likeNumber?+?1);
????????????}?else?{
????????????????isFirst?=?!isFirst;
????????????????textCancelValue?=?String.valueOf(likeNumber);
????????????}
????????}????????//文本的長度
????????int?textLength?=?textValue.length();????????//獲取繪制文字的坐標?getTextBounds?返回所有文本的聯(lián)合邊界
????????textPaint.getTextBounds(textValue,?0,?textValue.length(),?textRounds);????????//確定X坐標?距離手差10dp
????????int?textX?=?handBitmapWidth?+?SystemUtil.dp2px(getContext(),?20);????????//確定Y坐標?距離?大圖像的一半減去?文字區(qū)域高度的一半?即可得出?getTextBounds里的rect參數(shù)得到數(shù)值后,
????????//?查看它的屬性值?top、bottom會發(fā)現(xiàn)top是一個負數(shù);bottom有時候是0,有時候是正數(shù)。結(jié)合第一點很容易理解,因為baseline坐標看成原點(0,0),
????????//?那么相對位置top在它上面就是負數(shù),bottom跟它重合就為0,在它下面就為負數(shù)。像小寫字母j?g?y等,它們的bounds?bottom都是正數(shù),
????????//?因為它們都有降部(在西文字體排印學中,降部指的是一個字體中,字母向下延伸超過基線的筆畫部分)。
????????int?textY?=?height?/?2?-?(textRounds.top?+?textRounds.bottom)?/?2;????????//繪制文字?這種情況針對不同位數(shù)變化?如?99?到100?999到10000
????????if?(textLength?!=?textCancelValue.length()?||?textMaxMove?==?0)?{????????????//第一個參數(shù)就是文字內(nèi)容,第二個參數(shù)是文字的X坐標,第三個參數(shù)是文字的Y坐標,注意這個坐標
????????????//并不是文字的左上角?而是與左下角比較接近的位置
????????????//canvas.drawText(textValue,??textX,?textY,?textPaint);
????????????//點贊
????????????if?(isLike)?{????????????????//圓的畫筆根據(jù)設(shè)置的透明度進行變化
????????????????circlePaint.setAlpha((int)?(255?*?shingCircleAlpha));????????????????//畫圓
????????????????canvas.drawCircle(handBitmapWidth?/?2?+?SystemUtil.dp2px(getContext(),?10),?handBitmapHeight?/?2?+?SystemUtil.dp2px(getContext(),?10),?((handBitmapHeight?/?2?+?SystemUtil.dp2px(getContext(),?2))?*?shingCircleScale),?circlePaint);????????????????//根據(jù)透明度進行變化
????????????????oldTextPaint.setAlpha((int)?(255?*?(1?-?textAlpha)));????????????????//繪制之前的數(shù)字
????????????????canvas.drawText(textCancelValue,?textX,?textY?-?textMaxMove?+?textMoveDistance,?oldTextPaint);????????????????//設(shè)置新數(shù)字的透明度
????????????????textPaint.setAlpha((int)?(255?*?textAlpha));????????????????//繪制新數(shù)字(點贊后或者取消點贊)
????????????????canvas.drawText(textValue,?textX,?textY?+?textMoveDistance,?textPaint);
????????????}?else?{
????????????????oldTextPaint.setAlpha((int)?(255?*?(1?-?textAlpha)));
????????????????canvas.drawText(textCancelValue,?textX,?textY?+?textMaxMove?+?textMoveDistance,?oldTextPaint);
????????????????textPaint.setAlpha((int)?(255?*?textAlpha));
????????????????canvas.drawText(textValue,?textX,?textY?+?textMoveDistance,?textPaint);
????????????}????????????return;
????????}????????//下面這種情況區(qū)別與99?999?9999這種?就是相同位數(shù)變化
????????//把文字拆解成一個一個字符?就是獲取字符串中每個字符的寬度,把結(jié)果填入?yún)?shù)widths
????????//相當于measureText()的一個快捷方法,計算等價于對字符串中的每個字符分別調(diào)用measureText(),并把
????????//它們的計算結(jié)果分別填入widths的不同元素
????????textPaint.getTextWidths(textValue,?widths);????????//將字符串轉(zhuǎn)換為字符數(shù)組
????????char[]?chars?=?textValue.toCharArray();????????char[]?oldChars?=?textCancelValue.toCharArray();????????for?(int?i?=?0;?i?<?chars.length;?i++)?{????????????if?(chars[i]?==?oldChars[i])?{
????????????????textPaint.setAlpha(255);
????????????????canvas.drawText(String.valueOf(chars[i]),?textX,?textY,?textPaint);
????????????}?else?{????????????????//點贊
????????????????if?(isLike)?{
????????????????????circlePaint.setAlpha((int)?(255?*?shingCircleAlpha));
????????????????????canvas.drawCircle(handBitmapWidth?/?2?+?SystemUtil.dp2px(getContext(),?10),?handBitmapHeight?/?2?+?SystemUtil.dp2px(getContext(),?10),?((handBitmapHeight?/?2?+?SystemUtil.dp2px(getContext(),?2))?*?shingCircleScale),?circlePaint);
????????????????????oldTextPaint.setAlpha((int)?(255?*?(1?-?textAlpha)));
????????????????????canvas.drawText(String.valueOf(oldChars[i]),?textX,?textY?-?textMaxMove?+?textMoveDistance,?oldTextPaint);
????????????????????textPaint.setAlpha((int)?(255?*?textAlpha));
????????????????????canvas.drawText(String.valueOf(chars[i]),?textX,?textY?+?textMoveDistance,?textPaint);
????????????????}?else?{
????????????????????oldTextPaint.setAlpha((int)?(255?*?(1?-?textAlpha)));
????????????????????canvas.drawText(String.valueOf(oldChars[i]),?textX,?textY?+?textMaxMove?+?textMoveDistance,?oldTextPaint);
????????????????????textPaint.setAlpha((int)?(255?*?textAlpha));
????????????????????canvas.drawText(String.valueOf(chars[i]),?textX,?textY?+?textMoveDistance,?textPaint);
????????????????}
????????????}?????????????//下一位數(shù)字x坐標要加上前一位的寬度????
????????????textX?+=?widths[i];
????????}
我這里用了textValue和textCancelValue分別記錄變化前后的數(shù)字,下面可能對確定y坐標的代碼有疑問,這里解釋一下:
????????int?textY?=?height?/?2?-?(textRounds.top?+?textRounds.bottom)?/?2;
這里textRounds.top是負數(shù),坐標原點并不是在左上角,而是在文本的基線中,自己再查查相關(guān)資料和想想就明白了,上面代碼也有解釋。
透明度變化就不詳細講了,這里講講移動距離:
//點贊
????????????if?(isLike)?{????????????????//圓的畫筆根據(jù)設(shè)置的透明度進行變化
????????????????circlePaint.setAlpha((int)?(255?*?shingCircleAlpha));????????????????//畫圓
????????????????canvas.drawCircle(handBitmapWidth?/?2?+?SystemUtil.dp2px(getContext(),?10),?handBitmapHeight?/?2?+?SystemUtil.dp2px(getContext(),?10),?((handBitmapHeight?/?2?+?SystemUtil.dp2px(getContext(),?2))?*?shingCircleScale),?circlePaint);????????????????//根據(jù)透明度進行變化
????????????????oldTextPaint.setAlpha((int)?(255?*?(1?-?textAlpha)));????????????????//繪制之前的數(shù)字
????????????????canvas.drawText(textCancelValue,?textX,?textY?-?textMaxMove?+?textMoveDistance,?oldTextPaint);????????????????//設(shè)置新數(shù)字的透明度
????????????????textPaint.setAlpha((int)?(255?*?textAlpha));????????????????//繪制新數(shù)字(點贊后或者取消點贊)
????????????????canvas.drawText(textValue,?textX,?textY?+?textMoveDistance,?textPaint);
????????????}?else?{
????????????????oldTextPaint.setAlpha((int)?(255?*?(1?-?textAlpha)));
????????????????canvas.drawText(textCancelValue,?textX,?textY?+?textMaxMove?+?textMoveDistance,?oldTextPaint);
????????????????textPaint.setAlpha((int)?(255?*?textAlpha));
????????????????canvas.drawText(textValue,?textX,?textY?+?textMoveDistance,?textPaint);
????????????}
textMaxMove設(shè)置是20px,textMoveDistance設(shè)置是文字的高度14px
????????????????//繪制之前的數(shù)字
????????????????canvas.drawText(textCancelValue,?textX,?textY?-?textMaxMove?+?textMoveDistance,?oldTextPaint);????????????????//繪制新數(shù)字(點贊后或者取消點贊)
????????????????canvas.drawText(textValue,?textX,?textY?+?textMoveDistance,?textPaint);
這兩行就是繪制新數(shù)字,最主要就是y坐標的變化,舉個例子應該很好理解:假如現(xiàn)在104,我現(xiàn)在點贊要變成105,textCancelValue是104,textValue是105.因為textMoveDistance是從20變化0逐漸減少的,那么第一條公式是繪制105,textY - textMaxMove + textMoveDistance,y坐標越來越小,所以5就會上移,同理textY + textMoveDistance 根據(jù)這條公式4也會上移,因為數(shù)值越來越小,還有就是將數(shù)字轉(zhuǎn)換為字符串進行處理不難理解。
畫圓圈擴散主要是確定圓圈中心點,半徑大概確定就行:
canvas.drawCircle(handBitmapWidth?/?2?+?SystemUtil.dp2px(getContext(),?10),?handBitmapHeight?/?2?+?SystemUtil.dp2px(getContext(),?10),?((handBitmapHeight?/?2?+?SystemUtil.dp2px(getContext(),?2))?*?shingCircleScale),?circlePaint);
前兩個參數(shù)就是確定圓中心,我設(shè)置在小手圖像中心。
我是設(shè)置觸摸就觸發(fā)點贊事件:
@Override
????public?boolean?onTouchEvent(MotionEvent?event)?{????????switch?(event.getAction())?{????????????case?MotionEvent.ACTION_DOWN:
????????????????jump();????????????????break;
????????}????????return?super.onTouchEvent(event);
????}
jump方法如下:
/**
?????*?點贊事件觸發(fā)
?????*/
????private?void?jump()?{
????????isLike?=?!isLike;????????if?(isLike)?{
????????????++likeNumber;
????????????setLikeNum();????????????//自定義屬性?在ObjectAnimator中,是先根據(jù)屬性值拼裝成對應的set函數(shù)名字,比如下面handScale的拼裝方法就是
????????????//將屬性的第一個字母強制大寫后與set拼接,所以就是setHandScale,然后通過反射找到對應控件的setHandScale(float?handScale)函數(shù)
????????????//將當前數(shù)字值做為setHandScale(float?handScale)的參數(shù)傳入?set函數(shù)調(diào)用每隔十幾毫秒就會被用一次
????????????//ObjectAnimator只負責把當前運動動畫的數(shù)值傳給set函數(shù),set函數(shù)怎么來做就在里面寫就行
????????????ObjectAnimator?handScaleAnim?=?ObjectAnimator.ofFloat(this,?"handScale",?1f,?0.8f,?1f);????????????//設(shè)置動畫時間
????????????handScaleAnim.setDuration(duration);????????????//動畫?點亮手指的四點?從0?-?1出現(xiàn)
????????????ObjectAnimator?shingAlphaAnim?=?ObjectAnimator.ofFloat(this,?"shingAlpha",?0f,?1f);????????????//?shingAlphaAnim.setDuration(duration);
????????????//放大?點亮手指的四點
????????????ObjectAnimator?shingScaleAnim?=?ObjectAnimator.ofFloat(this,?"shingScale",?0f,?1f);????????????//畫中心圓形有內(nèi)到外擴散
????????????ObjectAnimator?shingClicleAnim?=?ObjectAnimator.ofFloat(this,?"shingCircleScale",?0.6f,?1f);????????????//畫出圓形有1到0消失
????????????ObjectAnimator?shingCircleAlphaAnim?=?ObjectAnimator.ofFloat(this,?"shingCircleAlpha",?0.3f,?0f);????????????//動畫集一起播放
????????????AnimatorSet?animatorSet?=?new?AnimatorSet();
????????????animatorSet.playTogether(handScaleAnim,?shingAlphaAnim,?shingScaleAnim,?shingClicleAnim,?shingCircleAlphaAnim);
????????????animatorSet.start();
????????}?else?{????????????//取消點贊
????????????--likeNumber;
????????????setLikeNum();
????????????ObjectAnimator?handScaleAnim?=?ObjectAnimator.ofFloat(this,?"handScale",?1f,?0.8f,?1f);
????????????handScaleAnim.setDuration(duration);
????????????handScaleAnim.start();????????????//手指上的四點消失,透明度設(shè)置為0
????????????setShingAlpha(0);
????????}
????}
上面用了幾個動畫函數(shù),這里運用了茲定于屬性,上面代碼解釋很清楚了
動畫會觸發(fā)下面相應setXXXX()方法
/**
?????*?手指縮放方法
?????*
?????*?@param?handScale
?????*/
????public?void?setHandScale(float?handScale)?{????????//傳遞縮放系數(shù)
????????this.handScale?=?handScale;????????//請求重繪View樹,即draw過程,視圖發(fā)生大小沒有變化就不會調(diào)用layout過程,并且重繪那些“需要重繪的”視圖
????????//如果是view就繪制該view,如果是ViewGroup,就繪制整個ViewGroup
????????invalidate();
????}????/**
?????*?手指上四點從0到1出現(xiàn)方法
?????*
?????*?@param?shingAlpha
?????*/
????public?void?setShingAlpha(float?shingAlpha)?{????????this.shiningAlpha?=?shingAlpha;
????????invalidate();
????}????/**
?????*?手指上四點縮放方法
?????*
?????*?@param?shingScale
?????*/
????@Keep????public?void?setShingScale(float?shingScale)?{????????this.shiningScale?=?shingScale;
????????invalidate();
????}????/**
?????*?設(shè)置數(shù)字變化
?????*/
????public?void?setLikeNum()?{????????//開始移動的Y坐標
????????float?startY;????????//最大移動的高度
????????textMaxMove?=?SystemUtil.dp2px(getContext(),?20);????????//如果點贊了?就下往上移
????????if?(isLike)?{
????????????startY?=?textMaxMove;
????????}?else?{
????????????startY?=?-textMaxMove;
????????}
????????ObjectAnimator?textInAlphaAnim?=?ObjectAnimator.ofFloat(this,?"textAlpha",?0f,?1f);
????????textInAlphaAnim.setDuration(duration);
????????ObjectAnimator?textMoveAnim?=?ObjectAnimator.ofFloat(this,?"textTranslate",?startY,?0);
????????textMoveAnim.setDuration(duration);
????????AnimatorSet?animatorSet?=?new?AnimatorSet();
????????animatorSet.playTogether(textInAlphaAnim,?textMoveAnim);
????????animatorSet.start();
????}????/**
?????*?設(shè)置數(shù)值透明度
?????*/
????public?void?setTextAlpha(float?textAlpha)?{????????this.textAlpha?=?textAlpha;
????????invalidate();
????}????/**
?????*?設(shè)置數(shù)值移動
?????*/
????public?void?setTextTranslate(float?textTranslate)?{
????????textMoveDistance?=?textTranslate;
????????invalidate();
????}????/**
?????*?畫出圓形波紋
?????*
?????*?@param?shingCircleScale
?????*/
????public?void?setShingCircleScale(float?shingCircleScale)?{????????this.shingCircleScale?=?shingCircleScale;
????????invalidate();
????}????/**
?????*?圓形透明度設(shè)置
?????*
?????*?@param?shingCircleAlpha
?????*/
????public?void?setShingCircleAlpha(float?shingCircleAlpha)?{????????this.shingCircleAlpha?=?shingCircleAlpha;
????????invalidate();
????}
效果如下:
這個簡單例子對一些自定義View的基本使用都涉及了,如繪制,canvas的一些基本用法等。
和即刻點贊效果還是有區(qū)別,可以通過加下動畫差值器優(yōu)化。
覺得文章不錯的小伙伴幫忙點點贊加關(guān)注哦 ,有什么問題的話也歡迎大家前來探討交流。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。