ich eine benutzerdefinierte Animation für meine Splash Screen-Aktivität entwickelt:Wie können wir diese android benutzerdefinierte Splash-Animation (cpu & RAM) optimieren?
=> Hier ist eine Animation, die zeigt, was passiert:
Natürlich meine echte App:
- ist mit verschiedenen Bildern (fullhd)
- ist ein bisschen langsamer im Vergleich zum GIF: 3s für 60 Zwischen Bildschirme.
Mein Designer stellte mir 60 Png-Dateien.
=> Ein Beispiel zur Veranschaulichung:
Mein Ziel ist:
- Start von einem zentralen Logo (hier mit SO) mit einem unteren Bild (Apple)
- führen Sie eine Morphing-Animation
- Ende auf dem Bildschirm wie die Homepage der App
dies ausführen zu können, habe ich ein Multilayer-Layout für die SpashScreenActivity mit:
- BackGround (unsichtbar): Das Layout der Homepage (MainActivity)
- Mittelgrund: mit dem Image des Apfels, wird durch die Droid und die bottomBar ersetzt, die
- frontground wachsen: das logo in einem Image mit dem Slogan in einem Textview
Hier wird der xml-Code für das SpashScreen Layout:
<?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 der XML-Code für die oben im Layout (via umfasst)
<?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 Hier ist der Code der Aktivität.
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 Und der Code des Imagemanager für das Verständnis (ich benutze 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:
Die updateImageViewLight Methode beweisbar anderen helfen kann mit dieser Art von Verhalten umgehen (PortedDuff ...) das war nicht leicht zu finden.
Die Animation funktioniert gut auf einem leistungsfähigen Gerät, aber oft verzögert, wenn das Gerät oder die App etwas anderes tut.
Ich habe versucht, diese Berechnung in Async Aufgabe zu laufen, aber es war weniger stark als in dem Mainthread
FRAGEN:
Ich bin für jede gebildete Beratung über meine Implementierung suchen, die helfen können zu verbessern:
- Speicherverbrauch
- CPU-Auslastung
Aber auch:
- potenzieller Leck
- Lesbarkeit des Codes
@Rod_Algonquin, in dem Sinn, Zeit beim ersten Zyklus nehmen es ist besser in der Leistung? – Anthony
Wäre es nicht ein Video-Stream macht CPU-Ressourcen am meisten frei, weil es auf GPU beschleunigt werden kann? –
Die Antwort ist wahrscheinlich ja, aber der Inhalt kann sich ändern (zB "bonjour"), so dass es auf Programmebene gehandhabt werden muss. Und ich bin mir nicht sicher, ob das Layout immer zu einem Pre-Recorder-Video passt. – Anthony