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

简单实用的Android UI微博动态点赞效果,第1张

概述  说起空间动态、微博的点赞效果,网上也是很泛滥,各种实现与效果一大堆。而详细实现的部分,讲述的也是参差不齐,另一方面估计也有很多大侠也不屑一顾,觉得完全没必要单独开篇来写和讲解吧。毕竟,也就是两个vi

  说起空间动态、微博的点赞效果,网上也是很泛滥,各种实现与效果一大堆。而详细实现的部分,讲述的也是参差不齐,另一方面估计也有很多大侠也不屑一顾,觉得完全没必要单独开篇来写和讲解吧。毕竟,也就是两个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会放大回缩并d出一个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,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",-parent.getHeight()/2) ); }}/***放大效果*/public class pulseAnimator extends BaseVIEwAnimator { @OverrIDe public voID prepare(VIEw target) { getAnimatorAgent().playTogether(  ObjectAnimator.offloat(target,"scaleY",1.2f,1),"scaleX",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”的效果,图片切换到选中状态;再点则恢复未选中,而且不会触发动画。  

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

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

总结

以上是内存溢出为你收集整理的简单实用的Android UI微博动态点赞效果全部内容,希望文章能够帮你解决简单实用的Android UI微博动态点赞效果所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/web/1147963.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-31
下一篇2022-05-31

发表评论

登录后才能评论

评论列表(0条)

    保存