Android之Window、WindowManager(一):PopupWindow添加浮动窗口内部过程

    通常情况下,如果想显示一个界面,首先想到的是建立一个Activity,然后所有的操作在Activity里面实现,或者是一个Dialog或者Toast。本文通过对PopupWindow的实现过程解析,指出添加界面的另外一种方式:直接用WindowManager显示添加或删除View的过程。


一、PopupWindow向Window添加视图的过程

PopupWindow是最简单的浮动窗口,显示在当前activity窗口上面,显示一个PopupWindow的代码非常简单,如下:

<span style="font-size:18px;">	//弹出pop窗口
	private void PopView(){
		View view =View.inflate(this, R.layout.list_item, null);
		PopupWindow popup=new PopupWindow(
				view,
				LayoutParams.WRAP_CONTENT,
				LayoutParams.WRAP_CONTENT,
				true);
		
		popup.setBackgroundDrawable(getResources().getDrawable(R.drawable.ic_launcher));
		popup.showAsDropDown(bt);	
	}</span>

很显然,new PopupWindo()以及setBackgroundDrawabl()都只是做了一些初始化的工作,Android加载PopupWindow浮动窗口的所有逻辑过程和工作,都是在showAsDropDown()方法中进行的,以此为突破口,可以逐级分析浮动窗口内部加载过程。

<span style="font-size:18px;">    /**
     * Display the content view in a popup window at the specified location. If the popup window cannot fit on screen, it will be clipped. See android.view.WindowManager.LayoutParams for more information on how gravity and the x and y parameters are related.
     */
    public void showAtLocation(View parent, int gravity, int x, int y) {
        showAtLocation(parent.getWindowToken(), gravity, x, y);
    }
</span>

parent一般是当前activity的view,主要是传递popupwindow当前activity界面的IBinder,继续往下走:


    public void showAtLocation(IBinder token, int gravity, int x, int y) {
        WindowManager.LayoutParams p = createPopupLayout(token);
        ...        
        invokePopup(p);
    }


核心方法只有两个:createPopupLayout和invokePopup。createPopupLayout主要是设置PopupWindow相对窗体的LayoutParams:

    private WindowManager.LayoutParams createPopupLayout(IBinder token) {
        // 生成layout parameters
        WindowManager.LayoutParams p = new WindowManager.LayoutParams();

        // 默认gravity在左上角
        // X、Y分别是相对偏移量
        p.gravity = Gravity.START | Gravity.TOP;
        p.width = mLastWidth = mWidth;
        p.height = mLastHeight = mHeight;

        //必须设置背景
        if (mBackground != null) {
            p.format = mBackground.getOpacity();
        } else {
            p.format = PixelFormat.TRANSLUCENT;
        }

        //
        p.flags = computeFlags(p.flags);
        //
        p.type = mWindowLayoutType;
        //
        p.token = token;
        //
        p.softInputMode = mSoftInputMode;
        p.setTitle("PopupWindow:" + Integer.toHexString(hashCode()));

        return p;
    }

invokePopup最终调用WindowManager.addView()方法,将PopupWindow中的内容视图及其LayoutParams属性,添加到Window:

    /**
     * 将PopupWindow的内容视图添加到window manger
     */
    private void invokePopup(WindowManager.LayoutParams p) {
        if (mContext != null) {
            p.packageName = mContext.getPackageName();
        }
        mPopupView.setFitsSystemWindows(mLayoutInsetDecor);
        setLayoutDirectionFromAnchor();
        mWindowManager.addView(mPopupView, p);  //将view添加到window中,p是相对window的属性
    }

分析到这里,我们可以总结一下:

Android平台是一个又一个的Activity组成的,每一个Activity有一个或者多个View构成。通常情况下,如果想显示一个界面,首先想到的是建立一个Activity,然后所有的操作在Activity里面实现,或者是一个Dialog或者Toast。这种方式固然简单,但是在有些情况下,我们要求的只是简单的显示,用Activity显然是多余,这个时候,我们如何处理呢?

通过PopupWindow的实现过程,我们了解,原来整个Android的窗口机制是基于一个叫做WindowManager,这个接口可以添加view到屏幕,也可以从屏幕删除view。它面向的对象一端是屏幕,另一端就是View,直接忽略我们以前的Activity或者Dialog之类的东东。其实我们的Activity或者Diolog底层的实现也是通过WindowManager,这个WindowManager是全局的,整个系统就是这个唯一的东东。它是显示View的最底层了。
所以,PopupWindow的过程,其实就是直接用WindowManager显示View的过程。


二、模仿PopupWindow的一个自定义CustomPopupWindow

这个Demo仅仅实现了showAtLocation、createPopupLayout、invokePopup三个方法,就可以实现简单的PopupWindow效果。

自定义CustomPopupWindow控件类:

package com.custom.view;

import android.content.Context;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.widget.PopupWindow;

public class CustomPoupuWindow {
	
	private int height;
	private int width;
	private View contView;
	private Context context;
	private WindowManager wm;

	public CustomPoupuWindow(View contView,int width,int height){
		this.contView=contView;
		this.context=contView.getContext();
		this.width=width;
		this.height=height;
		wm=(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
	}
	
	
	//在指定位置显示popupwindow
    public void showAtLocation(int gravity, int x, int y) {

        WindowManager.LayoutParams p = createPopupLayout();
        //p.windowAnimations = computeAnimationResource();
       
        //preparePopup(p);
        if (gravity == Gravity.NO_GRAVITY) {
            gravity = Gravity.TOP | Gravity.START;
        }
        p.gravity = gravity;
        p.x = x;
        p.y = y;

        invokePopup(p);
    }
    
    
    //设置popupWindow的contView相对window的属性
    private WindowManager.LayoutParams createPopupLayout() {
    	
    	//获取WindowManager
    	
        // generates the layout parameters 
        WindowManager.LayoutParams p = new WindowManager.LayoutParams();
        
        // these gravity settings put the view at the top left corner of the
        // screen. The view is then positioned to the appropriate location
        // by setting the x and y offsets to match the anchor's bottom
        // left corner
        p.gravity = Gravity.START | Gravity.TOP;
        p.width = width;
        p.height = height;
        
/*        if (mBackground != null) {
            p.format = mBackground.getOpacity();
        } else {
            p.format = PixelFormat.TRANSLUCENT;
        }*/
        
        p.format = PixelFormat.TRANSLUCENT;
        
        p.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN;
        p.type = WindowManager.LayoutParams.TYPE_PHONE;
        //p.token = token;
        //p.softInputMode = mSoftInputMode;
        p.setTitle("CustomPopupWindow:" );

        return p;
    }
    private void invokePopup(WindowManager.LayoutParams p) {

        //mPopupView.setFitsSystemWindows(mLayoutInsetDecor);
        //setLayoutDirectionFromAnchor();
        wm.addView(contView, p);
    }
}

调用CustomPopupWindow过程

	private void CustomPopView(){
		View view =View.inflate(this, R.layout.list_item, null);
		CustomPoupuWindow popup=new CustomPoupuWindow(
				view,
				LayoutParams.WRAP_CONTENT,
				LayoutParams.WRAP_CONTENT);
		popup.showAtLocation( Gravity.NO_GRAVITY, 0, 100);
		
	}

自定义XML文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <TextView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/file_name"
        android:layout_gravity="center_vertical"
        android:layout_marginLeft="20dp"
        android:background="@drawable/ic_launcher"
        />
    
</LinearLayout>

效果图:







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值