Android——仿QQ聊天撒花特效

2024-03-06 16:30
文章标签 android 特效 qq 聊天 撒花

本文主要是介绍Android——仿QQ聊天撒花特效,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这里写图片描述

实现这样的效果,你要知道贝塞尔曲线,何谓贝塞尔曲线?其实就是曲线,嘿嘿,关于曲线的概念大家可以去

Android绘图机制(二)——自定义View绘制形, 圆形, 三角形, 扇形, 椭圆, 曲线,文字和图片的坐标讲解
中看下,我们这里就直接写了

1.activity_main.xml

<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent">//撒花的区域<relativelayoutandroid:id="@+id/rlt_animation_layout"android:layout_width="match_parent"android:layout_height="match_parent"></relativelayout><buttonandroid:id="@+id/btn_start"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignparentbottom="true"android:layout_centerhorizontal="true"android:layout_marginbottom="23dp"android:text="开始撒花"></button>
</relativelayout>

2.Fllower

package com.lgl.test;import android.graphics.Bitmap;
import android.graphics.Path;import java.io.Serializable;public class Fllower implements Serializable {private static final long serialVersionUID = 1L;private Bitmap image;private float x;private float y;private Path path;private float value;public Bitmap getResId() {return image;}public void setResId(Bitmap img) {this.image = img;}public float getX() {return x;}public void setX(float x) {this.x = x;}public float getY() {return y;}public void setY(float y) {this.y = y;}public Path getPath() {return path;}public void setPath(Path path) {this.path = path;}public float getValue() {return value;}public void setValue(float value) {this.value = value;}@Overridepublic String toString() {return "Fllower [ x=" + x + ", y=" + y + ", path=" + path + ", value="+ value + "]";}}
3.FllowerAnimation
动画类
package com.lgl.test;import java.util.ArrayList;
import java.util.List;
import java.util.Random;import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.AccelerateInterpolator;/*** 撒花 用到的知识点: 1、android属性动画 2、Path路径绘制 3、贝塞尔曲线*/
public class FllowerAnimation extends View implements AnimatorUpdateListener {/*** 动画改变的属性值*/private float phase1 = 0f;private float phase2 = 0f;private float phase3 = 0f;/*** 小球集合*/private List<fllower> fllowers1 = new ArrayList<fllower>();private List<fllower> fllowers2 = new ArrayList<fllower>();private List<fllower> fllowers3 = new ArrayList<fllower>();/*** 动画播放的时间*/private int time = 4000;/*** 动画间隔*/private int delay = 400;int[] ylocations = { -100, -50, -25, 0 };/*** 资源ID*/// private int resId = R.drawable.fllower_love;public FllowerAnimation(Context context) {super(context);init(context);// this.resId = resId;
    }@SuppressWarnings("deprecation")private void init(Context context) {WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);width = wm.getDefaultDisplay().getWidth();height = (int) (wm.getDefaultDisplay().getHeight() * 3 / 2f);mPaint = new Paint();mPaint.setAntiAlias(true);// mPaint.setStrokeWidth(2);// mPaint.setColor(Color.BLUE);// mPaint.setStyle(Style.STROKE);
 pathMeasure = new PathMeasure();builderFollower(fllowerCount, fllowers1);builderFollower(fllowerCount, fllowers2);builderFollower(fllowerCount, fllowers3);}/*** 宽度*/private int width = 0;/*** 高度*/private int height = 0;/*** 曲线高度个数分割*/private int quadCount = 10;/*** 曲度*/private float intensity = 0.2f;/*** 第一批个数*/private int fllowerCount = 4;/*** 创建花*/private void builderFollower(int count, List<fllower> fllowers) {int max = (int) (width * 3 / 4f);int min = (int) (width / 4f);Random random = new Random();for (int i = 0; i < count; i++) {int s = random.nextInt(max) % (max - min + 1) + min;Path path = new Path();CPoint CPoint = new CPoint(s, ylocations[random.nextInt(3)]);List<cpoint> points = builderPath(CPoint);drawFllowerPath(path, points);Fllower fllower = new Fllower();fllower.setPath(path);Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.drawable.lift_flower);fllower.setResId(bitmap);fllowers.add(fllower);}}/*** 画曲线** @param path* @param points*/private void drawFllowerPath(Path path, List<cpoint> points) {if (points.size() > 1) {for (int j = 0; j < points.size(); j++) {CPoint point = points.get(j);if (j == 0) {CPoint next = points.get(j + 1);point.dx = ((next.x - point.x) * intensity);point.dy = ((next.y - point.y) * intensity);} else if (j == points.size() - 1) {CPoint prev = points.get(j - 1);point.dx = ((point.x - prev.x) * intensity);point.dy = ((point.y - prev.y) * intensity);} else {CPoint next = points.get(j + 1);CPoint prev = points.get(j - 1);point.dx = ((next.x - prev.x) * intensity);point.dy = ((next.y - prev.y) * intensity);}// create the cubic-spline pathif (j == 0) {path.moveTo(point.x, point.y);} else {CPoint prev = points.get(j - 1);path.cubicTo(prev.x + prev.dx, (prev.y + prev.dy), point.x- point.dx, (point.y - point.dy), point.x, point.y);}}}}/*** 曲线摇摆的幅度*/private int range = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 70, getResources().getDisplayMetrics());/*** 画路径** @param point* @return*/private List<cpoint> builderPath(CPoint point) {List<cpoint> points = new ArrayList<cpoint>();Random random = new Random();for (int i = 0; i < quadCount; i++) {if (i == 0) {points.add(point);} else {CPoint tmp = new CPoint(0, 0);if (random.nextInt(100) % 2 == 0) {tmp.x = point.x + random.nextInt(range);} else {tmp.x = point.x - random.nextInt(range);}tmp.y = (int) (height / (float) quadCount * i);points.add(tmp);}}return points;}/*** 画笔*/private Paint mPaint;/*** 测量路径的坐标位置*/private PathMeasure pathMeasure = null;@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);drawFllower(canvas, fllowers1);drawFllower(canvas, fllowers2);drawFllower(canvas, fllowers3);}/*** 高度往上偏移量,把开始点移出屏幕顶部*/private float dy = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,40, getResources().getDisplayMetrics());/*** @param canvas* @param fllowers*/private void drawFllower(Canvas canvas, List<fllower> fllowers) {for (Fllower fllower : fllowers) {float[] pos = new float[2];// canvas.drawPath(fllower.getPath(),mPaint);pathMeasure.setPath(fllower.getPath(), false);pathMeasure.getPosTan(height * fllower.getValue(), pos, null);// canvas.drawCircle(pos[0], pos[1], 10, mPaint);canvas.drawBitmap(fllower.getResId(), pos[0], pos[1] - dy, null);}}ObjectAnimator mAnimator1;ObjectAnimator mAnimator2;ObjectAnimator mAnimator3;public void startAnimation() {if (mAnimator1 != null && mAnimator1.isRunning()) {mAnimator1.cancel();}mAnimator1 = ObjectAnimator.ofFloat(this, "phase1", 0f, 1f);mAnimator1.setDuration(time);mAnimator1.addUpdateListener(this);mAnimator1.start();mAnimator1.setInterpolator(new AccelerateInterpolator(1f));if (mAnimator2 != null && mAnimator2.isRunning()) {mAnimator2.cancel();}mAnimator2 = ObjectAnimator.ofFloat(this, "phase2", 0f, 1f);mAnimator2.setDuration(time);mAnimator2.addUpdateListener(this);mAnimator2.start();mAnimator2.setInterpolator(new AccelerateInterpolator(1f));mAnimator2.setStartDelay(delay);if (mAnimator3 != null && mAnimator3.isRunning()) {mAnimator3.cancel();}mAnimator3 = ObjectAnimator.ofFloat(this, "phase3", 0f, 1f);mAnimator3.setDuration(time);mAnimator3.addUpdateListener(this);mAnimator3.start();mAnimator3.setInterpolator(new AccelerateInterpolator(1f));mAnimator3.setStartDelay(delay * 2);}/*** 跟新小球的位置** @param value* @param fllowers*/private void updateValue(float value, List<fllower> fllowers) {for (Fllower fllower : fllowers) {fllower.setValue(value);}}/*** 动画改变回调*/@Overridepublic void onAnimationUpdate(ValueAnimator arg0) {updateValue(getPhase1(), fllowers1);updateValue(getPhase2(), fllowers2);updateValue(getPhase3(), fllowers3);Log.i(tag, getPhase1() + "");invalidate();}public float getPhase1() {return phase1;}public void setPhase1(float phase1) {this.phase1 = phase1;}public float getPhase2() {return phase2;}public void setPhase2(float phase2) {this.phase2 = phase2;}public float getPhase3() {return phase3;}public void setPhase3(float phase3) {this.phase3 = phase3;}private String tag = this.getClass().getSimpleName();private class CPoint {public float x = 0f;public float y = 0f;/*** x-axis distance*/public float dx = 0f;/*** y-axis distance*/public float dy = 0f;public CPoint(float x, float y) {this.x = x;this.y = y;}}}
4.MainActivity
package com.lgl.test;import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.RelativeLayout;public class MainActivity extends Activity {private Button btn_start;// 撒花特效private RelativeLayout rlt_animation_layout;private FllowerAnimation fllowerAnimation;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 撒花初始化rlt_animation_layout = (RelativeLayout) findViewById(R.id.rlt_animation_layout);rlt_animation_layout.setVisibility(View.VISIBLE);fllowerAnimation = new FllowerAnimation(this);RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,RelativeLayout.LayoutParams.MATCH_PARENT);fllowerAnimation.setLayoutParams(params);rlt_animation_layout.addView(fllowerAnimation);btn_start = (Button) findViewById(R.id.btn_start);btn_start.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// 开始撒花
                fllowerAnimation.startAnimation();}});}
}

 这里写图片描述

 

转载于:https://www.cnblogs.com/zhujiabin/p/5353892.html

这篇关于Android——仿QQ聊天撒花特效的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/780648

相关文章

Android 12解决push framework.jar无法开机的方法小结

《Android12解决pushframework.jar无法开机的方法小结》:本文主要介绍在Android12中解决pushframework.jar无法开机的方法,包括编译指令、框架层和s... 目录1. android 编译指令1.1 framework层的编译指令1.2 替换framework.ja

Android开发环境配置避坑指南

《Android开发环境配置避坑指南》本文主要介绍了Android开发环境配置过程中遇到的问题及解决方案,包括VPN注意事项、工具版本统一、Gerrit邮箱配置、Git拉取和提交代码、MergevsR... 目录网络环境:VPN 注意事项工具版本统一:android Studio & JDKGerrit的邮

Android实现定时任务的几种方式汇总(附源码)

《Android实现定时任务的几种方式汇总(附源码)》在Android应用中,定时任务(ScheduledTask)的需求几乎无处不在:从定时刷新数据、定时备份、定时推送通知,到夜间静默下载、循环执行... 目录一、项目介绍1. 背景与意义二、相关基础知识与系统约束三、方案一:Handler.postDel

Android使用ImageView.ScaleType实现图片的缩放与裁剪功能

《Android使用ImageView.ScaleType实现图片的缩放与裁剪功能》ImageView是最常用的控件之一,它用于展示各种类型的图片,为了能够根据需求调整图片的显示效果,Android提... 目录什么是 ImageView.ScaleType?FIT_XYFIT_STARTFIT_CENTE

Android实现在线预览office文档的示例详解

《Android实现在线预览office文档的示例详解》在移动端展示在线Office文档(如Word、Excel、PPT)是一项常见需求,这篇文章为大家重点介绍了两种方案的实现方法,希望对大家有一定的... 目录一、项目概述二、相关技术知识三、实现思路3.1 方案一:WebView + Office Onl

Android实现两台手机屏幕共享和远程控制功能

《Android实现两台手机屏幕共享和远程控制功能》在远程协助、在线教学、技术支持等多种场景下,实时获得另一部移动设备的屏幕画面,并对其进行操作,具有极高的应用价值,本项目旨在实现两台Android手... 目录一、项目概述二、相关知识2.1 MediaProjection API2.2 Socket 网络

Android实现悬浮按钮功能

《Android实现悬浮按钮功能》在很多场景中,我们希望在应用或系统任意界面上都能看到一个小的“悬浮按钮”(FloatingButton),用来快速启动工具、展示未读信息或快捷操作,所以本文给大家介绍... 目录一、项目概述二、相关技术知识三、实现思路四、整合代码4.1 Java 代码(MainActivi

Android Mainline基础简介

《AndroidMainline基础简介》AndroidMainline是通过模块化更新Android核心组件的框架,可能提高安全性,本文给大家介绍AndroidMainline基础简介,感兴趣的朋... 目录关键要点什么是 android Mainline?Android Mainline 的工作原理关键

如何解决idea的Module:‘:app‘platform‘android-32‘not found.问题

《如何解决idea的Module:‘:app‘platform‘android-32‘notfound.问题》:本文主要介绍如何解决idea的Module:‘:app‘platform‘andr... 目录idea的Module:‘:app‘pwww.chinasem.cnlatform‘android-32

Android实现打开本地pdf文件的两种方式

《Android实现打开本地pdf文件的两种方式》在现代应用中,PDF格式因其跨平台、稳定性好、展示内容一致等特点,在Android平台上,如何高效地打开本地PDF文件,不仅关系到用户体验,也直接影响... 目录一、项目概述二、相关知识2.1 PDF文件基本概述2.2 android 文件访问与存储权限2.