当前位置:主页 > 移动开发 > Android代码 >

android仿iphone主题效果的主菜单

时间:2021-08-14 08:32:32 | 栏目:Android代码 | 点击:

现在很多第三方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); 
  } 
 } 
} 

这两个类的作用上面已经说了,有什么看不明白的欢迎提问,或自行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; 
 } 
 
 @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); 
 } 
} 

注意咯,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" > 
 
 <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> 

主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. */ 
 @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); 
 } 
}

源码下载:android仿iphone主题之主菜单

您可能感兴趣的文章:

相关文章