zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

Android播放gif动画,增加屏幕掉金币效果

Android动画 效果 增加 屏幕 播放 gif
2023-09-14 08:57:56 时间

前言:播放gif的版本有很多,我这边使用Android自带的Movie类播放gif动画,也是在别人的基础上进行修改.有同样需求的朋友可以参考我的demo.


1.效果图如下:

  



2.部分主要代码

MainActivity.java  给封装的GifView设置背景gif图片资源, 绘制金币,同时开启金币屏幕掉下来的效果,监听gif播放完毕动画,结束掉金币的动画

 

public class MainActivity extends Activity implements OnClickListener {

 private FlakeView flakeView;//金币掉落动画的主体动画

 private GifView imageView;

 private Button button_stop1;

 private PopupWindow pop;

 //---------------------------------------------------------------------------------------------

 @Override

 protected void onCreate(Bundle savedInstanceState){

 super.onCreate(savedInstanceState);

 setContentView(R.layout.activity_main);

 button_stop1=(Button) findViewById(R.id.button_stop1);

 imageView=(GifView) findViewById(R.id.imageView);

 imageView.setMovieResource(R.raw.icon_thank_lord_longen); // 设置背景gif图片资源 

 flakeView = new FlakeView(this);

 final LinearLayout container = (LinearLayout) findViewById(R.id.container);

 container.addView(flakeView); //将flakeView 添加到布局中

 flakeView.addFlakes(50);//设置同时出现在屏幕上的金币数量 建议64以内 过多会引起卡顿

 * 绘制的类型

 * @see View.LAYER_TYPE_HARDWARE

 * @see View.LAYER_TYPE_SOFTWARE

 * @see View.LAYER_TYPE_NONE

 flakeView.setLayerType(View.LAYER_TYPE_NONE, null);

 pop = new PopupWindow(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);

 pop.setOutsideTouchable(true);

 pop.setFocusable(true);

 pop.showAtLocation(container,Gravity.CENTER,0,0);

 MediaPlayer player = MediaPlayer.create(this, R.raw.shake);

 player.start();

 imageView.setOnGifPlayListener(new GifPlayListener(){

 @Override

 public void playFinish(){

 new Handler().postDelayed(new Runnable() {

 @Override

 public void run() {

 pop.dismiss();

 container.removeAllViews();

 Toast.makeText(MainActivity.this,"播放完成", 0).show();

 }, 500);

 button_stop1.setOnClickListener(this);

 @Override

 public void onClick(View v) {

 switch (v.getId()) {

 case R.id.button_stop1:

 if (imageView.isPaused()) {

 imageView.setPaused(false);

 }else {

 imageView.setPaused(true);

 break;

}

 

 


GifView.java 播放gif动画的自定义View

public class GifView extends View {

 private static final int DEFAULT_MOVIE_DURATION = 1000;//默认为1秒

 private GifPlayListener gifPlayListener;

 private int mMovieResourceId;

 private Movie mMovie;

 private long mMovieStart;

 private int mCurrentAnimationTime = 0;

 private float mLeft;

 private float mTop;

 private float mScale;

 private int mMeasuredMovieWidth;

 private int mMeasuredMovieHeight;

 private boolean mVisible = true;

 private volatile boolean mPaused = false;

 private int duration;

 public GifView(Context context) {

 this(context, null);

 public GifView(Context context, AttributeSet attrs) {

 this(context, attrs, R.styleable.CustomTheme_gifViewStyle);

 public GifView(Context context, AttributeSet attrs, int defStyle) {

 super(context, attrs, defStyle);

 setViewAttributes(context, attrs, defStyle);

 @SuppressLint("NewApi")

 private void setViewAttributes(Context context, AttributeSet attrs,int defStyle) {

 if (Build.VERSION.SDK_INT = Build.VERSION_CODES.HONEYCOMB) {

 setLayerType(View.LAYER_TYPE_SOFTWARE, null);

 // 从描述文件中读出gif的值,创建出Movie实例

 final TypedArray array = context.obtainStyledAttributes(attrs,R.styleable.GifView, defStyle, /*R.style.Widget_GifView*/0);

 mMovieResourceId = array.getResourceId(R.styleable.GifView_gif, -1);

 mPaused = array.getBoolean(R.styleable.GifView_paused, false);

 array.recycle();

 if (mMovieResourceId != -1) {

 mMovie = Movie.decodeStream(getResources().openRawResource(mMovieResourceId));

 * 设置gif图资源

 * @param movieResId

 public void setMovieResource(int movieResId) {

 this.mMovieResourceId = movieResId;

 mMovie = Movie.decodeStream(getResources().openRawResource(mMovieResourceId));

 duration = mMovie.duration();//取出动画的时长

 if (duration == 0) {

 duration = DEFAULT_MOVIE_DURATION;

 requestLayout();

 public void setMovie(Movie movie) {

 this.mMovie = movie;

 requestLayout();

 public Movie getMovie() {

 return mMovie;

 public void setMovieTime(int time) {

 mCurrentAnimationTime = time;

 invalidate();

 * 设置暂停

 * @param paused

 public void setPaused(boolean paused) {

 this.mPaused = paused;

 if (!paused) {

 mMovieStart = android.os.SystemClock.uptimeMillis() - mCurrentAnimationTime;

 invalidate();

 * 判断gif图是否停止了

 * @return

 public boolean isPaused() {

 return this.mPaused;

 @Override

 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

 if (mMovie != null) {

 int movieWidth = mMovie.width();

 int movieHeight = mMovie.height();

 int maximumWidth = MeasureSpec.getSize(widthMeasureSpec);

 float scaleW = (float) movieWidth / (float) maximumWidth;

 mScale = 1f / scaleW;

 mMeasuredMovieWidth = maximumWidth;

 mMeasuredMovieHeight = (int) (movieHeight * mScale);

 setMeasuredDimension(mMeasuredMovieWidth, mMeasuredMovieHeight);

 } else {

 setMeasuredDimension(getSuggestedMinimumWidth(),getSuggestedMinimumHeight());

 @Override

 protected void onLayout(boolean changed, int l, int t, int r, int b) {

 super.onLayout(changed, l, t, r, b);

 mLeft = (getWidth() - mMeasuredMovieWidth) / 2f;

 mTop = (getHeight() - mMeasuredMovieHeight) / 2f;

 mVisible = getVisibility() == View.VISIBLE;

 @Override

 protected void onDraw(Canvas canvas) {

 if (mMovie != null) {

 if (!mPaused){

 if(updateAnimationTime()){

 drawMovieFrame(canvas);

 invalidateView();

 }else{

 if(null!=gifPlayListener){

 gifPlayListener.playFinish();

 } else {

 drawMovieFrame(canvas);

 @SuppressLint("NewApi")

 private void invalidateView() {

 if (mVisible) {

 if (Build.VERSION.SDK_INT = Build.VERSION_CODES.JELLY_BEAN) {

 postInvalidateOnAnimation();

 } else {

 invalidate();

 private boolean updateAnimationTime() {

 long now = android.os.SystemClock.uptimeMillis();

 if (mMovieStart == 0) {// 如果第一帧,记录起始时间

 mMovieStart = now;

 }else if((now-mMovieStart) =duration){//动画需要结束

 return false;

 // 算出需要显示第几帧

 mCurrentAnimationTime = (int) ((now - mMovieStart) % duration);

 return true;

 private void drawMovieFrame(Canvas canvas) {

 mMovie.setTime(mCurrentAnimationTime);// 设置要显示的帧,绘制即可

 canvas.save(Canvas.MATRIX_SAVE_FLAG);

 canvas.scale(mScale, mScale);

 mMovie.draw(canvas, mLeft / mScale, mTop / mScale);

 canvas.restore();

 @SuppressLint("NewApi")

 @Override

 public void onScreenStateChanged(int screenState) {

 super.onScreenStateChanged(screenState);

 mVisible = screenState == SCREEN_STATE_ON;

 invalidateView();

 @SuppressLint("NewApi")

 @Override

 protected void onVisibilityChanged(View changedView, int visibility) {

 super.onVisibilityChanged(changedView, visibility);

 mVisible = visibility == View.VISIBLE;

 invalidateView();

 @Override

 protected void onWindowVisibilityChanged(int visibility) {

 super.onWindowVisibilityChanged(visibility);

 mVisible = visibility == View.VISIBLE;

 invalidateView();


public void setOnGifPlayListener(GifPlayListener gifPlayListener) { this.gifPlayListener = gifPlayListener; public interface GifPlayListener{ void playFinish(); }

 


Flake.java  金币的实体类

 

public class Flake {

 float x, y;

 float rotation;

 float speed;

 float rotationSpeed;

 int width,height;

 Bitmap bitmap;

 static HashMap Integer, Bitmap bitmapMap = new HashMap Integer, Bitmap 

 static Flake createFlake(float xRange, Bitmap originalBitmap,Context Context) {

 Flake flake = new Flake();

 DisplayMetrics metrics = getDisplayMetrics(Context);

 if (metrics.widthPixels = 1080) {

 flake.width = (int) (5 + (float) Math.random() * 80);

 float hwRatio = originalBitmap.getHeight() / originalBitmap.getWidth();

 flake.height = (int) (flake.width * hwRatio + 60);

 } else {

 flake.width = (int) (5 + (float) Math.random() * 50);

 float hwRatio = originalBitmap.getHeight() / originalBitmap.getWidth();

 flake.height = (int) (flake.width * hwRatio + 40);

 flake.x = (float) Math.random() * (xRange - flake.width);

 flake.y = 0 - (flake.height + (float) Math.random() * flake.height);

 flake.speed = 50 + (float) Math.random() * 150;

 flake.rotation = (float) Math.random() * 180 - 90;

 flake.rotationSpeed = (float) Math.random() * 90 - 45;

 flake.bitmap = bitmapMap.get(flake.width);

 if (flake.bitmap == null) {

 flake.bitmap = Bitmap.createScaledBitmap(originalBitmap,(int) flake.width, (int) flake.height, true);

 bitmapMap.put(flake.width,flake.bitmap);

 return flake;

 * 获取屏幕尺寸与密度.

 * @param context the context

 * @return mDisplayMetrics

 public static DisplayMetrics getDisplayMetrics(Context context) {

 Resources mResources;

 if (context == null) {

 mResources = Resources.getSystem();

 } else {

 mResources = context.getResources();

 //DisplayMetrics{density=1.5, width=480, height=854, scaledDensity=1.5, xdpi=160.421, ydpi=159.497}

 //DisplayMetrics{density=2.0, width=720, height=1280, scaledDensity=2.0, xdpi=160.42105, ydpi=160.15764}

 DisplayMetrics mDisplayMetrics = mResources.getDisplayMetrics();

 return mDisplayMetrics;

}


 

FlakeView.java   绘制金币的View  金币屏幕掉下的动画

 

public class FlakeView extends View {

 Bitmap droid; // The bitmap that all flakes use

 int numFlakes = 0; // Current number of flakes

 ArrayList Flake flakes = new ArrayList Flake // List of current flakes

 public ValueAnimator animator = ValueAnimator.ofFloat(0, 1);

 long startTime, prevTime; // Used to track elapsed time for animations and fps

 int frames = 0; // Used to track frames per second

 Paint textPaint; // Used for rendering fps text

 float fps = 0; // frames per second

 Matrix m = new Matrix(); // Matrix used to translate/rotate each flake during rendering

 String fpsString = "";

 String numFlakesString = "";

 * Constructor. Create objects used throughout the life of the View: the Paint and

 * the animator

 public FlakeView(Context context) {

 super(context);

 droid = BitmapFactory.decodeResource(getResources(), R.drawable.icon_coin);

 textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

 textPaint.setColor(Color.WHITE);

 textPaint.setTextSize(24);

 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

 @Override

 public void onAnimationUpdate(ValueAnimator arg0) {

 long nowTime = System.currentTimeMillis();

 float secs = (float) (nowTime - prevTime) / 100f;

 prevTime = nowTime;

 for (int i = 0; i numFlakes; ++i) {

 Flake flake = flakes.get(i);

 flake.y += (flake.speed * secs);

 if (flake.y getHeight()) {

 flake.y = 0 - flake.height;

 flake.rotation = flake.rotation + (flake.rotationSpeed * secs);

 invalidate();

 animator.setRepeatCount(ValueAnimator.INFINITE);

 animator.setDuration(3000);

 int getNumFlakes() {

 return numFlakes;

 private void setNumFlakes(int quantity) {

 numFlakes = quantity;

 numFlakesString = "numFlakes: " + numFlakes;

 * Add the specified number of droidflakes.

 public void addFlakes(int quantity) {

 for (int i = 0; i quantity; ++i) {

 flakes.add(Flake.createFlake(getWidth(),droid,getContext()));

 setNumFlakes(numFlakes + quantity);

 void subtractFlakes(int quantity) {

 for (int i = 0; i quantity; ++i) {

 int index = numFlakes - i - 1;

 flakes.remove(index);

 setNumFlakes(numFlakes - quantity);

 @Override

 protected void onSizeChanged(int w, int h, int oldw, int oldh) {

 super.onSizeChanged(w, h, oldw, oldh);

 // Reset list of droidflakes, then restart it with 8 flakes

 flakes.clear();

 numFlakes = 0;

 addFlakes(16);

 // Cancel animator in case it was already running

 animator.cancel();

 // Set up fps tracking and start the animation

 startTime = System.currentTimeMillis();

 prevTime = startTime;

 frames = 0;

 animator.start();

 @Override

 protected void onDraw(Canvas canvas) {

 super.onDraw(canvas);

 for (int i = 0; i numFlakes; ++i) {

 Flake flake = flakes.get(i);

 m.setTranslate(-flake.width / 2, -flake.height / 2);

 m.postRotate(flake.rotation);

 m.postTranslate(flake.width / 2 + flake.x, flake.height / 2 + flake.y);

 canvas.drawBitmap(flake.bitmap, m, null);

 // fps counter: count how many frames we draw and once a second calculate the

 // frames per second

 ++frames;

 long nowTime = System.currentTimeMillis();

 long deltaTime = nowTime - startTime;

 if (deltaTime 1000) {

 float secs = (float) deltaTime / 1000f;

 fps = (float) frames / secs;

// fpsString = "fps: " + fps;

 startTime = nowTime;

 frames = 0;

 public void pause() {

 animator.cancel();

 public void resume() {

 animator.start();

}


 

activity_main.xml  布局文件

 

 FrameLayout 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" 

 RelativeLayout

 android:layout_width="match_parent"

 android:layout_height="match_parent" 

 com.xx.gifdemo.GifView

 android:id="@+id/imageView"

 android:layout_width="wrap_content"

 android:layout_height="wrap_content"

 android:layout_gravity="center_horizontal"

 android:enabled="false"

 android:hardwareAccelerated="false" / 

 Button

 android:id="@+id/button_stop1"

 android:layout_width="wrap_content"

 android:layout_height="wrap_content"

 android:layout_alignParentBottom="true"

 android:layout_centerHorizontal="true"

 android:text="停止/继续"/ 

 /RelativeLayout 

 LinearLayout

 android:id="@+id/container"

 android:layout_width="wrap_content"

 android:layout_height="wrap_content"

 android:orientation="vertical"/ 

 /FrameLayout 

 

点击下载源码


一文读懂系列Android屏幕刷新机制 对一些大中型项目来说可能就不一样了:**他们涉及业务较多,设备种类较多,往往一个app内部集成了十几个子业务甚至上百个,这对应用性能要求就更加严格了,app的体验也会间接导致用户的留存问题**。 所以学习屏幕绘制这类理论性较强的知识也是非常有必要的。
airserver安卓版手机屏幕投屏电脑神器 AirServer一款投屏神器,可以帮你轻松地将iPhone、iPad投屏到Mac。是不是经常看到游戏主播用AirServer投屏?此外,AirServer也是视频Up主必备工具之一!用来录制演示教程不错。除了实现单个手机投屏到电脑或荧幕。如果你有多画面投屏或者跨设备投屏的需求,不妨试试这个软件。
浅谈 Android 屏幕适配 几组概念 屏幕上物理像素的总数。添加对多种屏幕的支持时, 应用不会直接使用分辨率;而只应关注通用尺寸和密度组指定的屏幕尺寸及密度。
ansen_666 有三年android开发经验,开发过加密.短视频.直播app,一直在互联网公司工作,目前就职于上海翼成科技,担任android开发组长。