源码网商城,靠谱的源码在线交易网站 我的订单 购物车 帮助

源码网商城

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

  • 时间:2020-08-04 17:18 编辑: 来源: 阅读:
  • 扫一扫,手机访问
摘要:简单实用的Android UI微博动态点赞效果
  说起空间动态、微博的点赞效果,网上也是很泛滥,各种实现与效果一大堆。而详细实现的部分,讲述的也是参差不齐,另一方面估计也有很多大侠也不屑一顾,觉得完全没必要单独开篇来写和讲解吧。毕竟,也就是两个view和一些简单的动画效果罢了。   单若是只讲这些,我自然也是不愿花这番功夫的。虽然自己很菜,可也不甘于太菜。所以偶尔看到些好东西,可以延伸学写下,我还是很情愿拿出来用用,顺带秀一秀逼格什么的。   不扯太多,先说说今天实现点赞效果用到的自以为不错的两个点: [b]Checkable [/b]用来扩展View实现选中状态切换 [b]AndroidViewAnimations [/b]基于nineoldandroids封装的android动画简易类库。究竟有多简单呢,就像这样 [i]AnimHelper.with(new PulseAnimator()).duration(1000).playOn(imageView); 作用: 在imageView上使用PulseAnimator这个动画效果,播放一秒。 [/i] 当然是从实现角度来看这个库啦,如果仅仅是使用,google/百度一大堆啦。    结合前两篇富[url=http://www.1sucai.cn/article/95544.htm]文本折叠展开[/url],加上我们的点赞view 做出的demo整合效果图: [img]http://files.jb51.net/file_images/article/201610/20161024141336293.gif?2016924141349[/img] [b]1.从实现看门道[/b]   其实从效果看无非就是点击切换图片,并添加一些简单动画效果而已,确实没什么难度。这里是因为引入了两个不错的新内容,使用下,权当新手尝鲜。 [b]1.1 Checkable接口实现CheckedImageView[/b]   系统本身提供了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,当点击之后被选中,再次点击则取消。而其背景/内容也会随之改变。比如下图所示效果: [img]http://files.jb51.net/file_images/article/201610/20161024141408852.gif?2016924141416[/img]      从代码上看,我们本身并没有直接定义当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); 感觉封装的挺简洁实用,所以很有必要学习分析一下。 [b]1.2 动画库的封装和快速框架[/b]   提到动画,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”的效果,图片切换到选中状态;再点则恢复未选中,而且不会触发动画。    至此,点赞这块内容和关注点也说完了,希望各位能有点儿收获,另外便于自己也能加深理解。  最后,附上示例源码地址: 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程素材网。
  • 全部评论(0)
联系客服
客服电话:
400-000-3129
微信版

扫一扫进微信版
返回顶部