自定义控件 | 仿《最美有物》点赞效果

继最美有物之后,又在饿了么上看到了类似的控件。果然需求这个东西是做不完的,以为功能做好就OK了吗,不要停,这里还差一个玩出花来的点赞效果!

直接上动图,看看两家的实现效果。

最美有物
饿了么

这样可爱的点赞特效,不得不给个好评啊, 有没有!

当然光点赞好评是不行的,今天得自己动手撸一个这样的笑脸点赞控件。

主要流程和实现分析

以最美有物的点赞效果为模板(相较饿了么更丰富),从整体的操作流程来分析:

点击触发 :

  1. 颜色切换(选中黄,未选中白)

  2. 按比例拉伸(两个笑脸同时升高,显示点赞比例的高度)

  3. 展示数据文本(百分比数据显示)

  4. 显示选中部分的笑脸动画(这一步与3文本数据同时进行,在拉伸至最高处停留后)

  5. 缩回至原始大小,保持选中状态

通过这个流程的分析,我们可以把主要的功能点划分为以下两类:

​ 1.动画类 : 脸部动画,拉伸动画

​ 2.控制类: 颜色切换,数据显示,拉伸比例

在自定义View的过程中,动画特效是最吸引人,也是最复杂的部分,虽然现在有高效炫酷的矢量动画库供我们选择, 但是基础动画的组合也是相当有用的,重点是发挥想象力。

其中,脸部动画在解压apk后找到相关图片,不出所料是个帧动画。

而主要的难点就在于如何进行拉伸操作。对于将一个圆形从中间拉伸成长条...

最开始想到的方案是拼接图形,即通过圆形 + 矩形 + 圆形的方式叠加这个控件。通过调节中间矩形的高度,来控制拉伸操作。但是这种方式结构略复杂,需要在一小块地方摆上三个图形,还要带上最外层的笑脸动画,还没写代码就感觉应该是性能低下的方案,另外一个致命的问题就是,不能有描边!大家可以参考饿了么的实现效果,在有描边的情况下,形状拼接的方案明显不可行。遂放弃。

而最终采用的则是使用圆角矩形作为外层Layout背景,通过控制内部笑脸的marginBottom,来动态的调节Layout的高度。这样,即可以保持笑脸始终处于控件的上部,同时也能控制相对简单的结构。

拉伸操作的实现

首先我们先简单的模拟下通过marginBottom控制拉伸的效果。

在布局里设置一个LinearLayout ,里面只有一个ImageView。用一个seekBar来模拟效果。

Layout:

 <LinearLayout
        android:id="@+id/backGround"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:background="@drawable/yellow_background"
        android:layout_above="@+id/seekBar"
        android:layout_centerHorizontal="true">

        <ImageView
            android:id="@+id/smileFace"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:src="@drawable/like_1"/>
    </LinearLayout>

这里Linearlayout中设置了一个背景,是自定义的圆角矩形shape,通过调大圆角,使其显示为一个正圆。

Activity:

    @Override
            public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
                LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams)smileFace.getLayoutParams();
                layoutParams.bottomMargin = i*3;
                smileFace.setLayoutParams(layoutParams);
            }

通过获取SmileFace的LayoutParams,通过Seekbar设置其下边距bottomMargin,来控制高度。

效果如下所示:

2017-07-26_13-46-10.gif

这样拉伸的原理就很清楚了。

我们需要在自定义控件中完成上述操作,并用属性动画替换掉seekBar。

自定义控件的封装

子控件的布局

考虑到实现目标里有两个并排的笑脸控件,这里采用继承LinearLayout的方式,可以把两个控件及中间的分割线直接摆放进去。

首先初始化两个脸部动画的ImageView及动画资源,以及两个显示点赞比例的数字及文本的TextView。
在初始化的时候设置好相关参数,提取出默认值并提供方法设置相关参数。然后把百分比,文字,包含笑脸的Layout,都添加到另外一个Linearlayout中,然后再将喜欢不喜欢添加到当前自定义控件中。

    //初始化图片
        imageLike = new ImageView(getContext());
        //添加动画资源  获得帧动画
        imageLike.setBackgroundResource(R.drawable.animation_like);
        animLike = (AnimationDrawable) imageLike.getBackground();
        //初始化文字
        likeNum = new TextView(getContext());
        likeNum.setText(like + "%");
        likeNum.setTextColor(defalutTextColor);
        TextPaint likeNumPaint = likeNum.getPaint();
        likeNumPaint.setFakeBoldText(true);
        likeNum.setTextSize(20f);
        likeText = new TextView(getContext());
        likeText.setText(defaultLike);
        likeText.setTextColor(defalutTextColor);

        .....

        disAll.addView(disNum, params);
        disAll.addView(disText, params);
        disAll.addView(disBack, params);
        likeAll.addView(likeNum, params);
        likeAll.addView(likeText, params);
        likeAll.addView(likeBack, params);

这里同时还要注意隐藏文字,以及默认设置为未选中状态。这一段代码虽然挺多,其实也只是在View中做了xml里的事情,了解了整体结构后其实非常简单,实际上直接写好XML再加载也是没问题的。

整体流程和动画分析

控件的事件其实只有两个点击事件,需要注意的是动画流程的控制。
1.拉伸属性动画 》 2.表情帧动画 》3.与2同时进行的平移动画

直接在控件设置onClickListener。点击开始执行拉伸动画。并在动画开始后限制点击事件,流程结束后释放。避免重复点击动画错乱。同时给属性动画设置监听,在拉伸执行完毕后,继续执行面部的动画。

通过属性动画,将喜欢不喜欢的数字比例设置为两个笑脸的bottomMargin,这里由于Max是使用两个数字和,显示的高度会依据数字的大小有差别,也可以设置为一个固定值,完全暗战比例来显示高度,这个可以依据自己的数据源和需求修改。由于在属性动画中同时设置两个高度,所以需要通过判断限制高度,当前magrin与达到数据要求时停止,从而有比例低的一方会停止拉伸。

拉伸的属性动画,与之对应还有一个缩回的动画,如果有需要还可以加上插值器,优化弹起的效果。

//背景伸展动画
    public void animBack() {
        //动画执行中不能点击
        imageDis.setClickable(false);
        imageLike.setClickable(false);

        final int max = Math.max(like * 4, disLike * 4);
        animatorBack = ValueAnimator.ofInt(5, max);
        animatorBack.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int magrin = (int) animation.getAnimatedValue();
                LayoutParams paramsLike
                        = (LayoutParams) imageLike.getLayoutParams();
                paramsLike.bottomMargin = magrin;

                if (magrin <= like * 4) {
                    imageLike.setLayoutParams(paramsLike);
                }
                if (magrin <= disLike * 4) {
                    imageDis.setLayoutParams(paramsLike);
                }
            }
        });
        isClose = false;
        animatorBack.addListener(this);
        animatorBack.setDuration(500);
        animatorBack.start();
    }

拉伸动画结束后,帧动画与平移动画共同构成了面部的表情动画,通过补间动画的配合使面部表情更加生动。同时也是用补间动画的结束监听来继续执行动画恢复原始状态。下图的animLike为帧动画,objectX,Y为对应轴的平移动画。

拉伸和恢复动画的结束监听,通过isClose区分.以及平移动画。

 @Override
    public void onAnimationEnd(Animator animation) {
        //重置帧动画
        animDis.stop();
        animLike.stop();

        //关闭时不执行帧动画
        if (isClose) {
            //收回后可点击
            imageDis.setClickable(true);
            imageLike.setClickable(true);
            //隐藏文字
            setVisibities(GONE);
            //恢复透明
            setBackgroundColor(Color.TRANSPARENT);
            return;
        }
        isClose = true;

        if (type == 0) {
            animLike.start();
            objectY(imageLike);
        } else {
            animDis.start();
            objectX(imageDis);
        }
    }

public void objectY(View view) {
        ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationY", -10.0f, 0.0f, 10.0f, 0.0f, -10.0f, 0.0f, 10.0f, 0);
        animator.setRepeatMode(ObjectAnimator.RESTART);
        //animator.setRepeatCount(1);
        animator.setDuration(1500);
        animator.start();
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                setBackUp(); //执行回弹动画
            }
        });
    }

最终效果

完整的动画流程实现后,我们的控件就基本完成了。在XML中直接使用,并使用setNum设置数字,基本实现了最美有物的点赞控件效果,简单的撸了一遍。看下最终效果图吧。

        smileView = (SmileView) findViewById(R.id.smileView);
        smileView.setNum(60,40);
2017-07-26_15-18-35.gif

附上完整的demo地址。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • 前言 最近在跟着Hencode学习自定义控件,一直想着自己能够照着别人写的demo写一个好看的View,就科学上网...
    Android_Simon阅读 358评论 0 1
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,392评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,016评论 4 62
  • 最后一晚了,正常来说应该写总结,看了看以前随手记下的主题,还是想写写朋友圈儿。 我有两个朋友圈儿,一个朋友圈主要是...
    铭铭大嫂阅读 589评论 1 4
  • 最好的感受时间的方式,就是看婴儿的成长,植物的生长。 之前有一段时间特别爱看黑人的微博,几乎全都是晒他的双...
    我是马依云阅读 311评论 0 0