欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

android仿iphone主题效果的主菜单

程序员文章站 2022-05-26 22:59:03
现在很多第三方launcher((如360launcher,golauncher)带有iphone主题,相信玩android的人大都知道。 本例实现仿iphone主题...

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

源码下载:

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

上一篇: 更可怜,下场

下一篇: 国际段子