android 仿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不用说,大家都是上面是个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:

<?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);
    }
}
    下载地址:

  就这么多了,~~完~~

2楼dahutiger1小时前
地址。完~~
1楼zhangag20101小时前
下载地址咧!