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

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

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

闲话不多说了!

主布局:main.xml

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:orientation="vertical" >

android:id="@+id/workspace"

android:layout_width="fill_parent"

android:layout_height="fill_parent" />

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" />

第一个自定义类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 mPackagesInfo;

public GridAdapter(Context context, List 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 /* 圆角半径 */) {

return getRoundCornerDrawable(ctx,

mContext.getResources().getDrawable(resId), roundPX);

}

private Drawable getRoundCornerDrawable(Context ctx, Drawable drawable,

float roundPX /* 圆角半径 */) {

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:

android:layout_width="@dimen/app_icon_width"

android:layout_height="@dimen/app_icon_height"

android:gravity="center"

android:orientation="vertical" >

android:id="@+id/icon"

android:layout_width="@dimen/app_icon_width"

android:layout_height="@dimen/app_icon_width"

android:layout_gravity="center_horizontal" />

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" />

主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 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 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 苹果菜单栏,android仿iphone主题效果的主菜单相关推荐

  1. android 仿iphone主题之主菜单

    现在很多第三方Launcher((如360Launcher,GoLauncher)带有iphone主题,相信玩Android的人大都知道. 本例实现仿iphone主题的launcher的冰山一角.如下 ...

  2. android+仿iphone,Android编程实现仿iphone抖动效果的方法(附源码)

    Android编程实现仿iphone抖动效果的方法(附源码) 时间:2021-05-20 本文实例讲述了Android编程实现仿iphone抖动效果的方法.分享给大家供大家参考,具体如下: 布局文件: ...

  3. android UI进阶之仿iphone的tab效果(二)

    今天把这个仿iphone效果的tab写完,这个例子参考国外rolle3k共享的代码,感谢rolle3k. 上篇博客我们写了一个Itab类,介绍了背景的绘制和简单的一个图的贴图方法.我们继续来完成Ita ...

  4. android UI进阶之仿iphone的tab效果

    相信很多人都喜欢iphone 酷炫的界面,虽然android的原生控件已经足够漂亮,但是往往不能满足用户越来越挑剔的眼光.其实,我们完全可以自己来绘制界面.今天我就来分享下做一个和iphone一样的t ...

  5. 精仿苹果x开机android,800元高仿iPhone X以假乱真!更可怕的是奸商套路

    原标题:800元高仿iPhone X以假乱真!更可怕的是奸商套路 每一代iPhone产品都不乏仿冒者,而iPhone X这样的高(zhuang)贵(bi)巅峰之作,同样少不了被模仿.抄袭,而从目前的情 ...

  6. html仿苹果浏览器,完美仿iPhone风格主题 领航浏览器体验

    1仿iPhone的图标式导航页 手机浏览器这个市场因其使用情况极为广泛和频繁因此吸引了无数厂商进入,不仅是传统的浏览器厂商也有许多新晋的手机软件厂商,其产品也从强调省流量.云概念.操作体验和自主核心. ...

  7. android handcent短信 仿iphone气泡聊天

    废话不说直接上图 mian.xml 代码如下 <?xml version="1.0" encoding="utf-8"?> <LinearLa ...

  8. android fragment 菜单栏,android UI:底部菜单栏的学习与制作——Fragment碎片一

    碎片(Fragment) 嵌入与活动中的UI片段,为了合理的分配布局而存在,这是我的简单理解.多用于兼顾手机与平板的UI,也适用于灵活高级的UI制作. Demo 简单的按键切换两片不同的Demo 新建 ...

  9. Android开发之高仿百度地图底部滑出菜单

    老套路上图: 底部菜单滑出效果如上图: 首先依赖三方库 implementation 'androidx.appcompat:appcompat:1.2.0'implementation 'com.g ...

最新文章

  1. 《LINQ实战》译者感言
  2. linux下创建mysql用户,并且给增删改查的权限
  3. 【核心API开发】Spark入门教程[3]
  4. 蚂蚁动态卡片,让App首页实现敏捷更新
  5. 64位ubuntu kylin 16.04下tiny4412开发环境搭建
  6. java 连接池连接mysql数据库需要哪些jar包_DBCP-基于Java8导入DBCP连接池所需JAR包并编写DBCPUtils工具类...
  7. python-字符串基础
  8. VGMP报文封装格式简介
  9. [新功能]文章预览功能
  10. 分子动力学模拟软件VMD的安装与使用
  11. 将vscode打造成无敌的IDE(5)打造shell IDE--三大神器
  12. (赠票)2021年佛山敏捷之旅暨第1届佛山DevOps社区Meetup
  13. 第一章 接口测试基础
  14. 移动通信网络规划:频谱划分
  15. Android WebView截屏空白或者一片黑如何解决?使用MediaProjection截图。
  16. mysql 存储数据时,报错Cause: java.sql.SQLException: Incorrect string value: '\xF4\x80\xB3\x81\xE8\xAE...'
  17. 一文梳理2020年大热的对比学习模型
  18. 如何在html中选择wrap,jQuery wrap()方法怎么用?
  19. 微信小程序开发笔记 进阶篇③——onfire.js事件订阅和发布在微信小程序中的使用
  20. OpenGL中gl,glu,glut的关系(扩展)

热门文章

  1. 设计模式篇04-建造者模式
  2. 【转】赛灵思官网资源导读
  3. 杏林白马药业IPO被终止:曾拟募资4.2亿 党百远家族为实控人
  4. 新型多功能6轴小型机器人
  5. macOS如何查看pdm文件
  6. Inno Setup 系列之较完整的Inno Setup 安装脚本,具备安装包常用的功能
  7. 端口隔离和VLAN内ARP代理
  8. 无任何格外需求的命令行C++飞机大战,内含BOSS,动画,万行代码!免费奉上!
  9. 目录扫描工具——Dirsearch
  10. 一文了解Coinlist Pro