FBReader中翻页方式相关源码分析

FBReader是一个电子书开源项目,关于它之前有人发过5篇pdf叙述项目框架,这里主要讲讲FBReaderJ的翻页方式。

翻页方式的配置在ScrollingPreferences中

public final ZLEnumOption<ZLView.Animation> AnimationOption =
		new ZLEnumOption<ZLView.Animation>("Scrolling", "Animation", ZLView.Animation.slide);

总共有四种翻页方式

public static enum Animation {
		none, curl, slide, shift
	}

其中curl是仿真翻页方式,slide是滑动翻页。


负责绘制的是ZLAndroidWidget

@Override
	protected void onDraw(final Canvas canvas) {
		final Context context = getContext();
		if (context instanceof ZLAndroidActivity) {
			((ZLAndroidActivity)context).createWakeLock();
		} else {
			System.err.println("A surprise: view's context is not a ZLAndroidActivity");
		}
		super.onDraw(canvas);

//		final int w = getWidth();
//		final int h = getMainAreaHeight();

		if (getAnimationProvider().inProgress()) {
			onDrawInScrolling(canvas);
		} else {
			onDrawStatic(canvas);
			ZLApplication.Instance().onRepaintFinished();
		}
	}

onDrawInScrolling是在滑动时绘制,onDrawStatic另外一个分支是画静态页面。

private void onDrawInScrolling(Canvas canvas) {
		final ZLView view = ZLApplication.Instance().getCurrentView();

//		final int w = getWidth();
//		final int h = getMainAreaHeight();

		final AnimationProvider animator = getAnimationProvider();
		final AnimationProvider.Mode oldMode = animator.getMode();
		animator.doStep();
		if (animator.inProgress()) {
			animator.draw(canvas);
			if (animator.getMode().Auto) {
				postInvalidate();
			}
			drawFooter(canvas);
		} else {
			.......
		}
	}

所以最后滑动效果委托给AnimationProvider完成,这里有两点要注意:

1、AnimationProvider的draw方法是一个模板方法,因为其中的drawInternal是一个抽象方法,这样子类就可以定制具体的翻页算法。

2、为什么不由ZLAndroidWidget的子类负责定制不同的翻页算法?

因为ZLAndroidWidget继承View,是直接在界面上显示的。如果在阅读器使用时改变翻页方式,就得更换ZLAndroidWidget的不同子类,所以就得调用removeView和addView,这样容易有bug,而且效率也不高。

具体的绘制算法再委托给AnimationProvider就不需要更换Activity的view;而且不同类层次的职责也更加明确,ZLAndroidWidget保存状态和管理图片,AnimationProvider只负责绘制滑动效果。

	final void draw(Canvas canvas) {
		myBitmapManager.setSize(myWidth, myHeight);
		final long start = System.currentTimeMillis();
		drawInternal(canvas);
		myDrawInfos.add(new DrawInfo(myEndX, myEndY, start, System.currentTimeMillis()));
		if (myDrawInfos.size() > 3) {
			myDrawInfos.remove(0);
		}
	}

	protected abstract void drawInternal(Canvas canvas);


负责滑动绘制的是SlideAnimationProvider,AnimationProvider总共有四个子类负责四种具体的翻页方式,这样就可以在运行时灵活改变翻页方式,而针对AnimationProvider写的代码还是不需要改变。

class SlideAnimationProvider extends SimpleAnimationProvider {
	private final Paint myPaint = new Paint();

	SlideAnimationProvider(BitmapManager bitmapManager) {
		super(bitmapManager);
	}

	@Override
	protected void drawInternal(Canvas canvas) {
		canvas.drawBitmap(getBitmapTo(), 0, 0, myPaint);
		myPaint.setColor(Color.rgb(127, 127, 127));
		if (myDirection.IsHorizontal) {
			final int dX = myEndX - myStartX;
			canvas.drawBitmap(getBitmapFrom(), dX, 0, myPaint);
			if (dX > 0 && dX < myWidth) {
				canvas.drawLine(dX, 0, dX, myHeight + 1, myPaint);
			} else if (dX < 0 && dX > -myWidth) {
				canvas.drawLine(dX + myWidth, 0, dX + myWidth, myHeight + 1, myPaint);
			}
		} else {
			final int dY = myEndY - myStartY;
			canvas.drawBitmap(getBitmapFrom(), 0, dY, myPaint);
			if (dY > 0 && dY < myHeight) {
				canvas.drawLine(0, dY, myWidth + 1, dY, myPaint);
			} else if (dY < 0 && dY > -myHeight) {
				canvas.drawLine(0, dY + myHeight, myWidth + 1, dY + myHeight, myPaint);
			}
		}
	}
}
drawInternal集中体现了滑动绘制的逻辑。FBReaderJ支持垂直滑动和水平滑动,也是在ScrollingPreferences中配置。

对于水平滑动,先把要翻到的那页画出来:canvas.drawBitmap(getBitmapTo(), 0, 0, myPaint);

然后根据滑动的距离绘制以前的那页:canvas.drawBitmap(getBitmapFrom(), dX, 0, myPaint);

注意滑动的距离是开始水平位置减现在的水平位置:final int dX = myEndX - myStartX;而这两个位置初始时是赋值成一样的:(AnimationProvider)

final void startManualScrolling(int x, int y) {
		if (!myMode.Auto) {
			myMode = Mode.ManualScrolling;
			myEndX = myStartX = x;
			myEndY = myStartY = y;
		}
	}

而触发开始滑动的堆栈是:

public boolean onTouchEvent(MotionEvent event)   ZLAndroidWidget.class

-->

public boolean onFingerMove(int x, int y)   FBView.class

-->

private void startManualScrolling(int x, int y)   FBView.class

-->

public void startManualScrolling(int x, int y, ZLView.Direction direction)   ZLAndroidWidget.class

-->

final void startManualScrolling(int x, int y)   AnimationProvider.class


startManualScrolling主要是记录了开始翻页时的那个触点。

阅读更多
换一批

没有更多推荐了,返回首页