利用.9图做背景,配合使用popwindow和listview可以实现这个需求,也可以自定义popwindow的布局,画边框加阴影,自定义三角形的view,然后...然后我就放弃了这个思路,老老实实直接让设计师给我切图了。andriod开发中写个阴影都要几十行xml代码,不像H5开发,一行css代码搞定了,反正我是受不了,怎么方便怎么来。
- 直接把切好的图(png格式)放进对应的drawable文件(分辨率不同)中,然后右键png文件,选中create 9-patch file这个选项,进入.9图编辑
- 其实.9图就是适应图片在各个可能拉伸变形的情景中用的
- 编辑四条边,该拉伸的地方调整一下,不希望它拉伸的地方不要选中,就ok了
- 创建popwindow布局文件
- 在相应的页面文件(activity)中初始化popwindow
// 初始化popwindow
public void initPopwindow() {
popListData \= new ArrayList<>();
mContentView \= LayoutInflater.from(getApplicationContext()).inflate(R.layout.video\_clip\_pop\_window, null);
mList \= mContentView.findViewById(R.id.list);
//初始化list的数据
for (int i = 0; i< styleList.length;i++) {
PopWindowSelectItem popSelectItem = new PopWindowSelectItem();
popSelectItem.setName(styleList\[i\]);
if (i != 0) {
popSelectItem.setSelect(false);
}else {
popSelectItem.setSelect(true);
}
popListData.add(popSelectItem);
}
mAdapter \= new VideoClipPopWindowAdapter(VideoProfessionalClipActivity.this,popListData);
mPopupWindow \= new PopupWindow(findViewById(R.id.profession\_clip\_main\_layout), DensityUtil.dip2px(VideoProfessionalClipActivity.this,320), DensityUtil.dip2px(VideoProfessionalClipActivity.this,155));
mPopupWindow.setFocusable(true);
mList.setAdapter(mAdapter);
mPopupWindow.setBackgroundDrawable(new ColorDrawable(0xffffffff));
mPopupWindow.setOutsideTouchable(true);
mPopupWindow.setContentView(mContentView);
mList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
if (currList \== STYLE\_LIST\_CODE) {
selectStyleIndex \= position;
selected\_style\_tag.setText(styleList\[position\]);
for (int i = 0; i < styleList.length; i++) {
if (selectTimeIndex \== i) {
popListData.get(position).setSelect(true);
}else {
popListData.get(position).setSelect(false);
}
}
}else {
selectTimeIndex \= position;
selected\_time\_tag.setText(timeList\[position\]);
for (int i = 0; i < timeList.length; i++) {
if (selectTimeIndex \== i) {
popListData.get(position).setSelect(true);
}else {
popListData.get(position).setSelect(false);
}
}
}
mPopupWindow.dismiss();
}
});
mPopupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
@Override
public void onDismiss() {
if (currList \== STYLE\_LIST\_CODE) {
direction\_icon\_left.setText("\\ue6d0");
}else {
direction\_icon\_right.setText("\\ue6d0");
}
//每次隐藏后滚动到顶部
mList.post(new Runnable() {
@Override
public void run() {
mList.setSelection(0);
}
});
}
});
mAdapter.notifyDataSetChanged();
}
大概流程就是用LayoutInflater
拿到popwindow的布局实例,从布局实例view中找到listview,初始化listview的数据,实例化adapter,adapter里面自定义了每个item的布局,所以还要有一个pop_window_item的布局文件,然后把布局内容塞给PopupWindow实例对象,设置item的监听回调,处理一些业务逻辑,监听popwindow消失的事件,里面主要处理icon的状态、让listview重新回到顶部(post到ui线程中渲染)。
给FontIconView动态设置内容的时候,setText必须要传unicode码,xml是有处理unicode的能力的,但是动态设置的时候,不能传字符串,比如:
,正确的写法应该是\ue6b8
- 还有一个需要注意的是:点击改变list数据,让listview重新绘制popwindow的内容,不能直接改变数组的引用,让后adapter.notifysetdatachange(),而是要先清空原有的数组内容,然后add或者addAll进去新的值,然后mAdapter.notifyDataSetChanged()才会有效,例如:
//视频时长下拉选择
@OnClick(R.id.select\_time)
public void selectTime() {
mList.setBackground(this.getDrawable(R.drawable.pop\_window\_bg\_right));
this.currList \= TIME\_LIST\_CODE;
direction\_icon\_right.setText("\\ue6be");
List newList = new ArrayList<>();
for (int i = 0; i< timeList.length;i++) {
PopWindowSelectItem popSelectItem = new PopWindowSelectItem();
popSelectItem.setName(timeList\[i\]);
if (i != selectTimeIndex) {
popSelectItem.setSelect(false);
}else {
popSelectItem.setSelect(true);
}
newList.add(popSelectItem);
}
popListData.clear();
popListData.addAll(newList);
mAdapter.notifyDataSetChanged();
//计算显示的位置
View parent = LayoutInflater.from(VideoProfessionalClipActivity.this).inflate(R.layout.activity\_video\_professional\_clip,null);
int\[\] location = new int\[2\];
style.getLocationOnScreen(location);
int top = location\[1\] + DensityUtil.dip2px(VideoProfessionalClipActivity.this,35);
mPopupWindow.update();
mPopupWindow.showAtLocation(parent,Gravity.TOP,0,top);
}
或者还有一种方法,adapter里面可以写个方法让外部调用,比如叫setData,里面负责给adapter里面要渲染的数据重新赋值,然后外面调用这个方法,传入新的值,最后mAdapter.notifyDataSetChanged()
- 贴上adapter的代码
public class VideoClipPopWindowAdapter extends BaseAdapter {
private List datas;
private Context context;
public VideoClipPopWindowAdapter(Context context, List datas) {
this.context \= context;
this.datas \= datas;
}
public int getCount() {
return datas.size();
}
public Object getItem(int position) {
return datas.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
convertView = LayoutInflater.from(context).inflate(R.layout.video\_clip\_popwindow\_item, null);
TextView tv = (TextView) convertView.findViewById(R.id.popwindow\_item\_text);
TextView icon = convertView.findViewById(R.id.popwindow\_item\_icon);
if (datas.get(position).isSelect()) {
icon.setVisibility(View.VISIBLE);
}else {
icon.setVisibility(View.INVISIBLE);
}
tv.setText(datas.get(position).getName());
return convertView;
}
}
- 最后记录一下自定义展示popwindow的位置怎么实现
//计算显示的位置
View parent = LayoutInflater.from(VideoProfessionalClipActivity.this).inflate(R.layout.activity\_video\_professional\_clip,null);
int\[\] location = new int\[2\];
style.getLocationOnScreen(location);
int top = location\[1\] + DensityUtil.dip2px(VideoProfessionalClipActivity.this,35);
mPopupWindow.update();
mPopupWindow.showAtLocation(parent,Gravity.TOP,0,top);
showAtLocation
第一个参数传入父节点的view实例,但是还想不管传什么,他都会找到root节点,目前不知道具体有什么区别,反正我是把当前页面的布局转换成view实例传进去了,也是root节点吧,第二个参数就是传入popwindow在页面中显示的位置,3,4传入具体的x,y值getLocationOnScreen
可以获取到具体的view在屏幕中的具体坐标,参数是int数组,第一位是x,第二位是y,会通过回调的方式返回给你,拿到你想参考的view的坐标就可以定位popwindow的位置了,我是参考了style的top值加上他本身的高度,就能定位了。
安卓开发任重道远~