手把手教你实现慕课网引导页效果
手把手教你实现慕课网引导页效果
前言
相信程序员大多用过或者听过慕课网这个网站,今天我们要讲的是慕课网Android客户端引导页效果的模仿实现。
首先,让我们看一下慕课网Android客户端的引导页是什么样的。
正文
1.分析
看上面那个GIF图,分析可知。首先是一个显示慕课网LOGO的Activity,然后跳转到了今天我们要讲的引导效果的Activity(命名为GuideActivity)。
- 根据它滑动的效果,可猜测是由ViewPager实现的。
- ViewPager中有三个播放着动画或者视频的Fragment。
- 底部有标识滑动到哪一页的小圆点。
- 当滑动到第三页时,出现登录、注册按钮。
2.验证
下面来验证上一步分析的结论。
在Android Studio中,有个工具叫做Android Device Monitor,我们能用它查看Android设备的截图的View层级。
如何找到Android Device Monitor?
在Android Studio中,依次打开Tools,Android,Android Device Monitor即可。
上图证明了我的分析大部分是正确的,VideoView证明播放的是视频而不是动画。
为了进一步证明播放的是视频,我们将慕课网Android客户端的apk文件解压。
哈哈,果然播放的是放置在apk文件本地的mp4视频。
编码
经过之前对慕课网导航效果的分析和验证,现在我们就来编码实现这一效果。
通过接下来的研究分析,我们可以学习到:
- 1.ViewPager+Fragment+FragmentPagerAdapter实现滑动导航页面
- 2.VideoView(播放视频)的基本使用
- 3.Activity转间动画
实现
1.首先,我们先实现显示Logo的Activity(简称MainActivity),这个比较简单。看上面的GIF图,MainActivity显示一会时间后向左滑动消失,GuideActivity从右边滑动出来。那就讲讲其中的Activity转间动画(Transition animation)。
一般使用下面的方式来实现:
public void overridePendingTransition (int enterAnim, int exitAnim)
//通常我们一般在startActivity(Intent)或finish()方法中调用此方法
//其中enterAnim表示新Activity的进入动画,exitAnim表示旧Activity的退出动画
//从Android4.1后,可以通过ActivityOptions将转间动画作为Bundle传入startActivity(Activity,Bundle)来实现
来看代码实现:
public class MainActivity extends AppCompatActivity {
private Handler mHandler = new Handler();
private Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
//使用handler定时跳转到GuideActivity
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(mContext,GuideActivity.class);
//设置跳转动画
//第一种方式实现
startActivity(new Intent(mContext,GuideActivity.class));
finish();
overridePendingTransition(R.anim.slide_in_right,R.anim.slide_out_left);
//第二种方式实现
// ActivityOptionsCompat optionsCompat =
// ActivityOptionsCompat.makeCustomAnimation(mContext,R.anim.slide_in_right,
// R.anim.slide_out_left);
// ActivityCompat.startActivity(MainActivity.this,intent,optionsCompat.toBundle());
// startActivity(new Intent(mContext,GuideActivity.class));
// finish();
}
}, 500);
}
}
运行效果:
2.接下来,实现滑动导航效果。相信现在讲这个的文章非常多,今天就当再复习一遍。呵呵,温故而知新。
我们使用ViewPager+Fragment+FragmentPagerAdapter来实现。
一般有三个步骤:
- 1.实现Fragment的子类
- 2.实现FragmentPagerAdapter适配器类
- 3.将第2步生成的Adapter设置为ViewPager的适配器,并使用addOnPageChangeListener方法对ViewPager的Page更改的事件进行监听来实现标识第几页的功能。
在这里,我们只需要创建一种Fragment即可。通过index参数来设置显示不同的图片和播放不同的视频。
下面是GuideFragment的代码:
/**
* Created by JohnTsai on 16/3/3.
*/
public class GuideFragment extends Fragment {
public static String TAG = GuideFragment.class.getSimpleName();
public static String KEY = "index";
private VideoView mVideoView;
private ImageView mImageView;
private int index;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View guideView = inflater.inflate(R.layout.fragment_guide,container,false);
mVideoView = (VideoView) guideView.findViewById(R.id.videoView);
mImageView = (ImageView) guideView.findViewById(R.id.wordView);
switch (index){
case 0:
mImageView.setImageResource(R.mipmap.guide_1);
break;
case 1:
mImageView.setImageResource(R.mipmap.guide_2);
break;
case 2:
mImageView.setImageResource(R.mipmap.guide_3);
break;
}
Log.d(TAG,"onCreateView");
return guideView;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle bundle = getArguments();
index = bundle.getInt(KEY);
Log.d(TAG,"onCreate");
}
public static GuideFragment newInstance(int index) {
Bundle args = new Bundle();
args.putInt(KEY, index);
GuideFragment fragment = new GuideFragment();
fragment.setArguments(args);
return fragment;
}
}
以及GuideActivity的代码
/**
* Created by JohnTsai on 16/3/3.
*/
public class GuideActivity extends AppCompatActivity{
private ImageView mDotOne,mDotTwo,mDotThree;
private LinearLayout mLayoutBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_guide);
//初始化控件
ViewPager mViewPager = (ViewPager) findViewById(R.id.viewPager);
mDotOne = (ImageView) findViewById(R.id.guide_dot1);
mDotTwo = (ImageView) findViewById(R.id.guide_dot2);
mDotThree = (ImageView) findViewById(R.id.guide_dot3);
mLayoutBtn = (LinearLayout) findViewById(R.id.btn_layout);
GuideFragment guideFragment1 = GuideFragment.newInstance(0);
GuideFragment guideFragment2 = GuideFragment.newInstance(1);
GuideFragment guideFragment3 = GuideFragment.newInstance(2);
List<? extends Fragment> mFragmentList = Arrays.asList(guideFragment1, guideFragment2, guideFragment3);
GuideFragmentPagerAdapter mAdapter = new GuideFragmentPagerAdapter(getSupportFragmentManager(), mFragmentList);
mViewPager.setAdapter(mAdapter);
//设置小圆点
mDotOne.setImageResource(R.mipmap.guide_dot_checked);
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
mDotOne.setImageResource(R.mipmap.guide_dot_normal);
mDotTwo.setImageResource(R.mipmap.guide_dot_normal);
mDotThree.setImageResource(R.mipmap.guide_dot_normal);
switch (position) {
case 0:
mDotOne.setImageResource(R.mipmap.guide_dot_checked);
break;
case 1:
mDotTwo.setImageResource(R.mipmap.guide_dot_checked);
break;
case 2:
mDotThree.setImageResource(R.mipmap.guide_dot_checked);
break;
}
//设置当滑动到最后一页时,显示两个按钮
mLayoutBtn.setVisibility(position==2? View.VISIBLE:View.GONE);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
public static class GuideFragmentPagerAdapter extends FragmentPagerAdapter{
private List <? extends Fragment> fragments;
public GuideFragmentPagerAdapter(FragmentManager fm,List<? extends Fragment> fragments) {
super(fm);
this.fragments = fragments;
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
}
}
- 最后我们学习VideoView的基本使用。
参考资料:
VideoView继承自SurfaceView,并实现了MediaPlayerControl接口。用于播放视频文件。
几个主要的方法:
- void setVideoPath(String path):以文件路径的方式设置VideoView播放的视频源。
- void setVideoURI(Uri uri):以Uri的方式设置VideoView播放的视频源,可以是网络Uri或本地Uri。
- void start():开始播放。
- void stopPlayback():停止播放。
- setMediaController(MediaController controller):设置MediaController控制器。
- setOnCompletionListener(MediaPlayer.onCompletionListener l):监听播放完成的事件。
- setOnErrorListener(MediaPlayer.OnErrorListener l):监听播放发生错误时候的事件。
- setOnPreparedListener(MediaPlayer.OnPreparedListener l)::监听视频装载完成的事件。
下面继续我们的慕课网视频引导效果的编码:
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View guideView = inflater.inflate(R.layout.fragment_guide, container, false);
mVideoView = (VideoView) guideView.findViewById(R.id.videoView);
//获取放置在raw文件夹得视频的Uri
Uri uri1 =Uri.parse("android.resource://" + getActivity().getPackageName() + "/" + R.raw.guide_1);
Uri uri2 =Uri.parse("android.resource://" + getActivity().getPackageName() + "/" + R.raw.guide_2);
Uri uri3 =Uri.parse("android.resource://" + getActivity().getPackageName() + "/" + R.raw.guide_3);
Log.d(TAG,"1:"+uri1
+"\n2:"+uri2
+"\n3:"+uri3);
mImageView = (ImageView) guideView.findViewById(R.id.wordView);
switch (index){
case 0:
mImageView.setImageResource(R.mipmap.guide_1);
mVideoView.setVideoURI(uri1);
break;
case 1:
mImageView.setImageResource(R.mipmap.guide_2);
mVideoView.setVideoURI(uri2);
break;
case 2:
mImageView.setImageResource(R.mipmap.guide_3);
mVideoView.setVideoURI(uri3);
break;
}
mVideoView.start();
//对VideoView的完成播放事件进行监听,完成后再次播放以达到循环播放的效果
mVideoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
mVideoView.start();
}
});
Log.d(TAG,"onCreateView");
return guideView;
}
运行效果:
看运行效果,发现一个问题,视频的比例和手机的比例不一致导致右边出现蓝色长条。
这里我们继承VideoView来自定义设置它的宽和高来解决这个问题:
代码如下:
/**
* Created by JohnTsai on 16/3/3.
*/
public class CustomWidthHeightVideoView extends VideoView{
private int mWidth = 0;
private int mHeight = 0;
public CustomWidthHeightVideoView(Context context) {
super(context);
}
public CustomWidthHeightVideoView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomWidthHeightVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setWidthHeight(int width,int height){
mWidth = width;
mHeight = height;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if(mWidth>0&&mHeight>0)
setMeasuredDimension(mWidth,mHeight);
else
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
}
}
在onMeasure方法中,将我们设置的宽和高通过setMeasuredDimension来自定义VideoView的宽和高。
//设置全屏播放
DisplayMetrics metrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
mVideoView.setWidthHeight(metrics.widthPixels,metrics.heightPixels);
运行效果:
好,慕课网引导效果的编码实现就到这。如果喜欢,请推荐一下。谢谢。