产品大大要一个唯品会一样的尺码助手弹框。首先找到了BubbleLayout,基于BubbleLayout实现该功能。
BubbleLayout项目地址
放一下实现效果吧
1.思路:尺码这一部分是一个GridView,没啥说的,在其item的点击事件上监听点击后在原来给尺码赋选取值的基础上弹出popupwindow,popupwindow上的布局外层用相对布局里面嵌套BubbleLayout带三角和一个叉号,里面也是一个GridView用于显示尺码助手
2.具体实现:
popupwindow的布局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/transparent"
>
<com.okbuy.android.ui.component.SizeRemindLayout.BubbleLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/bl_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:layout_marginTop="10dp"
android:padding="0dp"
app:bl_arrowDirection="bottom"
app:bl_arrowHeight="5dp"
app:bl_arrowPosition="0dp"
app:bl_arrowWidth="8dp"
app:bl_bubbleColor="#cc000000"
app:bl_cornersRadius="2dp"
>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/ll_only_had"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:paddingTop="4dp"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="仅剩"
android:textColor="#FFFFFF"
android:textSize="10dp"
/>
<TextView
android:id="@+id/tv_only_had"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#D70057"
android:textSize="10dp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="件"
android:textColor="#FFFFFF"
android:textSize="10dp"
/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="4dp"
android:background="#66FFFFFF"/>
</LinearLayout>
<com.okbuy.android.ui.recommend.MyGridView
android:id="@+id/gv_size_table_help"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:horizontalSpacing="0dp"
android:listSelector="@android:color/transparent"
android:scrollbars="none"
android:stretchMode="columnWidth"
android:verticalSpacing="0dp">
</com.okbuy.android.ui.recommend.MyGridView>
</LinearLayout>
</com.okbuy.android.ui.component.SizeRemindLayout.BubbleLayout>
<ImageView
android:id="@+id/iv_close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:src="@drawable/image_product_close"/>
</RelativeLayout>
GridView顶部会有仅剩几件的提醒,根据接口数据显示或者隐藏。
这里边有几点是做的时候比较费劲的地方,如BubbleLayout中三角的偏移量是变化的,BubbleLayout提供.setArrowPosition()方法来设置偏移量所以我们只需求得每个item的偏移量即可;由于尺码助手的提示内容不确定导致GridView的长度不是固定的需要获取BubbleLayout的宽高;popupwindow的显示位置。
(1)先来看BubbleLayout的宽高的获取,一开始想法是正常的控价获取宽高:int[] location = new int[2]; 和 .getLocationInWindow方法。但是发现该方法如果是固定的控件比如Textview可以正常获取宽高,GridView的内容不定直接这么写获取不到。所以想来想去只能拆开来计算,计算单个item的宽高在叠加:
int rows = 1;
int columns=dataHelper.size();
int horizontalBorderHeight=(new TeldToastUtils(mContext)).dp2px(9);
int itemWith = 0;
int totalHeight = 0;
//只计算每项高度*行数
View listItem = listAdapter.getView(0, null, gridView);
listItem.measure(0, 0); // 计算子项View 的宽高
totalHeight = listItem.getMeasuredHeight(); // 统计子项的高度
itemWith = listItem.getMeasuredWidth();
gridViewHeight = totalHeight+horizontalBorderHeight;
//宽度+padding以及竖线的宽
gridViewWith = itemWith * columns+((new TeldToastUtils(mContext)).dp2px(8)*(columns-1));
这里注意的是要考虑padding和分割线的宽度,高度也是根据实际的UI来计算。
(2)三角偏移量
当前我们在尺码的点击onclick中可以得到尺码item的宽高及位置。
//尺码item的位置
int[] location = new int[2];
v.getLocationInWindow(location);
//获取屏幕宽高
WindowManager wm = (WindowManager) mContext
.getSystemService(Context.WINDOW_SERVICE);
int pmWidth = wm.getDefaultDisplay().getWidth();
int pmHeight = wm.getDefaultDisplay().getHeight();
//三角偏移量
float x;
if((location[0]+v.getWidth()/2)<(width/2)){
x = location[0]+(v.getWidth()/2);
}else {
if((pmWidth-location[0]-(v.getWidth()/2))<(width/2)){
x = widthP - pmWidth + location[0]+v.getWidth()/2;
}else{
x = widthP/2;
}
}
这里偏移量的计算公式可以画个草图就知道了,当然前提条件是popupwindow如果空间够大是显示在尺码item的正上方左右对称的基础上此时偏移量就是popupwindow宽度的一半,在靠左和靠右时保证显示的位置是在尺码item的一半的位置。
(3)popupwindow的显示位置。
这个也是最坑爹的,这里有两个方法:popupWindow.showAtLocation()和popupWindow.showAsDropDown()之前一直用第二个在尝试,最后发现第二个方法巨坑,他在整个尺码再屏幕上位置考下时右下的item位置会显示错乱,最后的摸索到一点点规律,在屏幕一半以下的右边会错乱但是不是完全的。很崩溃,最后用第一个方法实现的。现在的已知条件:尺码item的位置以及宽高,popupwindow弹窗的宽高。还是在之上画画找到计算位置的公式(其中width和height是弹窗的宽高,高度动态添加“仅剩”的高度):
popupWindow.showAtLocation(v, Gravity.TOP|Gravity.LEFT, location[0]+(v.getWidth()/2)-(width/2), location[1]-height);
整个onclick的代码:
@Override
public void onClick(View v) {
// 如果可点击
if (v.isEnabled()) {
mRecommendSize = "";
setSelectSize(position);
mCallback.selectSize(getSelectSize());
//尺码弹窗
RelativeLayout rl = (RelativeLayout) LayoutInflater.from(mContext).inflate(R.layout.layout_size_remain_bubble, null);
ImageView ivClose = (ImageView) rl.findViewById(R.id.iv_close);
bubbleLayout = (BubbleLayout) rl.findViewById(R.id.bl_layout);
//设置三角朝下
bubbleLayout.setArrowDirection(ArrowDirection.BOTTOM);
ivClose.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(popupWindow.isShowing()){
popupWindow.dismiss();
}
}
});
int[] location = new int[2];
v.getLocationInWindow(location);
GridView mSizeTableGrid = (GridView) bubbleLayout.findViewById(R.id.gv_size_table_help);
LinearLayout onlyHad = (LinearLayout) bubbleLayout.findViewById(R.id.ll_only_had);
TextView textViewOnly = (TextView) bubbleLayout.findViewById(R.id.tv_only_had);
if(mDetailInfo != null){
if (mDetailInfo.getSizeTable() != null) {
List<ProductSizeHelper> dataHelper = mDetailInfo.getSizeHelper().get(pSize.sizeName);
//还有的尺码没有对照表!!!
if(dataHelper !=null && dataHelper.size()>0){
mSizeTableGrid.setAdapter(new SizeTableHelperAdapter(mContext, dataHelper));
mSizeTableGrid.setNumColumns(dataHelper.size());
getGridViewHeight(mSizeTableGrid,dataHelper);
if(pSize.leftSizeCount <= 5 ){
onlyHad.setVisibility(View.VISIBLE);
textViewOnly.setText(""+pSize.leftSizeCount);
}else {
onlyHad.setVisibility(View.GONE);
}
//获取尺码弹窗的大小
int w = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
int h = View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED);
onlyHad.measure(w, h);
//“仅剩几件”的高度
int heightOnly = onlyHad.getMeasuredHeight();
//尺码弹窗的高
int height = 0;
if(pSize.leftSizeCount <= 5 ){
height = gridViewHeight+(int)(bubbleLayout.getArrowHeight())+heightOnly;
}else{
height = gridViewHeight+(int)(bubbleLayout.getArrowHeight());
}
//尺码弹窗的宽
int width = gridViewWith;
//偏移量忽略“x”
int widthP = gridViewWith-(new TeldToastUtils(mContext)).dp2px(5);
//获取屏幕宽高
WindowManager wm = (WindowManager) mContext
.getSystemService(Context.WINDOW_SERVICE);
int pmWidth = wm.getDefaultDisplay().getWidth();
int pmHeight = wm.getDefaultDisplay().getHeight();
//三角偏移量
float x;
if((location[0]+v.getWidth()/2)<(width/2)){
x = location[0]+(v.getWidth()/2);
}else {
if((pmWidth-location[0]-(v.getWidth()/2))<(width/2)){
x = widthP - pmWidth + location[0] +v.getWidth()/2;
}else{
x = widthP/2;
}
}
bubbleLayout.setArrowPosition(x);
popupWindow = BubblePopupHelper.create(v.getContext(), rl,width);
popupWindow.showAtLocation(v, Gravity.TOP|Gravity.LEFT, location[0]+(v.getWidth()/2)-(width/2), location[1]-height);
// popupWindow.showAsDropDown(v,-(width-v.getWidth())/2,-hWeizhi);
}
}
}
}
}
这里因为是线上代码,数据源和一些回调可以忽略,根据实际需求就可以。有时间会拆分出demo。
总结:这里主要是获取弹窗的宽高,用于偏移量的计算和设置popupWindow位置,popupwindow的显示位置记得用.showAtLocation()他二者的区别可以自己研究研究,一个是相对于整个的屏幕,一个是相对于父控件。