咱们的态度是:天天进步一点点,理想终会被实现。android
前言
周一又到了,是否是感受一个周末还没过咋个都没了呢?既来之则安之,咱们仍是来学习点有用的,因为以前无心间看到了一个点赞的效果,感受多么高大上的,因此想着本身也来实现一下。所以有了此文,若是文中有错还望各位小伙伴指出出来,自定义View的大佬能够跳过了,(^__^) 嘻嘻……git
咱们仍是先看看实现效果:github
接下来咱们看看实现方法。canvas
点赞效果实现
1.分析怎么实现微信
分析:ide
咱们能够将这个点赞效果能够分为两个部分:布局
点击部分
咱们能够经过attrs自定义的属性,拿到图片的Drawable,经过调用drawable.draw(canvas)方法直接画出来。post
上方显示的动画部分
第二部分最开始我想到的是直接在上方画一个TextView,而后设置属性动画
达到咱们的效果,后来思考这种效果最好不增长自身控件的大小,假如在上方直接添加TextView那么必然怎么整个控件的高度,不少这种点赞的效果是放在列表中,高度有限。因此我最后想的是使用PopupWindow来实现,而后设置属性动画。学习
自定义View部分
咱们须要定义的属性有:动画
<?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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
2.咱们在构造方法中获取对应属性,而后初始化PopupWidow:
/**
* 初始化popupWindow
*/
private void initPopupWindow() {
mPopupWindow = new PopupWindow();
//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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
这里面要注意的是咱们要计算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) {
}
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
5.咱们onDraw()方法以前咱们还须要量测一下咱们控件的大小,假如咱们不量测宽高,咱们在XML中引用咱们的控件咱们本身设定一个宽高,比实际的图片的宽高要大,最终显示的图片仍是原图片大小,不会按照XML中设定的值放大或者缩小。
/**
* 量测点击控件的大小
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@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);
//根据量测的宽高,设置咱们画的Drawable的大小
@SuppressLint("DrawAllocation")
Rect rect = new Rect(0, 0, width, height);
agreeDrawable.setBounds(rect);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
6.画咱们的图片:
@Override
protected void onDraw(Canvas canvas) {
//将咱们的Drawable画到画布
agreeDrawable.draw(canvas);
}
1
2
3
4
5
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);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
这里自定义了一个View的点击事件方法,供外部调用。
MainActivity中添加自定义控件和属性
<?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>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
最终咱们的实现效果以下:
至此咱们的自定义点赞效果就完成了,源码我已经上传到Github上面,若是有须要的能够自行下载Github(https://github.com/scorpioLt/AgreeView)
总结
其实自定义View并无想得那么复杂,只要想通了原理,过程一步一步的写,像这个点赞效果主要就分为:点赞图片的缩放、向上移动的属性动画;上方的动画用PopupWindow实现,下方直接获取Drawable画到Canvas便可。
舒适提示:
我建立了一个技术交流群,群里有各个行业的大佬都有,你们能够在群里畅聊技术方面内容,以及文章推荐;若是有想加入的伙伴加我微信号【luotaosc】备注一下“加群” 另外关注公众号,还有一些我的收藏的视频: 回复“Android” ,获取Android视频连接。 回复“Java” ,获取Java视频连接。 回复“C++” ,获取C++视频连接。 回复“C” ,获取C视频连接。 回复“Python” ,获取Python视频连接等等。
原创不易,若是以为写得好,扫码关注一下点个赞,是我最大的动力。
关注我,必定会有意想不到的东西等你: 天天专一分享Android、JAVA干货
————————————————
版权声明:本文为CSDN博主「lt_sc」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处连接及本声明。
原文连接:https://blog.csdn.net/qq_34560959/article/details/80649405