Android中几乎所有app都有启动页这一基本功能,但是用途都不相同。
有的app的启动页用于加载广告,有的启动页用于加载后台,一旦后台数据加载好了才把启动页去了,不过想来很多app是两者兼顾的。
需求:

  • 展示 logo 页面3秒
  • 服务端可以控制是否播放广告
  • 服务端可以控制播放广告的秒数
  • 服务端可以控制广告的内容(图片)和广告详情页面的链接

注意:从服务端请求数据是在展示 3 秒启动页的时候获取的

启动页

启动包括冷启动和热启动:

  • 冷启动:是指进程从无到有的过程。因为要进行页面初始化,所以相对其他两个启动方式,消耗的时间是相对比较多的。
  • 热启动:是指之前的进程还在,在之前进程的基础上创建 Activity 的过程,耗时相对少一点。
      我们可以通过 Activity 的 theme 来修改这个白屏所显示的界面。根据上面的需求,我们需要显示3秒 logo 的页面。那么我们干脆将我们的logo设置为背景图就行了。

新建一个activity为SplashActivity,其对应布局文件为activity_splash.xml

并在AndroidManifest.xml中将SplashActivity设置为主入口:

1
2
3
4
5
6
7
8
9
<activity android:name=".MainActivity">
</activity>
<activity android:name=".SplashActivity"
android:theme="@style/Theme.AppCompat.NoActionBar.NoActionBarWithBackGround">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

在values中的themes.xml中添加:

1
2
3
4
5
6
<style name="Theme.AppCompat.NoActionBar.NoActionBarWithBackGround">
<item name="windowActionBar">false</item>//取消Actionbar
<item name="windowNoTitle">true</item>
<item name="android:windowFullscreen">true</item>//设置全屏
<item name="android:windowBackground">@drawable/splash</item>//设置背景图片
</style>

在AndroidManifest.xml中需要主题的Activity添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.splashactivity">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.SplashActivity">
<activity android:name=".MainActivity">
</activity>
<activity android:name=".SplashActivity"
android:theme="@style/Theme.AppCompat.NoActionBar.NoActionBarWithBackGround">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

SplashActivity:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.WindowManager;


public class SplashActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);//隐藏状态栏
//getSupportActionBar().hide();//隐藏标题栏
setContentView(R.layout.activity_splash);
Thread myThread = new Thread() {//创建子线程
@Override
public void run() {
try {
sleep(5000);//使程序休眠五秒
Intent intent = new Intent(getApplicationContext(), MainActivity.class);//启动MainActivity
startActivity(intent);
finish();//关闭当前活动
} catch (Exception e) {
e.printStackTrace();
}
}
};
myThread.start();//启动线程
}
}

广告页

广告页我尝试过两种方式:
1.glide 加载
2.通过下载文件,然后再加载
如果使用glide加载广告图片,如果网络比较差,会存在广告页面空白的情况,因为使用 glide 无法判断在 3 秒展示 logo 的页面是否加载好了广告图片。这给用户的体验是比较差的,也是不太友好的,因为用户在空白界面拜拜等待了 3 秒。所以后面使用了将广告图片下载到本地的方法。

动画开屏

新建EmptyActivity–AnimatedActivity
在build.gradle(:app)的dependencies中添加glide依赖:

implementation ‘com.github.bumptech.glide:glide:4.12.0’
然后点击sync now

新建动画所需的VectorAsset:

然后点击next,最后点击finish
转换svg的网站

或者去iconfont查找

activity_animated

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
tools:context=".AnimatedActivity">

<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/iv_top"
android:adjustViewBounds="true"
android:src="@drawable/ic_top_vector"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="16dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="180dp"
android:id="@+id/iv_heart"
android:adjustViewBounds="true"
android:src="@drawable/ic_heart"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/text_view"
android:textSize="48sp"
android:textStyle="bold"
android:textColor="#D75A4A"
android:layout_marginTop="8dp"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/iv_beat"
android:adjustViewBounds="true"/>

</LinearLayout>
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/iv_bottom"
android:adjustViewBounds="true"
android:layout_alignParentBottom="true"
android:src="@drawable/ic_bottom_vector"/>

</RelativeLayout>

新建ResourceFile

设置:

top_wave.xml

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:fromXDelta="-50%"
android:fromYDelta="0%"
android:duration="2500"/>
<alpha
android:fromAlpha="1.0"
android:toAlpha="0.5"
android:duration="2500"/>

</set>

bottom_wave.xml

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="2500"
android:fromXDelta="50%"
android:fromYDelta="0%" />
<alpha
android:duration="2500"
android:fromAlpha="1.0"
android:toAlpha="0.5" />
</set>

AnimatedActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.animation.ValueAnimator;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;

public class AnimatedActivity extends AppCompatActivity {
//Initialize variable
ImageView ivTop, ivHeart, ivBeat, ivBottom;
TextView textView;
CharSequence charSequence;
int index;
long delay = 200;
Handler mHandler = new Handler();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_animated);

//Assign variable
ivTop = findViewById(R.id.iv_top);
ivHeart = findViewById(R.id.iv_heart);
ivBottom = findViewById(R.id.iv_bottom);
textView = findViewById(R.id.text_view);

//set full screen
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN
, WindowManager.LayoutParams.FLAG_FULLSCREEN);

//Initialize top animation
Animation animation1 = AnimationUtils.loadAnimation(this
, R.anim.top_wave);
//start top animation
ivTop.setAnimation(animation1);

//Initialize object animation
ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(
ivHeart,
PropertyValuesHolder.ofFloat("scaleX", 1.2f),
PropertyValuesHolder.ofFloat("scaleY", 1.2f)
);
//Set duration
objectAnimator.setDuration(500);
//Set repeat count
objectAnimator.setRepeatCount(ValueAnimator.INFINITE);
//Set repeat mode
objectAnimator.setRepeatMode(ValueAnimator.REVERSE);
//Start animator
objectAnimator.start();

//Set animate text
animateText("Heart Beat");
//Load GIF
Glide.with(this).load("https://firebasestorage.googleapis.com/v0/b/demoapp-ae96a.appspot.com/o/heart_beat.gif?alt=media&token=b21dddd8-782c-457c-babd-f2e922ba172b")
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(ivBottom);
//Initialize bottom animation
Animation animation2 = AnimationUtils.loadAnimation(this
, R.anim.bottom_wave);
//start bottom animation
ivBottom.setAnimation(animation2);

//Initialize handler
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//Redirect to main activity
startActivity(new Intent(AnimatedActivity.this
, MainActivity.class)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
//Finish activity
finish();
}
}, 4000);
}

Runnable mRunnable = new Runnable() {
@Override
public void run() {
//When runnable is run
//Set text
textView.setText(charSequence.subSequence(0, index++));
//Check condition
if (index <= charSequence.length()) {
//When index is equal to text length
//Run handler
mHandler.postDelayed(mRunnable, delay);
}
}
};

//Create animated text method
public void animateText(CharSequence cs) {
//Set text
charSequence = cs;
//Clear index
index = 0;
//Clear text
textView.setText("");
//Remove call back
mHandler.removeCallbacks(mRunnable);
//Run handler
mHandler.postDelayed(mRunnable, delay);
}
}

在AndroidManifest.xml中添加权限:

1
<uses-permission android:name="android.permission.INTERNET"/>

不要忘记将AnimatedActivity设置为主入口.

完整项目