`

在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 items
	protected 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 event
	private 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 count
		if (mRowsCount < item.getRow() + item.getRowSpan())
			mRowsCount = item.getRow() + item.getRowSpan();

		// adjust columns count
		if (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;
	}

	@Override
	protected 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);
			}
		}
	}

	@Override
	protected 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 scrollLayout
		final 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 page
		if (getScrollX() != deltaX) {
			deltaX = deltaX - getScrollX();
			needRedraw = true;
		}

		if (getScrollY() != deltaY) {
			deltaY = deltaY - getScrollY();
			needRedraw = true;
		}

		if (needRedraw) {
			// only scroll one screen
			int startX = mColWidth;
			int startY = mRowHeight;

			if (deltaX > mColWidth) {
				startX = deltaX - mColWidth;
				deltaX = mColWidth;
			} else
				startX = getScrollX();

			if (deltaY > mRowHeight) {
				startY = deltaY - mRowHeight;
				deltaY = mRowHeight;
			} else
				startY = 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;
	}

	@Override
	public void computeScroll() {
		if (mScroller.computeScrollOffset()) {
			scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
			postInvalidate();
		}
	}

	@Override
	public 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 left
				col--;
			} else if (velocityX < -SNAP_VELOCITY && mCurCol < mColsCount - 1) {
				// Fling enough to move right
				col++;
			}

			if (velocityY > SNAP_VELOCITY && mCurRow > 0) {
				// Fling enough to move up
				row--;
			} else if (velocityY < -SNAP_VELOCITY && mCurRow < mRowsCount - 1) {
				// Fling enough to move down
				row++;
			}

			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;
	}

	@Override
	public 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;
		else
			return 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() {

		@Override
		public 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);
	}
}
分享到:
评论

相关推荐

    安卓Android源码——MetroUI.zip

    在实际开发过程中,我们可能需要对Android的默认启动器进行修改,或者开发一个独立的应用来模拟MetroUI的桌面环境。这涉及到对Launcher类的继承和重写,以及对Home键事件的处理。同时,为了保证兼容性,我们需要对...

    Android应用源码之MetroUI.zip

    【Android应用源码之MetroUI】是一个针对Android平台的用户界面设计示例,它采用了Windows Phone风格的Metro UI设计理念,提供了清新、简洁且富有现代感的界面体验。这个开源项目对于学习Android应用开发,尤其是...

    android 仿 windows metro 风格 源码

    在Android平台上,开发人员可以利用各种技术和库来实现独特的用户界面设计,其中之一就是仿照Windows Metro风格。"android 仿 windows metro 风格 源码"是一个专门用于创建类似Windows 8或Windows 10 Metro UI效果的...

    Android仿Win8的metro的UI界面源码.zip

    本资源是针对Android平台的一个开源项目,它实现了对Win8 metro风格UI的模仿,为开发者提供了在Android上构建类似界面的可能性。 这个源码库主要包含以下几个关键知识点: 1. **布局设计**:Win8 metro UI的核心...

    Android程序研发源码Android 仿Win8的metro的UI界面源码.zip

    1. **自定义View和布局**:由于Android原生并没有直接支持Metro UI风格的组件,所以需要创建自定义的View和Layout来模拟磁贴效果。这涉及到对Android的Canvas、Paint以及ViewGroup的深入理解。 2. **Material ...

    Android 仿Win8的metro的UI界面

    总之,通过以上方法,开发者可以在Android平台上创造出既美观又实用的仿Win8 Metro UI界面。这个过程既需要对Android SDK有深入理解,也需要对用户体验有敏锐的洞察。实践中不断尝试和优化,才能打造出最符合用户...

    Android代码-仿Win8的metro的UI界面源码.zip

    在Android平台上,开发人员可以创建出与Windows 8(Win8)风格的Metro UI界面相类似的用户界面。这种设计风格以其简洁、现代化的布局和大块色彩鲜艳的瓷砖而闻名,为用户提供了一种直观且触控友好的体验。在"Android...

    Android 仿Win8的metro的UI界面源码.rar

    在Android平台上,开发人员可以创建出类似于Windows 8 Metro UI的界面效果,这主要得益于Android的自定义布局和丰富的UI组件。"Android 仿Win8的metro的UI界面源码.rar"是一个压缩包,其中包含了实现这一效果的源...

    Android源码——仿Win8的metro的UI界面源码.zip

    通过分析和研究这个"Android源码——仿Win8的metro的UI界面源码",开发者不仅可以掌握如何在Android上构建美观的UI,还能学习到Android系统的核心编程技术,包括视图、动画、数据绑定以及响应式设计等方面的知识。...

    Android 仿Win8的metro的UI界面源码.zip

    在Android平台上,开发者可以利用Java语言来实现丰富的...通过研究这个项目源码,开发者不仅可以学习到如何在Android上实现Windows 8风格的界面,还可以深入理解Android的UI设计原理和编程实践,提升自己的开发技能。

    Android 仿Win8的metro的UI界面源码-IT计算机-毕业设计.zip

    在Android平台上,开发一款仿Windows 8 Metro UI风格的应用是一个挑战性的任务,因为这需要对Android UI组件、布局管理以及自定义视图有深入的理解。这个"Android 仿Win8的metro的UI界面源码"项目是针对IT计算机专业...

    Android源码——仿Win8的metro的UI界面源码.rar

    【压缩包子文件的文件名称列表】中的"1-1209291109500-L.png"可能是一个截图或者设计示例,展示了仿Win8 UI界面在Android上的实际效果,帮助开发者理解最终的视觉呈现,或者是关键功能的演示。 在这个项目中,...

    让您在 Android 设备上体验 Windows 10 PC Metro U.rar

    【标题】"让您在 Android 设备上体验 Windows 10 PC Metro U.rar" 提供的是一个应用程序,旨在将 Android 设备的用户界面转变为类似于 Windows 10 操作系统的 Metro UI 风格。这个应用程序的核心功能是模拟 Windows ...

    Android 仿Win8的metro

    在Android平台上,开发人员有时会受到启发,尝试模仿其他操作系统的设计元素,比如Windows 8的“Modern UI”或称为“Metro”风格。这种设计风格以其大胆的色彩、平滑的动画和磁贴式布局为特点,给用户带来独特的交互...

    Android仿Win8的metro的UI界面(上)

    在Android开发中,创建仿Win8 Metro UI界面可以带来独特的用户体验,这种设计风格以其简洁、直观的磁贴式布局而闻名。在这个主题中,我们将探讨如何实现这样的界面,并解决一些常见的问题。 首先,我们注意到描述中...

    Android仿WIN8风格UI

    在Android平台上实现Windows 8(以下简称WIN8)风格的用户界面(UI)是一种创新的设计实践,它可以为用户提供熟悉且独特的交互体验。以下是对这个主题的详细解析: **1. 设计理念** WIN8 UI的核心设计理念是“现代...

    android win8风格界面

    在Android上,我们可以使用自定义View或者第三方库如“android-metro-ui”来创建类似的磁贴效果。通过自定义View,开发者可以控制磁贴的大小、颜色、字体以及动态内容的刷新机制。第三方库则提供了一套完整的解决...

Global site tag (gtag.js) - Google Analytics