一、简介
前段时间在腾讯视频中看到一个效果,是一个广告轮播,然后一屏还显示了多页。看着这个效果看着还不错,就自己实现了下。
国际惯例先上效果图,如下:(虽然界面比较简陋,但是功能是全的)
============================分割线========================
二、原理
实现如上效果需要两个功能:一屏多页、无限滑动、自动切换,下边将分别简单的介绍其原理。
1、一屏多页
不限制子View在其范围内。
2、无限滑动
限于文字功底不足,所以举例说明一下。有一个ViewPager,有5页数据{1,2,3,4,5},那么在1前加一页为0,内容和5一样,同时在5后加一页为6,内容和1一样(Adapter中体现);然后在页面切换后判断,如果是0页时跳转到5页,6页时跳转到1页。
3、自动切换
每隔一段时间,切换到下一页
三、实现
(1)让ViewPager的父容器具有android:clipChildren="false"和android:layerType="software"这两个属性,前一句主要意思是不限制子View在其范围内,后一句是启动硬件加速。
(2)ViewPager设置marginLeft和marginRight属性,这两个大小会分别导致左右两边的page显示的大小。
这两步代码如下:
(3)创建可循环的Adapter,如下:
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import net.arvin.viewpagertransdemo.SimpleFragment;
import java.util.List;
/**
* Created by arvin on 2016/8/24 09:52
*/
public class LoopPagerAdapter extends FragmentStatePagerAdapter {
private List items;
public LoopPagerAdapter(FragmentManager fm, List items) {
super(fm);
this.items = items;
}
@Override
public int getCount() {
//比原来的页数多两页,因为在第一页前加;最后一页后也要加一页
return items.size() + 2;
}
@Override
public Fragment getItem(int position) {
//将position转化为对应在items的position
if (position == items.size() + 1) {
//在最后一页的时候显示第一页的内容,
position = 0;
} else if (position == 0) {
//在第一页的时候显示最后一页的内容
position = items.size() - 1;
} else {
position -= 1;
}
//显示相应页的内容
return new SimpleFragment(items.get(position));
}
}
其中SimpleFragment就是作为背景显示传入的颜色值,就不解释了。
(4)设置ViewPager,如下:
mPager = (ViewPager) findViewById(R.id.mPager);
mPager.setAdapter(new LoopPagerAdapter(getSupportFragmentManager(), mItems));
mPager.setPageMargin(Utils.dp2px(1));
mPager.setOffscreenPageLimit(mItems.size());
mPager.setCurrentItem(currentPosition);
mPager.setOnPageChangeListener(this);
setPageMargin这是设置页与页之间的距离;
currentPosition初始化为1,这才是真是的第一页的数据;
其他的都是ViewPager的基本东西,这里就不解释了;
(5)处理页面切换后的逻辑,如下:
@Override
public void onPageSelected(int position) {
currentPosition = position;
if (position == getColors().size() + 1) {
currentPosition = 1;
isNeedChange = true;
} else if (position == 0) {
isNeedChange = true;
currentPosition = getColors().size();
} else {
isNeedChange = false;
}
}
@Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_IDLE && isNeedChange) {
mPager.setCurrentItem(currentPosition, false);
}
}
首先,isNeedChange表示是否需要改变界面,也就是切换页面后position为第一页或者最后一页时就需要改变,其他时候就不需要,因为处于所有页面的中间;
然后,currentPosition表示切换后的真实位置,上边的代码逻辑也很简单,介绍原理时已说明;
最后,在onPageScrollStateChanged中在停止滚动且可以改变界面时,我们就跳转到真实的页面,且不加动画。
(6)自动切换
自动切换,每隔一段时间触发一个事件,可以用Timer,用法也很简单,就不介绍了,因为我们用的不是它;这里使用Handler去实现,然而Handler的使用往往会引起内存泄露
,所以我们需要封装一下,使用弱引用去处理一下,这里也就不详细介绍了,直接上Handler的代码:
public class WeakHandler extends Handler {
private WeakReference mCallback;
public WeakHandler(IWeakHandler activity) {
this.mCallback = new WeakReference<>(activity);
}
public WeakHandler(Looper looper,IWeakHandler activity){
super(looper);
this.mCallback = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (mCallback != null) {
mCallback.get().handleMessage(msg);
}
}
}
然后我们在主界面中,首先初始化一下这个WeakHandler:
mHandler = new WeakHandler(this);
这里需要实现一个IweakHandler的接收到消息的回调,收到消息后就应该切换界面,并再次发出一个自动切换的消息:
@Override
public void handleMessage(Message msg) {
if (msg.what == TRANS_ANIM_CODE) {
mPager.setCurrentItem(++currentPosition, true);
startTrans();
}
}
然后我们应该再哪里去发出或者移除自动切换的消息,没错就分别在界面可见和不可见时就行了:
@Override
protected void onResume() {
super.onResume();
startTrans();
}
@Override
protected void onPause() {
super.onPause();
stopTrans();
}
private void startTrans() {
mHandler.sendEmptyMessageDelayed(TRANS_ANIM_CODE, TRANS_ANIM_DURING);
}
private void stopTrans() {
mHandler.removeMessages(TRANS_ANIM_CODE);
}
这样我们就实现了自动切换了,这时候你可能会发现一个体验上的问题,当用户正在操作时,这个收到这个切换消息时,也毅然决然的切换了,让用户觉得怎么回事,我还想慢
慢的看会呢,接下来就来处理这个问题。
(7)在用户操作时不自动切换
正如这个小标题所说,那要怎么做才能不自动切换呢,对,就是在用户触摸到Page的时候就移除掉自动切换的消息,在用户没有触摸到Page的时候就发出自动切换的消息。这时
候就需要我们去重写ViewPager,监听触摸事件,并给出相应的回调,代码也很简单,如下:
public class UserOperateCallbackViewPager extends ViewPager {
private IUserOperating mUserOperating;
public UserOperateCallbackViewPager(Context context) {
super(context);
}
public UserOperateCallbackViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setUserOperating(IUserOperating userOperating) {
this.mUserOperating = userOperating;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
if (mUserOperating != null) {
mUserOperating.userOperating(true);
}
break;
case MotionEvent.ACTION_UP:
if (mUserOperating != null) {
mUserOperating.userOperating(false);
}
break;
}
return super.onTouchEvent(ev);
}
public interface IUserOperating {
void userOperating(boolean isOperating);
}
}
然后我们就用UserOperateCallbackViewPager去替换掉原生的ViewPager,再为新的ViewPager设置一个回调,并处理回调即可:
mPager.setUserOperating(this);
@Override
public void userOperating(boolean isOperating) {
if (isOperating) {
stopTrans();
} else {
startTrans();
}
}
这样我们的自动切换的功能就正式完成了。
=========================分割线====================