閱讀146 返回首頁    go 阿裏雲 go 技術社區[雲棲]


仿uc下部彈出菜單

先說說我怎麼會無聊到這種地步去弄這個代碼呢,在今年2月份的時候公司本來要做個這種彈出的菜單的,有5個按鈕每個都有一個菜單,記得網上有仿UC菜單的源碼,就下下來看,結果不符合要求,當時這個菜單由另一個同事在做代碼,組長叫我和他考慮界麵實現的可行性,我提出這種思路給他,他不采納,覺得太煩了,他就自己弄,因為我們的項目是車載導航上的一個主控程序上的菜單,屏幕分辨率固定,所以用5張圖片就ok了,所以到最後這個都不了了之了,現在也離開了那家公司,應該沒有什麼公司機密吧,沒用到公司項目,而且是我自己下班家裏搞得,公開了應該沒啥事,而且好像沒什麼技術含量,就是這個思路不錯,發給大家參考下(LZ BB了這麼多廢話全是因為今天七夕,"一個人的七夕".equals("死") == true)。
先來看UC菜單的效果



發現沒,UC的菜單箭頭絕對是對準所點擊按鈕的,有人可能覺得用不同的圖片就行了,對於一種機型可以這麼做,android機型千千萬,你要怎麼配型,這是個很大的工作量,吃力不討好啊。反編譯過UC的人就知道,UC裏沒有這種圖片,那他用的是什麼圖呢,UC用到下麵的3張圖片。


我當時就想這3張圖怎麼在xml裏添加進去呢,百撕不得其姐啊,當時公司要弄個翻頁時鍾(仿墨跡的),那邊我用到了圖片合成的方法,就想到這邊也可以用這種方法,就是必須先把.9圖拉伸下。
具體UC是怎麼合成的圖片,隻有問UC去了,我有一種方法也可以合成,就不知道是不是和UC的原理一樣了給你們參考下。

先說思路吧,看圖

如圖所示,可以把菜單的背景分成3部分,這樣用上麵的3張.9圖片根據不同大小進行拉伸組合就能得到想要的效果了。
為啥要把按鈕4等份,把其中的2份給圖1和圖3呢,這是為了防止按鈕居左或居右時,解決圖1和圖3的寬度為NULL情況。

看了上麵的分割,這樣布局的話,圖片2的箭頭是不是絕對的對準按鈕的中間啊,而且背景的合成是根據button來的,適應性是不是很強,雖然跟按鈕的大小位置有關,但是隨著button的大小位置的變化,背景圖的3塊區域也隨之變化組合成符合的圖片,瞬間智商上的優越感爆棚了

思路說完了,看下實現的方法把,就是以一個bitmap為畫布,把另一個繪製到上麵就行,這是我的方法,不知道還有沒其他的方法,望大能給個更好的方法。

怎麼把兩個bitmap合成一個呢
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
/**
         * 以一個Bitmap為畫布,畫上一個Bitmap
         * @param canvasBitmap 作為畫布的Bitmap
         * @param drawBitmap 要被繪製的Bitmap
         * @param top 從畫布的距離頂部的top位置開始
         * @param left 從畫布的距離左邊的left位置開始
         */
         private void drawbitMap(Bitmap canvasBitmap, Bitmap drawBitmap, int top, int left)
          {
            Canvas localCanvas = new Canvas(canvasBitmap);//以canvasBitmap生成畫布
            localCanvas.drawBitmap(drawBitmap, left, top, null);//在畫布上移left和top左標開始繪製drawBitmap
            localCanvas.save(Canvas.ALL_SAVE_FLAG);//保存
            localCanvas.restore();
            drawBitmap.recycle();//釋放掉drawBitmap,防止內存泄漏
          }


加載資源文件中的圖片為Drawable後怎麼把它拉伸到需要的大小並轉化為bitmap呢,我查了資料(百度,google),自己總結使用了下麵的一個方法,也可以看
dyh7077063的博客 :https://dyh7077063.iteye.com/blog/970672,今天才看到的博客,以前怎麼沒早看到,虧死了,寫的不錯,很多方法用的上。

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
/**
          * 把Drawable生成對應的Bitmap
          * @param paramRect 生成的Bitmap大小等一些參數
          * @param drawable 要繪製的drawable
          * @param canvasBitmap 將drawable繪製到canvasBitmap中
          */
          private void getBitMap(Rect paramRect, Drawable drawable, Bitmap canvasBitmap)
          {
                //中這個方法顧名思義,就是設置邊界,
                //用到的是.9圖,所以拉伸圖片不會失真,把drawable設置一個left、top點開始拉一個paramRect.right寬
                //paramRect.bottom高的矩形區域,按我的理解就是弄了這個區域,就是把圖片按.9圖的設置拉倒相應的
                //大小填充到矩形區域裏去,不明白看  的博客
            drawable.setBounds(0, 0, paramRect.right, paramRect.bottom);
            //用canvasBitmap生成一個畫布
            Canvas localCanvas = new Canvas(canvasBitmap);
            drawable.draw(localCanvas);//使用drawable的draw方法畫到畫布上
            localCanvas.save(Canvas.ALL_SAVE_FLAG);//保存
            localCanvas.restore();
          }


我解釋下drawable.setBounds這個方法:前麵圖片裏寫成其他部分填充白色,這是錯誤的,是以透明像素來填充的,誤導大家了啊,見諒哈,我已修改
有上麵的方法,在技術可行性方麵就不是問題了。

再來說怎麼確定3個圖片分別對應的大小呢,其實這個很容易,看上麵的圖可知,隻要能得到那個button(View)就行了,這個還是比較容易獲得的把

所以很快就能得到3個圖片對應要生成的大小了,看下麵的代碼,top和left表示的是在背景圖中繪製的左上角的起始位置。

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
//定義3個圖片的大小等一些參數
              Rect[] arrayOfRect = new Rect[3];
              arrayOfRect[0] = new Rect();
              arrayOfRect[0].top = 0;
              arrayOfRect[0].left = 0;
              arrayOfRect[0].right = this.parentLeft + this.parentWidth / 4;
              arrayOfRect[0].bottom = this.popupWindowHeight;
               
              arrayOfRect[1] = new Rect();
              arrayOfRect[1].top = 0;
              arrayOfRect[1].left = arrayOfRect[0].right;
              arrayOfRect[1].right = this.parentWidth / 2;
              arrayOfRect[1].bottom = this.popupWindowHeight;
               
              arrayOfRect[2] = new Rect();
              arrayOfRect[2].top = 0;
              arrayOfRect[2].left = this.parentLeft + this.parentWidth * 3 / 4;
              arrayOfRect[2].right = screenwidth - arrayOfRect[2].left;
              arrayOfRect[2].bottom = this.popupWindowHeight;


其中parentLeft為button的距離屏幕左邊距的距離,也就是getLeft方法得到的數值,parentWidth就是按鈕本身的寬度了,screenwidth 是屏幕寬度,popupWindowHeight為popupWindow的高度,具體你們看圖和源碼。

現在隻要循環調用把3張圖繪製到一張bitmap中在生成drawable就好。
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
         * 生成背景圖
         * @param context 上下文,為生成BitmapDrawable所需的
         * @param ArrayOfRect
         * @param ArrayOfDrawable
         * @return
         */
          private Drawable getDrawable(Context context,
                          Rect[] ArrayOfRect, Drawable[] ArrayOfDrawable)
          {
                  Bitmap.Config localConfig = Bitmap.Config.ARGB_8888;
                  //先更具popupWindow的大小生成一個Bitmap
                  Bitmap paramBitmap = Bitmap.createBitmap(screenwidth, popupWindowHeight, localConfig);
                   
                  //這裏循環把3個圖片繪製到paramBitmap中
                  for (int i = 0; i < ArrayOfDrawable.length; i++)
                  {
                          Rect localRect = ArrayOfRect<i>;
                      Bitmap localBitmap = Bitmap.createBitmap(localRect.right, localRect.bottom, localConfig);            
                      Drawable localDrawable = ArrayOfDrawable</i><i><i>;
                      getBitMap(localRect, localDrawable, localBitmap);//得到相應的drawable的BitMap
                      drawbitMap(paramBitmap, localBitmap, localRect.top, localRect.left);//在paramBitmap中繪製localBitmap
                      localBitmap.recycle();//釋放掉,要不多次運行有可能會內存泄漏
                  }
                  return new BitmapDrawable(context.getResources(), paramBitmap);
          }</i></i>


以上代碼就生成了相應大小的圖片了。

有了這些方法,就可實現菜單背景的生成。

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
public void show(View parent, Context context) {
                bg = mapDrawable.get(parent);//為節省資源,map中會保存以前生成的背景,根據父控件來獲得
                popupWindowHeight = popupWindow.getHeight();//得到popupWindow的高度,在popupWindow構造完後才能獲取
 this.parentLeft = parent.getLeft();//父控件的左邊距
                this.parentWidth = parent.getWidth();//父控件的寬度
                if(bg == null)//背景為空
                {
                        createDrawable(context);//生成背景圖
                        mapDrawable.put(parent, bg);//保存到map中
                }
                 
                popupWindow.setBackgroundDrawable(bg);//給popupWindow設置背景
                popupWindow.showAtLocation(parent, Gravity.BOTTOM, 0, parent.getHeight());// 距離底部的位置
                popupWindow.setAnimationStyle(R.style.popwin_anim_style);
                popupWindow.setFocusable(true);
                popupWindow.setOutsideTouchable(true);
                popupWindow.update();
}


mapDrawable是一個HashMap<View, Drawable>對象,用來保存生成的背景的,多次調用肯定會帶來內存和時間上的大量損耗,所以一個按鈕生成一個背景後保存下來下次再用是非常好的方法。

最後看我弄的效果哈


效果還行吧!!

發圖和部分代碼不給源碼是非常不人道的,但是希望下源碼人不要隻copy,那是沒有進步的,我們不光要模仿還要會思考,使用別人的方法達到別人沒有實現的效果也是有進步的,代碼還可優化,大家自己弄哈,由於源碼放在上家公司沒帶走,所以前天我下了個仿UC的菜單源碼進行修改來的,所以代碼有些亂望見諒,PopMenu類可以直接使用的:TestPullPopWindow.rar(1.01 MB, 下載次數: 641)


ps:轉載請標明出處,好歹給我們這些IT民工一些優越感吧,by:baofei

最後更新:2017-04-03 14:54:29

  上一篇:go 為什麼擁有6.5億用戶的Skype沒能成為另一個騰訊
  下一篇:go 手機衛士03-下載app並安裝