Viewpager在项目中随处可见,通常用来做引导页、图片轮翻,或结合Fragment搭建Tab+Fragment+ViewPager项目整体框架。简单总结了一下ViewPager的常见使用方式,由于比较简单直接上代码!
引导页一般分为两种,左右滑动引导,上下滑动引导,通过ViewPager轻松实现左右滑动的引导页,并且实现引导点跟随滑动而移动的引导页。
效果图:
activity_main.xml
<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/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</android.support.v4.view.ViewPager>
<FrameLayout
android:id="@+id/layout_frame"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_alignParentBottom="true" >
<LinearLayout
android:id="@+id/layout_point"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="horizontal" >
</LinearLayout>
</FrameLayout>
<Button
android:id="@+id/btn_go"
android:layout_width="wrap_content"
android:layout_height="35dp"
android:layout_above="@id/layout_frame"
android:layout_centerHorizontal="true"
android:layout_marginBottom="30dp"
android:background="@drawable/shape_white_blue_4"
android:padding="5dp"
android:text="立即进入"
android:textColor="@android:color/white"
android:textSize="14sp"
android:visibility="gone" />
</RelativeLayout>
引入v4包中的ViewPager,添加引导点的布局,其中FrameLayout包裹LinearLayout是因为,选中点(即亮点)需要跟随移动,当某个Pager被选中时选中点将该Pager之前的未选中点(即暗点)进行了遮挡,所以需要上下层关系,在布局中体现包裹关系。
MainActivity.java
/**
* Created by magic on 2016年9月28日.引导点跟随移动的引导页
*/
public class MainActivity extends Activity implements OnPageChangeListener {
ViewPager viewPager;
FrameLayout layout_frame;
LinearLayout layout_point;
// 带颜色的引导点
ImageView img_colorPoint;
Button btn_go;
List<View> list_view = new ArrayList<View>();
List<ImageView> list_pointView = new ArrayList<ImageView>();
//ViewPager 适配器
ViewPagerAdapter adapter;
// 两点之间间距
int pointSpacing;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initGuideView();
}
/**
* 初始化引导页相关
*/
private void initGuideView() {
//添加展示的View
LayoutInflater inflater = LayoutInflater.from(this);
list_view.add(inflater.inflate(R.layout.guide_one, null));
list_view.add(inflater.inflate(R.layout.guide_two, null));
list_view.add(inflater.inflate(R.layout.guide_three, null));
adapter = new ViewPagerAdapter(list_view);
viewPager.setAdapter(adapter);
//添加引导点
for (int i = 0; i < list_view.size(); i++) {
ImageView point = new ImageView(this);
//设置暗点
point.setBackgroundResource(R.drawable.img_guide_point);
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
lp.setMargins(10, 0, 10, 0);
point.setLayoutParams(lp);
list_pointView.add(point);
layout_point.addView(point);
}
//添加选中的引导点
img_colorPoint = new ImageView(MainActivity.this);
//设置亮点
img_colorPoint.setBackgroundResource(R.drawable.img_guide_point_selected);
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
img_colorPoint.setLayoutParams(lp);
layout_frame.addView(img_colorPoint);
layout_frame.post(new Runnable() {
@Override
public void run() {
//待布局绘制完毕 设置选中白点 的初始化位置
FrameLayout.LayoutParams l = (FrameLayout.LayoutParams) img_colorPoint.getLayoutParams();
l.leftMargin = list_pointView.get(0).getLeft();
img_colorPoint.setLayoutParams(l);
}
});
layout_point.post(new Runnable() {
@Override
public void run() {
// 获取引导的之间的间隔
pointSpacing = layout_point.getChildAt(1).getLeft()- layout_point.getChildAt(0).getLeft();
}
});
}
private void initView() {
viewPager = (ViewPager) findViewById(R.id.viewPager);
layout_frame = (FrameLayout) findViewById(R.id.layout_frame);
layout_point = (LinearLayout) findViewById(R.id.layout_point);
btn_go = (Button) findViewById(R.id.btn_go);
viewPager.setOnPageChangeListener(this);
btn_go.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this,TwoActivity.class));
}
});
}
@Override
public void onPageScrollStateChanged(int arg0) {
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
FrameLayout.LayoutParams l = (FrameLayout.LayoutParams) img_colorPoint
.getLayoutParams();
//根据滑动动态设置左外边距
l.leftMargin = (int) (list_pointView.get(arg0).getLeft() + pointSpacing
* arg1);
img_colorPoint.setLayoutParams(l);
}
@Override
public void onPageSelected(int arg0) {
//根据Pager位置设置btn的显示
if (arg0 == list_view.size() - 1) {
btn_go.setVisibility(View.VISIBLE);
} else {
btn_go.setVisibility(View.GONE);
}
}
}
ViewPager的onPageScrolled(int arg0, float arg1, int arg2)回调在滑动过程中会一直调用,参数arg0 :当前页面,点击滑动的页面;arg1:当前页面偏移的百分比;arg2:当前页面偏移的像素位置 。有个这个方法就可以计算小白点的偏移位置了,两引导点之间间距相对两个Pager之间的间距即ViewPager的“宽度”,因此偏移百分比是相同的。
ViewPagerAdapter.java
/**
* Created by magic on 2016年9月28日.ViewPager适配器
*/
public class ViewPagerAdapter extends PagerAdapter {
private List<View> views;
public ViewPagerAdapter(List<View> list) {
super();
this.views = list;
}
// 从当前container中删除指定位置(position)的View
@Override
public void destroyItem(View container, int position, Object object) {
((ViewPager) container).removeView(views.get(position));
}
// 第一:将当前视图添加到container中,第二:返回当前View
@Override
public Object instantiateItem(View container, int position) {
// 将当前视图添加到container中
((ViewPager) container).addView(views.get(position));
// 设置当前视图的唯一标示Key
return views.get(position);
}
// getCount():返回要滑动的View的个数
@Override
public int getCount() {
return views.size();
}
// 通过标识arg1找到view
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0 == arg1;
}
}
图片轮翻和引导页代码几乎是一致的,只是添加了定时器,用于自动切换界面。
效果图:
activity_two.xml
<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" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="180dp" >
<android.support.v4.view.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</android.support.v4.view.ViewPager>
<FrameLayout
android:id="@+id/layout_frame"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_alignParentBottom="true" >
<LinearLayout
android:id="@+id/layout_point"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="horizontal" >
</LinearLayout>
</FrameLayout>
</RelativeLayout>
</RelativeLayout>
TwoActivity.java
/**
* Created by magic on 2016年9月28日.图片轮番
*/
public class TwoActivity extends Activity implements OnPageChangeListener {
ViewPager viewPager;
FrameLayout layout_frame;
LinearLayout layout_point;
// 带颜色的引导点
ImageView img_colorPoint;
List<View> list_view = new ArrayList<View>();
List<ImageView> list_pointView = new ArrayList<ImageView>();
ViewPagerAdapter adapter;
// 两点之间间距
int pointSpacing;
//当前ViewPager选中位置
int pageSelect = 0;
//定时器
Timer timer;
Task task;
@SuppressLint("HandlerLeak")
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
viewPager.setCurrentItem(pageSelect);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_two);
initView();
initGuideView();
timer = new Timer();
task = new Task();
//设置定时延迟1000开始 时间周期1500
timer.schedule(task, 1000, 1500);
}
private void initGuideView() {
LayoutInflater inflater = LayoutInflater.from(this);
list_view.add(inflater.inflate(R.layout.guide_one, null));
list_view.add(inflater.inflate(R.layout.guide_two, null));
list_view.add(inflater.inflate(R.layout.guide_three, null));
adapter = new ViewPagerAdapter(list_view);
viewPager.setAdapter(adapter);
for (View view : list_view) {
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(TwoActivity.this,ThreeActivity.class));
}
});
}
for (int i = 0; i < list_view.size(); i++) {
ImageView point = new ImageView(this);
point.setBackgroundResource(R.drawable.img_guide_point);
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
lp.setMargins(10, 0, 10, 0);
point.setLayoutParams(lp);
list_pointView.add(point);
layout_point.addView(point);
}
img_colorPoint = new ImageView(TwoActivity.this);
img_colorPoint
.setBackgroundResource(R.drawable.img_guide_point_selected);
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT);
img_colorPoint.setLayoutParams(lp);
layout_frame.addView(img_colorPoint);
layout_frame.post(new Runnable() {
@Override
public void run() {
FrameLayout.LayoutParams l = (FrameLayout.LayoutParams) img_colorPoint
.getLayoutParams();
l.leftMargin = list_pointView.get(0).getLeft();
img_colorPoint.setLayoutParams(l);
}
});
layout_point.post(new Runnable() {
@Override
public void run() {
// 获取引导的之间的间隔
pointSpacing = layout_point.getChildAt(1).getLeft()
- layout_point.getChildAt(0).getLeft();
}
});
}
private void initView() {
viewPager = (ViewPager) findViewById(R.id.viewPager);
layout_frame = (FrameLayout) findViewById(R.id.layout_frame);
layout_point = (LinearLayout) findViewById(R.id.layout_point);
viewPager.setOnPageChangeListener(this);
}
@Override
public void onPageScrollStateChanged(int arg0) {
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
System.out.println("arg0:" + arg0 + " arg1:" + arg1 + " arg2:" + arg2);
FrameLayout.LayoutParams l = (FrameLayout.LayoutParams) img_colorPoint
.getLayoutParams();
l.leftMargin = (int) (list_pointView.get(arg0).getLeft() + pointSpacing
* arg1);
img_colorPoint.setLayoutParams(l);
}
@Override
public void onPageSelected(int arg0) {
pageSelect = arg0;
}
/**
* 定时任务类
*/
class Task extends TimerTask {
@Override
public void run() {
pageSelect++;
if (pageSelect >= list_view.size()) {
pageSelect = 0;
}
handler.sendEmptyMessage(pageSelect);
}
}
}
ViewPagerAdapter.java同上~~~
上面的图片轮翻可自动切换,也可手动滑动切换。但是存在一个问题就是当手动切换的时候滑动到第一个Pager或最后一个Pager时,再向前或向后滑动就不能滑了,这样的体验是不太好的 。
为了解决这个问题,自己冥思苦想,加上查找资源,无奈人丑脑笨,未能合理解决,利用ViewPager去做总会存在或多或少的bug,例如白屏、Pager数量影响滑动效果等问题。最后求助朋友,得到一前人写的自定义控件(该实现并非使用Viewpager,但和ViewPager实现图片轮翻效果一致就放一块整理了),完美解决了,哈哈~
效果图:
先上布局activity_three.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="180dp" >
<com.magic.custom.MyPagerGalleryView
android:id="@+id/mypager_galleryview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:id="@+id/layout_point"
android:layout_width="match_parent"
android:layout_height="15dp"
android:layout_alignParentBottom="true"
android:layout_marginBottom="20dp"
android:gravity="center_horizontal"
android:orientation="horizontal" >
</LinearLayout>
</RelativeLayout>
</LinearLayout>
主界面ThreeActivity.java
/**
* Created by magic on 2016年9月29日.图片轮翻循环效果
*/
public class ThreeActivity extends Activity {
MyPagerGalleryView mypager_galleryview;
LinearLayout layout_point;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_three);
mypager_galleryview = (MyPagerGalleryView) findViewById(R.id.mypager_galleryview);
layout_point = (LinearLayout) findViewById(R.id.layout_point);
mypager_galleryview
.start(this, new ArrayList<String>(), new int[] { R.drawable.img_guide_one,
R.drawable.img_guide_two, R.drawable.img_guide_three },
1500, layout_point,
R.drawable.img_guide_point_selected,
R.drawable.img_guide_point);
}
}
自定义控件:MyPagerGalleryView.java
/**
* 无限滚动广告栏组件
*/
@SuppressWarnings("deprecation")
public class MyPagerGalleryView extends Gallery implements
android.widget.AdapterView.OnItemClickListener,
android.widget.AdapterView.OnItemSelectedListener, OnTouchListener {
/** 显示的Activity */
private Context mContext;
/** 条目单击事件接口 */
private MyOnItemClickListener mMyOnItemClickListener;
/** 图片切换时间 */
private int mSwitchTime;
/** 自动滚动的定时器 */
private Timer mTimer;
/** 圆点容器 */
private LinearLayout mOvalLayout;
/** 当前选中的数组索引 */
private int curIndex = 0;
/** 上次选中的数组索引 */
private int oldIndex = 0;
/** 圆点选中时的背景ID */
private int mFocusedId;
/** 圆点正常时的背景ID */
private int mNormalId;
/** 图片资源ID组 */
private int[] mAdsId;
/** 图片网络路径数组 */
private List<String> list;
/** ImageView组 */
private List<ImageView> listImgs;
/** 广告条上面textView控件 */
private TextView adgallerytxt;
/** 广告条上的每一条文字的数组 */
private String[] txtViewpager;
private ImageLoader il;
public MyPagerGalleryView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MyPagerGalleryView(Context context) {
super(context);
}
public MyPagerGalleryView(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* 对外开放方法
*
* @param context
* 显示的Activity ,不能为null
* @param mris
* 图片的网络路径数组 ,为空时 加载 adsId
* @param adsId
* 图片组资源ID ,测试用
* @param switchTime
* 图片切换时间 写0 为不自动切换
* @param ovalLayout
* 圆点容器 ,可为空
* @param focusedId
* 圆点选中时的背景ID,圆点容器可为空写0
* @param normalId
* 圆点正常时的背景ID,圆点容器为空写0
* @param adgallerytxt
* 广告条上面textView控件
* @param txtViewpager
* 广告条上的每一条文字的数组
*
*/
public void start(Context context, List<String> list, int[] adsId,
int switchTime, LinearLayout ovalLayout, int focusedId, int normalId) {
this.mContext = context;
this.list = list;
this.mAdsId = adsId;
this.mSwitchTime = switchTime;
this.mOvalLayout = ovalLayout;
this.mFocusedId = focusedId;
this.mNormalId = normalId;
ininImages();// 初始化图片组
setAdapter(new AdAdapter());
this.setOnItemClickListener(this);
this.setOnTouchListener(this);
this.setOnItemSelectedListener(this);
this.setSoundEffectsEnabled(false);
this.setAnimationDuration(700); // 动画时间
this.setUnselectedAlpha(1); // 未选中项目的透明度
// 不包含spacing会导致onKeyDown()失效!!! 失效onKeyDown()前先调用onScroll(null,1,0)可处理
setSpacing(0);
// 取靠近中间 图片数组的整倍数
setSelection((getCount() / 2 / listImgs.size()) * listImgs.size()); // 默认选中中间位置为起始位置
setFocusableInTouchMode(true);
initOvalLayout();// 初始化圆点
startTimer();// 开始自动滚动任务
}
/** 初始化图片组 */
private void ininImages() {
listImgs = new ArrayList<ImageView>(); // 图片组
int len = list.size() != 0 ? list.size() : mAdsId.length;
for (int i = 0; i < len; i++) {
ImageView imageview = new ImageView(mContext); // 实例化ImageView的对象
imageview.setScaleType(ImageView.ScaleType.FIT_XY); // 设置缩放方式
imageview.setLayoutParams(new Gallery.LayoutParams(
Gallery.LayoutParams.MATCH_PARENT,
Gallery.LayoutParams.MATCH_PARENT));
if (list.size() == 0) {// 本地加载图片
imageview.setImageResource(mAdsId[i]); // 为ImageView设置要显示的图片
} else { // 网络加载图片
il = ImageLoader.getInstance();
il.init(ImageLoaderConfiguration.createDefault(mContext));
il.displayImage(list.get(i), imageview);
}
listImgs.add(imageview);
}
}
/** 初始化圆点 */
private void initOvalLayout() {
if (mOvalLayout != null && listImgs.size() < 2) {// 如果只有一第图时不显示圆点容器
mOvalLayout.removeAllViews();
mOvalLayout.getLayoutParams().height = 0;
} else if (mOvalLayout != null) {
mOvalLayout.removeAllViews();
// 圆点的大小是 圆点窗口的 70%;
int Ovalheight = (int) (mOvalLayout.getLayoutParams().height * 0.7);
// 圆点的左右外边距是 圆点窗口的 20%;
int Ovalmargin = (int) (mOvalLayout.getLayoutParams().height * 0.2);
android.widget.LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
Ovalheight, Ovalheight);
layoutParams.setMargins(Ovalmargin, 0, Ovalmargin, 0);
for (int i = 0; i < listImgs.size(); i++) {
View v = new View(mContext); // 员点
v.setLayoutParams(layoutParams);
v.setBackgroundResource(mNormalId);
mOvalLayout.addView(v);
}
// 选中第一个
mOvalLayout.getChildAt(0).setBackgroundResource(mFocusedId);
}
}
/** 无限循环适配器 */
class AdAdapter extends BaseAdapter {
@Override
public int getCount() {
if (listImgs.size() < 2)// 如果只有一张图时不滚动
return listImgs.size();
return Integer.MAX_VALUE;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return listImgs.get(position % listImgs.size()); // 返回ImageView
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
}
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
int kEvent;
if (isScrollingLeft(e1, e2)) { // 检查是否往左滑动
kEvent = KeyEvent.KEYCODE_DPAD_LEFT;
} else { // 检查是否往右滑动
kEvent = KeyEvent.KEYCODE_DPAD_RIGHT;
}
onKeyDown(kEvent, null);
return true;
}
/** 检查是否往左滑动 */
private boolean isScrollingLeft(MotionEvent e1, MotionEvent e2) {
return e2.getX() > (e1.getX() + 50);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
float distanceY) {
return super.onScroll(e1, e2, distanceX, distanceY);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (MotionEvent.ACTION_UP == event.getAction()
|| MotionEvent.ACTION_CANCEL == event.getAction()) {
startTimer();// 开始自动滚动任务
} else {
stopTimer();// 停止自动滚动任务
}
return false;
}
/** 图片切换事件 */
@Override
public void onItemSelected(AdapterView<?> arg0, View arg1, int position,
long arg3) {
curIndex = position % listImgs.size();
if (mOvalLayout != null && listImgs.size() > 1) { // 切换圆点
mOvalLayout.getChildAt(oldIndex).setBackgroundResource(mNormalId); // 圆点取消
mOvalLayout.getChildAt(curIndex).setBackgroundResource(mFocusedId);// 圆点选中
oldIndex = curIndex;
}
// adgallerytxt.setText("" + curIndex);
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
}
/** 项目点击事件 */
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int position,
long arg3) {
if (mMyOnItemClickListener != null) {
mMyOnItemClickListener.onItemClick(curIndex);
}
}
/** 设置项目点击事件监听器 */
public void setMyOnItemClickListener(MyOnItemClickListener listener) {
mMyOnItemClickListener = listener;
}
/** 项目点击事件监听器接口 */
public interface MyOnItemClickListener {
/**
* @param curIndex
* //当前条目在数组中的下标
*/
void onItemClick(int curIndex);
}
/** 停止自动滚动任务 */
public void stopTimer() {
if (mTimer != null) {
mTimer.cancel();
mTimer = null;
}
}
/** 开始自动滚动任务 图片大于1张才滚动 */
public void startTimer() {
if (mTimer == null && listImgs.size() > 1 && mSwitchTime > 0) {
mTimer = new Timer();
mTimer.schedule(new TimerTask() {
public void run() {
handler.sendMessage(handler.obtainMessage(1));
}
}, mSwitchTime, mSwitchTime);
}
}
/** 处理定时滚动任务 */
@SuppressLint("HandlerLeak")
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
// 不包含spacing会导致onKeyDown()失效!!!
// 失效onKeyDown()前先调用onScroll(null,1,0)可处理
onScroll(null, null, 1, 0);
onKeyDown(KeyEvent.KEYCODE_DPAD_RIGHT, null);
}
};
}
在此感谢MyPagerGalleryView控件的作者,让我们少做轮子~虽然我觉得造轮子是技术提升的一个重要手段。
未完待续…
曾经沧海难为水,除却巫山不是云。