dai_lm 发表于 2013-1-30 04:04:15

在Android上模拟MetroUI

在Android上模拟WP7的MetroUI

MetroItem
import android.view.View;public class MetroItem {private View mMetroView = null;private int mRow = 0;private int mRowSpan = 1;private int mCol = 0;private int mColSpan = 1;/** * @param v * @param row * @param col */public MetroItem(View v, int row, int col) {this(v, row, 1, col, 1);}/** * @param v * @param row * @param rowspan * @param col * @param colspan */public MetroItem(View v, int row, int rowspan, int col, int colspan) {mMetroView = v;if (row < 0)throw new IllegalArgumentException("row < 0");mRow = row;if (col < 0)throw new IllegalArgumentException("col < 0");mCol = col;if (rowspan < 1)throw new IllegalArgumentException("rowspan < 1");mRowSpan = rowspan;if (colspan < 1)throw new IllegalArgumentException("colspan < 1");mColSpan = colspan;}public View getMetroView() {return mMetroView;}public int getRow() {return mRow;}public int getRowSpan() {return mRowSpan;}public int getCol() {return mCol;}public int getColSpan() {return mColSpan;}}
MetroView
import java.util.ArrayList;import android.content.Context;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.VelocityTracker;import android.view.View;import android.view.ViewConfiguration;import android.view.ViewGroup;import android.widget.Scroller;import com.test.android.metro.R;import com.test.android.metro.model.MetroItem;public class MetroView extends ViewGroup {public static enum OrientationType {All, Vertical, Horizontal};private static final String TAG = "MetroView";private static final LayoutParams FILL_FILL = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);/** Default count of visible items */private static final int DEF_VISIBLE_ROWS = 5;private static final int DEF_VISIBLE_COLS = 2;private OrientationType mOrientation = OrientationType.Horizontal;// Count of visible itemsprotected int visibleRows = DEF_VISIBLE_ROWS;protected int visibleCols = DEF_VISIBLE_COLS;private Scroller mScroller;private VelocityTracker mVelocityTracker;private int mCurRow = 0, mCurCol = 0;protected int mRowsCount = 0, mColsCount = 0;private static final int TOUCH_STATE_REST = 0;private static final int TOUCH_STATE_SCROLLING = 1;private static final int SNAP_VELOCITY = 600;private int mTouchState = TOUCH_STATE_REST;private int mTouchSlop;private float mLastMotionX, mLastMotionY;private MetroListener metroListener;protected ArrayList<MetroItem> mMetroItems = new ArrayList<MetroItem>();private int mRowHeight, mColWidth;private int mGap = 0;// for long press eventprivate boolean mHasPerformedLongPress;private CheckForLongPress mPendingCheckForLongPress;public MetroView(Context context) {this(context, null);}public MetroView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public MetroView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs);initViewGroup(context);}private void initViewGroup(Context context) {mScroller = new Scroller(context);mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();setOnLongClickListener(longclickListener);}public void setGap(int gap) {mGap = gap;}/** * set row and column count for visible item 0 equals to not change current * value others equal to ** @param rowCount * @param colCount */public void setVisibleItems(int rowCount, int colCount) {if (rowCount < 0 || colCount < 0)throw new IllegalArgumentException("visible count < 0");if (rowCount != 0)visibleRows = rowCount;if (colCount != 0)visibleCols = colCount;}public int getVisibleRows() {return visibleRows;}public int getVisibleCols() {return visibleCols;}public void addMetroItem(MetroItem item) {mMetroItems.add(item);addView(item.getMetroView(), FILL_FILL);adjustRowCol(item);}public boolean deleteMetroItem(MetroItem item) {boolean ret = false;if (mMetroItems.contains(item)) {mMetroItems.remove(item);removeView(item.getMetroView());ret = true;}mRowsCount = 0;mColsCount = 0;for (MetroItem mi : mMetroItems) {adjustRowCol(mi);}return ret;}private void adjustRowCol(MetroItem item) {// adjust rows countif (mRowsCount < item.getRow() + item.getRowSpan())mRowsCount = item.getRow() + item.getRowSpan();// adjust columns countif (mColsCount < item.getCol() + item.getColSpan())mColsCount = item.getCol() + item.getColSpan();}public void clearMetroItem() {mMetroItems.clear();removeAllViews();mRowsCount = 0;mColsCount = 0;}public void setOrientation(OrientationType orientation) {mOrientation = orientation;}public OrientationType getOrientation() {return mOrientation;}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {final int itemCount = mMetroItems.size();if (itemCount != getChildCount())throw new IllegalArgumentException("contain unrecorded child");for (int i = 0; i < itemCount; i++) {final MetroItem item = mMetroItems.get(i);final View childView = item.getMetroView();if (childView.getVisibility() != View.GONE) {final int childLeft = (mColWidth + mGap) * item.getCol();final int childTop = (mRowHeight + mGap) * item.getRow();final int childWidth = (mColWidth + mGap) * item.getColSpan()- mGap;final int childHeight = (mRowHeight + mGap) * item.getRowSpan()- mGap;childView.layout(childLeft, childTop, childLeft + childWidth,childTop + childHeight);}}}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);final int width = MeasureSpec.getSize(widthMeasureSpec);final int height = MeasureSpec.getSize(heightMeasureSpec);mRowHeight = (height - (visibleRows - 1) * mGap) / visibleRows;mColWidth = (width - (visibleCols - 1) * mGap) / visibleCols;// The children are given the same width and height as the scrollLayoutfinal int itemCount = mMetroItems.size();for (int i = 0; i < itemCount; i++) {final MetroItem item = mMetroItems.get(i);final View childView = item.getMetroView();final int childWidth = MeasureSpec.makeMeasureSpec((mColWidth + mGap) * item.getColSpan() - mGap,MeasureSpec.EXACTLY);final int childHeight = MeasureSpec.makeMeasureSpec((mRowHeight + mGap) * item.getRowSpan() - mGap,MeasureSpec.EXACTLY);childView.measure(childWidth, childHeight);}scrollTo((mColWidth + mGap) * mCurCol, (mRowHeight + mGap) * mCurRow);}/** * According to the position of current layout scroll to the destination * page. */public void snapToDestination() {final int destRow = (getScrollY() + (mRowHeight + mGap) / 2)/ (mRowHeight + mGap);final int destCol = (getScrollX() + (mColWidth + mGap) / 2)/ (mColWidth + mGap);snapTo(destRow, destCol);}public void snapTo(int whichRow, int whichCol) {if (whichRow < 0)whichRow = 0;if (whichCol < 0)whichCol = 0;Log.d(TAG, String.format("snap to row:%d, col:%d", whichRow, whichCol));boolean needRedraw = false;if (mOrientation == OrientationType.Horizontal) {whichRow = 0;if (whichCol + visibleCols > mColsCount)whichCol = Math.max(mColsCount - visibleCols, 0);} else if (mOrientation == OrientationType.Vertical) {whichCol = 0;if (whichRow + visibleRows > mRowsCount)whichRow = Math.max(mRowsCount - visibleRows, 0);} else if (mOrientation == OrientationType.All) {if (whichRow + visibleRows > mRowsCount)whichRow = Math.max(mRowsCount - visibleRows, 0);if (whichCol + visibleCols > mColsCount)whichCol = Math.max(mColsCount - visibleCols, 0);}int deltaX = whichCol * (mColWidth + mGap);int deltaY = whichRow * (mRowHeight + mGap);// get the valid layout pageif (getScrollX() != deltaX) {deltaX = deltaX - getScrollX();needRedraw = true;}if (getScrollY() != deltaY) {deltaY = deltaY - getScrollY();needRedraw = true;}if (needRedraw) {// only scroll one screenint startX = mColWidth;int startY = mRowHeight;if (deltaX > mColWidth) {startX = deltaX - mColWidth;deltaX = mColWidth;} elsestartX = getScrollX();if (deltaY > mRowHeight) {startY = deltaY - mRowHeight;deltaY = mRowHeight;} elsestartY = getScrollY();mScroller.startScroll(startX, startY, deltaX, deltaY,Math.max(Math.abs(deltaX), Math.abs(deltaY)) * 2);mCurRow = whichRow;mCurCol = whichCol;invalidate(); // Redraw the layout}}public int getCurRow() {return mCurRow;}public int getCurCol() {return mCurCol;}@Overridepublic void computeScroll() {if (mScroller.computeScrollOffset()) {scrollTo(mScroller.getCurrX(), mScroller.getCurrY());postInvalidate();}}@Overridepublic boolean onTouchEvent(MotionEvent event) {if (mVelocityTracker == null) {mVelocityTracker = VelocityTracker.obtain();}mVelocityTracker.addMovement(event);final int action = event.getAction();float x = event.getX();float y = event.getY();if (mOrientation == OrientationType.Horizontal)y = 0;else if (mOrientation == OrientationType.Vertical)x = 0;switch (action) {case MotionEvent.ACTION_DOWN:if (!mScroller.isFinished()) {mScroller.abortAnimation();}mLastMotionX = x;mLastMotionY = y;break;case MotionEvent.ACTION_MOVE:int deltaX = (int) (mLastMotionX - x);int deltaY = (int) (mLastMotionY - y);mLastMotionX = x;mLastMotionY = y;scrollBy(deltaX, deltaY);break;case MotionEvent.ACTION_UP:final VelocityTracker velocityTracker = mVelocityTracker;velocityTracker.computeCurrentVelocity(1000);int velocityX = (int) velocityTracker.getXVelocity();int velocityY = (int) velocityTracker.getYVelocity();int row = mCurRow;int col = mCurCol;if (velocityX > SNAP_VELOCITY && mCurCol > 0) {// Fling enough to move leftcol--;} else if (velocityX < -SNAP_VELOCITY && mCurCol < mColsCount - 1) {// Fling enough to move rightcol++;}if (velocityY > SNAP_VELOCITY && mCurRow > 0) {// Fling enough to move uprow--;} else if (velocityY < -SNAP_VELOCITY && mCurRow < mRowsCount - 1) {// Fling enough to move downrow++;}if (row == mCurRow && col == mCurCol)snapToDestination();else {snapTo(row, col);if (metroListener != null)metroListener.scrollto(row, col);}if (mVelocityTracker != null) {mVelocityTracker.recycle();mVelocityTracker = null;}mTouchState = TOUCH_STATE_REST;break;case MotionEvent.ACTION_CANCEL:mTouchState = TOUCH_STATE_REST;break;}return true;}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {final int action = ev.getAction();if ((action == MotionEvent.ACTION_MOVE)&& (mTouchState != TOUCH_STATE_REST)) {return true;}final float x = ev.getX();switch (action) {case MotionEvent.ACTION_MOVE:final int xDiff = (int) Math.abs(mLastMotionX - x);if (xDiff > mTouchSlop) {mTouchState = TOUCH_STATE_SCROLLING;cancelLongClick();}break;case MotionEvent.ACTION_DOWN:mLastMotionX = x;mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST: TOUCH_STATE_SCROLLING;if (isLongClickable())postCheckForLongClick();break;case MotionEvent.ACTION_CANCEL:case MotionEvent.ACTION_UP:mTouchState = TOUCH_STATE_REST;cancelLongClick();break;}if (mHasPerformedLongPress)return true;elsereturn mTouchState != TOUCH_STATE_REST;}private void postCheckForLongClick() {mHasPerformedLongPress = false;if (mPendingCheckForLongPress == null)mPendingCheckForLongPress = new CheckForLongPress();postDelayed(mPendingCheckForLongPress,ViewConfiguration.getLongPressTimeout());}private void cancelLongClick() {if (!mHasPerformedLongPress && mPendingCheckForLongPress != null)removeCallbacks(mPendingCheckForLongPress);}class CheckForLongPress implements Runnable {public void run() {if (performLongClick())mHasPerformedLongPress = true;}}private OnLongClickListener longclickListener = new OnLongClickListener() {@Overridepublic boolean onLongClick(View v) {longclickEvent();return true;}};/** * long click event */protected void longclickEvent() {Log.d(TAG, "long click");}public void setMereoListener(MetroListener metroListener) {this.metroListener = metroListener;}public interface MetroListener {public void scrollto(int row, int col);}}
页: [1]
查看完整版本: 在Android上模拟MetroUI