现在很多第三方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.Workspaceandroid:id="@+id/workspace"android:layout_width="fill_parent"android:layout_height="fill_parent" /><com.xyz.workspace.PageIndicatorandroid: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 stubmScroller = new Scroller(context);mCurScreen = DEFAULT_SCREEN;mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {// TODO Auto-generated method stubif (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;}}}}@Overrideprotected 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;}@Overridepublic void computeScroll() {// TODO Auto-generated method stubif (mScroller.computeScrollOffset()) {scrollTo(mScroller.getCurrX(), mScroller.getCurrY());postInvalidate();}}@Overridepublic boolean onTouchEvent(MotionEvent event) {// TODO Auto-generated method stubif (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;}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {// TODO Auto-generated method stubfinal 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 stubmContext = ctx;}public PageIndicator(Context ctx, AttributeSet attrs) {super(ctx, attrs);// TODO Auto-generated constructor stubmContext = 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);}}
}

这两个 类的作用上面已经说了,有什么看不明白的欢迎提问,或自行google。

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;}@Overridepublic int getCount() {// TODO Auto-generated method stubint size = mPackagesInfo.size();return size / APP_PAGE_SIZE > 0&& size - (APP_PAGE_SIZE * (mPageIndex + 1)) > 0 ? APP_PAGE_SIZE: size % APP_PAGE_SIZE;}@Overridepublic Object getItem(int position) {// TODO Auto-generated method stubreturn mPackagesInfo.get(APP_PAGE_SIZE * mPageIndex + position);}@Overridepublic long getItemId(int position) {// TODO Auto-generated method stubreturn position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {// TODO Auto-generated method stubif (convertView == null) {convertView = new AppItem(mContext, (ResolveInfo) getItem(position));}convertView.setOnClickListener(this);convertView.setTag(Integer.valueOf(position));return convertView;}/** 点击启动app **/@Overridepublic void onClick(View v) {// TODO Auto-generated method stubint 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 /* 圆角半径 */) {return getRoundCornerDrawable(ctx,mContext.getResources().getDrawable(resId), roundPX);}private Drawable getRoundCornerDrawable(Context ctx, Drawable drawable,float roundPX /* 圆角半径 */) {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);}
}

注意咯,show函数就是替换显示对应iphone里app的图标(来源反编译iphone主题的launcher或锁屏),利用 包名 判断是哪个应用再换上对应图标,例如com.android.mms---信息,com.android.contacts---联系人,这里有个疑问,为什么phone模块的package_name的也是com.android.contacts,有人知道么?谢谢啦!

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" ><ImageViewandroid:id="@+id/icon"android:layout_width="@dimen/app_icon_width"android:layout_height="@dimen/app_icon_width"android:layout_gravity="center_horizontal" /><TextViewandroid: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>

主Activity就是获取所有app信息及初始化界面,

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. */@Overridepublic 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);}@Overridepublic void onChange(int cnt, int index) {// TODO Auto-generated method stubmIndicator.setIndication(cnt, index);}
}

下载地址:http://download.csdn.net/detail/zhouyuanjing/5047826

就这么多了,~~完~~

android 仿iphone主题之主菜单相关推荐

  1. android 苹果菜单栏,android仿iphone主题效果的主菜单

    现在很多第三方Launcher((如360Launcher,GoLauncher)带有iphone主题,相信玩Android的人大都知道. 本例实现仿iphone主题的launcher的冰山一角.如下 ...

  2. android 仿微信demo————微信主界面实现

    android 仿微信demo----微信启动界面实现 android 仿微信demo----注册功能实现(移动端) android 仿微信demo----注册功能实现(服务端) android 仿微 ...

  3. android 仿iphone滚轮,android仿iphone滚轮控件显示效果

    android仿iphone滚轮控件显示效果,供大家参考,具体内容如下 在论坛里看到的,自己弄个效果: 这个滚动的WheelView /* * Android Wheel Control. * htt ...

  4. android仿iphone日期时间选择器,Android仿iPhone日期时间选择器详解

    本文实例为大家分享了Android仿iPhone时间选择器的具体代码,供大家参考,具体内容如下 先看效果图 如何使用 import java.text.DateFormat; import java. ...

  5. android仿iphone苹果桌面源码拖拉排

    android仿iphone苹果桌面源码拖拉排序哦 仿苹果桌面 仿iphone ios桌面 launcher 本人见市场上很少仿排序拖拉这样的算法.所以改android源码.供大家学习使用哦. 这是a ...

  6. android 美团下拉菜单,Android仿美团分类下拉菜单实例代码

    本文实例为大家分享了Android仿美团下拉菜单的实现代码,分类进行选择,供大家参考,具体内容如下 效果图 操作平台 AS2.0 第三方框架:butterknife build.gradle depe ...

  7. Android 仿iphone提醒事项(一)

    最近用android仿写了一下iphone提醒事项,可动态实现表增加和数据增加,效果如下:        主要难点在第一页的动画实现和数据的存储交互. 看界面分析先将单个列表封装成一个自定义的view ...

  8. android+仿iphone,Android编程实现仿iphone抖动效果的方法(附源码)

    Android编程实现仿iphone抖动效果的方法(附源码) 时间:2021-05-20 本文实例讲述了Android编程实现仿iphone抖动效果的方法.分享给大家供大家参考,具体如下: 布局文件: ...

  9. Android 9.0 系统设置显示主菜单添加屏幕旋转菜单实现旋转屏幕功能

    1.前言 在android9.0的系统rom定制化开发中,在对系统设置进行定制开发中,有产品需求要求增加旋转屏幕功能的菜单,就是在点击旋转屏幕菜单后弹窗显示旋转0度,旋转 90度,旋转180度,旋转2 ...

最新文章

  1. 山西省内大学计算机,在山西省哪几所大学的计算机系比较有名
  2. 优秀的API接口设计原则及方法
  3. caffe基础(7): 命令行解析
  4. PL/SQL 处理流程
  5. html 闪烁文本,HTML最简单的文字闪烁代码
  6. IT巨头互掐云存储:Dropbox能否一马当先
  7. 将文件复制到ftp发生错误 请检查是否有权限_SE文件管理器2.8.6解锁完整功能版...
  8. mysql 5.6 密码_Mysql5.6 忘记root密码的解决办法
  9. Github查看文件历史提交和修改记录
  10. 基于用户画像的商品推荐挑战赛
  11. p51 thinkpad 拆解_ThinkPad P51值得买吗?联想ThinkPad P51移动工作站图解评测
  12. sqlplus工具linux,sqlplus 工具的使用
  13. 夏至 | 心怀热忱,认真生活 ;不负时光,不负自己
  14. 大学本科计算机专业那些课 左飞
  15. 无痕模式后如何找到历史_新高考“3+1+2”模式下,物理与历史如何选择更好
  16. 【小说】玻璃碎片-第三章
  17. 为大家推荐几个不错的公众号!
  18. C语言程序设计博客作业02
  19. 怎么开淘宝网店?淘宝网开店流程图解,淘宝开店教程!
  20. php微信群,PHP微信群加群强制分享转发裂变源码

热门文章

  1. 操作系统——进程与线程の选择题整理
  2. Android5.0及以上版本新特性
  3. 海贼王热血航线服务器维护,航海王热血航线连接服务器失败?解决方法一览
  4. Notion软件的Android版本下载指南来啦
  5. 《念奴娇·赤壁怀古》 苏轼
  6. 推荐计算机类英文ei源刊,2008EI英文刊源 计算机类 .doc
  7. Python开发的环境安装和配置
  8. Mysql索引面试题集锦
  9. 本地Word图文直接复制到富文本编辑器中
  10. onreadystatechange的认识