引导指示界面是个什么鬼东西?一张图即明了:
其实就是给刚安装你的软件的用户指示如何使用的一个蒙层,具有以下特点:
- 运行中显示,用户使用到特定的环境、特定的界面时才显示(这里请区别下第一次安装时刚启动时好几个滑动界面的“引导欢迎页”)
- 覆盖于原来界面之上,并且半透明,带有指示文字或图标
- 软件生命周期内只出现一次(阅后即焚)
- 不影响原来软件的操作
这一个小小的界面实现起来有一些小的诀窍,使用了ViewStub和SharePrefrence,下面一步步展示如何实现。
写布局
第一步就是写出这个展示出来效果的布局文件,这里例子很简单,就是一个全屏的.9半透明黑色图加上一个白色文字图片,图片居中。这一步直接给出代码。
根据你的情况自己实现自己所需要的布局。
layout\guide_root_slide_left_right.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:focusable="true" android:clickable="true" android:id="@+id/guide_root" android:background="@drawable/bg_dark" > <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/img_slide" android:layout_centerInParent="true" /> </RelativeLayout>
使用ViewStub
为什么使用ViewStub?首先看下我们的需求,第一是这个页面是动态根据条件呈现的,第二是它并不重要出现过一次后就不再需了,因为希望它不要占多少内存。以此来看,ViewStub就是实现这个引导指示层的最佳控件。
对于在运行时决定可见性的界面我们通常的做法是:
- 在布局文件中写好改view或者viewgroup,设置visibility为gone
- 在代码中根据运行条件将上述view或者viewgroup的可见性设为visible
这样子没什么不可,但是带来的问题是以后每次进这个界面,系统都会为这个view分配内存并且实例化,从而拖慢了速度,耗费资源(事实上我们只想让它显示仅仅一次就不要了)。而ViewStub呢?它是一个轻量级的View,类似于一个占位符。给他指定一个布局,它默认并不会inflate出来,直到你想让它显示出来的时候才会inflate你所指定的布局从而延迟分配了内存(类似于延迟加载)。
由于我们的半透明指示层是要蒙住全屏的,而且才原来的布局界面之上(布局树里面的控件是写在上面位置的先展示,写在下面位置的后展示),所以我们的原来的根布局需要为RelativeLayout,同时在根布局的最下面写上ViewStub:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/root" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!-- 原来的Activity内容--> ... <ViewStub android:id="@+id/guide_root_slide" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerInParent="true" android:layout="@layout/guide_root_slide_left_right"/> </RelativeLayout>
这里ViewStub可以当作是一个普通的View来加入到布局文件里面,同时给他的layout参数执行我们实际想要inflate出来的布局。等于给占位符设置一个“目标”。
增加逻辑代码
在你的activity代码中,获得这个ViewStub的引用:
ViewStub stubGuideSlide; // onCreate stubGuideSlide = (ViewStub) findViewById(R.id.guide_root_slide);
然后写一个方法,在你的运行判断逻辑(比如某个按钮的点击回调)里面执行这个方法,这里写上伪代码:
// some on Click listener ... showGuideSlide(); ... private void showGuideSlide(){ if(如果第一次到这里) 展示GuideSlide引导指示层 else 不展示 }
如何判断是否第一次走到这里呢?我们可以用SharePreference,我们可以随便定义属于这张引导指示层的key,然后判断他是否存在。如果不存在表示第一次进入,进入后我们把这个key对应的SharePeference随便设一个值,下回就不会走到这里了:(showGuideSlide的完整代码如下)
private void showGuideSlide() { if (SPUtils.contains(mContext, Constant.GuideVisibilityKey.ROOT_FRAGMENT_SLIDE)) { return; } try { final View guideSlideView = stubGuideSlide.inflate(); RelativeLayout rl = (RelativeLayout) guideSlideView.findViewById(R.id.guide_root); if (rl != null) { rl.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { guideSlideView.setVisibility(View.GONE); } }); } } catch (Exception e) { e.printStackTrace(); } SPUtils.put(mContext, Constant.GuideVisibilityKey.ROOT_FRAGMENT_SLIDE, true); }
上述代码需要解释几个地方:
- SPUtils是我自己写的一个SharePreference的帮助类,方便设值和检查是否含有某个key, Constant.GuideVisibilityKey.ROOT_FRAGMENT_SLIDE是这个key的名字
- stubGuideSlide.inflate()即通知ViewStub将指定的guide_root_slide_left_right.xml布局文件给inflate出来,从而展示出引导指示层
- 我们需要让用户点击这个指示层的任意位置后销毁指示层,所以我们先获取布局的根viewgroup即rl,然后给他设置点击事件,点击的时候把ViewStub给Gone掉,这个时候ViewStub就销毁了。这里不能直接给ViewStub设置点击事件,这样子是没有效果的,您可以尝试一下。