简单实用的Android UI微博动态点赞效果

  说起空间动态、微博的点赞效果,网上也是很泛滥,各种实现与效果一大堆。而详细实现的部分,讲述的也是参差不齐,另一方面估计也有很多大侠也不屑一顾,觉得完全没必要单独开篇来写和讲解吧。毕竟,也就是两个view和一些简单的动画效果罢了。
  单若是只讲这些,我自然也是不愿花这番功夫的。虽然自己很菜,可也不甘于太菜。所以偶尔看到些好东西,可以延伸学写下,我还是很情愿拿出来用用,顺带秀一秀逼格什么的。
  不扯太多,先说说今天实现点赞效果用到的自以为不错的两个点:

Checkable 用来扩展View实现选中状态切换
AndroidViewAnimations 基于nineoldandroids封装的android动画简易类库。究竟有多简单呢,就像这样

AnimHelper.with(new PulseAnimator()).duration(1000).playOn(imageView);
作用: 在imageView上使用PulseAnimator这个动画效果,播放一秒。

当然是从实现角度来看这个库啦,如果仅仅是使用,google/百度一大堆啦。   

结合前两篇富文本折叠展开,加上我们的点赞view 做出的demo整合效果图:

1.从实现看门道

  其实从效果看无非就是点击切换图片,并添加一些简单动画效果而已,确实没什么难度。这里是因为引入了两个不错的新内容,使用下,权当新手尝鲜。

1.1 Checkable接口实现CheckedImageView

  系统本身提供了android.widget.Checkable这样一个接口,方便我们继承实现View的选中和取消的状态。看下这个类:

public interface Checkable { /** * 设置view的选中状态 */ void setChecked(boolean checked); /** * 当前view是否被选中 */ boolean isChecked(); /** *改变view的选中状态到相反的状态 */ void toggle(); }

  通常这个接口用来帮助我们快速实现view的可选效果,增加了选中和取消两种状态和切换方法。另外为了方便View在状态改变时候快速地变看到效果(更背景或图片),我们可以直接通过selector控制图片,而其本身并不会自动改变drawable状态,因此这里还有必要重写drawableStateChanged
方法。我们先以定义一个通用的CheckedImageView为例:

public class CheckedImageView extends ImageView implements Checkable{ protected boolean isChecked;//选中状态 protected OnCheckedChangeListener onCheckedChangeListener;//状态改变事件监听 public static final int[] CHECKED_STATE_SET = { android.R.attr.state_checked }; public CheckedImageView(Context context) { super(context); initialize(); } public CheckedImageView(Context context, AttributeSet attrs) { super(context, attrs); initialize(); } private void initialize() { isChecked = false; } @Override public boolean isChecked() { return isChecked; } @Override public void setChecked(boolean isChecked) { if (this.isChecked != isChecked) { this.isChecked = isChecked; refreshDrawableState(); if (onCheckedChangeListener != null) { onCheckedChangeListener.onCheckedChanged(this, isChecked); } } } @Override public void toggle() {//改变状态 setChecked(!isChecked); } //初始DrawableState时候为它添加一个CHECKED_STATE,ImageView本身是没有这个状态的 @Override public int[] onCreateDrawableState(int extraSpace) { int[] states = super.onCreateDrawableState(extraSpace + 1); if (isChecked()) { mergeDrawableStates(states, CHECKED_STATE_SET); } return states; } //当view的选中状态被改变的时候通知ImageView改变背景或内容,这个view会自动在drawable状态集中选择与当前状态匹配的图片 @Override protected void drawableStateChanged() { super.drawableStateChanged(); Drawable drawable = getDrawable(); if (drawable != null) { int[] myDrawableState = getDrawableState(); drawable.setState(myDrawableState); invalidate(); } } //设置状态改变监听事件 public void setOnCheckedChangeListener(OnCheckedChangeListener onCheckedChangeListener) { this.onCheckedChangeListener = onCheckedChangeListener; } //当选中状态改变时监听接口触发该事件 public static interface OnCheckedChangeListener { public void onCheckedChanged(CheckedImageView checkedImgeView, boolean isChecked); } }

这是一个通用的可被选中ImageView,当点击之后被选中,再次点击则取消。而其背景/内容也会随之改变。比如下图所示效果:

  

  从代码上看,我们本身并没有直接定义当view点击之后,调用setImage()或者setBackground()来改变内容,而是通过使用View本身的DrawableState来绘制和更改,查找与它对应匹配的图片,而这些状态所对应的图片,都预先在selector中配置好。关于selector这里不做介绍,自行查阅学习。   

  既然提到selector,顺带提下之前遇到的坑,关于他的匹配原则。比如下边这样一个selector:

<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:drawable="@drawable/icon_pressed"></item> <item android:state_checked="true" android:drawable="@drawable/icon_checked"></item> <item android:drawable="@drawable/icon_normal"></item> </selector>

  当view同时有上边两个状态(如state_pressed和state_checked)的时候,view优先显示第一个状态时候的图片(icon_pressed)。这是因为它是由上到下有序查找的,当找到第一个状态与他定义的所相符所在行时,就优先显示这行的图片。所以当我们将最后一行

< item android:drawable=”@drawable/icon_normal”>< /item>

放在第一行时,无论是否选中状态或按下状态,都显示的是icon_normal。初学者一定要注意,我当初就因为这个原因耗费了很多时间查找缘由。

  回到我们的点赞实现。这里实现的点赞View PraiseView 包含了一个 CheckedImageView 和一个 TextView ,点赞之后,ImageView会放大回缩并弹出一个TextView”+1”的动画效果。

public class PraiseView extends FrameLayout implements Checkable{//同样继承Checkable protected OnPraisCheckedListener praiseCheckedListener; protected CheckedImageView imageView; //点赞图片 protected TextView textView; //+1 protected int padding; public PraiseView(Context context) { super(context); initalize(); } public PraiseView(Context context, AttributeSet attrs) { super(context, attrs); initalize(); } protected void initalize() { setClickable(true); imageView = new CheckedImageView(getContext()); imageView.setImageResource(R.drawable.blog_praise_selector); FrameLayout.LayoutParams flp = new LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT,Gravity.CENTER); addView(imageView, flp); textView = new TextView(getContext()); textView.setTextSize(10); textView.setText("+1"); textView.setTextColor(Color.parseColor("#A24040")); textView.setGravity(Gravity.CENTER); addView(textView, flp); textView.setVisibility(View.GONE); } @Override public boolean performClick() { checkChange(); return super.performClick(); } @Override public void toggle() { checkChange(); } public void setChecked(boolean isCheacked) { imageView.setChecked(isCheacked); } public void checkChange() {//点击切换状态 if (imageView.isChecked) { imageView.setChecked(false); } else { imageView.setChecked(true); textView.setVisibility(View.VISIBLE); //放大动画 AnimHelper.with(new PulseAnimator()).duration(1000).playOn(imageView); //飘 “+1”动画 AnimHelper.with(new SlideOutUpAnimator()).duration(1000).playOn(textView); } //调用点赞事件 if (praiseCheckedListener != null) { praiseCheckedListener.onPraisChecked(imageView.isChecked); } } public boolean isChecked() { return imageView.isChecked; } public void setOnPraisCheckedListener(OnPraisCheckedListener praiseCheckedListener) { this.praiseCheckedListener = praiseCheckedListener; } public interface OnPraisCheckedListener{ void onPraisChecked(boolean isChecked); } }

  过于自定的View大概就这么多了,Checkable这个小巧方便的类,不知道你会用了没。至于上边用到的两个动画效果集:

AnimHelper.with(new PulseAnimator()).duration(1000).playOn(imageView);
AnimHelper.with(new SlideOutUpAnimator()).duration(1000).playOn(textView);
感觉封装的挺简洁实用,所以很有必要学习分析一下。

1.2 动画库的封装和快速框架

  提到动画,Android本身自带的动画类Animation已经做到支持3.0及以上了,虽然也做了很好的封装,但是做起复杂动画来还是不够像上边那样简洁。在关于动画兼容方面,github上的大牛Jake Wharton又做了一套动画开源库NineOldAndroids,效果很好而且支持3.0级以前的版本,确实很值得称赞。而在此基础上,有很多高手又做了二次封装,实现了复杂动画,同时保证方便简洁,而且通用性和扩展性更高。我们这里的动画使用的就是这样一个简单的封装。
  比如,要在XXView上时用XXAnimator这样的动画,持续Duration秒。就这么一行代码:

AnimHelper.with(new SlideOutUpAnimator()).duration(1000).playOn(textView);
  来看一下基于NineOldAndroids的ViewAnimations具体实现。

1). 首先定义一个基本动画效果类BaseViewAnimator

  这个BaseViewAnimator动画类使用一个动画集合AnimatorSet,包装成单个动画类似的用法,并定义了一个abstract方法prepare():

public abstract class BaseViewAnimator { public static final long DURATION = 1000; private AnimatorSet mAnimatorSet; private long mDuration = DURATION; { mAnimatorSet = new AnimatorSet(); } protected abstract void prepare(View target); public BaseViewAnimator setTarget(View target) { reset(target); prepare(target); return this; } public void animate() { start(); } /** * reset the view to default status * * @param target */ public void reset(View target) { ViewHelper.setAlpha(target, 1); ViewHelper.setScaleX(target, 1); ViewHelper.setScaleY(target, 1); ViewHelper.setTranslationX(target, 0); ViewHelper.setTranslationY(target, 0); ViewHelper.setRotation(target, 0); ViewHelper.setRotationY(target, 0); ViewHelper.setRotationX(target, 0); ViewHelper.setPivotX(target, target.getMeasuredWidth() / 2.0f); ViewHelper.setPivotY(target, target.getMeasuredHeight() / 2.0f); } /** * start to animate */ public void start() { mAnimatorSet.setDuration(mDuration); mAnimatorSet.start(); } public BaseViewAnimator setDuration(long duration) { mDuration = duration; return this; } public BaseViewAnimator setStartDelay(long delay) { getAnimatorAgent().setStartDelay(delay); return this; } public long getStartDelay() { return mAnimatorSet.getStartDelay(); } public BaseViewAnimator addAnimatorListener(AnimatorListener l) { mAnimatorSet.addListener(l); return this; } public void cancel(){ mAnimatorSet.cancel(); } public boolean isRunning(){ return mAnimatorSet.isRunning(); } public boolean isStarted(){ return mAnimatorSet.isStarted(); } public void removeAnimatorListener(AnimatorListener l) { mAnimatorSet.removeListener(l); } public void removeAllListener() { mAnimatorSet.removeAllListeners(); } public BaseViewAnimator setInterpolator(Interpolator interpolator) { mAnimatorSet.setInterpolator(interpolator); return this; } public long getDuration() { return mDuration; } public AnimatorSet getAnimatorAgent() { return mAnimatorSet; } }

  复杂动画效果基类BaseViewAnimator使用一个AnimatorSet集合来添加各种动画 ,并绑定到目标targetView ,使用 prepare(View target) 的abstract方法供其子类实现具体的动画效果。   

2). 其次基于这个类实现我们的各种动画效果XXAnimator

 当我们要实现具体的动画效果时,可以直接继承这个类并实现prepaer方法。比如这里定义的上划消失SlideOutUpAnimator 和放大回缩动画PulseAnimator

/** *上划消失(飘+1) */ public class SlideOutUpAnimator extends BaseViewAnimator { @Override public void prepare(View target) { ViewGroup parent = (ViewGroup)target.getParent(); getAnimatorAgent().playTogether( ObjectAnimator.ofFloat(target, "alpha", 1, 0), ObjectAnimator.ofFloat(target,"translationY",0,-parent.getHeight()/2) ); } } /** *放大效果 */ public class PulseAnimator extends BaseViewAnimator { @Override public void prepare(View target) { getAnimatorAgent().playTogether( ObjectAnimator.ofFloat(target, "scaleY", 1, 1.2f, 1), ObjectAnimator.ofFloat(target, "scaleX", 1, 1.2f, 1) ); } }

 上边两种动画效果就是对BaseViewAnimator的两种实现,动画本身使用的库是NineOldAndroids。

3). 最后封装一个动画管理工具类AnimHelper供外部使用

 首先定义了一个静态类,使用helper来实例化这个静态类,并设置各个参数选项。

public class AnimHelper { private static final long DURATION = BaseViewAnimator.DURATION; private static final long NO_DELAY = 0; /** *实例化得到AnimationComposer的唯一接口 */ public static AnimationComposer with(BaseViewAnimator animator) { return new AnimationComposer(animator); } /** *定义与动画效果相关联的各种参数, *使用这种方法可以保证对象的构建和他的表示相互隔离开来 */ public static final class AnimationComposer { private List<Animator.AnimatorListener> callbacks = new ArrayList<Animator.AnimatorListener>(); private BaseViewAnimator animator; private long duration = DURATION; private long delay = NO_DELAY; private Interpolator interpolator; private View target; private AnimationComposer(BaseViewAnimator animator) { this.animator = animator; } public AnimationComposer duration(long duration) { this.duration = duration; return this; } public AnimationComposer delay(long delay) { this.delay = delay; return this; } public AnimationComposer interpolate(Interpolator interpolator) { this.interpolator = interpolator; return this; } public AnimationComposer withListener(Animator.AnimatorListener listener) { callbacks.add(listener); return this; } public AnimManager playOn(View target) { this.target = target; return new AnimManager(play(), this.target); } private BaseViewAnimator play() { animator.setTarget(target); animator.setDuration(duration) .setInterpolator(interpolator) .setStartDelay(delay); if (callbacks.size() > 0) { for (Animator.AnimatorListener callback : callbacks) { animator.addAnimatorListener(callback); } } animator.animate(); return animator; } } /** *动画管理类 */ public static final class AnimManager{ private BaseViewAnimator animator; private View target; private AnimManager(BaseViewAnimator animator, View target){ this.target = target; this.animator = animator; } public boolean isStarted(){ return animator.isStarted(); } public boolean isRunning(){ return animator.isRunning(); } public void stop(boolean reset){ animator.cancel(); if(reset) animator.reset(target); } } }

 这段代码使用了类似Dialog的builder模式,感兴趣的可以搜一下 JAVA设计模式-Builder.晚点会另开一篇讲解。
(注: 复杂动画这一部分的内容这里只是拿出来展示和使用,包装和实现是由代码家大大原创,有想了解更多动画及效果的请点其名字链接)

 运行一下,就可以看到前面所演示的效果了。点击第一下,,伴随着图标变大一下并飘出“+1”的效果,图片切换到选中状态;再点则恢复未选中,而且不会触发动画。  

 至此,点赞这块内容和关注点也说完了,希望各位能有点儿收获,另外便于自己也能加深理解。
 最后,附上示例源码地址:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

时间: 2024-09-20 20:28:26

简单实用的Android UI微博动态点赞效果的相关文章

简单实用的Android UI微博动态点赞效果_Android

说起空间动态.微博的点赞效果,网上也是很泛滥,各种实现与效果一大堆.而详细实现的部分,讲述的也是参差不齐,另一方面估计也有很多大侠也不屑一顾,觉得完全没必要单独开篇来写和讲解吧.毕竟,也就是两个view和一些简单的动画效果罢了. 单若是只讲这些,我自然也是不愿花这番功夫的.虽然自己很菜,可也不甘于太菜.所以偶尔看到些好东西,可以延伸学写下,我还是很情愿拿出来用用,顺带秀一秀逼格什么的. 不扯太多,先说说今天实现点赞效果用到的自以为不错的两个点: Checkable 用来扩展View实现选中状态切

简单实用的Android studio 调试技巧

说到android studio的调试,很多人可能会说,这有什么可讲的不就是一个断点调试么,刚开始我也是这么认为的,直到我了解之后,才发现,调试原来可以玩的这么牛.下面我分别一一做介绍. 条件断点(Conditional Breakpoints) 这个调试模式是我最喜欢的,简直不能再方便了,以前遇到在循环里面打断点,需要看某个条件下的值,我只能一遍遍点击,直到满足条件. 那么这个条件断点改怎么用呢,在你的断点上点击右键,就会弹出一个选择对话框,在里面的condition框里面填写上你所需要中断的

一个简单实用的Android调试应用技巧

在应用开发中,我们常常会进行日志打印或者debug调试,以此来分析运行时的一些信息,便于发现bug和问题.Android Studio的Debug功能很好用,但是有时候有些情况下,就显得不是那么快捷和便利. 比如 我们调试的点在应用一打开的时候,很靠前,例如Application的onCreate方法中,以至于我们不能足够快的设置进程为debug模式 虽然上面的情况可以通过Android Studio的debug运行来解决,但是如果项目很大的话,运行起来也会比较耽误时间 那么怎么解决上面的问题呢

PS基础教程:简单几步为圣诞树添加动态闪光效果

原图 效果 [1] [2] [3] [4]  下一页

Photoshop制作动态下雨特效的3个简单实用方法

Photoshop制作动态下雨特效的3个简单实用方法   △ 这是原图 △ 方法一的效果 △ 方法二的效果 △ 方法三的效果 是的,你没看错,第三种方法是真的在"下"雨,是动态的,是动画的,而且悄悄剧透给小伙伴们,它还是最简单的,比第二种一键下雨的动作预设大法还要简单.那我们闲言少叙,马上开始"变身老龙王",让雨下起来喽. 方法一:滤镜大法 第一步,如下图操作所示,在PS中将原图打开,新建一个图层,命名为"雨". △ 新建雨图层 第二步,如下图所

Android UI实现多行文本折叠展开效果_Android

上文介绍了单行文本水平触摸滑动效果,通过EditText实现TextView单行长文本水平滑动效果. 本文继续介绍了多行文本折叠展开,自定义布局View实现多行文本折叠和展开 1.概述 经常在APP中能看到有引用文章或大段博文的内容,他们的展示样式也有点儿意思,默认是折叠的,当你点击文章之后它会自动展开.再次点击他又会缩回去. 网上有找到部分效果,感觉不是很满意.最后自己尝试用 自定义布局layout 写了个demo.比较简陋,不过可以用了.有这方面需求的朋友可以稍加改造下.如有更好的创意,也不

Android UI实现多行文本折叠展开效果

上文介绍了单行文本水平触摸滑动效果,通过EditText实现TextView单行长文本水平滑动效果. 本文继续介绍了多行文本折叠展开,自定义布局View实现多行文本折叠和展开 1.概述 经常在APP中能看到有引用文章或大段博文的内容,他们的展示样式也有点儿意思,默认是折叠的,当你点击文章之后它会自动展开.再次点击他又会缩回去. 网上有找到部分效果,感觉不是很满意.最后自己尝试用 自定义布局layout 写了个demo.比较简陋,不过可以用了.有这方面需求的朋友可以稍加改造下.如有更好的创意,也不

《Android UI基础教程》——2.4节显示列表

2.4 显示列表 Android UI基础教程 用来开发应用最常见的视图类型之一就是ListView.这个视图呈现了一个垂直滚动的项目列表.每一行都会有一些文本但是通常也会包含其他视图,例如ImageView和按钮等(联系人应用就是很好的例子).当你需要把数据列表呈现给用户的时候,使用ListView最合适.它是如此常见,以至于Android实际上提供了展示一个列表的内置活动. 2.4.1 ListActivity 一个ListActivity将绑定到一个包含有ListView的默认视图.不必要

《Android UI基础教程》——1.3节 Android UI基础

1.3 Android UI基础 Android UI基础教程 用户界面(UI)是应用程序和用户之间联系的桥梁.事实上,对于用户来说,UI就是应用程序.Android 的UI框架足够强大,能够创建复杂的带有图形和动画的 UI,同时它也足够灵活,能够从小屏幕的手持设备扩展到平板电脑以及电视.本节介绍Android UI开发的基础知识,通过本节的学习,你就可以开始为自己的应用程序创建出色的UI了. 1.3.1 主屏幕和通知栏 要创建Android应用程序,首先你应该了解基本的Android OS 本