真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

PopupWindow源碼分析-創(chuàng)新互聯(lián)

目錄介紹
  • 1.最簡(jiǎn)單的創(chuàng)建方法
    • 1.1 PopupWindow構(gòu)造方法
    • 1.2 顯示PopupWindow
    • 1.3 最簡(jiǎn)單的創(chuàng)建
    • 1.4 注意問(wèn)題寬和高屬性
  • 2.源碼分析
    • 2.1 setContentView(View contentView)
    • 2.2 showAsDropDown()源碼
    • 2.3 dismiss()源碼分析
    • 2.4 PopupDecorView源碼分析
  • 3.經(jīng)典總結(jié)
    • 3.1 PopupWindow和Dialog有什么區(qū)別?
    • 3.2 創(chuàng)建和銷毀的大概流程
    • 3.3 為何彈窗點(diǎn)擊一下就dismiss呢?
  • 4.PopupWindow封裝庫(kù)介紹

好消息

  • 博客筆記大匯總【16年3月到至今】,包括Java基礎(chǔ)及深入知識(shí)點(diǎn),Android技術(shù)博客,Python學(xué)習(xí)筆記等等,還包括平時(shí)開(kāi)發(fā)中遇到的bug匯總,當(dāng)然也在工作之余收集了大量的面試題,長(zhǎng)期更新維護(hù)并且修正,持續(xù)完善……開(kāi)源的文件是markdown格式的!同時(shí)也開(kāi)源了生活博客,從12年起,積累共計(jì)47篇[近20萬(wàn)字],轉(zhuǎn)載請(qǐng)注明出處,謝謝!
  • 鏈接地址:https://github.com/yangchong211/YCBlogs
  • 如果覺(jué)得好,可以star一下,謝謝!當(dāng)然也歡迎提出建議,萬(wàn)事起于忽微,量變引起質(zhì)變!
  • PopupWindow封裝庫(kù)項(xiàng)目地址:https://github.com/yangchong211/YCDialog
  • 02.Toast源碼深度分析
    • 最簡(jiǎn)單的創(chuàng)建,簡(jiǎn)單改造避免重復(fù)創(chuàng)建,show()方法源碼分析,scheduleTimeoutLocked吐司如何自動(dòng)銷毀的,TN類中的消息機(jī)制是如何執(zhí)行的,普通應(yīng)用的Toast顯示數(shù)量是有限制的,用代碼解釋為何Activity銷毀后Toast仍會(huì)顯示,Toast偶爾報(bào)錯(cuò)Unable to add window是如何產(chǎn)生的,Toast運(yùn)行在子線程問(wèn)題,Toast如何添加系統(tǒng)窗口的權(quán)限等等
  • 03.DialogFragment源碼分析
    • 最簡(jiǎn)單的使用方法,onCreate(@Nullable Bundle savedInstanceState)源碼分析,重點(diǎn)分析彈窗展示和銷毀源碼,使用中show()方法遇到的IllegalStateException分析
  • 05.PopupWindow源碼分析
    • 顯示PopupWindow,注意問(wèn)題寬和高屬性,showAsDropDown()源碼,dismiss()源碼分析,PopupWindow和Dialog有什么區(qū)別?為何彈窗點(diǎn)擊一下就dismiss呢?
  • 06.Snackbar源碼分析
    • 最簡(jiǎn)單的創(chuàng)建,Snackbar的make方法源碼分析,Snackbar的show顯示與點(diǎn)擊消失源碼分析,顯示和隱藏中動(dòng)畫源碼分析,Snackbar的設(shè)計(jì)思路,為什么Snackbar總是顯示在最下面
  • 07.彈窗常見(jiàn)問(wèn)題
    • DialogFragment使用中show()方法遇到的IllegalStateException,什么常見(jiàn)產(chǎn)生的?Toast偶爾報(bào)錯(cuò)Unable to add window,Toast運(yùn)行在子線程導(dǎo)致崩潰如何解決?

1.最簡(jiǎn)單的創(chuàng)建方法

1.1 PopupWindow構(gòu)造方法
  • 如下所示
    public PopupWindow (Context context)
    public PopupWindow(View contentView)
    public PopupWindow(int width, int height)
    public PopupWindow(View contentView, int width, int height)
    public PopupWindow(View contentView, int width, int height, boolean focusable)
1.2 顯示PopupWindow
  • 如下所示
    showAsDropDown(View anchor):相對(duì)某個(gè)控件的位置(正左下方),無(wú)偏移
    showAsDropDown(View anchor, int xoff, int yoff):相對(duì)某個(gè)控件的位置,有偏移
    showAtLocation(View parent, int gravity, int x, int y):相對(duì)于父控件的位置(例如正中央Gravity.CENTER,下方Gravity.BOTTOM等),可以設(shè)置偏移或無(wú)偏移
1.3 最簡(jiǎn)單的創(chuàng)建
  • 具體如下所示

    順昌網(wǎng)站制作公司哪家好,找成都創(chuàng)新互聯(lián)公司!從網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、APP開(kāi)發(fā)、響應(yīng)式網(wǎng)站建設(shè)等網(wǎng)站項(xiàng)目制作,到程序開(kāi)發(fā),運(yùn)營(yíng)維護(hù)。成都創(chuàng)新互聯(lián)公司自2013年創(chuàng)立以來(lái)到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來(lái)保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選成都創(chuàng)新互聯(lián)公司。
    //創(chuàng)建對(duì)象
    PopupWindow popupWindow = new PopupWindow(this);
    View inflate = LayoutInflater.from(this).inflate(R.layout.view_pop_custom, null);
    //設(shè)置view布局
    popupWindow.setContentView(inflate);
    popupWindow.setWidth(LinearLayout.LayoutParams.WRAP_CONTENT);
    popupWindow.setHeight(LinearLayout.LayoutParams.WRAP_CONTENT);
    //設(shè)置動(dòng)畫的方法
    popupWindow.setAnimationStyle(R.style.BottomDialog);
    //設(shè)置PopUpWindow的焦點(diǎn),設(shè)置為true之后,PopupWindow內(nèi)容區(qū)域,才可以響應(yīng)點(diǎn)擊事件
    popupWindow.setTouchable(true);
    //設(shè)置背景透明
    popupWindow.setBackgroundDrawable(new ColorDrawable(0x00000000));
    //點(diǎn)擊空白處的時(shí)候讓PopupWindow消失
    popupWindow.setOutsideTouchable(true);
    // true時(shí),點(diǎn)擊返回鍵先消失 PopupWindow
    // 但是設(shè)置為true時(shí)setOutsideTouchable,setTouchable方法就失效了(點(diǎn)擊外部不消失,內(nèi)容區(qū)域也不響應(yīng)事件)
    // false時(shí)PopupWindow不處理返回鍵,默認(rèn)是false
    popupWindow.setFocusable(false);
    //設(shè)置dismiss事件
    popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
        @Override
        public void onDismiss() {
    
        }
    });
    boolean showing = popupWindow.isShowing();
    if (!showing){
        //show,并且可以設(shè)置位置
        popupWindow.showAsDropDown(mTv1);
    }
1.4 注意問(wèn)題寬和高屬性
  • 先看問(wèn)題代碼,下面這個(gè)不會(huì)出現(xiàn)彈窗,思考:為什么?

    PopupWindow popupWindow = new PopupWindow(this);
    View inflate = LayoutInflater.from(this).inflate(R.layout.view_pop_custom, null);
    popupWindow.setContentView(inflate);
    popupWindow.setAnimationStyle(R.style.BottomDialog);
    popupWindow.showAsDropDown(mTv1);
  • 注意:必須設(shè)置寬和高,否則不顯示任何東西
    • 這里的WRAP_CONTENT可以換成fill_parent 也可以是具體的數(shù)值,它是指PopupWindow的大小,也就是contentview的大小,注意popupwindow根據(jù)這個(gè)大小顯示你的View,如果你的View本身是從xml得到的,那么xml的第一層view的大小屬性將被忽略。相當(dāng)于popupWindow的width和height屬性直接和第一層View相對(duì)應(yīng)。

2.源碼分析

2.1 setContentView(View contentView)源碼分析
  • 首先先來(lái)看看源碼

    • 可以看出,先判斷是否show,如果沒(méi)有showing的話,則進(jìn)行contentView賦值,如果mWindowManager為null,則取獲取mWindowManager,這個(gè)很重要。最后便是根據(jù)SDK版本而不是在構(gòu)造函數(shù)中設(shè)置附加InDecor的默認(rèn)設(shè)置,因?yàn)闃?gòu)造函數(shù)中可能沒(méi)有上下文對(duì)象。我們只想在這里設(shè)置默認(rèn),如果應(yīng)用程序尚未設(shè)置附加InDecor。

      public void setContentView(View contentView) {
      //判斷是否show,如果已經(jīng)show,則返回
      if (isShowing()) {
          return;
      }
      //賦值
      mContentView = contentView;
      
      if (mContext == null && mContentView != null) {
          mContext = mContentView.getContext();
      }
      
      if (mWindowManager == null && mContentView != null) {
          mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
      }
      
      //在這里根據(jù)SDK版本而不是在構(gòu)造函數(shù)中設(shè)置附加InDecor的默認(rèn)設(shè)置,因?yàn)闃?gòu)造函數(shù)中可能沒(méi)有上下文對(duì)象。我們只想在這里設(shè)置默認(rèn),如果應(yīng)用程序尚未設(shè)置附加InDecor。
      if (mContext != null && !mAttachedInDecorSet) {
          setAttachedInDecor(mContext.getApplicationInfo().targetSdkVersion
                  >= Build.VERSION_CODES.LOLLIPOP_MR1);
      }

    }

  • 接著來(lái)看一下setAttachedInDecor源碼部分
    • 執(zhí)行setAttachedInDecor給一個(gè)變量賦值為true,表示已經(jīng)在decor里注冊(cè)了(注意:現(xiàn)在還沒(méi)有使用WindowManager把PopupWindow添加到DecorView上)
      public void setAttachedInDecor(boolean enabled) {
      mAttachedInDecor = enabled;
      mAttachedInDecorSet = true;
      }
2.2 showAsDropDown()源碼
  • 先來(lái)看一下showAsDropDown(View anchor)部分代碼

    • 可以看出,調(diào)用這個(gè)方法,默認(rèn)偏移值都是0;關(guān)于這個(gè)attachToAnchor(anchor, xoff, yoff, gravity)方法作用,下面再說(shuō)。之后通過(guò)createPopupLayoutParams方法創(chuàng)建和初始化LayoutParams,然后把這個(gè)LayoutParams傳過(guò)去,把PopupWindow真正的樣子,也就是view創(chuàng)建出來(lái)。
      public void showAsDropDown(View anchor) {
      showAsDropDown(anchor, 0, 0);
      }

    //主要看這個(gè)方法
    //注意啦:關(guān)于更多內(nèi)容,可以參考我的博客大匯總:https://github.com/yangchong211/YCBlogs
    public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {
    if (isShowing() || mContentView == null) {
    return;
    }

    TransitionManager.endTransitions(mDecorView);
    
    //下面單獨(dú)講
    //https://github.com/yangchong211/YCBlogs
    attachToAnchor(anchor, xoff, yoff, gravity);
    
    mIsShowing = true;
    mIsDropdown = true;
    
    //通過(guò)createPopupLayoutParams方法創(chuàng)建和初始化LayoutParams
    final WindowManager.LayoutParams p = createPopupLayoutParams(anchor.getWindowToken());
    preparePopup(p);
    
    final boolean aboveAnchor = findDropDownPosition(anchor, p, xoff, yoff,
            p.width, p.height, gravity);
    updateAboveAnchor(aboveAnchor);
    p.accessibilityIdOfAnchor = (anchor != null) ? anchor.getAccessibilityViewId() : -1;
    
    invokePopup(p);

    }

  • 接著來(lái)看看attachToAnchor(anchor, xoff, yoff, gravity)源碼

    • 執(zhí)行了一個(gè)attachToAnchor,意思是PopupWindow類似一個(gè)錨掛在目標(biāo)view的下面,這個(gè)函數(shù)主要講xoff、yoff(x軸、y軸偏移值)、gravity(比如Gravity.BOTTOM之類,指的是PopupWindow放在目標(biāo)view哪個(gè)方向邊緣的位置)這個(gè)attachToAnchor有點(diǎn)意思,通過(guò)弱引用保存目標(biāo)view和目標(biāo)view的rootView(我們都知道:通過(guò)弱引用和軟引用可以防止內(nèi)存泄漏)、這個(gè)rootview是否依附在window、還有保存偏差值、gravity
    • 關(guān)于四種引用的深入介紹可以參考我的這邊文章:01.四種引用比較與源碼分析

      private void attachToAnchor(View anchor, int xoff, int yoff, int gravity) {
      detachFromAnchor();
      
      final ViewTreeObserver vto = anchor.getViewTreeObserver();
      if (vto != null) {
          vto.addOnScrollChangedListener(mOnScrollChangedListener);
      }
      
      final View anchorRoot = anchor.getRootView();
      anchorRoot.addOnAttachStateChangeListener(mOnAnchorRootDetachedListener);
      
      mAnchor = new WeakReference<>(anchor);
      mAnchorRoot = new WeakReference<>(anchorRoot);
      mIsAnchorRootAttached = anchorRoot.isAttachedToWindow();
      
      mAnchorXoff = xoff;
      mAnchorYoff = yoff;
      mAnchoredGravity = gravity;
      }
  • 接著再來(lái)看看preparePopup(p)這個(gè)方法源碼
    • 把這個(gè)LayoutParams傳過(guò)去,把PopupWindow真正的樣子,也就是view創(chuàng)建出來(lái),在這個(gè)preparePopup函數(shù)里,一開(kāi)始準(zhǔn)備backgroundView,因?yàn)橐话鉳BackgroundView是null,所以把之前setContentView設(shè)置的contentView作為mBackgroundView。
    • PopupWindow源碼分析
  • 接著看看createDecorView(mBackgroundView)這個(gè)方法源碼
    • 把PopupWindow的根view創(chuàng)建出來(lái),并把contentView通過(guò)addView方法添加進(jìn)去。PopupDecorView繼承FrameLayout,其中沒(méi)有繪畫什么,只是復(fù)寫了dispatchKeyEvent和onTouchEvent之類的事件分發(fā)的函數(shù),還有實(shí)現(xiàn)進(jìn)場(chǎng)退場(chǎng)動(dòng)畫的執(zhí)行函數(shù)
    • PopupWindow源碼分析
    • PopupWindow源碼分析
  • 最后看看invokePopup(WindowManager.LayoutParams p)源碼
    • 執(zhí)行invokePopup(p),這個(gè)函數(shù)主要將popupView添加到應(yīng)用DecorView的相應(yīng)位置,通過(guò)之前創(chuàng)建WindowManager完成這個(gè)步驟,現(xiàn)在PopupWIndow可以看得到。
    • 并且請(qǐng)求在下一次布局傳遞之后運(yùn)行Enter轉(zhuǎn)換。
    • PopupWindow源碼分析
2.3 dismiss()源碼分析
  • 通過(guò)對(duì)象調(diào)用該方法可以達(dá)到銷毀彈窗的目的。
    • 重點(diǎn)看一下這個(gè)兩個(gè)方法。移除view和清除錨視圖
    • PopupWindow源碼分析
  • 接著看看dismissImmediate(View decorView, ViewGroup contentHolder, View contentView)源碼

    • 第一步,通過(guò)WindowManager注銷PopupView
    • 第二步,PopupView移除contentView
    • 第三步,講mDecorView,mBackgroundView置為null

      private void dismissImmediate(View decorView, ViewGroup contentHolder, View contentView) {
      // If this method gets called and the decor view doesn't have a parent,
      // then it was either never added or was already removed. That should
      // never happen, but it's worth checking to avoid potential crashes.
      if (decorView.getParent() != null) {
          mWindowManager.removeViewImmediate(decorView);
      }
      
      if (contentHolder != null) {
          contentHolder.removeView(contentView);
      }
      
      // This needs to stay until after all transitions have ended since we
      // need the reference to cancel transitions in preparePopup().
      mDecorView = null;
      mBackgroundView = null;
      mIsTransitioningToDismiss = false;
      }
2.4 PopupDecorView源碼分析
  • 通過(guò)createDecorView(View contentView)方法可以知道,是PopupDecorView直接new出來(lái)的布局對(duì)象decorView,外面包裹了一層PopupDecorView,這里的PopupDecorView也是我們自定義的FrameLayout的子類,然后看一下里面的代碼:

    • 可以發(fā)現(xiàn)其重寫了onTouchEvent時(shí)間,這樣我們?cè)邳c(diǎn)擊popupWindow外面的時(shí)候就會(huì)執(zhí)行pupopWindow的dismiss方法,取消PopupWindow。
    private class PopupDecorView extends FrameLayout {
        private TransitionListenerAdapter mPendingExitListener;
    
        public PopupDecorView(Context context) {
            super(context);
        }
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            final int x = (int) event.getX();
            final int y = (int) event.getY();
    
            if ((event.getAction() == MotionEvent.ACTION_DOWN)
                    && ((x < 0) || (x >= getWidth()) || (y < 0) || (y >= getHeight()))) {
                dismiss();
                return true;
            } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
                dismiss();
                return true;
            } else {
                return super.onTouchEvent(event);
            }
        }
    }

3.經(jīng)典總結(jié)

3.1 PopupWindow和Dialog有什么區(qū)別?
  • 兩者最根本的區(qū)別在于有沒(méi)有新建一個(gè)window,PopupWindow沒(méi)有新建,而是將view加到DecorView;Dialog是新建了一個(gè)window,相當(dāng)于走了一遍Activity中創(chuàng)建window的流程
  • 從源碼中可以看出,PopupWindow最終是執(zhí)行了mWindowManager.addView方法,全程沒(méi)有新建window
3.2 創(chuàng)建和銷毀的大概流程
  • 源碼比較少,比較容易懂,即使不太懂,只要借助有道詞典翻譯一下英文注釋,還是可以搞明白的。
  • 總結(jié)一下PopupWindow的創(chuàng)建出現(xiàn)、消失有哪些重要操作
    • 創(chuàng)建PopupWindow的時(shí)候,先創(chuàng)建WindowManager,因?yàn)閃IndowManager擁有控制view的添加和刪除、修改的能力。這一點(diǎn)關(guān)于任主席的藝術(shù)探索書上寫的很詳細(xì)……
    • 然后是setContentView,保存contentView,這個(gè)步驟就做了這個(gè)
    • 顯示PopupWindow,這個(gè)步驟稍微復(fù)雜點(diǎn),創(chuàng)建并初始化LayoutParams,設(shè)置相關(guān)參數(shù),作為以后PopupWindow在應(yīng)用DecorView里哪里顯示的憑據(jù)。然后創(chuàng)建PopupView,并且將contentView插入其中。最后使用WindowManager將PopupView添加到應(yīng)用DecorView里。
    • 銷毀PopupView,WindowManager把PopupView移除,PopupView再把contentView移除,最后把對(duì)象置為null
3.3 為何彈窗點(diǎn)擊一下就dismiss呢?
  • PopupWindow通過(guò)為傳入的View添加一層包裹的布局,并重寫該布局的點(diǎn)擊事件,實(shí)現(xiàn)點(diǎn)擊PopupWindow之外的區(qū)域PopupWindow消失的效果

4.PopupWindow封裝庫(kù)介紹

項(xiàng)目地址:https://github.com/yangchong211/YCDialog
  • 鏈?zhǔn)骄幊?,十分方便,更多?nèi)容可以直接參考我的開(kāi)源demo
    new CustomPopupWindow.PopupWindowBuilder(this)
        //.setView(R.layout.pop_layout)
        .setView(contentView)
        .setFocusable(true)
        //彈出popWindow時(shí),背景是否變暗
        .enableBackgroundDark(true)
        //控制亮度
        .setBgDarkAlpha(0.7f)
        .setOutsideTouchable(true)
        .setAnimationStyle(R.style.popWindowStyle)
        .setOnDissmissListener(new PopupWindow.OnDismissListener() {
            @Override
            public void onDismiss() {
                //對(duì)話框銷毀時(shí)
            }
        })
        .create()
        .showAsDropDown(tv6,0,10);

關(guān)于其他內(nèi)容介紹

01.關(guān)于博客匯總鏈接
  • 1.技術(shù)博客匯總
  • 2.開(kāi)源項(xiàng)目匯總
  • 3.生活博客匯總
  • 4.喜馬拉雅音頻匯總
  • 5.其他匯總
02.關(guān)于我的博客
  • 我的個(gè)人站點(diǎn):www.yczbj.org,www.ycbjie.cn
  • github:https://github.com/yangchong211
  • 知乎:https://www.zhihu.com/people/yang-chong-69-24/pins/posts
  • 簡(jiǎn)書:http://www.jianshu.com/u/b7b2c6ed9284
  • csdn:http://my.csdn.net/m0_37700275
  • 喜馬拉雅聽(tīng)書:http://www.ximalaya.com/zhubo/71989305/
  • 開(kāi)源中國(guó):https://my.oschina.net/zbj1618/blog
  • 泡在網(wǎng)上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
  • 郵箱:yangchong211@163.com
  • 阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
  • segmentfault頭條:https://segmentfault.com/u/xiangjianyu/articles

另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內(nèi)外云服務(wù)器15元起步,三天無(wú)理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。


分享標(biāo)題:PopupWindow源碼分析-創(chuàng)新互聯(lián)
URL分享:http://weahome.cn/article/desceh.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部