在我们初次使用App或升级后首次使用App会有一个引导页,通常用来介绍新版本特性。大多数引导页以图文为主,而那些配以动画或视频的引导页总是让人眼前一亮。而作为开发者的我们,在弄明白原理后,总是迫切的想要去实现。本文将从实践出发,讲解具体的实现过程,最终实现一个完整的包含视频的引导页。
技术选型
在Android中常用VideoView实现简单的视频播放功能,另一种方法是使用MediaPlayer + SurfaceView来实现,本质上VideoView对它俩进行了封装,使开发者不用关心其细节,降低开发难度。
写到这里,相信大家心里已经有了实现思路,想要实现带视频播放功能的引导页无非就是在ViewPager中每个页面添加VideoView,滑动到相应页面播放则对应的视频。
那么教程到此就可以结束了!!!
但是这么做会有一个问题,每次打开引导页播放在视频前会先黑屏一下,并且ViewPager滑动时两个页面之间会有明显的黑缝隙。作为一个有追求的开发者这显然是不能接受的。
TextureView
TextureView又是个什么东东的呢?我们知道SurfaceView为了提高工作效率,它的工作方式是创建一个置于应用窗口之后的新窗口,SurfaceView的内容不在应用窗口上,所以不能使用变换(平移、缩放、旋转等)。也难以放在ListView或者ScrollView中,不能使用UI控件的一些特性比如View.setAlpha()。
作为SurfaceView的兄弟,TextureView终于在Android 4.0来救场了。
TextureView并没有创建新的窗口,可以像使用普通View一样执行变换操作,使用TextureView务必开启硬件加速。
实践
科学证明使用MediaPlayer与TextureView能确保达到最佳效果。
视频文件
视频文件来源于闲鱼App
1 2 3 4 5 6
| //将视频文件放至在assets/guide目录中 ├── guide │ ├── guide_four.mp4 │ ├── guide_one.mp4 │ ├── guide_three.mp4 │ └── guide_two.mp4
|
GuideActivity
MediaPlayer的setOnPreparedListener()方法可以设置一个监听器,在视频预处理完成之后产生回调,这里我们可以将视频默认设置到第一帧,从而避免黑屏。
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 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
| public class GuideActivity extends AppCompatActivity { private ViewPager viewPager = null; private List<ImageView> indicators = new ArrayList<>(4); private List<View> views = new ArrayList<>(4); private List<MediaPlayer> mediaPlayers = new ArrayList<>(4);
private TextureView textureViewOne; private TextureView textureViewTwo; private TextureView textureViewThree; private TextureView textureViewFour;
private MediaPlayer mediaPlayerOne; private MediaPlayer mediaPlayerTwo; private MediaPlayer mediaPlayerThree; private MediaPlayer mediaPlayerFour;
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.guide); int flag = WindowManager.LayoutParams.FLAG_FULLSCREEN; Window w = getWindow(); w.setFlags(flag, flag);
initVideos(); initViews(); }
private void initVideos() { try { mediaPlayerOne = createMediaPlayer("guide/guide_one.mp4"); mediaPlayerTwo = createMediaPlayer("guide/guide_two.mp4"); mediaPlayerThree = createMediaPlayer("guide/guide_three.mp4"); mediaPlayerFour = createMediaPlayer("guide/guide_four.mp4");
mediaPlayers.add(mediaPlayerOne); mediaPlayers.add(mediaPlayerTwo); mediaPlayers.add(mediaPlayerThree); mediaPlayers.add(mediaPlayerFour); } catch (IOException e) { finish(); e.printStackTrace(); } }
private MediaPlayer createMediaPlayer(String filePath) throws IOException { MediaPlayer mediaPlayer = new MediaPlayer(); mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); AssetFileDescriptor fileDescriptor = getAssets().openFd(filePath); mediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(), fileDescriptor.getStartOffset(), fileDescriptor.getLength()); return mediaPlayer; }
final ViewPager.OnPageChangeListener changeListener = new ViewPager.OnPageChangeListener() { @Override public void onPageSelected(int index) { for (int i = 0; i < mediaPlayers.size(); i++) { MediaPlayer mediaPlayer = mediaPlayers.get(i); ImageView indicate = indicators.get(i); if (i == index) { mediaPlayer.start(); indicate.setBackgroundResource(R.drawable.page_now); continue; } if (mediaPlayer.isPlaying()) { mediaPlayer.pause(); mediaPlayer.seekTo(0); }
indicate.setBackgroundResource(R.drawable.page); } }
@Override public void onPageScrolled(int pos, float positionOffset, int pix) { }
@Override public void onPageScrollStateChanged(int arg0) {
} };
private PagerAdapter adapter = new PagerAdapter() { @Override public int getCount() { return views.size(); }
@Override public boolean isViewFromObject(View view, Object object) { return view == object; }
@Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView(views.get(position)); }
@Override public Object instantiateItem(ViewGroup container, int position) { View view = views.get(position); container.addView(view); return view; } };
private void initViews() { LayoutInflater inflater = LayoutInflater.from(this); ImageView p1 = (ImageView) this.findViewById(R.id.p1); ImageView p2 = (ImageView) this.findViewById(R.id.p2); ImageView p3 = (ImageView) this.findViewById(R.id.p3); ImageView p4 = (ImageView) this.findViewById(R.id.p4); indicators.add(p1); indicators.add(p2); indicators.add(p3); indicators.add(p4);
View viewOne = inflater.inflate(R.layout.guide_one, null); View viewTwo = inflater.inflate(R.layout.guide_two, null); View viewThree = inflater.inflate(R.layout.guide_three, null); View viewFour = inflater.inflate(R.layout.guide_four, null); views.add(viewOne); views.add(viewTwo); views.add(viewThree); views.add(viewFour);
textureViewOne = (TextureView) viewOne.findViewById(R.id.texture_view); textureViewTwo = (TextureView) viewTwo.findViewById(R.id.texture_view); textureViewThree = (TextureView) viewThree.findViewById(R.id.texture_view); textureViewFour = (TextureView) viewFour.findViewById(R.id.texture_view);
textureViewOne.setSurfaceTextureListener(new TextureListener(mediaPlayerOne, 1)); textureViewTwo.setSurfaceTextureListener(new TextureListener(mediaPlayerTwo, 2)); textureViewThree.setSurfaceTextureListener(new TextureListener(mediaPlayerThree, 3)); textureViewFour.setSurfaceTextureListener(new TextureListener(mediaPlayerFour, 4));
viewPager = (ViewPager) this.findViewById(R.id.view_pager); viewPager.setAdapter(adapter); viewPager.setOffscreenPageLimit(views.size()); viewPager.addOnPageChangeListener(changeListener); }
private class TextureListener implements TextureView.SurfaceTextureListener { private MediaPlayer mediaPlayer; private int index;
public TextureListener(MediaPlayer mediaPlayer, int index) { this.mediaPlayer = mediaPlayer; this.index = index; }
@Override public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { Surface surface = new Surface(surfaceTexture); mediaPlayer.setSurface(surface); mediaPlayer.prepareAsync(); mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { @Override public void onPrepared(MediaPlayer mp) { if (index == 1) { mp.start(); } else { mp.seekTo(0); } } }); }
@Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { }
@Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { if (mediaPlayer != null) { mediaPlayer.stop(); mediaPlayer.release(); mediaPlayer = null; } return true; }
@Override public void onSurfaceTextureUpdated(SurfaceTexture surface) {
} } }
|
guide.xml
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
| <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" >
<android.support.v4.view.ViewPager android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="match_parent" >
</android.support.v4.view.ViewPager>
<LinearLayout android:id="@+id/indicate_layout" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="@dimen/guide_indicate_mar_bottom" android:layout_marginTop="@dimen/guide_indicate_mar_top" android:gravity="center" android:orientation="horizontal" >
<ImageView android:id="@+id/p1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/page_now" android:contentDescription="@string/app_name" />
<ImageView android:id="@+id/p2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:background="@drawable/page" android:contentDescription="@string/app_name" />
<ImageView android:id="@+id/p3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:background="@drawable/page" android:contentDescription="@string/app_name" />
<ImageView android:id="@+id/p4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:background="@drawable/page" android:contentDescription="@string/app_name" /> </LinearLayout>
</RelativeLayout>
|
guide_one.xml、guide_two.xml、guide_three.xml、guide_four.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?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" >
<TextureView android:id="@+id/texture_view" android:layout_width="match_parent" android:layout_height="match_parent" />
</RelativeLayout>
|
page.xml
1 2 3 4 5 6 7 8 9 10
| <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <size android:height="8dp" android:width="8dp"/> <stroke android:color="#c9c9c9" android:width="2px"/> </shape>
|
page_now.xml
1 2 3 4 5 6 7 8
| <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <size android:height="8dp" android:width="8dp"/> <solid android:color="#ff6600"/> </shape>
|
dimens.xml
1 2 3 4 5
| <dimen name="guide_big_font_size">25.7dp</dimen> <dimen name="guide_normal_font_size">17.9dp</dimen> <dimen name="guide_indicate_mar_top">22dp</dimen> <dimen name="guide_indicate_mar_bottom">50dp</dimen> <dimen name="guide_indicate_text_bottom">180dp</dimen>
|
成果
本教程效果

闲鱼App效果
