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

当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 callbacks = new ArrayList();

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微博动态点赞效果相关推荐

  1. php 开发ui框架,5 个优秀前端 UI 框架

    随着 Web 技术的不断发展,前端开发框架层出不穷,各有千秋,今天小编为大家奉上前端 UI 框架的开源项目,希望大家能够喜欢!如果大家有 UI 框架相关的开源项目,也可以托管到码云上,我们会及时给予推 ...

  2. Android自定义View教你一步一步实现即刻点赞效果

    前言 今天朋友看了HenCoder的自定义View后说,HenCoder对自定义View讲的不错.实践中仿写即刻的点赞你有思路吗,你不实现一下?二话不说,看了朋友手机效果,对他说:实现不难,用到了位移 ...

  3. vue3使用的移动端UI框架,vue一般用什么ui框架

    用vue 一般都配合什么 UI 框架 vue只是一个js库,不算框架,他不限制你使用使用UI框架,理论上你可以使用任意UI框架. 常见的vue的UI框架有:elementUI(饿了么的UI框架),Vu ...

  4. android静态动画,LayoutAnimation给ListView中的item设置动态出场效果(实例)

    LayoutAnimation作用于ViewGroup,为ViewGroup指定一个动画,当它的子元素出场时都按照这个动画出场. LayoutAnimation作用于viewgroup有两种方式: 1 ...

  5. android日历提醒之简单实用

    前言:我们在自己的项目开发中,经常会有预约提醒.定时提醒等方面的需求,这时我们可以使用安卓自己的系统日历来实现. 通过代码向系统日历中写入日历事件.设置提醒,就可以实现到特定时间时提醒用户的功能. 当 ...

  6. Android之RootTools框架简单使用

    Android目录结构 data app:用户安装的应用 data:应用的专属文件夹 system:系统的配置信息,注册表文件 anr:anr异常的记录信息 dev:devices的缩写 存放设备所对 ...

  7. android剪切板权限,android剪切板一些简单实用方法

    1.ClipboardManager clipboardManager = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE) ...

  8. android热更新框架选型,Android热更新框架简单比较

    1.Tinker Tinker 的方案,都是让 Classloader 去加载新的类.如果不重启,原来的类还在虚拟机中,就无法加载新类.因此,只有在下次重启的时候,在还没走到业务逻辑之前抢先加载补丁中 ...

  9. android在搭建框架时要注意,Android开发搭建应用框架步骤和注意的问题

    每个人对应用框架的理解不相同,但是最终达到的效果应该是一样: 降低项目的复杂性 易扩展.易修改.可重用性强.可维护性强 职责单一,功能清晰 在android开发项目中,我们首先要考虑的是这个项目或者说 ...

最新文章

  1. CDH大数据集群安全风险汇总
  2. My Site Cleanup Job
  3. 关于求职简历的碎碎念。
  4. [转].NET 开源项目 Polly 介绍
  5. 【DP】【递归】分离与合体
  6. 抱歉(HDU-1418)
  7. mysql数据库主从出现1236错误
  8. Windows Mobile 5.0新增API介绍(转自MSDN)
  9. iOS开发之alloc底层探索之旅
  10. webSSH如何安装?如何使用?解决Web端远程连接终端~~运维篇
  11. quartz 整合 postgresql 附带例子
  12. @scheduled 定时任务执行一段时间后莫名其妙停止 Ftp假死
  13. 微信安卓6.5.3以上版本网页上传不了图片的解决方案
  14. 清理win7系统盘空间
  15. Spring Boot入门(24):Spring Boot事务 | 超级详细,建议收藏
  16. 正则化,岭回归Shrinkage,lasso稀疏性推导和论文总结
  17. mysql hint是什么_mysql hint是什么意思
  18. 诸葛亮,我去你的“ 天命有归,不可以智力争也”
  19. TCP协议之RST与FIN
  20. JSP~~JSTL~~XML标签库

热门文章

  1. 传世单机版怎么建立服务端?
  2. Android关闭输入法
  3. 周志华教授:如何做研究与写论文?
  4. 网络空间安全省赛A解析
  5. 获取crumbIssuer
  6. BackupPC - 恢复选项Restore options
  7. 论文阅读:A Neural Probabilistic Language Model 一种神经概率语言模型
  8. fpga 级联fifo(VHDL)
  9. 安卓视频播放器(TV)
  10. chrome上字体模糊问题处理