package com.example.ipcplayer.widget; import java.util.ArrayList; import com.example.ipcplayer.utils.OutOfMemoryHandle; import android.content.Context; import android.graphics.Canvas; import android.graphics.Rect; import android.os.Handler; import android.os.Message; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.ViewParent; import android.view.animation.Interpolator; import android.widget.Scroller; /** * 普通模式下的屏幕容器,管理各个屏幕 * * @author yuankai * @version 1.0 */ public class OnlineWorkspace extends ViewGroup implements OnClickListener, OnLongClickListener { private final static String TAG = "OnlineWorkspace"; private static final int INVALID_SCREEN = -1; private final static float BOUNCE_RATIO = 0.1f; private IWorkspaceListener mListener = null; /** * Fling 切换到下一个屏幕的最小速度 */ private static final int SNAP_VELOCITY = 150; // 触屏状态 private final static int TOUCH_STATE_REST = 0; private final static int TOUCH_STATE_SCROLLING = 1; private int mTouchState = TOUCH_STATE_REST; // 设置滚动速度 private int mScrollingDuration = 400; // 默认屏幕 private int mMainScreen; // 当前屏幕 protected int mCurrentScreen; private int mNextScreen = INVALID_SCREEN; private Scroller mScroller; private VelocityTracker mVelocityTracker; // 切换屏幕无反弹效果,用于标识从预览界面进入桌面的切换 private boolean mSnapWhthNoElastic = true; // Wysie: Values taken from CyanogenMod (Donut era) Browser private static final double ZOOM_SENSITIVITY = 1.6; private static final double ZOOM_LOG_BASE_INV = 1.0 / Math .log(2.0 / ZOOM_SENSITIVITY); private float mLastMotionX; private float mLastMotionY; // lock 标志,为ture时不响应触屏事件 private boolean mLocked; private boolean mAllowLongPress; // private boolean mIsPressed; private MotionEvent mCurrentDownEvent; private int mTouchSlop; private int mMaximumVelocity; private boolean mLongClickView = false; // 弹性特效 private int mScrollingBounce = 20; // 边缘弹性长度与屏幕宽度的比例 private static final float EDGE_BOUNCE_MAX_RATIO = 0.17f; private int mEdgeBounceMaxLength; private boolean mPreventFC = true; private boolean mScrollRight = true; private final int MSG_SCROLL_START = 0; private final int MSG_SCROLL_RESTART = 1; private boolean mStoped = false; private Handler mTimerHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case MSG_SCROLL_START: // 检测滑动方向 int nextScreen = mScrollRight ? mCurrentScreen + 1 : mCurrentScreen - 1; if (nextScreen >= getChildCount()) mScrollRight = false; if (nextScreen < 0) mScrollRight = true; // 获取目标屏幕索引 nextScreen = mScrollRight ? mCurrentScreen + 1 : mCurrentScreen - 1; if (mStoped) return; // 滑动操作 snapToScreen(nextScreen, mScrollingDuration * 5, true); mTimerHandler.sendEmptyMessageDelayed(MSG_SCROLL_START, 6000); break; case MSG_SCROLL_RESTART: mStoped = false; mTimerHandler.sendEmptyMessageDelayed(MSG_SCROLL_START, 6000); break; } } }; /** * 开启计时滚动 */ public void startScroll() { this.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent ev) { if (isPressed(ev)) {// 当前WorkSpace控件被按下,则停止计时滑动,1秒后重新开启 mStoped = true; mTimerHandler.removeMessages(MSG_SCROLL_START, null); mTimerHandler.removeMessages(MSG_SCROLL_RESTART, null); mTimerHandler.sendEmptyMessageDelayed(MSG_SCROLL_RESTART, 500); } return false; } }); mStoped = false; mTimerHandler.sendEmptyMessageDelayed(MSG_SCROLL_START, 6000); } /** * 停止计时滚动 */ public void stopScroll() { mStoped = true; mTimerHandler.removeMessages(MSG_SCROLL_START, null); } /** * * @param context * context */ public OnlineWorkspace(Context context) { this(context, null); } /*** * * @param context * Context * @param att * 属性集 */ public OnlineWorkspace(Context context, AttributeSet att) { super(context, att); mCurrentScreen = mMainScreen; setBounceAmount(mScrollingBounce); final ViewConfiguration configuration = ViewConfiguration .get(getContext()); mTouchSlop = configuration.getScaledTouchSlop(); mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int childLeft = 0; final int count = getChildCount(); final int height = b - t; for (int i = 0; i < count; i++) { final View child = getChildAt(i); if (child.getVisibility() != View.GONE) { final int childWidth = child.getMeasuredWidth(); child.layout(childLeft, 0, childLeft + childWidth, height); childLeft += childWidth; } } // 重新计算边界弹性最大长度 mEdgeBounceMaxLength = (int) ((r - l) * EDGE_BOUNCE_MAX_RATIO); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); final int width = MeasureSpec.getSize(widthMeasureSpec); final int widthMode = MeasureSpec.getMode(widthMeasureSpec); if (widthMode != MeasureSpec.EXACTLY) { throw new IllegalStateException( "Workspace can only be used in EXACTLY mode."); } final int heightMode = MeasureSpec.getMode(heightMeasureSpec); if (heightMode != MeasureSpec.EXACTLY) { throw new IllegalStateException( "Workspace can only be used in EXACTLY mode."); } // The children are given the same width and height as the workspace final int count = getChildCount(); for (int i = 0; i < count; i++) { View view = getChildAt(i); if (view != null) { view.measure(widthMeasureSpec, heightMeasureSpec); } } } public boolean isWidgetAtLocationScrollable(int x, int y) { return false; } public void unbindWidgetScrollableViews() { } public void unbindWidgetScrollableViewsForWidget(int widgetId) { } @Override protected void dispatchDraw(Canvas canvas) { final int screenNum = getChildCount(); if (screenNum <= 0) { return; } boolean fastDraw = mTouchState != TOUCH_STATE_SCROLLING && mNextScreen == INVALID_SCREEN; // If we are not scrolling or flinging, draw only the current screen View childView = null; final long drawingTime = getDrawingTime(); if (fastDraw) { childView = getChildAt(mCurrentScreen); if (childView.getVisibility() == VISIBLE) { drawChild(canvas, childView, drawingTime); } } else { // If we are flinging, draw only the current screen and the target // screen if (mNextScreen >= 0 && mNextScreen < screenNum && Math.abs(mCurrentScreen - mNextScreen) == 1) { childView = getChildAt(mCurrentScreen); if (childView.getVisibility() == VISIBLE) { drawChild(canvas, childView, drawingTime); } childView = getChildAt(mNextScreen); if (childView.getVisibility() == VISIBLE) { drawChild(canvas, childView, drawingTime); } } else { // If we are scrolling, draw all of our children final int min = (mCurrentScreen - 1) < 0 ? 0 : (mCurrentScreen - 1); final int max = (mCurrentScreen + 1) < screenNum ? (mCurrentScreen + 1) : screenNum - 1; for (int i = min; i <= max; i++) { childView = getChildAt(i); if (childView.getVisibility() == VISIBLE) { drawChild(canvas, childView, drawingTime); } } // for (int i = 0; i < screenNum; i++) // { // childView = getChildAt(i); // if(childView.getVisibility() == VISIBLE) // { // drawChild(canvas, childView, drawingTime); // } // } } } } @Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { int saveCount = canvas.save(); super.drawChild(canvas, child, drawingTime); canvas.restoreToCount(saveCount); return true; } @Override public boolean dispatchUnhandledMove(View focused, int direction) { if (direction == View.FOCUS_LEFT) { if (getCurrentScreen() > 0) { snapToScreen(getCurrentScreen() - 1, false); return true; } } else if (direction == View.FOCUS_RIGHT) { if (getCurrentScreen() < getChildCount() - 1) { snapToScreen(getCurrentScreen() + 1, false); return true; } } return super.dispatchUnhandledMove(focused, direction); } @Override public void onClick(View v) { } @Override public boolean onLongClick(View v) { return false; } /** * 设置翻屏时间 * * @param duration * 翻屏时间 */ public void setScrollDuration(int duration) { mScrollingDuration = duration; } /** * 设置弹性特效参数 * * @param bounce * 屏幕边缘可以拉伸的距离 */ public void setBounceAmount(int bounce) { mScrollingBounce = bounce; Interpolator interpolator = new ElasticInterpolator(mScrollingBounce * BOUNCE_RATIO); // Interpolator interpolator = new // OvershootInterpolator(mScrollingBounce * BOUNCE_RATIO); mScroller = new Scroller(getContext(), interpolator); } @Override public void addView(View child) { if (!(child instanceof CellLayout)) { throw new IllegalArgumentException( "A Workspace can only have CellLayout children."); } initCellLayout((CellLayout) child); super.addView(child); } @Override public void addView(View child, int index) { if (!(child instanceof CellLayout)) { throw new IllegalArgumentException( "A Workspace can only have CellLayout children."); } initCellLayout((CellLayout) child); super.addView(child, index); } @Override public void addView(View child, int index, LayoutParams params) { if (!(child instanceof CellLayout)) { throw new IllegalArgumentException( "A Workspace can only have CellLayout CellLayout."); } initCellLayout((CellLayout) child); super.addView(child, index, params); } @Override public void addView(View child, int width, int height) { if (!(child instanceof CellLayout)) { throw new IllegalArgumentException( "A Workspace can only have CellLayout children."); } initCellLayout((CellLayout) child); super.addView(child, width, height); } @Override public void addView(View child, LayoutParams params) { if (!(child instanceof CellLayout)) { throw new IllegalArgumentException( "A Workspace can only have CellLayout children."); } initCellLayout((CellLayout) child); super.addView(child, params); } protected void initCellLayout(CellLayout screen) { screen.setOnLongClickListener(this); } /** * 添加屏幕 * * @param screen * 屏幕 * @param position * 添加位置 */ public void addScreen(CellLayout screen, int position) { addView(screen, position); if (mListener != null) { mListener.onUpdateTotalNum(getChildCount()); } } /** * 删除屏幕 * * @param screen * 屏幕id */ public void removeScreen(int screen) { if (screen < 0 || screen >= getChildCount()) { return; } final CellLayout layout = (CellLayout) getChildAt(screen); if (layout == null) { return; } removeView(layout); int currentScreen = mCurrentScreen; boolean refresh = false; if (screen < currentScreen) { currentScreen -= 1; refresh = true; } else if (screen == currentScreen) { currentScreen = 0; refresh = true; } mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1)); if (refresh) { setCurrentScreen(mCurrentScreen); } if (screen <= mMainScreen) { int mainScreen = mMainScreen; if (screen < mMainScreen) { mainScreen -= 1; } else if (screen == mMainScreen) { mainScreen = 0; } mMainScreen = Math .max(0, Math.min(mainScreen, getChildCount() - 1)); } if (mListener != null) { mListener.onUpdateTotalNum(getChildCount()); } } /** * 指定屏是否有子元素 * * @param screenId * 屏ID * @return 是否有 */ public boolean hasChildElement(int screenId) { CellLayout child = (CellLayout) getChildAt(screenId); if (child != null) { return child.getChildCount() > 0; } else { return false; } } /** * Unlocks the SlidingDrawer so that touch events are processed. * * @see #lock() */ public void unlock() { mLocked = false; } /** * Locks the SlidingDrawer so that touch events are ignores. * * @see #unlock() */ public void lock() { mLocked = true; } void enableChildrenCache() { final int count = getChildCount(); for (int i = 0; i < count; i++) { if (i >= mCurrentScreen - 1 || i <= mCurrentScreen + 1) { final CellLayout layout = (CellLayout) getChildAt(i); layout.setChildrenDrawingCacheEnabled(true); layout.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_LOW); layout.setChildrenDrawnWithCacheEnabled(true); } } } void clearChildrenCache() { clearChildrenCache(false); } void clearChildrenCache(boolean needGc) { final int count = getChildCount(); for (int i = 0; i < count; i++) { final CellLayout layout = (CellLayout) getChildAt(i); layout.setChildrenDrawnWithCacheEnabled(false); } if (needGc) { OutOfMemoryHandle.gcIfAllocateOutOfHeapSize(); } } /** * 设置默认屏幕 * * @param screen * 屏幕 */ public void setMainScreen(int screen) { if (screen >= getChildCount() || screen < 0) { // Log.i(LOG_TAG, "Cannot reset default screen to " + screen); return; } mMainScreen = screen; } /** * * @return 当前主屏 */ public int getMainScreen() { return mMainScreen; } /** * @return 当前是否显示默认屏幕 */ public boolean isMainScreenShowing() { return mCurrentScreen == mMainScreen; } /** * 获取当前显示的屏幕id * * @return 当前屏幕. */ public int getCurrentScreen() { return mCurrentScreen; } /** * 设置当前屏幕 * * @param currentScreen * 当前屏幕id. */ public void setCurrentScreen(int currentScreen) { mCurrentScreen = Math.max(0, Math.min(currentScreen, getChildCount() - 1)); mNextScreen = mCurrentScreen; scrollTo(mCurrentScreen * getWidth(), 0); // 更新指示器 if (mListener != null) { mListener.onUpdateCurrent(currentScreen); } postInvalidate(); } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); postInvalidate(); } else { if (mSnapWhthNoElastic) { mSnapWhthNoElastic = false; setBounceAmount(mScrollingBounce); } if (mNextScreen != INVALID_SCREEN) { mCurrentScreen = Math.max(0, Math.min(mNextScreen, getChildCount() - 1)); mNextScreen = INVALID_SCREEN; // 更新点状指示器 if (mListener != null) { mListener.onUpdateCurrent(mCurrentScreen); } clearChildrenCache(mPreventFC); } } } /** * 设置监听器 * * @param listener */ public void setWorkspaceListener(IWorkspaceListener listener) { mListener = listener; } @Override public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) { int screen = indexOfChild(child); if (screen != mCurrentScreen || !mScroller.isFinished()) { if (!mLocked) { snapToScreen(screen, true); } return true; } return false; } @Override protected boolean onRequestFocusInDescendants(int direction, Rect previouslyFocusedRect) { int focusableScreen; if (mNextScreen != INVALID_SCREEN) { focusableScreen = mNextScreen; } else { focusableScreen = mCurrentScreen; } if (focusableScreen < getChildCount()) { getChildAt(focusableScreen).requestFocus(direction, previouslyFocusedRect); } return false; } @Override public void addFocusables(ArrayList<View> views, int direction, int focusableMode) { if (mCurrentScreen >= getChildCount()) { return; } getChildAt(mCurrentScreen).addFocusables(views, direction); if (direction == View.FOCUS_LEFT) { if (mCurrentScreen > 0) { getChildAt(mCurrentScreen - 1).addFocusables(views, direction); } } else if (direction == View.FOCUS_RIGHT) { if (mCurrentScreen < getChildCount() - 1) { getChildAt(mCurrentScreen + 1).addFocusables(views, direction); } } } boolean DEBUG = true; @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if (mLocked) { return true; } final int action = ev.getAction(); if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) { return true; } final float x = ev.getX(); final float y = ev.getY(); switch (action) { case MotionEvent.ACTION_DOWN: { mLastMotionX = x; mLastMotionY = y; mAllowLongPress = true; mLongClickView = false; mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST : TOUCH_STATE_SCROLLING; mTouchState = TOUCH_STATE_SCROLLING; // 记录down事件 if (mCurrentDownEvent != null) { mCurrentDownEvent.recycle(); } mCurrentDownEvent = MotionEvent.obtain(ev); break; } case MotionEvent.ACTION_MOVE: { final int xDiff = (int) Math.abs(x - mLastMotionX); final int yDiff = (int) Math.abs(y - mLastMotionY); boolean xMoved = xDiff > mTouchSlop; boolean yMoved = yDiff > mTouchSlop; if ((xMoved || yMoved)) { if (mLongClickView) { mLongClickView = false; } else if (xDiff > yDiff) { mTouchState = TOUCH_STATE_SCROLLING; enableChildrenCache(); } else { } if (mAllowLongPress) { mAllowLongPress = false; final View currentScreen = getChildAt(mCurrentScreen); currentScreen.cancelLongPress(); } } break; } case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: { clearChildrenCache(); mTouchState = TOUCH_STATE_REST; mAllowLongPress = false; break; } default: break; } // // if(DEBUG){ // return true; // }else return mTouchState != TOUCH_STATE_REST; } /** * 根据Touch事件ev检测当前View是否被按下 * * @param ev * @return */ private boolean isPressed(MotionEvent ev) { Rect workspaceRect = new Rect(); getHitRect(workspaceRect); Rect r = new Rect(); r.left = r.right = (int) ev.getX(); r.bottom = r.top = (int) ev.getY(); return workspaceRect.intersects(r.left, r.top, r.right, r.bottom); } float x1 = 0; float y1 = 0; float tempX = 0; float tempY = 0; @Override public boolean onTouchEvent(MotionEvent ev) { if (mLocked) { return true; } if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(ev); final int action = ev.getAction(); final float x = ev.getX(); switch (action) { case MotionEvent.ACTION_DOWN: if (!isPressed(ev))// 按下区域不在当前View,不响应该事件 break; if (!mScroller.isFinished()) { mScroller.abortAnimation(); } mLastMotionX = x; if(getParent()!=null){ if(getParent().getParent()!=null) getParent().getParent().requestDisallowInterceptTouchEvent(true); } x1 = ev.getX(); y1 = ev.getY(); break; case MotionEvent.ACTION_MOVE: if (!isPressed(ev))// 按下区域不在当前View,不响应该事件 break; if(getParent()!=null){ if(getParent().getParent()!=null) getParent().getParent().requestDisallowInterceptTouchEvent(true); } if (mTouchState == TOUCH_STATE_SCROLLING) { // Scroll to follow the motion event final int deltaX = (int) (mLastMotionX - x); final int scrollX = getScrollX(); final int width = getWidth(); mLastMotionX = x; if (deltaX < 0) { if (scrollX > -mEdgeBounceMaxLength) { scrollBy(Math.min(deltaX, mEdgeBounceMaxLength), 0); } } else if (deltaX > 0) { final int lastIndex = getChildCount() - 1; if (lastIndex >= 0) { final int lastRight = getChildAt(lastIndex).getRight(); final int availableToScroll = lastRight - scrollX - width + mEdgeBounceMaxLength; if (availableToScroll > 0) { scrollBy(deltaX, 0); } } } final int screenWidth = getWidth(); final int whichScreen = (getScrollX() + (screenWidth / 2)) / screenWidth; if (whichScreen != mCurrentScreen && mListener != null) { mListener.onUpdateCurrent(whichScreen); } } break; case MotionEvent.ACTION_UP: mLongClickView = false; tempX = ev.getX(); tempY = ev.getY(); if(Math.abs(x1 - tempX) < 5 && Math.abs(y1 - tempY) < 5){ snapToDestination(); mListener.onWorkspaceClick(mCurrentScreen); break; } if (mTouchState == TOUCH_STATE_SCROLLING) { final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); final int velocityX = (int) velocityTracker.getXVelocity(); if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) { // Fling hard enough to move left snapToScreen(mCurrentScreen - 1, false); } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) { // Fling hard enough to move right snapToScreen(mCurrentScreen + 1, false); } else { snapToDestination(); } if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } } mTouchState = TOUCH_STATE_REST; break; case MotionEvent.ACTION_CANCEL: // 此处代码同MotionEvent.ACTION_UP 是一样的为了解决listView在上下滑动的时候容易卡住问题。 mLongClickView = false; if (mTouchState == TOUCH_STATE_SCROLLING) { final VelocityTracker velocityTracker = mVelocityTracker; velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); final int velocityX = (int) velocityTracker.getXVelocity(); if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) { // Fling hard enough to move left snapToScreen(mCurrentScreen - 1, false); } else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) { // Fling hard enough to move right snapToScreen(mCurrentScreen + 1, false); } else { snapToDestination(); } if (mVelocityTracker != null) { mVelocityTracker.recycle(); mVelocityTracker = null; } } mTouchState = TOUCH_STATE_REST; break; } return true; } /** * 重载方法,应对在横竖屏时getWidth()不准确的问题。 * * @param whichScreen * @param elastic * @param width */ private void snapToScreen(int whichScreen, boolean noElastic, int width, int duration) { if (mScroller == null || width <= 0) { return; } if (noElastic) { mSnapWhthNoElastic = true; mScroller = new Scroller(getContext()); } if (!mScroller.isFinished()) { return; } enableChildrenCache(); whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1)); boolean changingScreens = whichScreen != mCurrentScreen; mNextScreen = whichScreen; if (mListener != null) { mListener.onUpdateCurrent(mNextScreen); } View focusedChild = getFocusedChild(); if (focusedChild != null && changingScreens && focusedChild == getChildAt(mCurrentScreen)) { focusedChild.clearFocus(); } final int newX = whichScreen * width; final int delta = newX - getScrollX(); mScroller.startScroll(getScrollX(), 0, delta, 0, duration); invalidate(); } /** * 重载方法,应对在横竖屏时getWidth()不准确的问题。 * * @param whichScreen * @param elastic * @param width */ private void snapToScreen(int whichScreen, boolean noElastic, int width) { snapToScreen(whichScreen, noElastic, width, mScrollingDuration); } public void snapToScreen(int whichScreen, boolean noElastic) { if (whichScreen >= 0 && whichScreen < getChildCount()) { snapToScreen(whichScreen, noElastic, getWidth()); } } public void snapToScreen(int whichScreen, int duration, boolean noElastic) { if (whichScreen >= 0 && whichScreen < getChildCount()) { snapToScreen(whichScreen, noElastic, getWidth(), duration); } } private void snapToDestination() { final int screenWidth = getWidth(); int whichScreen = (getScrollX() + (screenWidth / 2)) / screenWidth; whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1)); snapToScreen(whichScreen, false); } /** * 查找组件所在屏幕索引 * * @param v * 组件 * @return 屏幕索引 */ public int getScreenForView(View v) { int result = -1; if (v != null) { ViewParent vp = v.getParent(); int count = getChildCount(); for (int i = 0; i < count; i++) { if (vp == getChildAt(i)) { return i; } } } return result; } /** * * @return 当前屏幕View */ public CellLayout getCurrentScreenView() { return (CellLayout) getChildAt(mCurrentScreen); } /** * 监听器 * * @author yuankai * @version 1.0 * @date 2011-6-4 */ public interface IWorkspaceListener { /** * 更新总屏幕数时回调 * * @param total * 总数 */ public void onUpdateTotalNum(int total); /** * 更新当前屏幕索引时回调 * * @param current */ public void onUpdateCurrent(int current); /** * 点击事件回调 * */ public void onWorkspaceClick(int current); } }