自定义Ripple效果的Colorfulbar

2024-03-04 17:40

本文主要是介绍自定义Ripple效果的Colorfulbar,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

近日在github上看到的一种效果。正好最近也在进阶学习自定义ViewGroup,便开始着手进行对这种效果小小实现了一把。
原效果:出自于 open-android/Android1

open-android/Android1是open-android/Android 下的一个子链接。

原创效果:效果实现有一些出入,不要在意这些细节。
images

思考如何去做:
1. 图标和文字是用自定义View还是自定义ViewGroup实现比较好
2. 图标和文字的动画
3. Ripple效果的实现
回答:
1. 图标和文字用自定义ViewGroup实现比较好,中间摆放一个ImageView和TextView。为什么?为了验证到底是自定义View好还是自定义ViewGroup好,我花了2-3晚上去码代码去尝试,得出的结论是自定义ViewGroup显得更加简洁一点,对view的动画操作比较简单。
2. 动画自然是View的属性动画
3. 最外层是重写的LinearLayout,根据点击的坐标重绘,实现ripple效果。


AnimItem : 图标icon+文字Text的ViewGroup
public class AnimItemV extends ViewGroup implements View.OnClickListener

成员变量。其中,status为1时显示的只有icon,为2时显示icon和文字。初始值默认为1,显示icon。

    private ImageView icon;private TextView text;private int x;private int y;//1-icon,2-icon+textprivate int status = 1;private static final String TAG = AnimItemV.class.getSimpleName();public AnimItemV(Context context){this(context, null);}public AnimItemV(Context context, AttributeSet attrs){this(context, attrs, -1);}public AnimItemV(Context context, AttributeSet attrs, int defStyleAttr){super(context, attrs, defStyleAttr);setOnClickListener(this);}@TargetApi(Build.VERSION_CODES.LOLLIPOP)public AnimItemV(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){super(context, attrs, defStyleAttr, defStyleRes);setOnClickListener(this);}

onMeassure

    @Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){setTag(1);int childCount = getChildCount();int widthMode = MeasureSpec.getMode(widthMeasureSpec);int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);int heightMode = MeasureSpec.getMode(heightMeasureSpec);int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);/*** wrap_content下的width,height*/int width = 0;int height = 0;for (int i = 0; i < childCount; i++){View childView = getChildAt(i);measureChild(childView, widthMeasureSpec, heightMeasureSpec);MarginLayoutParams childMargin = (MarginLayoutParams) childView.getLayoutParams();//子view的宽int childWidth = childView.getMeasuredWidth() + childMargin.leftMargin + childMargin.rightMargin;//子view的高int childHeight = childView.getMeasuredHeight() + childMargin.topMargin + childMargin.bottomMargin;width = Math.max(width, childWidth);height += childHeight;}width = Math.max(width, height);height = Math.max(width, height);//sizeWidth = width, sizeHeight = height是为了应对weight的情况sizeWidth = width;sizeHeight = height;setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? sizeWidth : width + getPaddingLeft() + getPaddingRight(),(heightMode == MeasureSpec.EXACTLY) ? sizeHeight : height + getPaddingTop() + getPaddingBottom());}

onLayout,坐标的计算并不复杂,画个草稿就可以想明白。

    @Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b){int width = getWidth();int height = getHeight();icon = (ImageView) getChildAt(0);MarginLayoutParams iconMargin = (MarginLayoutParams) icon.getLayoutParams();int iconWidth = icon.getMeasuredWidth() + iconMargin.leftMargin + iconMargin.rightMargin;int iconHeight = icon.getMeasuredHeight() + iconMargin.topMargin + iconMargin.bottomMargin;text = (TextView) getChildAt(1);text.setAlpha(0.0f);MarginLayoutParams textMargin = (MarginLayoutParams) text.getLayoutParams();int textWidth = text.getMeasuredWidth() + textMargin.leftMargin + textMargin.rightMargin;int textHeight = text.getMeasuredHeight() + textMargin.topMargin + textMargin.bottomMargin;int lc = (width - iconWidth) / 2;int tc = (width - iconWidth) / 2;int rc = lc + iconWidth;int bc = tc + iconHeight;icon.layout(lc, tc, rc, bc);int lc1 = (width - textWidth) / 2;int tc1 = bc;int rc1 = lc1 + textWidth;int bc1 = tc1 + textHeight;/*** -tc字体全部显示*/
//            text.layout(lc1,tc1-tc,rc1,bc1-tc);text.layout(lc1, tc1, rc1, bc1);}

最后是ImageView和TextView的属性动画,提供Public方法供外部调用。在写复原动画的时候要特别注意Y轴的数值变化,见注释。

    /*** icon向上移动,text向上渐显动画*/public void startAnim(){MarginLayoutParams iconMargin = (MarginLayoutParams) icon.getLayoutParams();int iconWidth = icon.getMeasuredWidth() + iconMargin.leftMargin + iconMargin.rightMargin;int tc = (getWidth() - iconWidth) / 2;//icon Y轴向上移动tcObjectAnimator iconYAnim = ObjectAnimator.ofFloat(icon, "TranslationY", -tc).setDuration(500);//text透明度从0变到1ObjectAnimator textAlphaAnim = ObjectAnimator.ofFloat(text, "Alpha", text.getAlpha(), 1.0f).setDuration(500);//text Y轴向上移动tcObjectAnimator textYAnim = ObjectAnimator.ofFloat(text, "TranslationY", -tc).setDuration(500);AnimatorSet animatorSet = new AnimatorSet();animatorSet.playTogether(iconYAnim, textAlphaAnim, textYAnim);animatorSet.start();setTag(2);}/*** 复原动画*/public void reverseAnim(){MarginLayoutParams iconMargin = (MarginLayoutParams) icon.getLayoutParams();int iconWidth = icon.getMeasuredWidth() + iconMargin.leftMargin + iconMargin.rightMargin;int tc = (getWidth() - iconWidth) / 2;/***注意这里是0,因为是-tc+tc,所以是0*///icon Y轴向下移动0ObjectAnimator iconYAnim = ObjectAnimator.ofFloat(icon, "TranslationY", 0).setDuration(500);//text透明度从1变到0ObjectAnimator textAlphaAnim = ObjectAnimator.ofFloat(text, "Alpha", text.getAlpha(), 0.0f).setDuration(500);//text Y轴向下移动0ObjectAnimator textYAnim = ObjectAnimator.ofFloat(text, "TranslationY", 0).setDuration(500);AnimatorSet animatorSet = new AnimatorSet();animatorSet.playTogether(iconYAnim, textAlphaAnim, textYAnim);animatorSet.start();setTag(1);}

最后,加上onckick事件,通过tag的获取到的status来判断应该执行哪个动画效果。

    @Overridepublic void onClick(View view){if ((int) getTag() == 1){startAnim();}else if ((int) getTag() == 2){reverseAnim();}}
AnimLinearLayout
public class AnimLinearLayout extends LinearLayout
    //存放一组颜色private int[] rippleColors;//子ViewGroup的X轴坐标private int childX;//子ViewGroup的Y轴坐标private int childY;//ripple的半径private float radius;//ripple的颜色private int rippleColor;//....getter and setter....public AnimLinearLayout(Context context, AttributeSet attrs){this(context, attrs, -1);}public AnimLinearLayout(Context context, AttributeSet attrs, int defStyleAttr){super(context, attrs, defStyleAttr);}@TargetApi(Build.VERSION_CODES.LOLLIPOP)public AnimLinearLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes){super(context, attrs, defStyleAttr, defStyleRes);}

在onAttachedToWindow中,遍历子view并为每一个子view设置OnTouchListener

    @Overrideprotected void onAttachedToWindow(){super.onAttachedToWindow();int childCount = getChildCount();for (int i = 0; i < childCount; i++){final AnimItemV animItemV = (AnimItemV) getChildAt(i);animItemV.setOnTouchListener(new OnTouchListener(){@Overridepublic boolean onTouch(View view, MotionEvent motionEvent){for (int i = 0; i < getChildCount(); i++){if (animItemV.getId() == getChildAt(i).getId()){continue;}((AnimItemV) getChildAt(i)).reverseAnim();}return false;}});}}

在onInterceptTouchEvent中,手指按下时获取x,y坐标,手指松开时执行相应的动画效果。

    @Overridepublic boolean onInterceptTouchEvent(MotionEvent event){Log.e(TAG, TAG + " onInterceptTouchEvent");int action = event.getAction();switch (action){case MotionEvent.ACTION_DOWN:int childCount = getChildCount();int x = (int) event.getX();int y = (int) event.getY();Log.e(TAG, "x-->" + x);for (int i = 0; i < childCount; i++){AnimItemV animItemV = (AnimItemV) getChildAt(i);if(x>=animItemV.getTop()&&x<=animItemV.getRight()){setRippleColor(getResources().getColor(rippleColors[i]));break;}}setChildX(x);setChildY(y);break;case MotionEvent.ACTION_UP:int radius = Math.max(getWidth(), getHeight());ValueAnimator animator = ValueAnimator.ofInt(0, radius);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){@Overridepublic void onAnimationUpdate(ValueAnimator valueAnimator){int currentRadius = (int) valueAnimator.getAnimatedValue();setRadius((float) (currentRadius));invalidate();}});animator.setInterpolator(new AccelerateDecelerateInterpolator());animator.start();animator.addListener(new AnimatorListenerAdapter(){@Overridepublic void onAnimationEnd(Animator animation){super.onAnimationEnd(animation);setBackgroundColor(rippleColor);}});break;}return super.onInterceptTouchEvent(event);}

layout.xml 由于是继承自LinearLayout,所以LinearLayout的一些属性可以直接用。

<com.gaoyy.learningcustomview.view.AnimLinearLayout
        android:id="@+id/activity_colorful_bar"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="#888888"android:gravity="center"android:orientation="horizontal"tools:context="com.gaoyy.learningcustomview.ui.ColorfulBarActivity"android:layout_alignParentBottom="true"><com.gaoyy.learningcustomview.view.AnimItemV
            android:id="@+id/item1"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"><ImageView
                android:layout_width="50dp"android:layout_height="50dp"android:src="@mipmap/ic_moment"/><TextView
                android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Moment"android:textColor="#ffffff"android:textSize="16sp"/></com.gaoyy.learningcustomview.view.AnimItemV><com.gaoyy.learningcustomview.view.AnimItemV
            android:id="@+id/item2"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"><ImageView
                android:layout_width="50dp"android:layout_height="50dp"android:src="@mipmap/ic_friend"/><TextView
                android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Friend"android:textColor="#ffffff"android:textSize="16sp"/></com.gaoyy.learningcustomview.view.AnimItemV>...</com.gaoyy.learningcustomview.view.AnimLinearLayout>

项目地址:https://github.com/gaoyuyu/LearningCustomView

这篇关于自定义Ripple效果的Colorfulbar的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/773941

相关文章

Java实现自定义table宽高的示例代码

《Java实现自定义table宽高的示例代码》在桌面应用、管理系统乃至报表工具中,表格(JTable)作为最常用的数据展示组件,不仅承载对数据的增删改查,还需要配合布局与视觉需求,而JavaSwing... 目录一、项目背景详细介绍二、项目需求详细介绍三、相关技术详细介绍四、实现思路详细介绍五、完整实现代码

一文详解Java Stream的sorted自定义排序

《一文详解JavaStream的sorted自定义排序》Javastream中的sorted方法是用于对流中的元素进行排序的方法,它可以接受一个comparator参数,用于指定排序规则,sorte... 目录一、sorted 操作的基础原理二、自定义排序的实现方式1. Comparator 接口的 Lam

如何自定义一个log适配器starter

《如何自定义一个log适配器starter》:本文主要介绍如何自定义一个log适配器starter的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录需求Starter 项目目录结构pom.XML 配置LogInitializer实现MDCInterceptor

Druid连接池实现自定义数据库密码加解密功能

《Druid连接池实现自定义数据库密码加解密功能》在现代应用开发中,数据安全是至关重要的,本文将介绍如何在​​Druid​​连接池中实现自定义的数据库密码加解密功能,有需要的小伙伴可以参考一下... 目录1. 环境准备2. 密码加密算法的选择3. 自定义 ​​DruidDataSource​​ 的密码解密3

spring-gateway filters添加自定义过滤器实现流程分析(可插拔)

《spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔)》:本文主要介绍spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔),本文通过实例图... 目录需求背景需求拆解设计流程及作用域逻辑处理代码逻辑需求背景公司要求,通过公司网络代理访问的请求需要做请

Kotlin Compose Button 实现长按监听并实现动画效果(完整代码)

《KotlinComposeButton实现长按监听并实现动画效果(完整代码)》想要实现长按按钮开始录音,松开发送的功能,因此为了实现这些功能就需要自己写一个Button来解决问题,下面小编给大... 目录Button 实现原理1. Surface 的作用(关键)2. InteractionSource3.

使用WPF实现窗口抖动动画效果

《使用WPF实现窗口抖动动画效果》在用户界面设计中,适当的动画反馈可以提升用户体验,尤其是在错误提示、操作失败等场景下,窗口抖动作为一种常见且直观的视觉反馈方式,常用于提醒用户注意当前状态,本文将详细... 目录前言实现思路概述核心代码实现1、 获取目标窗口2、初始化基础位置值3、创建抖动动画4、动画完成后

uniapp小程序中实现无缝衔接滚动效果代码示例

《uniapp小程序中实现无缝衔接滚动效果代码示例》:本文主要介绍uniapp小程序中实现无缝衔接滚动效果的相关资料,该方法可以实现滚动内容中字的不同的颜色更改,并且可以根据需要进行艺术化更改和自... 组件滚动通知只能实现简单的滚动效果,不能实现滚动内容中的字进行不同颜色的更改,下面实现一个无缝衔接的滚动

Java实现图片淡入淡出效果

《Java实现图片淡入淡出效果》在现代图形用户界面和游戏开发中,**图片淡入淡出(FadeIn/Out)**是一种常见且实用的视觉过渡效果,它可以用于启动画面、场景切换、轮播图、提示框弹出等场景,通过... 目录1. 项目背景详细介绍2. 项目需求详细介绍2.1 功能需求2.2 非功能需求3. 相关技术详细

使用animation.css库快速实现CSS3旋转动画效果

《使用animation.css库快速实现CSS3旋转动画效果》随着Web技术的不断发展,动画效果已经成为了网页设计中不可或缺的一部分,本文将深入探讨animation.css的工作原理,如何使用以及... 目录1. css3动画技术简介2. animation.css库介绍2.1 animation.cs