自定义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

相关文章

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

防近视护眼台灯什么牌子好?五款防近视效果好的护眼台灯推荐

在家里,灯具是属于离不开的家具,每个大大小小的地方都需要的照亮,所以一盏好灯是必不可少的,每个发挥着作用。而护眼台灯就起了一个保护眼睛,预防近视的作用。可以保护我们在学习,阅读的时候提供一个合适的光线环境,保护我们的眼睛。防近视护眼台灯什么牌子好?那我们怎么选择一个优秀的护眼台灯也是很重要,才能起到最大的护眼效果。下面五款防近视效果好的护眼台灯推荐: 一:六个推荐防近视效果好的护眼台灯的

自定义类型:结构体(续)

目录 一. 结构体的内存对齐 1.1 为什么存在内存对齐? 1.2 修改默认对齐数 二. 结构体传参 三. 结构体实现位段 一. 结构体的内存对齐 在前面的文章里我们已经讲过一部分的内存对齐的知识,并举出了两个例子,我们再举出两个例子继续说明: struct S3{double a;int b;char c;};int mian(){printf("%zd\n",s

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

Oracle type (自定义类型的使用)

oracle - type   type定义: oracle中自定义数据类型 oracle中有基本的数据类型,如number,varchar2,date,numeric,float....但有时候我们需要特殊的格式, 如将name定义为(firstname,lastname)的形式,我们想把这个作为一个表的一列看待,这时候就要我们自己定义一个数据类型 格式 :create or repla

HTML5自定义属性对象Dataset

原文转自HTML5自定义属性对象Dataset简介 一、html5 自定义属性介绍 之前翻译的“你必须知道的28个HTML5特征、窍门和技术”一文中对于HTML5中自定义合法属性data-已经做过些介绍,就是在HTML5中我们可以使用data-前缀设置我们需要的自定义属性,来进行一些数据的存放,例如我们要在一个文字按钮上存放相对应的id: <a href="javascript:" d

一步一步将PlantUML类图导出为自定义格式的XMI文件

一步一步将PlantUML类图导出为自定义格式的XMI文件 说明: 首次发表日期:2024-09-08PlantUML官网: https://plantuml.com/zh/PlantUML命令行文档: https://plantuml.com/zh/command-line#6a26f548831e6a8cPlantUML XMI文档: https://plantuml.com/zh/xmi

argodb自定义函数读取hdfs文件的注意点,避免FileSystem已关闭异常

一、问题描述 一位同学反馈,他写的argo存过中调用了一个自定义函数,函数会加载hdfs上的一个文件,但有些节点会报FileSystem closed异常,同时有时任务会成功,有时会失败。 二、问题分析 argodb的计算引擎是基于spark的定制化引擎,对于自定义函数的调用跟hive on spark的是一致的。udf要通过反射生成实例,然后迭代调用evaluate。通过代码分析,udf在

鸿蒙开发中实现自定义弹窗 (CustomDialog)

效果图 #思路 创建带有 @CustomDialog 修饰的组件 ,并且在组件内部定义controller: CustomDialogController 实例化CustomDialogController,加载组件,open()-> 打开对话框 , close() -> 关闭对话框 #定义弹窗 (CustomDialog)是什么? CustomDialog是自定义弹窗,可用于广告、中

mybatis框架基础以及自定义插件开发

文章目录 框架概览框架预览MyBatis框架的核心组件MyBatis框架的工作原理MyBatis框架的配置MyBatis框架的最佳实践 自定义插件开发1. 添加依赖2. 创建插件类3. 配置插件4. 启动类中注册插件5. 测试插件 参考文献 框架概览 MyBatis是一个优秀的持久层框架,它支持自定义SQL、存储过程以及高级映射,为开发者提供了极大的灵活性和便利性。以下是关于M