我们应该如何优化这个 Android 自定义启动动画的 CPU 和 RAM 占用?

12

我为我的启动画面活动开发了一个自定义动画:

=>这是一个展示正在发生的事情的动画:

enter image description here

当然,我的真实应用程序:

  • 是使用不同图片的(fullhd)
  • 比GIF慢一点:60个中间屏幕需要3秒钟。

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

=>以下是一个示例说明:

enter image description here

我的目标是:

  1. 从带有底部图片(苹果)的中央标志(此处为SO)开始
  2. 运行形态变化动画
  3. 结束于应用程序主页屏幕

为了运行它,我在SpashScreenActivity中有一个多层布局,其中包括:

  • 背景(不可见):主页(MainActivity)的布局
  • 中间层:带有苹果ImageView和底部栏增大的Droid,它将替换苹果ImageView
  • 前景层:标志在ImageView中,其标语在TextView中

以下是SpashScreen布局的XML代码:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="bottom">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:visibility="invisible"
        android:orientation="vertical">

        <FrameLayout
            android:id="@+id/fl_logo_top_marge_hidden"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:visibility="invisible"
            android:background="@color/colorPrimary" />

        <include
            android:visibility="invisible"
            android:id="@+id/l_logo_activate_hidden"
            layout="@layout/part_logo_activate"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <FrameLayout
            android:visibility="invisible"
            android:id="@+id/fl_logo_bottom_marge_hidden"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:background="@color/colorPrimary" />

        <fr.millezimsolutions.app.splashanimation.SquareAspectWidthBasedImageView
            android:visibility="invisible"
            android:id="@+id/iv_home_hidden"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scaleType="fitXY"
            android:src="@drawable/p_log_android" />

        <FrameLayout
            android:visibility="invisible"
            android:id="@+id/fl_bar_hidden"
            android:layout_width="match_parent"
            android:layout_height="@dimen/start_degustation_bar_height"
            android:background="@color/colorAccent"
            android:gravity="bottom" />
    </LinearLayout>


    <LinearLayout
        android:id="@+id/fl_middle"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentBottom="true"
        android:background="@color/colorPrimary"
        android:gravity="bottom"
        android:orientation="vertical">

        <fr.millezimsolutions.app.splashanimation.FitXCropTopImageView
            android:id="@+id/iv_slogan"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/colorTransparent"
            android:scaleType="fitStart"
            android:src="@drawable/p_log_apple" />

        <FrameLayout
            android:id="@+id/fl_logo_bottom_bar"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:background="@color/colorAccent"
            android:gravity="bottom" />

    </LinearLayout>

    <LinearLayout
        android:id="@+id/fl_front"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <FrameLayout
            android:id="@+id/fl_logo_top_layout"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="40"
            android:background="@color/colorPrimary" />

        <FrameLayout
            android:id="@+id/fl_logo_top_marge"
            android:layout_width="match_parent"
            android:layout_height="5dp"
            android:background="@color/colorTransparent" />

        <include
            android:id="@+id/l_logo_activate"
            layout="@layout/part_logo_activate"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <FrameLayout
            android:id="@+id/fl_logo_bottom_marge"
            android:layout_width="match_parent"
            android:layout_height="5dp"
            android:background="@color/colorTransparent" />

        <FrameLayout
            android:id="@+id/fl_logo_bottom_layout"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="40"
            android:background="@color/colorTransparent" />
    </LinearLayout>
</RelativeLayout>

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

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorTransparent"
    android:gravity="bottom"
    android:orientation="vertical">

    <FrameLayout
        android:id="@+id/fl_home_marginTop"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <ImageView
        android:id="@+id/iv_millezimuLogo"
        android:layout_width="wrap_content"
        android:layout_height="64dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginLeft="@dimen/marge"
        android:layout_marginRight="@dimen/marge"
        android:src="@drawable/p_log_so" />

    <TextView
        android:id="@+id/tv_slogan"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginBottom="@dimen/marge_small"
        android:layout_marginTop="@dimen/marge_small_border"
        android:gravity="center"
        android:hint=""
        android:text="Bonjour"
        android:textColor="@color/colorAccent"
        android:textSize="20sp" />

    <ImageView
        android:id="@+id/iv_sponsorLogo"
        android:layout_width="wrap_content"
        android:layout_height="50dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginLeft="@dimen/marge"
        android:layout_marginRight="@dimen/marge"
        android:src="@drawable/p_log_so"
        android:visibility="gone" />

    <TextView
        android:id="@+id/tv_sponsorLogo"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_gravity="center_horizontal"
        android:layout_marginLeft="@dimen/marge"
        android:layout_marginRight="@dimen/marge"
        android:gravity="center"
        android:textColor="@color/colorAccent"
        android:textSize="20sp"
        android:visibility="gone" />

    <FrameLayout
        android:id="@+id/fl_home_marginBottom"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

</LinearLayout>

以下是该活动的代码。

 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方法可能有助于其他人处理这种行为(PortedDuff...),这种行为以前很难找到。

动画在强大的设备上正常工作,但如果设备或应用程序执行其他操作,则经常会出现延迟。

我已尝试在Async Task中运行此计算,但比在主线程中 less 强大。

问题:

我正在寻求任何关于我的实现的建议,这可能有助于改进:

  • 内存消耗
  • CPU使用率

但同时也包括:

  • 潜在泄漏
  • 代码可读性

将其变为视频流难道不会释放大部分CPU资源吗?因为它可以在GPU上加速。 - huseyin tugrul buyukisik
1
尝试使用Glide库,它在播放GIF方面表现很好,可能会完成更好的任务。 - Lokanath
@Anthony,你可以使用动态矢量图来制作低内存的启动画面动画,请参考这个链接:https://lewismcgeary.github.io/posts/animated-vector-drawable-pathMorphing/。 - appukrb
@appukrb 感谢提供链接,这是一个强大的库,但是在我的实际项目中我使用的是纯位图,我只是为了在StackOverflow上展示才放了苹果和安卓。 - Anthony
我几天前做了类似的实现。我遇到了同样的性能问题,然后我最终使用了GIF并在Webview中显示它。可以使用HTML和CSS创建良好的布局,始终适合所需的屏幕。 - N-JOY
显示剩余3条评论
1个回答

3
尚未完全回答,但这是一次尝试,需要继续和评论。
第一个优化:
- 创建自定义视图(此处为ImageView扩展),用于波形,在onDraw方法上进行刷新 - 将const成员定义为静态final类成员 - 尽可能避免在onDraw中创建对象实例(Canvas和Paint),以及不必要的对象更新
以下是扩展了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);
    }
    }

结果:

  • 动画更加流畅,占用资源较少。但是它与布局动画完全不同步。需要解决这个问题。有什么最佳实践吗?
  • 这显然是onDraw方法的初始步骤,在第一个周期中需要时间。
  • 在临时画布上工作,最终将画布绘制到主视图画布上,效果大大提高。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接