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聊天撒花特效的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:https://blog.csdn.net/weixin_30439131/article/details/95133422
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/780648

相关文章

在Android平台上实现消息推送功能

《在Android平台上实现消息推送功能》随着移动互联网应用的飞速发展,消息推送已成为移动应用中不可或缺的功能,在Android平台上,实现消息推送涉及到服务端的消息发送、客户端的消息接收、通知渠道(... 目录一、项目概述二、相关知识介绍2.1 消息推送的基本原理2.2 Firebase Cloud Me

Android中Dialog的使用详解

《Android中Dialog的使用详解》Dialog(对话框)是Android中常用的UI组件,用于临时显示重要信息或获取用户输入,本文给大家介绍Android中Dialog的使用,感兴趣的朋友一起... 目录android中Dialog的使用详解1. 基本Dialog类型1.1 AlertDialog(

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda

Android自定义Scrollbar的两种实现方式

《Android自定义Scrollbar的两种实现方式》本文介绍两种实现自定义滚动条的方法,分别通过ItemDecoration方案和独立View方案实现滚动条定制化,文章通过代码示例讲解的非常详细,... 目录方案一:ItemDecoration实现(推荐用于RecyclerView)实现原理完整代码实现

Android App安装列表获取方法(实践方案)

《AndroidApp安装列表获取方法(实践方案)》文章介绍了Android11及以上版本获取应用列表的方案调整,包括权限配置、白名单配置和action配置三种方式,并提供了相应的Java和Kotl... 目录前言实现方案         方案概述一、 androidManifest 三种配置方式

Android WebView无法加载H5页面的常见问题和解决方法

《AndroidWebView无法加载H5页面的常见问题和解决方法》AndroidWebView是一种视图组件,使得Android应用能够显示网页内容,它基于Chromium,具备现代浏览器的许多功... 目录1. WebView 简介2. 常见问题3. 网络权限设置4. 启用 JavaScript5. D

Android如何获取当前CPU频率和占用率

《Android如何获取当前CPU频率和占用率》最近在优化App的性能,需要获取当前CPU视频频率和占用率,所以本文小编就来和大家总结一下如何在Android中获取当前CPU频率和占用率吧... 最近在优化 App 的性能,需要获取当前 CPU视频频率和占用率,通过查询资料,大致思路如下:目前没有标准的

使用Java发送邮件到QQ邮箱的完整指南

《使用Java发送邮件到QQ邮箱的完整指南》在现代软件开发中,邮件发送功能是一个常见的需求,无论是用户注册验证、密码重置,还是系统通知,邮件都是一种重要的通信方式,本文将详细介绍如何使用Java编写程... 目录引言1. 准备工作1.1 获取QQ邮箱的SMTP授权码1.2 添加JavaMail依赖2. 实现

Android开发中gradle下载缓慢的问题级解决方法

《Android开发中gradle下载缓慢的问题级解决方法》本文介绍了解决Android开发中Gradle下载缓慢问题的几种方法,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、网络环境优化二、Gradle版本与配置优化三、其他优化措施针对android开发中Gradle下载缓慢的问

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后