android长按home键源码分析以及模拟长按home事件--弹出近期任务

本文原创http://blog.csdn.net/yanbin1079415046,转载请注明出处。

最近比较闲,抽空看看launcher模块,发现虽然有些东西看不太明白,但是还是挺好玩的。虽然没有多少实质性的进展,但是通过网上不断的查资料,对framework层的有些东西多多少少算是混了个眼熟。今天网上看到一个帖子说如何让用户长按home键的时候不弹出当前正在运行的这个应用程序?就看了一下长按home处理的源代码。下面我们就从分析长按home键的源代码开始,看看如何去解决这个问题。最后我们将写一个小demo来模拟android长按home键功能实现。

    首先,关于home键的处理流程,推荐大家看一篇wdming的帖子,这里就不再赘述了。Android的全局键(home键/长按耳机键)详解【android源码解析】

    分析的源代码使用的是qinjun同学提供的。大家可以到这里去下载:Android学习进阶路线导航线路(Android源码分享) .

   大家可以先看一下我们模拟出来的长按home事件的效果图。

  《android长按home键源码分析以及模拟长按home事件--弹出近期任务》

    一:android长按home键流程

    home键在KeyEvent中的键值为3.

    public static final int KEYCODE_HOME            = 3;

   当用户按下home键的时候(包括长按),程序会进入到PhoneWindowManager.java类中的public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags,int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags)这个方法中进行处理。如果用户是连续点击home,此时就要执行长按home事件了。即执行mHandler.postDelayed(mHomeLongPress, ViewConfiguration.getGlobalActionKeyTimeout());对应的代码。也就会跳转到mHomeLongPress这个Runnable接着往下执行。

   interceptKeyBeforeDispatching这个方法位于PhoneWindowManager.java中。位置为:\frameworks\base\policy\src\com\android\internal\policy\impl\PhoneWindowManager.java  

public boolean interceptKeyBeforeDispatching(WindowState win, int action, int flags,
            int keyCode, int scanCode, int metaState, int repeatCount, int policyFlags) {
        final boolean down = (action == KeyEvent.ACTION_DOWN);
		...
		//4、用户按下home,然后马上释放。此时这个条件成立。将之前postDelayed的事件remove掉。此时就不会执行长按home事件。
        if ((keyCode == KeyEvent.KEYCODE_HOME) && !down) {
            mHandler.removeCallbacks(mHomeLongPress);
        }
		//5、第一次按下home,mHomePressed为false。
        if (mHomePressed) {
            if (keyCode == KeyEvent.KEYCODE_HOME) {
			//a、如果用户连续按下home,此时暂时没有up事件。所以就不走这里。
			//b、如果用户没有连续按下home,此时过来的是up(move或者...)事件。即!down为true,执行该方法
                if (!down) {
                    mHomePressed = false;
                    if (!canceled) {
                        boolean incomingRinging = false;
                        try {
                            ITelephony telephonyService = getTelephonyService();
                            if (telephonyService != null) {
                                incomingRinging = telephonyService.isRinging();
                            }
                        } catch (RemoteException ex) {
                            Log.w(TAG, "RemoteException from getPhoneInterface()", ex);
                        }
        
                        if (incomingRinging) {
                            Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");
                        } else {
							//单击home处理
                            launchHomeFromHotKey();
                        }
                    } else {
                        Log.i(TAG, "Ignoring HOME; event canceled.");
                    }
                }
            }
            return true;
        }
        
		...
		
        //  1、第一次处理home按下
        if (keyCode == KeyEvent.KEYCODE_HOME) {

            // If a system window has focus, then it doesn't make sense
            // right now to interact with applications.
            WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;
            if (attrs != null) {
                final int type = attrs.type;
                if (type == WindowManager.LayoutParams.TYPE_KEYGUARD
                        || type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG) {
                    // the "app" is keyguard, so give it the key
                    return false;
                }
                final int typeCount = WINDOW_TYPES_WHERE_HOME_DOESNT_WORK.length;
                for (int i=0; i<typeCount; i++) {
                    if (type == WINDOW_TYPES_WHERE_HOME_DOESNT_WORK[i]) {
                        // don't do anything, but also don't pass it to the app
                        return true;
                    }
                }
            }
            //  2、第一次按下home,会调用postDelayed发送一个延时处理的操作。同时将mHomePressed置为true。
			// 如果第5 步没有进入if (!down),此时就要执行长按home方法了。
            if (down && repeatCount == 0) {
                if (!keyguardOn) {
                    mHandler.postDelayed(mHomeLongPress, ViewConfiguration.getGlobalActionKeyTimeout());
                }
                mHomePressed = true;
            }
            return true;
        } //其他键的处理
		else if(...){...}

mHomeLongPress的代码如下。它的作用是请求生成一个弹出近期任务的对话框。执行showRecentAppsDialog()方法

/**
     * When a home-key longpress expires, close other system windows and launch the recent apps
     */
    Runnable mHomeLongPress = new Runnable() {
        public void run() {
            /*
             * Eat the longpress so it won't dismiss the recent apps dialog when
             * the user lets go of the home key
             */
            mHomePressed = false;//将mHomePressed还原,就可以接收下次home单击事件了。
            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_RECENT_APPS);
            showRecentAppsDialog();//弹出近期任务的对话框
        }
    };

showRecentAppsDialog()方法如下。它会去创建一个RecentApplicationsDialog对象,从而完成弹出任务对话框的操作。

/**
     * Create (if necessary) and launch the recent apps dialog
     */
    void showRecentAppsDialog() {
        if (mRecentAppsDialog == null) {
            mRecentAppsDialog = new RecentApplicationsDialog(mContext);
        }
        mRecentAppsDialog.show();
    }

接下来就是创建RecentApplicationsDialog对象来显示近期任务了。该类位置为:\frameworks\base\policy\src\com\android\internal\policy\impl\RecentApplicationsDialog.java

代码中已经做了详细的注解,这里就不再仔细说。最重要的一步就是在onStart()中的reloadButtons()方法。它是获取近期任务的方法。

public class RecentApplicationsDialog extends Dialog implements OnClickListener {
    // Elements for debugging support
    private static final boolean DBG_FORCE_EMPTY_LIST = false;
    static private StatusBarManager sStatusBar;
    private static final int NUM_BUTTONS = 8;
    private static final int MAX_RECENT_TASKS = NUM_BUTTONS * 2;    // allow for some discards
    final TextView[] mIcons = new TextView[NUM_BUTTONS];
    View mNoAppsText;
    IntentFilter mBroadcastIntentFilter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);//广播接收者
    Handler mHandler = new Handler();
	//将每个TextView相关的intent信息和drawable信息删除
    Runnable mCleanup = new Runnable() {
        public void run() {
            // dump extra memory we're hanging on to
            for (TextView icon: mIcons) {
                icon.setCompoundDrawables(null, null, null, null);
                icon.setTag(null);
            }
        }
    };

    private int mIconSize;

    public RecentApplicationsDialog(Context context) {
        super(context, com.android.internal.R.style.Theme_Dialog_RecentApplications);

        final Resources resources = context.getResources();
        mIconSize = (int) resources.getDimension(android.R.dimen.app_icon_size);
    }

    /**
     * We create the recent applications dialog just once, and it stays around (hidden)
     * until activated by the user.
     *
     * @see PhoneWindowManager#showRecentAppsDialog
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Context context = getContext();

		//StatusBarManager这个系统服务只供系统内部使用,不对外开放。作用是对状态栏进行管理,我们不去管它。
		//该类位置:\frameworks\base\core\java\android\app\StatusBarManager.java
        if (sStatusBar == null) {
            sStatusBar = (StatusBarManager)context.getSystemService(Context.STATUS_BAR_SERVICE);
        }

		//请求窗口的一些属性
        Window window = getWindow();
        window.requestFeature(Window.FEATURE_NO_TITLE);
        window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
        window.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
                WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
        window.setTitle("Recents");

        setContentView(com.android.internal.R.layout.recent_apps_dialog);
        final WindowManager.LayoutParams params = window.getAttributes();
        params.width = WindowManager.LayoutParams.MATCH_PARENT;
        params.height = WindowManager.LayoutParams.MATCH_PARENT;
        window.setAttributes(params);
        window.setFlags(0, WindowManager.LayoutParams.FLAG_DIM_BEHIND);

		//最多放八个近期任务
        mIcons[0] = (TextView)findViewById(com.android.internal.R.id.button0);
        mIcons[1] = (TextView)findViewById(com.android.internal.R.id.button1);
        mIcons[2] = (TextView)findViewById(com.android.internal.R.id.button2);
        mIcons[3] = (TextView)findViewById(com.android.internal.R.id.button3);
        mIcons[4] = (TextView)findViewById(com.android.internal.R.id.button4);
        mIcons[5] = (TextView)findViewById(com.android.internal.R.id.button5);
        mIcons[6] = (TextView)findViewById(com.android.internal.R.id.button6);
        mIcons[7] = (TextView)findViewById(com.android.internal.R.id.button7);
        mNoAppsText = findViewById(com.android.internal.R.id.no_applications_message);

		//每个近期任务的点击事件监听
        for (TextView b: mIcons) {
            b.setOnClickListener(this);
        }
    }

    /**
     * Handler for user clicks.  If a button was clicked, launch the corresponding activity.
     */
    public void onClick(View v) {

        for (TextView b: mIcons) {
            if (b == v) {
                // prepare a launch intent and send it
				//b.getTag得到从ActivityManager取得的每一个任务的intent信息
                Intent intent = (Intent)b.getTag();
                if (intent != null) {
                    intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
                    try {
                        getContext().startActivity(intent);
                    } catch (ActivityNotFoundException e) {
                        Log.w("Recent", "Unable to launch recent task", e);
                    }
                }
                break;
            }
        }
        dismiss();
    }

    /**
     * Set up and show the recent activities dialog.
	 显示近期任务对话框
     */
    @Override
    public void onStart() {
        super.onStart();
        reloadButtons();
        if (sStatusBar != null) {
            sStatusBar.disable(StatusBarManager.DISABLE_EXPAND);
        }

        // receive broadcasts
		//注册广播,接收ACTION_CLOSE_SYSTEM_DIALOGS的intent。比如当有电话打过来时,dialog会被让位给打电话的ui
        getContext().registerReceiver(mBroadcastReceiver, mBroadcastIntentFilter);
        mHandler.removeCallbacks(mCleanup);
    }

    /**
     * Dismiss the recent activities dialog.
     */
    @Override
    public void onStop() {
        super.onStop();

        if (sStatusBar != null) {
            sStatusBar.disable(StatusBarManager.DISABLE_NONE);
        }

		//
        // stop receiving broadcasts
		//接触广播
        getContext().unregisterReceiver(mBroadcastReceiver);

        mHandler.postDelayed(mCleanup, 100);
     }

    /**
     * Reload the 6 buttons with recent activities
	 * 最重要的方法,如何从ActivityManager中获取到近期任务。并将其信息"注入"到每一个icon(TextView)中。
     */
    private void reloadButtons() {

        final Context context = getContext();
        final PackageManager pm = context.getPackageManager();
        final ActivityManager am = (ActivityManager)
                context.getSystemService(Context.ACTIVITY_SERVICE);
        final List<ActivityManager.RecentTaskInfo> recentTasks =
                am.getRecentTasks(MAX_RECENT_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE);

        ActivityInfo homeInfo = 
            new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
                    .resolveActivityInfo(pm, 0);

		//近期任务中的图片的创建以及它的选中事件等等图标颜色变化之类的处理。可以想象成我们listview中元素创建以及点击状态变化的处理。
		//该类位置:\frameworks\base\policy\src\com\android\internal\policy\impl\IconUtilities.java
        IconUtilities iconUtilities = new IconUtilities(getContext());

        // Performance note:  Our android performance guide says to prefer Iterator when
        // using a List class, but because we know that getRecentTasks() always returns
        // an ArrayList<>, we'll use a simple index instead.
        int index = 0;
        int numTasks = recentTasks.size();
		//在这里,当前的运行的应用程序一般会第一个加载出来。所以可以在这里将其从近期任务中剔除。方法同剔除
		//launcher的方法。
        for (int i = 0; i < numTasks && (index < NUM_BUTTONS); ++i) {
            final ActivityManager.RecentTaskInfo info = recentTasks.get(i);

            // for debug purposes only, disallow first result to create empty lists
            if (DBG_FORCE_EMPTY_LIST && (i == 0)) continue;

            Intent intent = new Intent(info.baseIntent);
            if (info.origActivity != null) {
                intent.setComponent(info.origActivity);
            }

            // Skip the current home activity.
			//launcher应用程序不需要显示。
            if (homeInfo != null) {
                if (homeInfo.packageName.equals(
                        intent.getComponent().getPackageName())
                        && homeInfo.name.equals(
                                intent.getComponent().getClassName())) {
                    continue;
                }
            }

            intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
                    | Intent.FLAG_ACTIVITY_NEW_TASK);
            final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
            if (resolveInfo != null) {
                final ActivityInfo activityInfo = resolveInfo.activityInfo;
                final String title = activityInfo.loadLabel(pm).toString();
                Drawable icon = activityInfo.loadIcon(pm);

                if (title != null && title.length() > 0 && icon != null) {
                    final TextView tv = mIcons[index];
                    tv.setText(title);
                    icon = iconUtilities.createIconDrawable(icon);
                    tv.setCompoundDrawables(null, icon, null, null);
                    tv.setTag(intent);
                    tv.setVisibility(View.VISIBLE);
                    tv.setPressed(false);
                    tv.clearFocus();
                    ++index;
                }
            }
        }

        // handle the case of "no icons to show"
        mNoAppsText.setVisibility((index == 0) ? View.VISIBLE : View.GONE);

        // hide the rest
        for (; index < NUM_BUTTONS; ++index) {
            mIcons[index].setVisibility(View.GONE);
        }
    }

    /**
     * This is the listener for the ACTION_CLOSE_SYSTEM_DIALOGS intent.  It's an indication that
     * we should close ourselves immediately, in order to allow a higher-priority UI to take over
     * (e.g. phone call received).
     */
    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(action)) {
                String reason = intent.getStringExtra(PhoneWindowManager.SYSTEM_DIALOG_REASON_KEY);
                if (! PhoneWindowManager.SYSTEM_DIALOG_REASON_RECENT_APPS.equals(reason)) {
                    dismiss();
                }
            }
        }
    };
}

至此,我们长按home事件的源代码就已经分析完毕。接下来,我们就模拟长按home事件,获取用户近期任务列表。相信有了上面的知识,下面的事情就不是难事了。

二:模拟长按home键弹出近期任务列表

有两个要注意的地方,这里先说出来。

a、final List<ActivityManager.RecentTaskInfo> recentTasks = am.getRecentTasks(MAX_RECENT_TASKS, ActivityManager.RECENT_IGNORE_UNAVAILABLE)中的ActivityManager.RECENT_IGNORE_UNAVAILABLE这个值不对我们开放,我们可以手动写死。它的值为:0x0002

b、注意加入权限。<uses-permission android:name=”android.permission.GET_TASKS”/>

 由于这个工程比较简单,我们只使用了一个MainActivity来实现功能。这里就大概的给童鞋们说一说。具体的可以看demo。

1、在应用程序启动的时候就去执行查找近期任务列表的操作。

    private Dialog mDialog;
    private static int MAX_RECENT_TASKS = 12;    // allow for some discards
    private static int repeatCount = 12;//保证上面两个值相等
    //用来存放每一个recentApplication的信息,我们这里存放应用程序名,应用程序图标和intent。
    private List<HashMap<String,Object>> appInfos = new ArrayList<HashMap<String, Object>>();
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        reloadButtons();//程序加载的时候就加载出最近任务
    }

2reloadButtons()方法的代码如下,获取近期任务代码与源码基本一致,只是在不显示launcher那里做了一些处理。按照下面的代码,如果我们不做处理,我们得到的近期任务列表数据将会少一个。具体看代码中的注释。

   /**
     * 核心方法,加载最近启动的应用程序
     * 注意:这里我们取出的最近任务为  MAX_RECENT_TASKS + 1个,因为有可能最近任务中包好Launcher2。
     * 这样可以保证我们展示出来的  最近任务 为 MAX_RECENT_TASKS 个
     */
    private void reloadButtons() {
        
        //得到包管理器和activity管理器
        final Context context = this;
        final PackageManager pm = context.getPackageManager();
        final ActivityManager am = (ActivityManager)
                context.getSystemService(Context.ACTIVITY_SERVICE);
        //从ActivityManager中取出用户最近launch过的 MAX_RECENT_TASKS + 1 个,以从早到晚的时间排序,
        //注意这个   0x0002,它的值在launcher中是用ActivityManager.RECENT_IGNORE_UNAVAILABLE
        //但是这是一个隐藏域,因此我把它的值直接拷贝到这里
        final List<ActivityManager.RecentTaskInfo> recentTasks =
                am.getRecentTasks(MAX_RECENT_TASKS + 1, 0x0002);

        //这个activity的信息是我们的launcher
        ActivityInfo homeInfo = 
            new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)
                    .resolveActivityInfo(pm, 0);
        int numTasks = recentTasks.size();
        for (int i = 0; i < numTasks && (i < MAX_RECENT_TASKS); i++) {
            HashMap<String, Object> singleAppInfo = new HashMap<String, Object>();//当个启动过的应用程序的信息
            final ActivityManager.RecentTaskInfo info = recentTasks.get(i);

            Intent intent = new Intent(info.baseIntent);
            if (info.origActivity != null) {
                intent.setComponent(info.origActivity);
            }
            //TODO 测试用,无意义代码
            String currentInfo = "PackageName==" + intent.getComponent().getPackageName() + ",ClassName==" + intent.getComponent().getClassName();
            /**
             * 如果找到是launcher,直接continue,后面的appInfos.add操作就不会发生了
             */
            if (homeInfo != null) {
                if (homeInfo.packageName.equals(
                        intent.getComponent().getPackageName())
                        && homeInfo.name.equals(
                                intent.getComponent().getClassName())) {
                    MAX_RECENT_TASKS = MAX_RECENT_TASKS + 1;
                    continue;
                }
            }

            //设置intent的启动方式为 创建新task()【并不一定会创建】
            intent.setFlags((intent.getFlags()&~Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
                    | Intent.FLAG_ACTIVITY_NEW_TASK);
            //获取指定应用程序activity的信息(按我的理解是:某一个应用程序的最后一个在前台出现过的activity。)
            final ResolveInfo resolveInfo = pm.resolveActivity(intent, 0);
            if (resolveInfo != null) {
                final ActivityInfo activityInfo = resolveInfo.activityInfo;
                final String title = activityInfo.loadLabel(pm).toString();
                Drawable icon = activityInfo.loadIcon(pm);

                if (title != null && title.length() > 0 && icon != null) {
                    singleAppInfo.put("title", title);
                    singleAppInfo.put("icon", icon);
                    singleAppInfo.put("tag", intent);
                }
            }
            appInfos.add(singleAppInfo);
        }
        MAX_RECENT_TASKS = repeatCount;
    }

3、通过上面的步骤,我们就获得了近期任务列表,并将其存放在了appInfos这个list中,接下来就是展示这个list的工作了。我这里就是简单的弹出一个dialogdialog中放一个gridview。自定义gridview的adapter,在getView方法中进行任务点击事件的处理。代码如下:

MainActivity中的按钮点击事件:

public void click(View view){
        int id = view.getId();
        switch (id) {
        case R.id.btn:
            generateDialog();
            break;
        }
    }
    
    /**
     * 弹出最近任务对话框
     */
    public void generateDialog(){
        mDialog = new Dialog(this,R.style.dialog);
        LayoutInflater mInflater = LayoutInflater.from(this);
        View dialogView = mInflater.inflate(R.layout.choose_dialog, null);
        final GridView mGridView = (GridView) dialogView.findViewById(R.id.choose_dialog_gridview);
        mGridView.setAdapter(new MyAppAdapter());
        mDialog.setContentView(dialogView);
        mDialog.setCanceledOnTouchOutside(true);
        mDialog.show();
    }

弹出dialog中的gridview适配。

/**
     * gridview的适配器
     * @author yanbin
     *
     */
    private class MyAppAdapter implements ListAdapter{

        @Override
        public void registerDataSetObserver(DataSetObserver observer) {
        }

        @Override
        public void unregisterDataSetObserver(DataSetObserver observer) {
        }

        @Override
        public int getCount() {
            return appInfos.size();
        }

        @Override
        public Object getItem(int position) {
            return appInfos.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public boolean hasStableIds() {
            return false;
        }

        /**
         * 自定义view
         */
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            LayoutInflater mInflater = LayoutInflater.from(MainActivity.this);
            View infoView = mInflater.inflate(R.layout.choose_dialog_detail_info, null);
            ImageView mImageView = (ImageView) infoView.findViewById(R.id.app_icon);
            TextView mTextView = (TextView) infoView.findViewById(R.id.choose_dialog_detail_tv);
            String title = (String) appInfos.get(position).get("title");
            Drawable icon = (Drawable) appInfos.get(position).get("icon");
            Intent singleIntent = (Intent) appInfos.get(position).get("tag");
            infoView.setTag(singleIntent);
            mImageView.setImageDrawable(icon);
            mTextView.setText(title);
            infoView.setOnClickListener(new SingleAppClickListener());
            return infoView;
        }

        @Override
        public int getItemViewType(int position) {
            return 0;
        }

        @Override
        public int getViewTypeCount() {
            return 1;
        }

        @Override
        public boolean isEmpty() {
            return false;
        }

        @Override
        public boolean areAllItemsEnabled() {
            return false;
        }

        @Override
        public boolean isEnabled(int position) {
            return false;
        }
    }

图标点击事件处理:

/**
     * 点击应用图标即可启动程序
     * @author yanbin
     *
     */
    private class SingleAppClickListener implements View.OnClickListener{
        @Override
        public void onClick(View v) {
            Intent intent = (Intent)v.getTag();
            if (intent != null) {
                intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY);
                try {
                    MainActivity.this.startActivity(intent);
                    if(mDialog != null)
                        mDialog.dismiss();
                } catch (ActivityNotFoundException e) {
                    Log.w("Recent", "Unable to launch recent task", e);
                }
            }
        }
        
    }

用到的一些资源文件和布局文件如下:

choose_dialog.xml  对话框。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
     >
    <LinearLayout 
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_gravity="center"
        >
        <GridView
        android:id="@+id/choose_dialog_gridview"
        android:layout_width="wrap_content"
        android:gravity="center"
        android:layout_height="wrap_content"
        android:verticalSpacing="15dp"
        android:horizontalSpacing="15dp"
        android:numColumns="3"
        android:background="@drawable/bg_y"
        ></GridView>
    </LinearLayout>
    

</LinearLayout>

choose_dialog_detail_info.xml   gridview中每一个item的布局。

<?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:layout_gravity="center"
     >
    
    <ImageView 
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:src="@drawable/ic_launcher"
        android:layout_centerInParent="true"
        android:id="@+id/app_icon"
        />
    
    <TextView 
        android:layout_marginTop="20dp"
        android:layout_below="@+id/app_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:singleLine="true"
        android:background="@null"
        android:text="cccccccccc"
        android:layout_centerHorizontal="true"
        android:id="@+id/choose_dialog_detail_tv"
        />

</RelativeLayout>

colors.xml

<?xml version="1.0" encoding="utf-8"?>
  <!-- Copyright (C) 2007 The Android Open Source Project -->
  
<resources>
  <color name="transparent">#00000000</color>
</resources>

styles.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="dialog" parent="@android:style/Theme.Dialog">
        <item name="android:windowFrame">@null</item><!--边框-->
        <item name="android:windowIsFloating">true</item><!--是否浮现在activity之上-->
        <item name="android:windowIsTranslucent">true</item><!--半透明-->
        <item name="android:windowNoTitle">true</item><!--无标题-->
        <item name="android:windowBackground">@color/transparent</item><!--背景透明 -->
        <item name="android:backgroundDimEnabled">true</item><!--背景暗淡-->
    </style>    
</resources>

到这里,我们模拟长按home的代码就全部展示完了。有不足的地方望指正,谢谢。

DEMO下载地址:
http://download.csdn.net/detail/yanbin1079415046/4604418

 

 

 

    原文作者:Android源码分析
    原文地址: https://blog.csdn.net/yanbin1079415046/article/details/8026613
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞