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 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

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

Android里面的Service种类以及启动方式

《Android里面的Service种类以及启动方式》Android中的Service分为前台服务和后台服务,前台服务需要亮身份牌并显示通知,后台服务则有启动方式选择,包括startService和b... 目录一句话总结:一、Service 的两种类型:1. 前台服务(必须亮身份牌)2. 后台服务(偷偷干

Android kotlin语言实现删除文件的解决方案

《Androidkotlin语言实现删除文件的解决方案》:本文主要介绍Androidkotlin语言实现删除文件的解决方案,在项目开发过程中,尤其是需要跨平台协作的项目,那么删除用户指定的文件的... 目录一、前言二、适用环境三、模板内容1.权限申请2.Activity中的模板一、前言在项目开发过程中,尤

利用Python编写一个简单的聊天机器人

《利用Python编写一个简单的聊天机器人》这篇文章主要为大家详细介绍了如何利用Python编写一个简单的聊天机器人,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 使用 python 编写一个简单的聊天机器人可以从最基础的逻辑开始,然后逐步加入更复杂的功能。这里我们将先实现一个简单的

Android数据库Room的实际使用过程总结

《Android数据库Room的实际使用过程总结》这篇文章主要给大家介绍了关于Android数据库Room的实际使用过程,详细介绍了如何创建实体类、数据访问对象(DAO)和数据库抽象类,需要的朋友可以... 目录前言一、Room的基本使用1.项目配置2.创建实体类(Entity)3.创建数据访问对象(DAO

Python基于火山引擎豆包大模型搭建QQ机器人详细教程(2024年最新)

《Python基于火山引擎豆包大模型搭建QQ机器人详细教程(2024年最新)》:本文主要介绍Python基于火山引擎豆包大模型搭建QQ机器人详细的相关资料,包括开通模型、配置APIKEY鉴权和SD... 目录豆包大模型概述开通模型付费安装 SDK 环境配置 API KEY 鉴权Ark 模型接口Prompt

Android WebView的加载超时处理方案

《AndroidWebView的加载超时处理方案》在Android开发中,WebView是一个常用的组件,用于在应用中嵌入网页,然而,当网络状况不佳或页面加载过慢时,用户可能会遇到加载超时的问题,本... 目录引言一、WebView加载超时的原因二、加载超时处理方案1. 使用Handler和Timer进行超

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存