首页 > 系统 > Android > 正文

android仿iphone主题效果的主菜单

2019-10-22 18:22:38
字体:
来源:转载
供稿:网友

现在很多第三方Launcher((如360Launcher,GoLauncher)带有iphone主题,相信玩Android的人大都知道。

本例实现仿iphone主题的launcher的冰山一角。如下图:

android,iphone,主菜单      

从效果看,大概就能猜出用什么控件类(支持左右滑动的控件类+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主题之主菜单

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持VEVB武林网。


注:相关教程知识阅读请移步到Android开发频道。
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表