package com.poqop.document; import java.util.HashMap; import java.util.Map; import android.content.Context; import android.graphics.Canvas; import android.graphics.RectF; import android.os.Handler; import android.util.Log; import android.view.GestureDetector; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.GestureDetector.OnGestureListener; import android.view.animation.TranslateAnimation; import android.widget.ImageView; import android.widget.Scroller; import com.hyrt.cei.util.MyTools; import com.poqop.document.events.ZoomListener; import com.poqop.document.models.CurrentPageModel; import com.poqop.document.models.DecodingProgressModel; import com.poqop.document.models.ZoomModel; import com.poqop.document.multitouch.MultiTouchZoom; /* liming * documentview我们可以认为是一个显示容器,它里面存放的内容是一个个的页(page),而每个页是由若干个pagetreenode组成的 */ public class DocumentView extends ImageView implements ZoomListener { final ZoomModel zoomModel; private final CurrentPageModel currentPageModel; DecodeService decodeService; public static final HashMap<Integer, Page> pages = new HashMap<Integer, Page>(); private boolean isInitialized = false; private int pageToGoTo; private float lastX; private float lastY; private VelocityTracker velocityTracker;// 跟踪触摸速度 private final Scroller scroller; DecodingProgressModel progressModel; private RectF viewRect; private boolean inZoom; private long lastDownEventTime; private static final int DOUBLE_TAP_TIME = 500; private MultiTouchZoom multiTouchZoom; private GestureDetector mGestureDetector; private Context context; private boolean flage; public DocumentView(Context context, final ZoomModel zoomModel, DecodingProgressModel progressModel, CurrentPageModel currentPageModel) { super(context); this.context = context; this.zoomModel = zoomModel; this.progressModel = progressModel; this.currentPageModel = currentPageModel; setKeepScreenOn(true); scroller = new Scroller(getContext()); setFocusable(true); setFocusableInTouchMode(true); initMultiTouchZoomIfAvailable(zoomModel); mGestureDetector = new GestureDetector(new LearnGestureListener()); } private void initMultiTouchZoomIfAvailable(ZoomModel zoomModel) { try { multiTouchZoom = (MultiTouchZoom) Class .forName("org.vudroid.core.multitouch.MultiTouchZoomImpl") .getConstructor(ZoomModel.class).newInstance(zoomModel); } catch (Exception e) { System.out.println("Multi touch zoom is not available: " + e); } } public void setDecodeService(DecodeService decodeService) { this.decodeService = decodeService; } private void init() { if (isInitialized) { return; } final int width = decodeService.getEffectivePagesWidth(); final int height = decodeService.getEffectivePagesHeight(); for (int i = 0; i < decodeService.getPageCount(); i++) { pages.put(i, new Page(this, i)); pages.get(i).setAspectRatio(width, height); } isInitialized = true; invalidatePageSizes(); goToPageImpl(pageToGoTo); } private void goToPageImpl(final int toPage) { scrollTo(0, pages.get(toPage).getTop()); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); // bounds could be not updated post(new Runnable() { public void run() { currentPageModel.setCurrentPageIndex(getCurrentPage()); } }); if (inZoom) { return; } // on scrollChanged can be called from scrollTo just after new layout // applied so we should wait for relayout post(new Runnable() { public void run() { updatePageVisibility(); } }); } private void updatePageVisibility() { for (Page page : pages.values()) { page.updateVisibility(); } } public void commitZoom() { for (Page page : pages.values()) { page.invalidate(); } inZoom = false; } public void showDocument() { // use post to ensure that document view has width and height before // decoding begin post(new Runnable() { public void run() { try { init(); updatePageVisibility(); } catch (Exception e) { // TODO Auto-generated catch block Log.v("menu", "������Ϣ�ǡ�������" + e.getMessage()); e.printStackTrace(); } } }); } public void goToPage(int toPage) { if (isInitialized) { goToPageImpl(toPage); } else { pageToGoTo = toPage; } } public int getCurrentPage() { for (Map.Entry<Integer, Page> entry : pages.entrySet()) { if (entry.getValue().isVisible()) { return entry.getKey(); } } return 0; } public void zoomChanged(float newZoom, float oldZoom) { Log.v("menu", "zoomChanged==="); inZoom = true; stopScroller(); final float ratio = newZoom / oldZoom; invalidatePageSizes(); scrollTo( (int) ((getScrollX() + getWidth() / 2) * ratio - getWidth() / 2), (int) ((getScrollY() + getHeight() / 2) * ratio - getHeight() / 2)); postInvalidate();// Android程序中可以使用的界面刷新方法有两种,分别是利用Handler和利用postInvalidate()来实现在线程中刷新界面 } @Override public boolean onTouchEvent(MotionEvent ev) { super.onTouchEvent(ev); if (ev.getPointerCount() == 2) { scaleWithFinger(ev); } else { if (multiTouchZoom != null) { if (multiTouchZoom.onTouchEvent(ev)) { return true; } if (multiTouchZoom.isResetLastPointAfterZoom()) { setLastPosition(ev); multiTouchZoom.setResetLastPointAfterZoom(false); } } if (velocityTracker == null) { velocityTracker = VelocityTracker.obtain();// 创建VelocityTracker对 } velocityTracker.addMovement(ev);// 将当前的移动事件传递给VelocityTracker对象 switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: // zoomModel.increaseZoom(); Log.v("LM3", "按下"); stopScroller(); setLastPosition(ev); if (ev.getEventTime() - lastDownEventTime < DOUBLE_TAP_TIME) { zoomModel.toggleZoomControls(); } else { lastDownEventTime = ev.getEventTime(); } break; case MotionEvent.ACTION_MOVE: Log.v("LM3", "活动中"); scrollBy((int) (lastX - ev.getX()), (int) (lastY - ev.getY())); setLastPosition(ev); break; case MotionEvent.ACTION_UP: Log.v("LM3", "̧抬起"); velocityTracker.computeCurrentVelocity(1000);// 计算当前的速度,1000表示每秒多少像素(pix/second) // getXVelocity ()��getYVelocity ()��õ�ǰ���ٶ� scroller.fling(getScrollX(), getScrollY(), (int) -velocityTracker.getXVelocity(), (int) -velocityTracker.getYVelocity(), getLeftLimit(), getRightLimit(), getTopLimit(), getBottomLimit()); velocityTracker.recycle(); velocityTracker = null; break; } } return mGestureDetector.onTouchEvent(ev); } private void setLastPosition(MotionEvent ev) { lastX = ev.getX(); lastY = ev.getY(); } @Override public boolean dispatchKeyEvent(KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_DOWN) { switch (event.getKeyCode()) { case KeyEvent.KEYCODE_DPAD_RIGHT: lineByLineMoveTo(1); return true; case KeyEvent.KEYCODE_DPAD_LEFT: lineByLineMoveTo(-1); return true; case KeyEvent.KEYCODE_DPAD_DOWN: verticalDpadScroll(1); return true; case KeyEvent.KEYCODE_DPAD_UP: verticalDpadScroll(-1); return true; } } return super.dispatchKeyEvent(event); } private void verticalDpadScroll(int direction) { scroller.startScroll(getScrollX(), getScrollY(), 0, direction * getHeight() / 2); invalidate(); } private void lineByLineMoveTo(int direction) { if (direction == 1 ? getScrollX() == getRightLimit() : getScrollX() == getLeftLimit()) { scroller.startScroll(getScrollX(), getScrollY(), direction * (getLeftLimit() - getRightLimit()), (int) (direction * pages.get(getCurrentPage()).bounds.height() / 50)); } else { scroller.startScroll(getScrollX(), getScrollY(), direction * getWidth() / 2, 0); } invalidate(); } private int getTopLimit() { return 0; } private int getLeftLimit() { return 0; } private int getBottomLimit() { if (pages.size() == 0) { // Toast.makeText(context, "文件错误!", 2); // MyTools.exitShow(context, this, "文件错误!"); return -1; } return (int) pages.get(pages.size() - 1).bounds.bottom - getHeight(); } private int getRightLimit() { return (int) (getWidth() * zoomModel.getZoom()) - getWidth(); } @Override public void scrollTo(int x, int y) { if (getBottomLimit() == -1) { if (!flage) { MyTools.exitShow(context, this, "文件错误!"); flage = true; } return; } super.scrollTo(Math.min(Math.max(x, getLeftLimit()), getRightLimit()), Math.min(Math.max(y, getTopLimit()), getBottomLimit())); viewRect = null; } RectF getViewRect() { if (viewRect == null) { viewRect = new RectF(getScrollX(), getScrollY(), getScrollX() + getWidth(), getScrollY() + getHeight()); } return viewRect; } @Override public void computeScroll() { if (scroller.computeScrollOffset()) { scrollTo(scroller.getCurrX(), scroller.getCurrY()); } } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); for (Page page : pages.values()) { page.draw(canvas); } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); float scrollScaleRatio = getScrollScaleRatio(); invalidatePageSizes(); invalidateScroll(scrollScaleRatio); commitZoom(); } void invalidatePageSizes() { if (!isInitialized) { return; } float heightAccum = 0; int width = getWidth(); float zoom = zoomModel.getZoom(); for (int i = 0; i < pages.size(); i++) { Page page = pages.get(i); float pageHeight = page.getPageHeight(width, zoom); page.setBounds(new RectF(0, heightAccum, width * zoom, heightAccum + pageHeight)); heightAccum += pageHeight; } } private void invalidateScroll(float ratio) { if (!isInitialized) { return; } stopScroller(); final Page page = pages.get(0); if (page == null || page.bounds == null) { return; } scrollTo((int) (getScrollX() * ratio), (int) (getScrollY() * ratio)); } private float getScrollScaleRatio() { final Page page = pages.get(0); if (page == null || page.bounds == null) { return 0; } final float v = zoomModel.getZoom(); return getWidth() * v / page.bounds.width(); } private void stopScroller() { if (!scroller.isFinished()) { scroller.abortAnimation(); } } public HashMap<Integer, Page> getPageMap() { return pages; } public void putMap() { final int width = decodeService.getEffectivePagesWidth(); final int height = decodeService.getEffectivePagesHeight(); for (int i = 0; i < decodeService.getPageCount(); i++) { pages.put(i, new Page(this, i)); pages.get(i).setAspectRatio(width, height); } invalidatePageSizes(); showDocument(); } class LearnGestureListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onSingleTapUp(MotionEvent ev) { return true; } @Override public void onShowPress(MotionEvent ev) { } @Override public void onLongPress(MotionEvent ev) { zoomModel.decreaseZoom(); invalidatePageSizes(); postInvalidate(); updatePageVisibility(); commitZoom(); } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { return true; } @Override public boolean onDown(MotionEvent ev) { return true; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { return true; } @Override public boolean onDoubleTap(MotionEvent event) {// ˫����Ļʱ���� Log.d("LM2", "onDoubleTap"); zoomModel.increaseZoom(); invalidatePageSizes(); postInvalidate(); updatePageVisibility(); commitZoom(); return true; } } // 两点触屏后之间的长度 private float beforeLenght; private float afterLenght; /* * 通过多点触屏放大或缩小图像 beforeLenght用来保存前一时间两点之间的距离 afterLenght用来保存当前时间两点之间的距离 */ public void scaleWithFinger(MotionEvent event) { float moveX = event.getX(1) - event.getX(0); float moveY = event.getY(1) - event.getY(0); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: beforeLenght = (float) Math.sqrt((moveX * moveX) + (moveY * moveY)); break; case MotionEvent.ACTION_MOVE: // 得到两个点之间的长度 afterLenght = (float) Math.sqrt((moveX * moveX) + (moveY * moveY)); float gapLenght = afterLenght - beforeLenght; if (gapLenght == 0) { break; } // 如果当前时间两点距离大于前一时间两点距离,则传0,否则传1 if (gapLenght > 1) { float size = zoomModel.getZoom(); zoomModel.setZoom(size + 0.2f); zoomModel.commit(); } else if (gapLenght < 1) { float size = zoomModel.getZoom(); if (size > 1) { zoomModel.setZoom(size - 0.2f); zoomModel.commit(); } } beforeLenght = afterLenght; break; } } }