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.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); } }
源码下载:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。