我们如何优化(cpu&ram)这个android自定义初始化animation?

我为Splash Screen活动开发了一个自定义animation

=>这是一个animation,显示正在发生的事情:

在此处输入图像描述

当然我的真实应用:

  • 是用不同的图片(fullhd)
  • 与GIF相比有点慢:60个中间屏幕为3s。

我的设计师为我提供了60个png文件。

=>举例说明:

在此处输入图像描述

我的目标是:

  1. 从中央徽标(此处带有SO)开始,带有底部图片(Apple)
  2. 运行变形animation
  3. 像应用程序的主页一样在屏幕上结束

为了运行它,我有一个SpashScreenActivity的多层布局:

  • BackGround(不可见):homePage的布局(MainActivity)
  • MiddleGround:Apple的ImageView被Droid和bottomBar所取代
  • FrontGround:ImageView中的徽标,其标语为TextView

以下是SpashScreen布局的xml代码:

                     

2布局顶部的xml代码(通过include)

          

3这是活动的代码。

  import android.content.Intent; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.ViewGroup; import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.LinearLayout; public class SplashScreenActivity extends AppCompatActivity { // Splash screen timer private static int SPLASH_TIME_OUT = 3000; private int finalTopMarge, finalBottomMarge, topMargeDec, bottomMargeInc; long currentTimeStamp; private int[] mSplashAnimFrames = {R.drawable.p_wave_spashscreen_00, R.drawable.p_wave_spashscreen_01, R.drawable.p_wave_spashscreen_02, R.drawable.p_wave_spashscreen_03, R.drawable.p_wave_spashscreen_04, R.drawable.p_wave_spashscreen_05, R.drawable.p_wave_spashscreen_06, R.drawable.p_wave_spashscreen_07, R.drawable.p_wave_spashscreen_08, R.drawable.p_wave_spashscreen_09, R.drawable.p_wave_spashscreen_10, R.drawable.p_wave_spashscreen_11, R.drawable.p_wave_spashscreen_12, R.drawable.p_wave_spashscreen_13, R.drawable.p_wave_spashscreen_14, R.drawable.p_wave_spashscreen_15, R.drawable.p_wave_spashscreen_16, R.drawable.p_wave_spashscreen_17, R.drawable.p_wave_spashscreen_18, R.drawable.p_wave_spashscreen_19, R.drawable.p_wave_spashscreen_20, R.drawable.p_wave_spashscreen_21, R.drawable.p_wave_spashscreen_22, R.drawable.p_wave_spashscreen_23, R.drawable.p_wave_spashscreen_24, R.drawable.p_wave_spashscreen_25, R.drawable.p_wave_spashscreen_26, R.drawable.p_wave_spashscreen_27, R.drawable.p_wave_spashscreen_28, R.drawable.p_wave_spashscreen_29, R.drawable.p_wave_spashscreen_30, R.drawable.p_wave_spashscreen_31, R.drawable.p_wave_spashscreen_32, R.drawable.p_wave_spashscreen_33, R.drawable.p_wave_spashscreen_34, R.drawable.p_wave_spashscreen_35, R.drawable.p_wave_spashscreen_36, R.drawable.p_wave_spashscreen_37, R.drawable.p_wave_spashscreen_38, R.drawable.p_wave_spashscreen_39, R.drawable.p_wave_spashscreen_40, R.drawable.p_wave_spashscreen_41, R.drawable.p_wave_spashscreen_42, R.drawable.p_wave_spashscreen_43, R.drawable.p_wave_spashscreen_44, R.drawable.p_wave_spashscreen_45, R.drawable.p_wave_spashscreen_46, R.drawable.p_wave_spashscreen_47, R.drawable.p_wave_spashscreen_48, R.drawable.p_wave_spashscreen_49, R.drawable.p_wave_spashscreen_50, R.drawable.p_wave_spashscreen_51, R.drawable.p_wave_spashscreen_52, R.drawable.p_wave_spashscreen_53, R.drawable.p_wave_spashscreen_54, R.drawable.p_wave_spashscreen_55, R.drawable.p_wave_spashscreen_56, R.drawable.p_wave_spashscreen_57, R.drawable.p_wave_spashscreen_58, R.drawable.p_wave_spashscreen_59}; private final int C_STOP = 120, C_MOVE = 40, C_BAR = 80; private int bottomBarRatio; private ImageView finalImageView; private int targetWidth, targetHeight; private Rect mImageViewRect; private Paint paint; private Bitmap original; private Bitmap result; private boolean setupOk = false; private ImageView mImageView; private Bitmap mask; private FrameLayout ltm; private FrameLayout lbm; private FrameLayout lbb; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_splash_screen); // Indique que l'ecran est full Screen getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); ImageManager.create(this); } @Override protected void onResume() { super.onResume(); int delay = SPLASH_TIME_OUT / C_STOP; bottomBarRatio = getResources().getDimensionPixelSize(R.dimen.bar_nav_height) / (C_STOP - C_BAR); runCycle(0, delay); } private void logStamp() { long oldTimeStamp = currentTimeStamp; currentTimeStamp = System.currentTimeMillis(); long delay = currentTimeStamp - oldTimeStamp; Log.v("TIMESTAMP", String.valueOf(delay)); } public void runCycle(final int cycle, final int delay) { if (BuildConfig.DEBUG) logStamp(); Handler cyclic = new Handler(); cyclic.postDelayed(new Runnable() { @Override public void run() { if (cycle >= C_STOP) { closeActivity(); } else { runCycle(cycle + 1, delay); if (cycle >= C_MOVE) { // Copy des hauteurs pour les marges initFinalLogoMargeHeight(); // Decroissance du poid de layout superieur MoveUpLogo(); // bouger la bar if (cycle >= C_BAR) { updateBottomBar(cycle - C_BAR); } findViewById(R.id.fl_front).requestLayout(); } if (setupFinalView()) { if ((cycle % 2) == 0) updateImageViewLight(cycle / 2); } } } }, delay); } private boolean setupFinalView() { if (!setupOk) { finalImageView = (ImageView) findViewById(R.id.iv_home_hidden); targetWidth = finalImageView.getWidth(); targetHeight = finalImageView.getHeight(); mImageViewRect = new Rect(0, 0, finalImageView.getWidth(), finalImageView.getHeight()); mImageView = (ImageView) findViewById(R.id.iv_slogan); mImageView.setBackgroundResource(R.drawable.p_log_apple); paint = new Paint(Paint.ANTI_ALIAS_FLAG); ltm = (FrameLayout) findViewById(R.id.fl_logo_top_marge); lbm = (FrameLayout) findViewById(R.id.fl_logo_bottom_marge); lbb = ((FrameLayout) findViewById(R.id.fl_logo_bottom_bar)); if (targetWidth > 0 && targetHeight > 0) { original = ImageManager.decodeSampledBitmapFromResource(getResources(), R.drawable.p_log_android, targetWidth, targetHeight); result = Bitmap.createBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_4444); setupOk = true; } } return setupOk; } private void MoveUpLogo() { ViewGroup.LayoutParams ltmp = ltm.getLayoutParams(); ltmp.height -= topMargeDec; ViewGroup.LayoutParams lbmp = lbm.getLayoutParams(); lbmp.height += bottomMargeInc; } private void initFinalLogoMargeHeight() { if (finalBottomMarge == 0) { finalTopMarge = findViewById(R.id.fl_logo_top_marge_hidden).getHeight(); topMargeDec = (findViewById(R.id.fl_logo_top_marge).getHeight() - finalTopMarge) / C_BAR; finalBottomMarge = findViewById(R.id.fl_logo_bottom_marge_hidden).getHeight() + findViewById(R.id.fl_bar_hidden).getHeight() + findViewById(R.id.iv_home_hidden).getHeight(); bottomMargeInc = (finalBottomMarge - findViewById(R.id.fl_logo_bottom_marge).getHeight()) / C_BAR; } } private void updateBottomBar(int cycle) { LinearLayout.LayoutParams lbbp = (LinearLayout.LayoutParams) lbb.getLayoutParams(); lbbp.height = cycle * bottomBarRatio; lbb.setLayoutParams(lbbp); } private void closeActivity() { overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); Intent i = new Intent(SplashScreenActivity.this, MainActivity.class); startActivity(i); finish(); overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); } private int getNext(int index) { if (index < (mSplashAnimFrames.length - 1)) index++; else index = mSplashAnimFrames.length - 1; return mSplashAnimFrames[index]; } public void updateImageViewLight(int index) { mask = ImageManager.decodeSampledBitmapFromResource(getResources(), getNext(index), targetWidth, targetHeight); Canvas mCanvas = new Canvas(result); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); mCanvas.drawBitmap(original, null, mImageViewRect, null); mCanvas.drawBitmap(mask, null, mImageViewRect, paint); paint.setXfermode(null); mImageView.setImageBitmap(result); } } 

4和ImageManager的代码了解(我使用UIL)

 public class ImageManager { private static Context context; public static ImageLoader getImageLoader() { return ImageLoader.getInstance(); } public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId, int reqWidth, int reqHeight) { // First decode with inJustDecodeBounds=true to check dimensions final BitmapFactory.Options options = new BitmapFactory.Options(); options.outWidth = reqWidth; options.outHeight = reqHeight; options.inJustDecodeBounds = true; options.inPreferredConfig = Bitmap.Config.RGB_565; BitmapFactory.decodeResource(res, resId, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return getResourceImageForCanvas(resId, new ImageSize(reqWidth, reqHeight)); } public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int halfHeight = height / 2; final int halfWidth = width / 2; // Calculate the largest inSampleSize value that is a power of 2 and keeps both // height and width larger than the requested height and width. while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) { inSampleSize *= 2; } } return inSampleSize; } public static Bitmap getResourceImageForCanvas(int bitmapResourceId, ImageSize targetImageSize) { DisplayImageOptions options = new DisplayImageOptions.Builder().bitmapConfig(Bitmap.Config.RGB_565).build(); return getImageLoader().loadImageSync("drawable://" + bitmapResourceId, targetImageSize, options); // } public static void create(Context context) { try { ImageManager.context = context; initImageLoader(); } catch (IOException e) { e.printStackTrace(); } } private static void initImageLoader() throws IOException { // Create global configuration and initialize ImageLoader with this // configuration BitmapFactory.Options opt = new BitmapFactory.Options(); // opt.inScaled = false; opt.inSampleSize = 1; opt.inDither = true; opt.inPreferredConfig = Bitmap.Config.RGB_565; opt.inPreferQualityOverSpeed = false; DisplayImageOptions defaultOptions = new DisplayImageOptions.Builder()./* cacheInMemory(true). */cacheOnDisk(true).decodingOptions(opt).imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) .bitmapConfig(Bitmap.Config.RGB_565).build(); ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context).defaultDisplayImageOptions(defaultOptions).memoryCacheSizePercentage(13).writeDebugLogs().build(); ImageLoader.getInstance().init(config); } } 

状态:

updateImageViewLight方法可以certificate可以帮助其他人处理这种不容易find的行为(PortedDuff …)。

animation在function强大的设备上运行良好,但如果设备或应用程序执行其他操作,则通常会滞后。

我曾尝试在Async Task中运行此计算,但它的function不如mainThread

问题:

我正在寻找有关我的实施的任何有根据的建议,这可能有助于改善:

  • 记忆消耗
  • CPU使用率

但也 :

  • 潜在的泄漏
  • 代码可读性

尚未完全回答,但这是一个尝试,需要继续和评论。

第一次优化:

  • 为wave创建自定义视图(此处为ImageView扩展),将在onDraw方法上刷新
  • 将const成员定义为static final类成员
  • 尽可能多地避免在onDrawCanvasPaint )中创建对象实例以及不必要的对象更新

以下是扩展ImageView的CustomView的代码

  public class WaveFillingImageView extends ImageView { private final static int[] mSplashAnimFrames = {R.drawable.p_wave_spashscreen_00, R.drawable.p_wave_spashscreen_01, R.drawable.p_wave_spashscreen_02, R.drawable.p_wave_spashscreen_03, R.drawable.p_wave_spashscreen_04, R.drawable.p_wave_spashscreen_05, R.drawable.p_wave_spashscreen_06, R.drawable.p_wave_spashscreen_07, R.drawable.p_wave_spashscreen_08, R.drawable.p_wave_spashscreen_09, R.drawable.p_wave_spashscreen_10, R.drawable.p_wave_spashscreen_11, R.drawable.p_wave_spashscreen_12, R.drawable.p_wave_spashscreen_13, R.drawable.p_wave_spashscreen_14, R.drawable.p_wave_spashscreen_15, R.drawable.p_wave_spashscreen_16, R.drawable.p_wave_spashscreen_17, R.drawable.p_wave_spashscreen_18, R.drawable.p_wave_spashscreen_19, R.drawable.p_wave_spashscreen_20, R.drawable.p_wave_spashscreen_21, R.drawable.p_wave_spashscreen_22, R.drawable.p_wave_spashscreen_23, R.drawable.p_wave_spashscreen_24, R.drawable.p_wave_spashscreen_25, R.drawable.p_wave_spashscreen_26, R.drawable.p_wave_spashscreen_27, R.drawable.p_wave_spashscreen_28, R.drawable.p_wave_spashscreen_29, R.drawable.p_wave_spashscreen_30, R.drawable.p_wave_spashscreen_31, R.drawable.p_wave_spashscreen_32, R.drawable.p_wave_spashscreen_33, R.drawable.p_wave_spashscreen_34, R.drawable.p_wave_spashscreen_35, R.drawable.p_wave_spashscreen_36, R.drawable.p_wave_spashscreen_37, R.drawable.p_wave_spashscreen_38, R.drawable.p_wave_spashscreen_39, R.drawable.p_wave_spashscreen_40, R.drawable.p_wave_spashscreen_41, R.drawable.p_wave_spashscreen_42, R.drawable.p_wave_spashscreen_43, R.drawable.p_wave_spashscreen_44, R.drawable.p_wave_spashscreen_45, R.drawable.p_wave_spashscreen_46, R.drawable.p_wave_spashscreen_47, R.drawable.p_wave_spashscreen_48, R.drawable.p_wave_spashscreen_49, R.drawable.p_wave_spashscreen_50, R.drawable.p_wave_spashscreen_51, R.drawable.p_wave_spashscreen_52, R.drawable.p_wave_spashscreen_53, R.drawable.p_wave_spashscreen_54, R.drawable.p_wave_spashscreen_55, R.drawable.p_wave_spashscreen_56, R.drawable.p_wave_spashscreen_57, R.drawable.p_wave_spashscreen_58, R.drawable.p_wave_spashscreen_59}; private Paint paint; private long nextDrawTimeStamp; private boolean init = false, isStarted = false; private Bitmap original, result; private Rect mImageViewRect; private int index = 0; private LogAndStat las; private Canvas mCanvas; private final int timeTick = 50; public WaveFillingImageView(Context context) { super(context); init(context); } public WaveFillingImageView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public WaveFillingImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } @DebugLog private void init(Context context) { paint = new Paint(Paint.ANTI_ALIAS_FLAG); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); las = new LogAndStat("View", mSplashAnimFrames.length); } public void start() { isStarted = true; nextDrawTimeStamp = System.currentTimeMillis(); invalidate(); } @DebugLog @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // Before Layout if (getWidth() == 0 || getHeight() == 0) return; // Init variables if (!init) { las.logStamp("init onDraw"); original = ImageManager.decodeSampledBitmapFromResourcewithUIL(getResources(), R.drawable.p_log_android, getWidth(), getHeight()); result = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_4444); mCanvas = new Canvas(result); mImageViewRect = new Rect(0, 0, getWidth(), getHeight()); init = true; las.logStamp("init onDraw"); } // If tick reached for refresh if (System.currentTimeMillis() >= nextDrawTimeStamp) { nextDrawTimeStamp += timeTick; las.logStamp(index); Bitmap mask = ImageManager.decodeSampledBitmapFromResourcewithUIL(getResources(), getResourceForNextCycle(index), mImageViewRect.width(), mImageViewRect.height()); mCanvas.drawBitmap(original, null, mImageViewRect, null); mCanvas.drawBitmap(mask, null, mImageViewRect, paint); canvas.drawBitmap(result, 0, 0, null); index++; } if (isStarted) // Invalidate during animation to call again on Draw if (index < mSplashAnimFrames.length) { las.logStamp("invalidate"); invalidate(); } else { las.logStats(); } } @DebugLog private int getResourceForNextCycle(int index) { if (index < (mSplashAnimFrames.length - 1)) index++; else index = mSplashAnimFrames.length - 1; return mSplashAnimFrames[index]; } @DebugLog @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, widthMeasureSpec); } } 

结果:

  • animation被扼杀了,似乎占用了更少的资源。 但是它与布局animation完全不同步。 需要这样做。 任何最佳做法?
  • 这显然是onDraw方法的初始化步骤,需要在第一个周期花费时间
  • 在临时canvas上工作以最终在主视图canvas上绘制canvas得到了很大改进