先来看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的博客 :http://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