Как мы можем оптимизировать (cpu & ram) эту андроидную пользовательскую анимацию всплеска?

Я разработал пользовательскую анимацию для моей активности Splash Screen:

= > Вот анимация, которая показывает, что происходит:

введите описание изображения здесь

Конечно, мое настоящее приложение:

  • с разными изображениями (fullhd)
  • немного медленнее по сравнению с GIF: 3s для 60 промежуточных экранов.

Мой дизайнер предоставил мне 60 png файлов.

= > Один пример для иллюстрации:

введите описание изображения здесь

Моя цель:

  • начинайте с центрального логотипа (здесь с SO) с нижним изображением (Apple)
  • запустить анимацию морфинга
  • на экране, как на домашней странице приложения

Чтобы запустить это, у меня есть один многослойный макет для SpashScreenActivity с:

  • BackGround (невидимый): макет homePage (MainActivity)
  • MiddleGround: с ImageView Apple, который заменен Droid и нижним уровнем, которые растут
  • FrontGround: логотип в ImageView с его слоганом в TextView

Вот код xml для макета SpashScreen:

<?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>

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);
    }

}

STATUS:

Метод updateImageViewLight может, по-видимому, помочь другим справиться с таким поведением (PortedDuff...), которое было нелегко найти.

Анимация отлично работает на мощном устройстве, но часто отстает, если устройство или приложение делают что-то еще.

Я попытался запустить этот расчет в Async Task, но он был менее мощным, чем в mainThread

ВОПРОСЫ:

Я ищу любой образованный совет по моей реализации, который может помочь улучшить:

  • потребление памяти
  • Использование ЦП

Но также:

  • потенциальная утечка
  • читаемость кода

Ответ 1

Еще не ответил полностью, но это попытка, которую нужно продолжить и прокомментировать.

Первая оптимизация:

  • Создайте настраиваемый вид (здесь ImageView extension) для волны, которая будет обновляться по методу onDraw
  • define const member как static final членов класса
  • как можно больше, избегайте создания экземпляров объектов в onDraw (Canvas и Paint), а также ненужного обновления объекта

Вот код CustomView, который расширяет ImageView

    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);
    }
    }

Результаты:

  • Анимация задушена и кажется, что она потребляет меньше ресурсов. Однако он полностью не синхронизирован с анимацией компоновки. Вам нужно это сделать. Любые лучшие практики?
  • Это явно шаг init метода onDraw, который занимает время в первом цикле
  • Работа с временным холстом для окончательного рисования холста на холсте основного вида значительно улучшилась.