现在很多第三方Launcher((如360Launcher,GoLauncher)带有iphone主题,相信玩Android的人大都知道。
本例实现仿iphone主题的launcher的冰山一角。如下图:
从效果看,大概就能猜出用什么控件类(支持左右滑动的控件类+GridView),支持左右滑动的控件类,有很多了比如常用的Gallery,ViewPager,ViewFlipper,ViewFlow等等,本例自定义继承ViewGroup的。看过launcher源码的人应该都知道 有个Workspace类继承ViewGroup实现主菜单的。
闲话不多说了!
主布局:main.xml
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical" >
- <com.xyz.workspace.Workspace
- android:id="@+id/workspace"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent" />
- <com.xyz.workspace.PageIndicator
- android:id="@+id/indicator"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_centerHorizontal="true"
- android:layout_marginBottom="20dip" />
- </RelativeLayout>
第一个自定义类Workspace就是实现左右滑动的,第二个类PageIndicator做指示器用。
Workspace.java
- package com.xyz.workspace;
- import java.util.List;
- import android.content.Context;
- import android.util.AttributeSet;
- import android.view.MotionEvent;
- import android.view.VelocityTracker;
- import android.view.View;
- import android.view.ViewConfiguration;
- import android.view.ViewGroup;
- import android.widget.Scroller;
- public class Workspace extends ViewGroup {
- private static final String TAG = "Workspace";
- private Scroller mScroller;
- private VelocityTracker mVelocityTracker;
- private static final int DEFAULT_SCREEN = 0;
- private static final int TOUCH_STATE_REST = 0;
- private static final int TOUCH_STATE_SCROLLING = 1;
- private static final int SNAP_VELOCITY = 600;
- public static final int APP_PAGE_SIZE = 16;
- private int mCurScreen;
- private int mTouchState = TOUCH_STATE_REST;
- private int mTouchSlop;
- private float mLastMotionX;
- private float mLastMotionY;
- private OnViewChangedListener mOnViewChangedListener;
- public Workspace(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- // TODO Auto-generated constructor stub
- }
- public Workspace(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- // TODO Auto-generated constructor stub
- mScroller = new Scroller(context);
- mCurScreen = DEFAULT_SCREEN;
- mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
- }
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- // TODO Auto-generated method stub
- if (changed) {
- int childLeft = 0;
- final int childCount = getChildCount();
- for (int i = 0; i < childCount; i++) {
- final View childView = getChildAt(i);
- if (childView.getVisibility() != View.GONE) {
- final int childWidth = childView.getMeasuredWidth();
- childView.layout(childLeft, 0, childLeft + childWidth,
- childView.getMeasuredHeight());
- childLeft += childWidth;
- }
- }
- }
- }
- @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(
- "ScrollLayout only canmCurScreen run at EXACTLY mode!");
- }
- final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
- if (heightMode != MeasureSpec.EXACTLY) {
- throw new IllegalStateException(
- "ScrollLayout only can run at EXACTLY mode!");
- }
- final int count = getChildCount();
- for (int i = 0; i < count; i++) {
- getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
- }
- scrollTo(mCurScreen * width, 0);
- }
- public void snapToDestination() {
- final int screenWidth = getWidth();
- final int destScreen = (getScrollX() + screenWidth / 2) / screenWidth;
- snapToScreen(destScreen);
- }
- public void snapToScreen(int whichScreen) {
- whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
- if (getScrollX() != (whichScreen * getWidth())) {
- final int delta = whichScreen * getWidth() - getScrollX();
- mScroller.startScroll(getScrollX(), 0, delta, 0,
- Math.abs(delta) * 2);
- mCurScreen = whichScreen;
- invalidate();
- }
- if (mOnViewChangedListener != null) {
- mOnViewChangedListener.onChange(getChildCount(), whichScreen);
- }
- }
- public void setToScreen(int whichScreen) {
- whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
- mCurScreen = whichScreen;
- scrollTo(whichScreen * getWidth(), 0);
- }
- public int getCurScreen() {
- return mCurScreen;
- }
- @Override
- public void computeScroll() {
- // TODO Auto-generated method stub
- if (mScroller.computeScrollOffset()) {
- scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
- postInvalidate();
- }
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- // TODO Auto-generated method stub
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(event);
- final int action = event.getAction();
- final float x = event.getX();
- final float y = event.getY();
- switch (action) {
- case MotionEvent.ACTION_DOWN:
- if (!mScroller.isFinished()) {
- mScroller.abortAnimation();
- }
- mLastMotionX = x;
- break;
- case MotionEvent.ACTION_MOVE:
- int deltaX = (int) (mLastMotionX - x);
- mLastMotionX = x;
- scrollBy(deltaX, 0);
- break;
- case MotionEvent.ACTION_UP:
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000);
- int velocityX = (int) velocityTracker.getXVelocity();
- if (velocityX > SNAP_VELOCITY && mCurScreen > 0) {
- snapToScreen(mCurScreen - 1);
- } else if (velocityX < -SNAP_VELOCITY
- && mCurScreen < getChildCount() - 1) {
- snapToScreen(mCurScreen + 1);
- } else {
- snapToDestination();
- }
- 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) {
- // TODO Auto-generated method stub
- 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_MOVE:
- final int xDiff = (int) Math.abs(mLastMotionX - x);
- if (xDiff > mTouchSlop) {
- mTouchState = TOUCH_STATE_SCROLLING;
- }
- break;
- case MotionEvent.ACTION_DOWN:
- mLastMotionX = x;
- mLastMotionY = y;
- mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
- : TOUCH_STATE_SCROLLING;
- break;
- case MotionEvent.ACTION_CANCEL:
- case MotionEvent.ACTION_UP:
- mTouchState = TOUCH_STATE_REST;
- break;
- }
- return mTouchState != TOUCH_STATE_REST;
- }
- public void setOnViewChangedListener(OnViewChangedListener l) {
- mOnViewChangedListener = l;
- }
- public interface OnViewChangedListener {
- public void onChange(int cnt, int index);
- }
- }
PageIndicator.java:
- package com.xyz.workspace;
- import android.content.Context;
- import android.util.AttributeSet;
- import android.view.View;
- import android.widget.ImageView;
- import android.widget.LinearLayout;
- public class PageIndicator extends LinearLayout {
- private Context mContext;
- public PageIndicator(Context ctx) {
- super(ctx);
- // TODO Auto-generated constructor stub
- mContext = ctx;
- }
- public PageIndicator(Context ctx, AttributeSet attrs) {
- super(ctx, attrs);
- // TODO Auto-generated constructor stub
- mContext = ctx;
- }
- public void setIndication(int cnt, int index) {
- if (index < 0 || index > cnt)
- index = 0;
- removeAllViews();
- for (int i = 0; i < cnt; i++) {
- ImageView iv = new ImageView(mContext);
- iv.setImageResource(index == i ? R.drawable.indicator_current
- : R.drawable.indicator);
- if (i != 0 || i != cnt - 1) {
- iv.setPadding(4, 0, 4, 0);
- }
- addView(iv);
- }
- }
- }
ViewGroup实现好了,剩下就是实现GridView显示系统所有app,主要工作也就是实现GridView的适配器---GridAdapter
- package com.xyz.workspace;
- import java.util.List;
- import android.content.ComponentName;
- import android.content.Context;
- import android.content.Intent;
- import android.content.pm.ResolveInfo;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.view.ViewGroup;
- import android.widget.BaseAdapter;
- import static com.xyz.workspace.Workspace.APP_PAGE_SIZE;
- public class GridAdapter extends BaseAdapter implements OnClickListener {
- private Context mContext;
- private int mPageIndex;
- private List<ResolveInfo> mPackagesInfo;
- public GridAdapter(Context context, List<ResolveInfo> listInfo, int page) {
- mContext = context;
- mPackagesInfo = listInfo;
- mPageIndex = page;
- }
- @Override
- public int getCount() {
- // TODO Auto-generated method stub
- int size = mPackagesInfo.size();
- return size / APP_PAGE_SIZE > 0
- && size - (APP_PAGE_SIZE * (mPageIndex + 1)) > 0 ? APP_PAGE_SIZE
- : size % APP_PAGE_SIZE;
- }
- @Override
- public Object getItem(int position) {
- // TODO Auto-generated method stub
- return mPackagesInfo.get(APP_PAGE_SIZE * mPageIndex + position);
- }
- @Override
- public long getItemId(int position) {
- // TODO Auto-generated method stub
- return position;
- }
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- // TODO Auto-generated method stub
- if (convertView == null) {
- convertView = new AppItem(mContext, (ResolveInfo) getItem(position));
- }
- convertView.setOnClickListener(this);
- convertView.setTag(Integer.valueOf(position));
- return convertView;
- }
- /** 点击启动app **/
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- int pos = (Integer) v.getTag();
- ResolveInfo info = (ResolveInfo) getItem(pos);
- Intent i = new Intent(Intent.ACTION_MAIN);
- i.addCategory(Intent.CATEGORY_LAUNCHER);
- i.setComponent(new ComponentName(info.activityInfo.packageName,
- info.activityInfo.name));
- mContext.startActivity(i);
- }
- }
GridView的每个item不用说,一看就知道是一个LinearLayout上面是个ImageView,下面一个TextView了。我把它封装了下---AppItem:
- package com.xyz.workspace;
- import android.content.Context;
- import android.content.pm.PackageManager;
- import android.content.pm.ResolveInfo;
- import android.graphics.Bitmap;
- import android.graphics.Canvas;
- import android.graphics.Paint;
- import android.graphics.PixelFormat;
- import android.graphics.PorterDuffXfermode;
- import android.graphics.Rect;
- import android.graphics.RectF;
- import android.graphics.Bitmap.Config;
- import android.graphics.PorterDuff.Mode;
- import android.graphics.drawable.BitmapDrawable;
- import android.graphics.drawable.Drawable;
- import android.util.AttributeSet;
- import android.view.LayoutInflater;
- import android.widget.ImageView;
- import android.widget.RelativeLayout;
- import android.widget.TextView;
- public class AppItem extends RelativeLayout {
- private Context mContext;
- private ImageView mAppIcon;
- private TextView mAppName;
- private ResolveInfo mAppInfo;
- private PackageManager mPackageManager;
- public AppItem(Context context) {
- super(context);
- mContext = context;
- mPackageManager = context.getPackageManager();
- LayoutInflater.from(context).inflate(R.layout.app_item, this);
- mAppIcon = (ImageView) findViewById(R.id.icon);
- mAppName = (TextView) findViewById(R.id.app_name);
- }
- public AppItem(Context context, ResolveInfo info) {
- this(context);
- mAppInfo = info;
- show();
- }
- private void show() {
- String packageName = mAppInfo.activityInfo.packageName;
- String appName = mAppInfo.activityInfo.loadLabel(mPackageManager)
- .toString();
- if (appName.equals("拨号")) {
- mAppIcon.setImageResource(R.drawable.com_android_phone);
- } else if (packageName.equals("com.android.contacts")) {
- mAppIcon.setImageResource(R.drawable.com_android_contacts);
- } else if (packageName.equals("com.android.mms")) {
- mAppIcon.setImageResource(R.drawable.com_android_mms);
- } else if (packageName.equals("com.android.music")) {
- mAppIcon.setImageResource(R.drawable.com_android_music);
- } else if (packageName.equals("com.android.browser")) {
- mAppIcon.setImageResource(R.drawable.com_android_browser);
- } else if (packageName.equals("com.android.settings")) {
- mAppIcon.setImageResource(R.drawable.com_android_settings);
- } else if (packageName.equals("com.android.email")) {
- mAppIcon.setImageResource(R.drawable.com_android_email);
- } else if (packageName.equals("com.android.calendar")) {
- mAppIcon.setImageResource(R.drawable.com_android_calendar);
- } else if (packageName.equals("com.android.calculator2")) {
- mAppIcon.setImageResource(R.drawable.com_android_calculator2);
- } else if (packageName.equals("com.android.deskclock")) {
- mAppIcon.setImageResource(R.drawable.com_android_deskclock);
- } else if (packageName.equals("com.android.camera")) {
- mAppIcon.setImageResource(R.drawable.com_android_camera);
- } else if (packageName.equals("com.android.soundrecorder")) {
- mAppIcon.setImageResource(R.drawable.com_android_soundrecorder);
- } else if (packageName.equals("com.tencent.mobileqq")) {
- mAppIcon.setImageResource(R.drawable.com_tencent_qq);
- } else if (packageName.equals("com.tencent.mm")) {
- mAppIcon.setImageResource(R.drawable.com_tencent_mm);
- } else if (packageName.equals("com.tencent.mtt")) {
- mAppIcon.setImageResource(R.drawable.com_tencent_mtt);
- } else if (packageName.equals("com.sina.weibo")) {
- mAppIcon.setImageResource(R.drawable.com_sina_weibo);
- } else if (packageName.equals("com.sds.android.ttpod")) {
- mAppIcon.setImageResource(R.drawable.com_sds_android_ttpod);
- //
- } else if (packageName.equals("com.youdao.dict")) {
- mAppIcon.setImageResource(R.drawable.com_youdao_dict);
- } else {
- mAppIcon.setImageDrawable(getRoundCornerDrawable(mContext,
- mAppInfo.activityInfo.loadIcon(mPackageManager), 20));
- }
- mAppName.setText(appName);
- }
- private Drawable getRoundCornerDrawable(Context ctx, int resId,
- float roundPX /* <span style="font-size:14px;">圆角半径 </span>*/) {
- return getRoundCornerDrawable(ctx,
- mContext.getResources().getDrawable(resId), roundPX);
- }
- private Drawable getRoundCornerDrawable(Context ctx, Drawable drawable,
- float roundPX /* <span style="font-size:14px;">圆角半径 </span>*/) {
- int w = ctx.getResources()
- .getDimensionPixelSize(R.dimen.app_icon_width);
- int h = w;
- Bitmap bitmap = Bitmap
- .createBitmap(
- w,
- h,
- drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
- : Bitmap.Config.RGB_565);
- Canvas canvas = new Canvas(bitmap);
- drawable.setBounds(0, 0, w, h);
- drawable.draw(canvas);
- int width = bitmap.getWidth();
- int height = bitmap.getHeight();
- Bitmap retBmp = Bitmap.createBitmap(width, height, Config.ARGB_8888);
- Canvas can = new Canvas(retBmp);
- final int color = 0xff424242;
- final Paint paint = new Paint();
- final Rect rect = new Rect(0, 0, width, height);
- final RectF rectF = new RectF(rect);
- paint.setColor(color);
- paint.setAntiAlias(true);
- can.drawARGB(0, 0, 0, 0);
- can.drawRoundRect(rectF, roundPX, roundPX, paint);
- paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
- can.drawBitmap(bitmap, rect, rect, paint);
- return new BitmapDrawable(retBmp);
- }
- }
AppItem引用一个布局:
app_item.xml:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/app_icon_width"
- android:layout_height="@dimen/app_icon_height"
- android:gravity="center"
- android:orientation="vertical" >
- <ImageView
- android:id="@+id/icon"
- android:layout_width="@dimen/app_icon_width"
- android:layout_height="@dimen/app_icon_width"
- android:layout_gravity="center_horizontal" />
- <TextView
- android:id="@+id/app_name"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:ellipsize="marquee"
- android:maxWidth="@dimen/app_icon_height"
- android:singleLine="true"
- android:textColor="@android:color/white"
- android:textSize="12sp" />
- </LinearLayout>
MainActivty.java:
- package com.xyz.workspace;
- import java.util.List;
- import com.xyz.workspace.Workspace.OnViewChangedListener;
- import android.app.Activity;
- import android.content.Intent;
- import android.content.pm.ResolveInfo;
- import android.os.Bundle;
- import android.view.Gravity;
- import android.widget.GridView;
- import static com.xyz.workspace.Workspace.APP_PAGE_SIZE;
- public class MainActivity extends Activity implements OnViewChangedListener {
- private Workspace mWorkspace;
- private PageIndicator mIndicator;
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- mWorkspace = (Workspace) findViewById(R.id.workspace);
- mIndicator = (PageIndicator) findViewById(R.id.indicator);
- List<ResolveInfo> apps = loadApps();
- for (int i = 0; i < Math.ceil(1.0f * apps.size() / APP_PAGE_SIZE); i++) {
- GridView grid = new GridView(this);
- grid.setNumColumns(4);
- grid.setHorizontalSpacing(10);
- grid.setVerticalSpacing(40);
- grid.setPadding(30, 50, 30, 20);
- grid.setGravity(Gravity.CENTER);
- grid.setAdapter(new GridAdapter(this, apps, i));
- mWorkspace.addView(grid);
- }
- mWorkspace.setOnViewChangedListener(this);
- mIndicator.setIndication(mWorkspace.getChildCount(), 0);
- }
- private List<ResolveInfo> loadApps() {
- Intent i = new Intent(Intent.ACTION_MAIN, null);
- i.addCategory(Intent.CATEGORY_LAUNCHER);
- return getPackageManager().queryIntentActivities(i, 0);
- }
- @Override
- public void onChange(int cnt, int index) {
- // TODO Auto-generated method stub
- mIndicator.setIndication(cnt, index);
- }
- }
源码下载:http://download.csdn.net/detail/beihaizuopeng/5121976