Android 自定義View 點讚效果

2021-03-02 Android 編程之旅

周一又到了,是不是感覺一個周末還沒過咋個都沒了呢?既來之則安之,我們還是來學習點有用的,由於之前無意間看到了一個點讚的效果,感覺多麼高大上的,所以想著自己也來實現一下。因此有了此文,如果文中有錯還望各位小夥伴指出出來,自定義View的大佬可以跳過了,(*^__^*) 嘻嘻……

我們還是先看看實現效果:

接下來我們看看實現方法。

分析:

我們可以將這個點讚效果可以分為兩個部分:

點擊部分

我們可以通過attrs自定義的屬性,拿到圖片的Drawable,通過調用drawable.draw(canvas)方法直接畫出來。

上方顯示的動畫部分

        

        第二部分最開始我想到的是直接在上方畫一個TextView,然後設置屬性動畫 達到我們的效果,後來思考這種效果最好不增加自身控制項的大小,假如在上方直接添加TextView那麼必然怎麼整個控制項的高度,很多這種點讚的效果是放在列表中,高度有限。所以我最後想的是使用PopupWindow來實現,然後設置屬性動畫。

我們需要定義的屬性有:

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <declare-styleable name="AgreeView">
       
       <attr name="distance" format="integer"/>
       
       <attr name="from_y" format="integer"/>
       
       <attr name="from_alpha" format="float"/>
       
       <attr name="to_alpha" format="float"/>
       
       <attr name="duration" format="integer"/>
       
       <attr name="text" format="string"/>
       
       <attr name="text_size" format="integer"/>
       
       <attr name="text_color" format="color"/>
       
       <attr name="img" format="reference"/>
       
       <attr name="animation_img" format="reference"/>
       
       <attr name="animation" format="enum">
           <enum name="text" value="0"/>
           <enum name="img" value="1"/>
       </attr>
   </declare-styleable>
</resources>

2.我們在構造方法中獲取對應屬性,然後初始化PopupWidow:


   private void initPopupWindow() {
       mPopupWindow = new PopupWindow();
       
       RelativeLayout layout = new RelativeLayout(mContext);
       
       RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
               ViewGroup.LayoutParams.WRAP_CONTENT);
       layoutParams.addRule(RelativeLayout.CENTER_HORIZONTAL);
       layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
       tvAnimation = new AppCompatTextView(mContext);
       tvAnimation.setIncludeFontPadding(false);
       tvAnimation.setTextSize(TypedValue.COMPLEX_UNIT_DIP, text_size);
       tvAnimation.setTextColor(text_color);
       if (animationMode == ANIMATION_MODE_TEXT) {
           tvAnimation.setText(text);
       } else {
           tvAnimation.setText("");
           tvAnimation.setBackgroundDrawable(animalDrawable);
       }
       tvAnimation.setLayoutParams(layoutParams);
       layout.addView(tvAnimation);
       mPopupWindow.setContentView(layout);

       
       int w = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
       int h = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
       tvAnimation.measure(w, h);
       mPopupWindow.setWidth(tvAnimation.getMeasuredWidth());
       Log.e(TAG, "distance==== " + distance);
       mPopupWindow.setHeight(distance + tvAnimation.getMeasuredHeight());
       mPopupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
       mPopupWindow.setFocusable(false);
       mPopupWindow.setTouchable(false);
       mPopupWindow.setOutsideTouchable(false);
   }

這裡面要注意的是我們要計算PopupWidow的高度和寬度,我們將 RelativeLayout

作為ViewGroup,用  AppCompatTextView作為動畫控制項,如果是圖片則直接設置背景圖片。

3.設置我們點讚View上方的動畫:


   private void setPopAnimation() {
       mAnimationSet = new AnimationSet(true);
       TranslateAnimation translateAnim = new TranslateAnimation(0, 0, from_y, -to_y);
       AlphaAnimation alphaAnim = new AlphaAnimation(from_alpha, to_alpha);
       mAnimationSet.addAnimation(translateAnim);
       mAnimationSet.addAnimation(alphaAnim);
       mAnimationSet.setDuration(duration);
       mAnimationSet.setAnimationListener(new Animation.AnimationListener() {
           @Override
           public void onAnimationStart(Animation animation) {
           }

           @Override
           public void onAnimationEnd(Animation animation) {
               if (mPopupWindow != null && mPopupWindow.isShowing()) {
                   new Handler().post(new Runnable() {
                       @Override
                       public void run() {
                           mPopupWindow.dismiss();
                       }
                   });
               }
           }

           @Override
           public void onAnimationRepeat(Animation animation) {
           }
       });
   }

4.設置我們點讚圖片的動畫效果:


   private void setScaleAnimation() {
       ObjectAnimator scaleX = ObjectAnimator.ofFloat(this, "scaleX", 1f, 0.8f, 1.2f, 1f);
       ObjectAnimator scaleY = ObjectAnimator.ofFloat(this, "scaleY", 1f, 0.8f, 1.2f, 1f);
       scaleX.setDuration(duration);
       scaleY.setDuration(duration);
       scaleX.setInterpolator(new AccelerateDecelerateInterpolator());
       scaleY.setInterpolator(new AccelerateDecelerateInterpolator());
       AnimatorSet animatorSet = new AnimatorSet();
       animatorSet.play(scaleX).with(scaleY);
       animatorSet.start();
   }

5.我們onDraw()方法之前我們還需要量測一下我們控制項的大小,假如我們不量測寬高,我們在XML中引用我們的控制項我們自己設定一個寬高,比實際的圖片的寬高要大,最終顯示的圖片還是原圖片大小,不會按照XML中設定的值放大或者縮小。


   @Override
   protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
       super.onMeasure(widthMeasureSpec, heightMeasureSpec);
       int width;
       int height;
       int w_mode = MeasureSpec.getMode(widthMeasureSpec);
       int w_size = MeasureSpec.getSize(widthMeasureSpec);

       int h_mode = MeasureSpec.getMode(heightMeasureSpec);
       int h_size = MeasureSpec.getSize(heightMeasureSpec);

       if (w_mode == MeasureSpec.AT_MOST || w_mode == MeasureSpec.UNSPECIFIED) {
           width = agreeDrawable.getIntrinsicWidth();
       } else {
           width = w_size;
       }

       if (h_mode == MeasureSpec.AT_MOST || h_mode == MeasureSpec.UNSPECIFIED) {
           height = agreeDrawable.getIntrinsicHeight();
       } else {
           height = h_size;
       }
       setMeasuredDimension(width, height);

       
       @SuppressLint("DrawAllocation")
       Rect rect = new Rect(0, 0, width, height);
       agreeDrawable.setBounds(rect);
   }

6.畫我們的圖片:

@Override
   protected void onDraw(Canvas canvas) {
       
       agreeDrawable.draw(canvas);
   }

7.當我們點擊圖片的時候觸發動畫:

@Override
   public void onClick(View v) {
       if (mPopupWindow != null && !mPopupWindow.isShowing()) {
           int offsetY = -getHeight() - mPopupWindow.getHeight();
           mPopupWindow.showAsDropDown(this, getWidth() / 2 - mPopupWindow.getWidth() / 2, offsetY);
           mPopupWindow.update();
           if (mAnimationSet == null) {
               setPopAnimation();
           }
           tvAnimation.startAnimation(mAnimationSet);
           setScaleAnimation();
           
           if (clickListener != null) {
               clickListener.onAgreeClick(v);
           }
       }
   }

這裡自定義了一個View的點擊事件方法,供外部調用。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:agreeview="http://schemas.android.com/apk/res-auto"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context="com.lt.agreeview.MainActivity">

   <com.lt.agreeview.AgreeView
       android:id="@+id/agreeView4"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_marginBottom="8dp"
       android:layout_marginTop="8dp"
       agreeview:animation="text"
       agreeview:animation_img="@drawable/ic_favorite_black_24dp"
       agreeview:distance="100"
       agreeview:from_y="60"
       agreeview:layout_constraintBottom_toBottomOf="parent"
       agreeview:layout_constraintEnd_toStartOf="@+id/agreeView3"
       agreeview:layout_constraintHorizontal_bias="0.5"
       agreeview:layout_constraintStart_toStartOf="parent"
       agreeview:layout_constraintTop_toTopOf="parent"
       agreeview:text="我喜歡你+1"
       agreeview:text_color="@color/text_color">
   </com.lt.agreeview.AgreeView>

   <com.lt.agreeview.AgreeView
       android:id="@+id/agreeView3"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       agreeview:animation="img"
       agreeview:animation_img="@drawable/ic_grade_black_24dp"
       agreeview:distance="100"
       agreeview:from_y="60"
       agreeview:layout_constraintBottom_toBottomOf="@+id/agreeView4"
       agreeview:layout_constraintEnd_toEndOf="parent"
       agreeview:layout_constraintHorizontal_bias="0.5"
       agreeview:img="@drawable/ic_grade_black_24dp"
       agreeview:layout_constraintStart_toEndOf="@+id/agreeView4"
       agreeview:layout_constraintTop_toTopOf="@+id/agreeView4"
       agreeview:text_color="@color/text_color">
   </com.lt.agreeview.AgreeView>

   <android.support.constraint.Group
       android:id="@+id/group"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"/>

</android.support.constraint.ConstraintLayout>

最終我們的實現效果如下:

至此我們的自定義點讚效果就完成了,源碼我已經上傳到Github上面,如果有需要的可以自行下載Github(https://github.com/scorpioLt/AgreeView)

        其實自定義View並沒有想得那麼複雜,只要想通了原理,過程一步一步的寫,像這個點讚效果主要就分為:點讚圖片的縮放、向上移動的屬性動畫;上方的動畫用PopupWindow實現,下方直接獲取Drawable畫到Canvas即可。


溫馨提示:

我創建了一個技術交流群,群裡有各個行業的大佬都有,大家可以在群裡暢聊技術方面內容,以及文章推薦;如果有想加入的夥伴加我微信號【luotaosc】備註一下「加群

另外關注公眾號,還有一些個人收藏的視頻:

回復「Android」 ,獲取Android視頻連結。

回復「Java」 ,獲取Java視頻連結。

回復「C++」 ,獲取C++視頻連結。

回復「C」 ,獲取C視頻連結。

回復「Python」 ,獲取Python視頻連結等等。

原創不易,如果覺得寫得好,掃碼關注一下點個讚,是我最大的動力。

備註:程序圈LT

相關焦點

  • Android自定義View入門及實戰案例分析
    複寫view中的一些函數3. 為自定義View類增加屬性(兩種方式)4. 繪製控制項(導入布局)5. 響應用戶事件6. 定義回調函數(根據自己需求來選擇)二、哪些方法需要被重寫   view中onDraw()是個空函數,也就是說具體的視圖都要覆寫該函數來實現自己的繪製。
  • 一篇文章搞懂Android 自定義viewgroup的難點
    很多網上隨便搜搜的概念和流程圖 這裡不再過多描述了,建議大家看本文之前,先看看基本的自定義viewgroup流程,心中有個大概即可。4.父view調用子view的layout方法的時候會把之前measure階段確定的位置和大小都傳遞給子view。5.對於自定義view/viewgroup來說 我們幾乎只需要關注下面三種需求:對於已有的android自帶的view,我們只需要重寫他的onMeasure方法即可。修改一下這個尺寸即可完成需求。
  • Android 自定義View篇(十)實現跑馬燈垂直滾動效果
    本文是對上篇文章的一個補充,股票 APP 列表底部有一個實時更新交易的跑馬燈效果,縱觀市面上很多產品都應用到這個效果,決定自己動手實現一下。開發準備工作 1、實現效果圖,建議自定義 ViewFlipper 實現自己的需求。
  • android 自定義view大小 - CSDN
    --場景1-->android:layout_width="match_parent"android:layout_height="match_parent"那麼按照我們的期望,希望子View的尺寸要是300dp*300dp,如果子View的布局參數是<!
  • android 自定義view大小專題及常見問題 - CSDN
    Android自定義View概述Android開發進階的必經之路為什麼要自定義View自定義View的基本方法自定義View的最基本的三個方法分別是: onMeasure()、onLayout()、onDraw();View在Activity中顯示出來,要經歷測量、布局和繪製三個步驟,分別對應三個動作:measure、layout和draw。
  • Android RecyclerView自定義LayoutManager
    在第一篇中已經講過,LayoutManager主要用於布局其中的Item,在LayoutManager中能夠對每個Item的大小,位置進行更改,將它放在我們想要的位置,在很多優秀的效果中,都是通過自定義LayoutManager來實現的,比如:Github: https://github.com
  • 這些都是Android中不規則形狀View的布局實現!
    而對於非方形的,Android官方並沒有給出非常好的解決方案.有的無非就是自定義View了.然而自定義View非常麻煩,需要重寫很多方法,而且稍微不注意可能就會喪失一些特性或者造成一些Bug。而且即便是自定義View,其實那個自定義View還是方的!!!
  • Android-SetContentView內部原理
    view, ViewGroup.LayoutParams params) {        getDelegate().setContentView(view, params);    }我們發現是代理模式,繼續查詢getDelegate(),點進去查看:|
  • Android實現快遞時間軸功能
    前言具體實現1.最終效果如下:
  • Android如何獲取WebView內容高度
    效果1 . 分頁加載2 . 動態獲取高度,點擊閱讀更多,會將幾個隱藏的div,顯示出來,造成WebView內容高度變化。><com.trs.studyview.view.TRSScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/scrollView"
  • 從零開始的Android新項目8 - Data Binding高級篇
    原文:http://blog.zhaiyifan.cn/2016/07/06/android-new-project-from-0-p8/本文是MarkZhai同學系列文章的第8篇,剛剛完稿,此文承接 《從零開始的Android新項目7 - Data Binding入門篇》,繼續介紹Data Binding的進階內容,建議沒看過上篇的同學先前往閱讀,效果更佳,第7篇早前並沒有在我公眾號發布
  • 自定義Toast以及玩轉SnackBar
    v = inflate.inflate(R.layout.item_custom_toast, null);                        TextView tv = (TextView) v.findViewById(R.id.id_text);        tv.setText("Hello,我是自定義Toast內容");
  • 架構組件之 ViewModel | 中文教學視頻
    通過使用 ViewModel,開發者可以方便的將 UI 界面和數據邏輯剝離開來,從而達到 UI 界面 Activity 和 Fragment 負責顯示數據和處理用戶操作;ViewModel 則負責提供和管理 UI 界面的數據,並且負責和數據層通訊的效果。與此同時,也讓您在開發過程中更好地遵循單一職責的設計原則。此外,ViewModel 的另一大特點是它不會因為配置變更而銷毀。
  • Android設置選項開發及自定義Preference樣式
    4 android:title=設置 >567 android:title=關於 >89101112 preference:tipstring=>13preference:titlestring=自定義測試 >1415 android:action=android.intent.action.VIEW16 android:data=http://www.baidu.com />171819
  • Android自定義View-蜘蛛網屬性圖(五邊形圖)
    右上角的頂點為第一個點,順時針計算,position 依次是 0,1,2,3,4  第四步:繪製圖標先來看看這一步的效果:第五步:繪製中心點的分數這一步完成之後就可以得到最終效果了,就是圖一的效果:文字的坐標是中心點,那麼計算出文字的寬度和高度就可以居中顯示文字了。
  • Android平臺View的按鍵事件KeyDown用法
    摘要:平時設計自己的顯示類View時需要捕獲按鍵事件,比如KeyEvent、首先引入android.view.KeyEvent
  • Android N功能:自定義快捷圖標/應用更簡潔
    Android N功能:自定義快捷圖標/應用更簡潔  據外媒androidheadlines報導稱,谷歌將為Android 7.0正式版增加一個新特性,即允許用戶自定義快速設置中心  外媒稱,此次自定義快速設置欄並不是普通意義上的自定義,而是向三方開發者開放了更多的通知欄快捷圖標權限,這意味著任何開發者都可以在快速設置中開發一些小功能,並向裡面增加新項目,甚至可以無縫集成。
  • 一個功能強大的自定義SeekBar
    本篇來自 二轉 的投稿,主要講解了一個功能強大的自定義SeekBar控制項,希望對大家有所幫助!二轉 的博客地址:http://www.jianshu.com/u/4060186e538c最近在工作上的需要,自定義了一個漂亮而強大的自定義view,但不僅僅只是一個SeekBar而已哦,一定要耐心看完。剛開始是不願意自己去寫的,這東西太浪費時間,UI這東西不一定是個技術活,但一定是個細活。
  • Android解決Textview內容中含有文字和數字換行不整齊的問題
    效果圖:上面的是原生Textview,第一行末尾數字整體換行了,下面是自定義Textview,第一行末尾數字分別在第一行和第二行展示
  • android tv放大專題及常見問題 - CSDN
    在tv上開發gridview有焦點放大這個效果還是很普遍的做法,今天就講下這個實現方案,當然要實現這個效果有很多種,我這裡只是講其中的一種實現方案,也是比較簡單而且容易看懂的一個,首先看下效果圖是怎麼樣的?