一.概述
在我们下载了一个App第一次进入的时候,或者是已有App更新之后第一次进入时,一般都会有一个引导界面,这个界面用于展示本App的基本功能,或者是更新之后版本的重大改进;效果图如下:
这里就简单的记录以下这个功能的实现;
二.实现过程
1.布局页面分析
布局代码如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v4.view.ViewPager
android:id="@+id/vp_guide"
android:layout_width="match_parent"
android:layout_height="match_parent">
</android.support.v4.view.ViewPager>
<Button
android:id="@+id/btn_start"
android:textColor="@color/selector_txt_guide"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:background="@drawable/selector_btn_guide"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="60dp"
android:visibility="invisible"
android:text="开始体验"/>
<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>
注意:
对于布局文件中Button的Visibility设置为false,不可见.在引导页的最后一页的时候,再将其设置为可见.
2.业务逻辑
难点:观察效果图注意到底部的小红点,当引导图滑动时,底部的小红点是跟随着滑动的.
2.1对小红点随页面移动而移动的分析如下:
ViewPager有一个监听事件: ViewPager.setOnPageChangeListener,
其中有一个重写的方法是这样的:
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
其中positionOffset参数是VIewPager中页面移动的百分比.
这样我们就可以得到一个思路:
小红点移动的距离(小红点的leftMargin值)=两小红点间距离*页面移动百分比(positionOffset)+当前位置的距离
2.2接下来分析两小红点之间的距离
从分析图片中可以看到,两小红点之间的距离(mPointDis)=第二个红点的left值-第一个红点的left值.
mPointDis = ll_container.getChildAt(1).getLeft() - ll_container.getChildAt(0).getLeft();
注意:
mPointDis 值必须要在View的位置确定了之后才可以得到(View的绘制有如下几个步骤:measures->layout(确定位置)->draw),所以我们要对小红点设置监听,当其位置确定了之后才拿mPointDis值.
//监听layout方法结束,位置确定好了之后,再计算值
iv_red_point.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//layout方法执行结束的回调
//移除监听避免重复回调
iv_red_point.getViewTreeObserver().removeOnGlobalLayoutListener(this);
/*计算两个圆点的距离 第二个圆点的left值-第一个圆点的left值
* measures->layout(确定位置)->draw Acivity的onCreate方法结束后才会走这个流程
* */
mPointDis = ll_container.getChildAt(1).getLeft() - ll_container.getChildAt(0).getLeft();
Log.d("------>" + "计算两个圆点的距离", mPointDis + "");
}
});
2.3监听VIewPager的滑动,小红点随之而动
小红点的移动也就是在底部线性布局中的marginLeft值的改变.
由上诉分析得来的公式:
小红点移动的距离(小红点的leftMargin值)=两小红点间距离*页面移动百分比(positionOffset)+当前位置的距离
可以得到如下代码:
int leftMargin = (int) (mPointDis * positionOffset) + position * mPointDis;
然后将leftMargin的值从新设置给小红点的参数即可:
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams)
iv_red_point.getLayoutParams(); //拿到小红点的布局参数
layoutParams.leftMargin = leftMargin; //修改小红点的布局参数
iv_red_point.setLayoutParams(layoutParams); //重新设置小红点的布局参数
2.4业务逻辑完整代码
package com.example.lenovo.ruizhiapp;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import com.example.lenovo.utils.PrefUtils;
import java.util.ArrayList;
/**
* 新手引导界面
* Created by lenovo on 2016/7/7.
*/
public class GuideActivity extends Activity {
private ViewPager vp_guide;
//引导页图片ID数组
private int[] mImageIds = new int[]{R.drawable.guide_1,
R.drawable.guide_2,
R.drawable.guide_3};
//imageView的集合
private ArrayList<ImageView> mImageViewList;
private LinearLayout ll_container;
private ImageView iv_red_point;
private int mPointDis;
private Button btn_start;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_guid);
initViews();//初始化界面控件
initData();//先初始化数据,然后再设置数据
vp_guide.setAdapter(new GuideAdapter());//设置数据
}
private void initViews() {
vp_guide = (ViewPager) findViewById(R.id.vp_guide);
ll_container = (LinearLayout) findViewById(R.id.ll_container);
iv_red_point = (ImageView) findViewById(R.id.iv_red_point);
btn_start = (Button) findViewById(R.id.btn_start);
//对ViewPager页面滑动的监听
vp_guide.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override //页面滑动过程中的回调方法
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
Log.d("------>" + "当前位置:", position + "");
Log.d("------>" + "偏移百分比:", positionOffset + "");
//更新小红点的距离=移动百分比*两个原点间的间距+当前位置
int leftMargin = (int) (mPointDis * positionOffset) + position * mPointDis;
RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams)
iv_red_point.getLayoutParams(); //拿到小红点的布局参数
layoutParams.leftMargin = leftMargin; //修改小红点的布局参数
iv_red_point.setLayoutParams(layoutParams); //重新设置小红点的布局参数
}
@Override //某个页面被选中
public void onPageSelected(int position) {
//最后一个页面才显示"开始体验的按钮"
if (position==mImageViewList.size()-1){ //灵活代码
//只在引导页面的最后一页显示按钮
btn_start.setVisibility(View.VISIBLE);
}else{
//其余引导页面不显示按钮
btn_start.setVisibility(View.INVISIBLE);
}
}
@Override //页面状态发生变化 (滑-->不滑)
public void onPageScrollStateChanged(int state) {
}
});
//监听layout方法结束,位置确定好了之后,再计算值
iv_red_point.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
//layout方法执行结束的回调
//移除监听避免重复回调
iv_red_point.getViewTreeObserver().removeOnGlobalLayoutListener(this);
/*计算两个圆点的距离 第二个圆点的left值-第一个圆点的left值
* measures->layout(确定位置)->draw Acivity的onCreate方法结束后才会走这个流程
* */
mPointDis = ll_container.getChildAt(1).getLeft() - ll_container.getChildAt(0).getLeft();
Log.d("------>" + "计算两个圆点的距离", mPointDis + "");
}
});
btn_start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(getApplicationContext(),MainActivity.class));
//表示已经不是第一次进入,跟新SP
PrefUtils.setBoolean(getApplicationContext(),ConstantValues.IS_FIRST_ENTER,false);
//结束引导界面
finish();
}
});
}
//初始化数据
private void initData() {
mImageViewList = new ArrayList<ImageView>();
for (int i = 0; i < mImageIds.length; i++) {
ImageView imageView = new ImageView(this);
imageView.setBackgroundResource(mImageIds[i]);//通过设置背景,可以让宽高填充布局
mImageViewList.add(imageView);
//初始化底部小圆点
ImageView point = new ImageView(this);
point.setImageResource(R.drawable.shape_point_gray);//设置图片<shape>
//初始化布局参数,宽高包裹内容,父控件是谁就申明谁的布局参数
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
if (i > 0) {
//从第二个点开始设置原点之间的间隔
layoutParams.leftMargin = 10;
}
point.setLayoutParams(layoutParams);//设置布局参数
ll_container.addView(point);//给容器添加原点
}
}
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 imageView = mImageViewList.get(position);
container.addView(imageView);
return imageView;
}
@Override //销毁Item
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
}
}