* 很多App第一次打开时都有一个新手引导页,这个引导页看起来很简单,但是做起来还是需要用刀不少知识的 *
ListView是非常重要的!!!!
先把布局文件写好:
<?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.support.v4.view.ViewPager
android:id="@+id/vp_guide"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开始体验"
android:id="@+id/btn_start"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="60dp" />
<LinearLayout
android:id="@+id/ll_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="30dp"
android:orientation="horizontal"></LinearLayout>
</RelativeLayout>
然后在GuideActivity中初始化数据.
(去掉标题:requestWindowFeature(Window.FEATURE_NO_TITLE);//去掉标题,这个恶搞方法必须在setcontentview之前调用)
private ViewPager mViewPager;
//怎样维护这个对象数组?
private ArrayList<ImageView> mImageViewList;//imageview的集合
//引导页图片id数组
private int[] mImageIds = new int[]{R.mipmap.guide_1,R.mipmap.guide_2,R.mipmap.guide_3};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);//去掉标题,这个恶搞方法必须在setcontentview之前调用
setContentView(R.layout.guide_activity);
mViewPager = (ViewPager) findViewById(R.id.vp_guide);
initData();
mViewPager.setAdapter(new GuideAdapter());//设置数据
}
//初始化ImageViewer对象.初始化数据
private void initData(){
mImageViewList = new ArrayList<ImageView>();
for (int i=0;i<mImageIds.length;i++){
ImageView view = new ImageView(this);
view.setBackgroundResource(mImageIds[i]);//通过设置背景,可以让宽高填充布局
//view.setImageResource(resId);这个方法不一定能填充满
mImageViewList.add(view);
}
}
然后开始设置数据
class GuideAdapter extends PagerAdapter{
@Override
//返回item的个数
public int getCount() {
return mImageViewList.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
//初始化item的布局
public Object instantiateItem(ViewGroup container, int position) {
ImageView view = mImageViewList.get(position);//获取这个ImageViewer,之前通过集合已经把要使用的创建好了,用的时候直接拿过来用
container.addView(view);//把这个对象塞给container容器
return view;
}
@Override
//销毁item布局
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View)object);
}
}
这时就完成了一个简单的引导.注意一定要先初始化数据!!!
接下来是页面指示器小圆点的完成,这个功能有助于用户体验.
这三个小圆点属于自定义控件,先初始化线性布局.
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#cccccc"></solid>
<size android:height="10dp" android:width="10dp"></size>
</shape>
因为引导页需要一个小红点覆盖原来的小灰点,所以还要有个xml文件描述小红点.如下代码:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#ff0000"></solid>
<size android:height="10dp" android:width="10dp"></size>
</shape>
根据布局文件来看,需要有个相对布局包裹着一个线性布局.所以修改布局文件的代码如下:黄色部分为修改的代码
<?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.support.v4.view.ViewPager
android:id="@+id/vp_guide"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开始体验"
android:id="@+id/btn_start"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="60dp" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="30dp">
<LinearLayout
android:id="@+id/ll_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"></LinearLayout>
<ImageView
android:id="@+id/iv_red_point"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/shape_point_red"/>
</RelativeLayout>
</RelativeLayout>
然后根据需求开始设置小红点,这里用到了自定义控件.就是关于布局绘制的一个流程.先测量,然后确定位置(layout执行完),最后再进行一个绘制.这三步之后才能把这个控件画在界面上.
计算两个小红点之间的距离
移动距离=第二个圆点left的值-第一个圆点left的值.
还要设置页面滑动的一个监听:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);//去掉标题,这个恶搞方法必须在setcontentview之前调用
setContentView(R.layout.guide_activity);
mViewPager = (ViewPager) findViewById(R.id.vp_guide);
llContainer = (LinearLayout) findViewById(R.id.ll_container);
ivRedPoint = (ImageView)findViewById(R.id.iv_red_point);
initData();
mViewPager.setAdapter(new GuideAdapter());//设置数据
//设置页面滑动的一个监听
mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
//当页面滑动过程中的回调
System.out.println("当前位置:"+position+";移动偏移百分百:"+positionOffset);
//更新小红点的距离
}
@Override
public void onPageSelected(int position) {
//某个页面被选中
}
@Override
public void onPageScrollStateChanged(int state) {
//页面状态发生变化的回调,暂时不用
}
});
//计算两个小红点之间的距离
//移动距离=第二个圆点left的值-第一个圆点left的值
//measure->layout->draw(activity的 oncreate方法执行结束之后才走此流程,这就解释了为什么打印距离为0)
/* mPointDis = llContainer.getChildAt(1).getLeft()-llContainer.getChildAt(0).getLeft();
System.out.println("圆点距离:"+mPointDis); //因为打印距离是0,所以这个方法不能直接这样写*/
//监听layout方法结束的事件.结束之后位置确定好之后再获取圆点间距
//视图树?
ivRedPoint.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
ivRedPoint.getViewTreeObserver().removeOnGlobalLayoutListener(this);//避免重复回调
//layout方法执行结束的回调,这时可以使用这个方法
mPointDis = llContainer.getChildAt(1).getLeft()-llContainer.getChildAt(0).getLeft();
System.out.println("圆点距离:"+mPointDis);
}
});
}
注意:根据图片可知要想得到小红点的距离,就要拿到移动间距.
//measure->layout->draw(activity的 oncreate方法执行结束之后才走此流程,这就解释了为什么打印距离为0)
直接这样使用是不行的.监听layout方法结束的事件.结束之后位置确定好之后再获取圆点间距.才可以使用
(这里讲了一个视图树的概念,可以看看.)
注意回调的概念.不要不理解啊
最后怎么更新小红点的距离呢?
//更新小红点的距离
int dis = (int) (mPointDis * positionOffset);
改变小红点的布局参数就可以了.就相当于更新marginleft的值
System.out.println("当前位置:"+position+";移动偏移百分百:"+positionOffset);
//更新小红点的距离
int leftMargin = (int) (mPointDis * positionOffset + position * mPointDis);//计算小红点当前的左边距
RelativeLayout.LayoutParams params =(RelativeLayout.LayoutParams) ivRedPoint.getLayoutParams();//当前的布局参数
params.leftMargin = leftMargin;//修改左边距
ivRedPoint.setLayoutParams(params);//重新设置布局参数
这里就完成了基本的新手引导.具体代码可以在这里下载https://github.com/ZoeSj/SmartXinHua.git