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

相关文章

Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)

《Vue项目的甘特图组件之dhtmlx-gantt使用教程和实现效果展示(推荐)》文章介绍了如何使用dhtmlx-gantt组件来实现公司的甘特图需求,并提供了一个简单的Vue组件示例,文章还分享了一... 目录一、首先 npm 安装插件二、创建一个vue组件三、业务页面内 引用自定义组件:四、dhtmlx

前端原生js实现拖拽排课效果实例

《前端原生js实现拖拽排课效果实例》:本文主要介绍如何实现一个简单的课程表拖拽功能,通过HTML、CSS和JavaScript的配合,我们实现了课程项的拖拽、放置和显示功能,文中通过实例代码介绍的... 目录1. 效果展示2. 效果分析2.1 关键点2.2 实现方法3. 代码实现3.1 html部分3.2

CSS自定义浏览器滚动条样式完整代码

《CSS自定义浏览器滚动条样式完整代码》:本文主要介绍了如何使用CSS自定义浏览器滚动条的样式,包括隐藏滚动条的角落、设置滚动条的基本样式、轨道样式和滑块样式,并提供了完整的CSS代码示例,通过这些技巧,你可以为你的网站添加个性化的滚动条样式,从而提升用户体验,详细内容请阅读本文,希望能对你有所帮助...

基于Python实现PDF动画翻页效果的阅读器

《基于Python实现PDF动画翻页效果的阅读器》在这篇博客中,我们将深入分析一个基于wxPython实现的PDF阅读器程序,该程序支持加载PDF文件并显示页面内容,同时支持页面切换动画效果,文中有详... 目录全部代码代码结构初始化 UI 界面加载 PDF 文件显示 PDF 页面页面切换动画运行效果总结主

React实现原生APP切换效果

《React实现原生APP切换效果》最近需要使用Hybrid的方式开发一个APP,交互和原生APP相似并且需要IM通信,本文给大家介绍了使用React实现原生APP切换效果,文中通过代码示例讲解的非常... 目录背景需求概览技术栈实现步骤根据 react-router-dom 文档配置好路由添加过渡动画使用

SpringBoot 自定义消息转换器使用详解

《SpringBoot自定义消息转换器使用详解》本文详细介绍了SpringBoot消息转换器的知识,并通过案例操作演示了如何进行自定义消息转换器的定制开发和使用,感兴趣的朋友一起看看吧... 目录一、前言二、SpringBoot 内容协商介绍2.1 什么是内容协商2.2 内容协商机制深入理解2.2.1 内容

使用Python实现生命之轮Wheel of life效果

《使用Python实现生命之轮Wheeloflife效果》生命之轮Wheeloflife这一概念最初由SuccessMotivation®Institute,Inc.的创始人PaulJ.Meyer... 最近看一个生命之轮的视频,让我们珍惜时间,因为一生是有限的。使用python创建生命倒计时图表,珍惜时间

【前端学习】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