自定义的思路主要是继承viewpager,然后重写swapTouchEvent方法修改切换的手势,改成上下滑动来触发切换事件
public class VerticalViewPager extends ViewPager {
private boolean isPagingEnabled = true;
public VerticalViewPager(Context context) {
super(context);
}
public VerticalViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
private MotionEvent swapTouchEvent(MotionEvent event) {
float width = getWidth();
float height = getHeight();
event.setLocation((event.getY() / height) * width, (event.getX() / width) * height);
return event;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return super.onInterceptTouchEvent(swapTouchEvent(MotionEvent.obtain(event)))&&this.isPagingEnabled;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return super.onTouchEvent(swapTouchEvent(MotionEvent.obtain(ev)))&&this.isPagingEnabled;
}
public void setPagingEnabled(boolean b) {
this.isPagingEnabled = b;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int height = 0;
for(int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
int h = child.getMeasuredHeight();
if(h > height) height = h;
}
heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
这里还涉及高度重绘和是否允许滑动的代码,整个viewpager的高度是通过最大子item的高度决定的,是否滑动可以通过setPagingEnabled来设置,默认是允许滑动
以上只是设置切换手势,切换的动画需要重新设置
public class VerticalTransformer implements ViewPager.PageTransformer{
@Override
public void transformPage(View view, float position) {
view.setTranslationX(view.getWidth() * -position);
float yPosition = position * view.getHeight();
view.setTranslationY(yPosition);
}
}
原本是x方向进行切换,但是设置成-position就抵消了,y方向进行切换,可以设置透明度,有需要可以
设置
mVerticalViewPager.setPageTransformer(true, new VerticalTransformer());
通过以上设置就完成一个垂直切换的viewpager,是不是很简单
下面是完整的调用
private void initVerticalPager() {
if (mVerticalViewPager == null) {
mVerticalViewPager = (VerticalViewPager) getRootView().findViewById(R.id.vertical_viewpager);
mVerticalPagerAdapter = new VerticalPagerAdapter();
mVerticalViewPager.setAdapter(mVerticalPagerAdapter);
mVerticalViewPager.setOverScrollMode(View.OVER_SCROLL_NEVER);
mVerticalViewPager.setPageTransformer(true, new VerticalTransformer());
} else {
mVerticalPagerAdapter.notifyDataSetChanged();
}
}
适配器
private class VerticalPagerAdapter extends PagerAdapter {
@Override
public int getCount() {
return Integer.MAX_VALUE;
}
@Override
public boolean isViewFromObject(View view, Object o) {
return view == o;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View) object);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
View view = null;
view = View.inflate(mContext, R.layout.weiget_main_information, null);
switch(position%count){
case 0:
view = ...;
break;
case 1:
...
}
container.addView(view);
return view;
}
}
这里设置一个伪无限循环的viewpager,所以在getCount方法中放回整形的最大值,实际上我们使用app的间内几乎不太可能通过滑动达到这个最大值,至于循环,就是根据需求在instantiateItem中实例化新的view,每次都要实例化新的view,并初始化数据,不用担心产生太多垃圾,因为在destroyItem方法中会去掉这个view,等待gc回收
private static int SWITCH_TIME = 30*1000;
public void startSwitchTask() {
if (handler == null) {
handler = new Handler();
}
handler.removeCallbacksAndMessages(null);
handler.postDelayed(runnable,SWITCH_TIME);
}
public void stopSwitchTask() {
if (handler != null) {
handler.removeCallbacksAndMessages(null);
handler = null;
}
}
private Runnable runnable = new Runnable( ) {
public void run ( ) {
doSwitch();
handler.postDelayed(this,SWITCH_TIME);
}
};
private void doSwitch() {
if(mVerticalViewPager ==null){
return;
}
mVerticalViewPager.setCurrentItem(mVerticalViewPager.getCurrentItem()+1);
}
实现定时的巧妙之处是
先通过handler.postDelayed触发runnable,然后在runnable中完成业务,并触发handler.postDelayed方法,这种方式由于使用了handler,需要在act声明周期内有效,如果act回收的时候,需要在onDestroy方法中调用stopSwitchTask方法,避免内存泄露。
doSwitch方法进行切换viewpager,position自增,无需维护额外的变量
为了避免一开始的时候需要上翻,所以设置position在中间的位置,例如1000,就足够了