前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >android仿iphone主题效果的主菜单

android仿iphone主题效果的主菜单

作者头像
砸漏
发布2020-10-28 12:42:13
4520
发布2020-10-28 12:42:13
举报
文章被收录于专栏:恩蓝脚本

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

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

从效果看,大概就能猜出用什么控件类(支持左右滑动的控件类+GridView),支持左右滑动的控件类,有很多了比如常用的Gallery,ViewPager,ViewFlipper,ViewFlow等等,本例自定义继承ViewGroup的。看过launcher源码的人应该都知道 有个Workspace类继承ViewGroup实现主菜单的。

闲话不多说了!

主布局:main.xml

代码语言:javascript
复制
<?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

代码语言:javascript
复制
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:

代码语言:javascript
复制
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

代码语言:javascript
复制
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:

代码语言:javascript
复制
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:

代码语言:javascript
复制
<?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:

代码语言:javascript
复制
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); 
} 
}

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-09-11 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档