android 仿iphone主题之主菜单
现在很多第三方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主题之主菜单相关推荐
- android 苹果菜单栏,android仿iphone主题效果的主菜单
现在很多第三方Launcher((如360Launcher,GoLauncher)带有iphone主题,相信玩Android的人大都知道. 本例实现仿iphone主题的launcher的冰山一角.如下 ...
- android 仿微信demo————微信主界面实现
android 仿微信demo----微信启动界面实现 android 仿微信demo----注册功能实现(移动端) android 仿微信demo----注册功能实现(服务端) android 仿微 ...
- android 仿iphone滚轮,android仿iphone滚轮控件显示效果
android仿iphone滚轮控件显示效果,供大家参考,具体内容如下 在论坛里看到的,自己弄个效果: 这个滚动的WheelView /* * Android Wheel Control. * htt ...
- android仿iphone日期时间选择器,Android仿iPhone日期时间选择器详解
本文实例为大家分享了Android仿iPhone时间选择器的具体代码,供大家参考,具体内容如下 先看效果图 如何使用 import java.text.DateFormat; import java. ...
- android仿iphone苹果桌面源码拖拉排
android仿iphone苹果桌面源码拖拉排序哦 仿苹果桌面 仿iphone ios桌面 launcher 本人见市场上很少仿排序拖拉这样的算法.所以改android源码.供大家学习使用哦. 这是a ...
- android 美团下拉菜单,Android仿美团分类下拉菜单实例代码
本文实例为大家分享了Android仿美团下拉菜单的实现代码,分类进行选择,供大家参考,具体内容如下 效果图 操作平台 AS2.0 第三方框架:butterknife build.gradle depe ...
- Android 仿iphone提醒事项(一)
最近用android仿写了一下iphone提醒事项,可动态实现表增加和数据增加,效果如下: 主要难点在第一页的动画实现和数据的存储交互. 看界面分析先将单个列表封装成一个自定义的view ...
- android+仿iphone,Android编程实现仿iphone抖动效果的方法(附源码)
Android编程实现仿iphone抖动效果的方法(附源码) 时间:2021-05-20 本文实例讲述了Android编程实现仿iphone抖动效果的方法.分享给大家供大家参考,具体如下: 布局文件: ...
- Android 9.0 系统设置显示主菜单添加屏幕旋转菜单实现旋转屏幕功能
1.前言 在android9.0的系统rom定制化开发中,在对系统设置进行定制开发中,有产品需求要求增加旋转屏幕功能的菜单,就是在点击旋转屏幕菜单后弹窗显示旋转0度,旋转 90度,旋转180度,旋转2 ...
最新文章
- 山西省内大学计算机,在山西省哪几所大学的计算机系比较有名
- 优秀的API接口设计原则及方法
- caffe基础(7): 命令行解析
- PL/SQL 处理流程
- html 闪烁文本,HTML最简单的文字闪烁代码
- IT巨头互掐云存储:Dropbox能否一马当先
- 将文件复制到ftp发生错误 请检查是否有权限_SE文件管理器2.8.6解锁完整功能版...
- mysql 5.6 密码_Mysql5.6 忘记root密码的解决办法
- Github查看文件历史提交和修改记录
- 基于用户画像的商品推荐挑战赛
- p51 thinkpad 拆解_ThinkPad P51值得买吗?联想ThinkPad P51移动工作站图解评测
- sqlplus工具linux,sqlplus 工具的使用
- 夏至 | 心怀热忱,认真生活 ;不负时光,不负自己
- 大学本科计算机专业那些课 左飞
- 无痕模式后如何找到历史_新高考“3+1+2”模式下,物理与历史如何选择更好
- 【小说】玻璃碎片-第三章
- 为大家推荐几个不错的公众号!
- C语言程序设计博客作业02
- 怎么开淘宝网店?淘宝网开店流程图解,淘宝开店教程!
- php微信群,PHP微信群加群强制分享转发裂变源码