先看效果稠集,在TitleBar上面有一個圖片的控件,當(dāng)點擊了圖片出現(xiàn)了一個可以擴(kuò)展的PopupWindow瓢宦,同時可以通過點擊事件去做相對應(yīng)的操作
所需要的類的:Item adapter listview 還有一個管理的ExtendPopupWindow
1斥季、為了可擴(kuò)展性的話锦溪,我們需要在PopupWindow中加載的是一個可以動態(tài)設(shè)置的item,其實呢减余,可以在item中設(shè)置過多的屬性,以滿足實際的項目需要(比如說惩系,可以設(shè)置一個紅點位岔,提醒用戶這里面有更新的東西),這里我只設(shè)置了一個title堡牡,就是和上面的git圖中的一個文字的tiitle抒抬,
package com.example.popupwindow;
import android.content.Context;
/**
* @author shiming
* @time 2017/5/11 18:37
* @desc 可以擴(kuò)展的item
*/
public class PopupItem {
private String title;
private int tag;
public int getIcon() {
return icon;
}
public void setIcon(int icon) {
this.icon = icon;
}
private int icon;
private Context context;
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
public int getTag() {
return tag;
}
public void setTag(int tag) {
this.tag = tag;
}
public PopupItem(int tag, String title) {
this.tag = tag;
this.title = title;
}
public PopupItem(Context context,String title, int tag, int icon) {
this(tag,title);
this.icon=icon;
this.context = context;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
2、PopupMenuExtendAdapter的創(chuàng)建
1晤柄、R.layout.nim_popup_menu_list_item擦剑,layout布局,可以通過自定義芥颈,這里就是簡單的使用的l惠勒,使用了ViewHolder 去管理
2、通過對數(shù)據(jù)的獲取PopupItem item = mPopupItemList.get(position);設(shè)置layout應(yīng)該顯示的內(nèi)容
package com.example.popupwindow;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
/**
* @author shiming
* @time 2017/5/11 19:08
* @desc 可以動態(tài)的設(shè)置很多
*/
public class PopupMenuExtendAdapter extends BaseAdapter {
private Context mContext;
private List<PopupItem> mPopupItemList;
private final LayoutInflater mInflater;
public PopupMenuExtendAdapter(Context context, List<PopupItem> popupItemList) {
mContext = context;
mPopupItemList = popupItemList;
//通過這個方法去拿到 infalter
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
@Override
public int getCount() {
return mPopupItemList.size();
}
@Override
public Object getItem(int position) {
return mPopupItemList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder cache;
if (convertView == null) {
cache = new ViewHolder();
convertView = mInflater.inflate(R.layout.nim_popup_menu_list_item, null);
cache.icon = (ImageView) convertView.findViewById(R.id.popup_menu_icon);
cache.title = (TextView) convertView.findViewById(R.id.popup_menu_title);
convertView.setTag(cache);
} else {
cache = (ViewHolder) convertView.getTag();
}
PopupItem item = mPopupItemList.get(position);
cache.title.setText(item.getTitle());
if (item.getIcon() != 0) {
cache.icon.setVisibility(View.VISIBLE);
cache.icon.setImageResource(item.getIcon());
} else {
cache.icon.setVisibility(View.GONE);
}
// 下面代碼實現(xiàn)數(shù)據(jù)綁定
return convertView;
}
private final class ViewHolder {
public ImageView icon;
public TextView title;
}
}
3爬坑、PopupMenuExtendListview的創(chuàng)建纠屋,其實也是簡單的擴(kuò)張了listview,對listview的寬度重新的測量盾计,代碼如下
package com.example.popupwindow;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ListView;
/**
* @author shiming
* @time 2017/5/8 19:56
* @desc popup Menu 為了可以擴(kuò)展 這里繼承了 listview
*/
public class PopupMenuExtendListview extends ListView {
public PopupMenuExtendListview(Context context) {
this(context, null);
}
public PopupMenuExtendListview(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* 1.View本身大小多少售担,這由onMeasure()決定;
* 2.View在ViewGroup中的位置如何署辉,這由onLayout()決定族铆;
* 3.繪制View,onDraw()定義了如何繪制這個View哭尝。
*/
/**
* View本身大小多少哥攘,這由onMeasure()決定
* @param widthMeasureSpec 代模式的32 位
* @param heightMeasureSpec 代模式的32位
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = measureWidthByChilds() + getPaddingLeft() + getPaddingRight();
//重新的測量這個 精確的模式下面
super.onMeasure(MeasureSpec.makeMeasureSpec(width,MeasureSpec.EXACTLY), heightMeasureSpec);
}
/**
* 測量listview中的孩子的寬度
*
* @return width
*/
private int measureWidthByChilds() {
int width = 0;
View view = null;
for (int i = 0; i < getAdapter().getCount(); i++) {
view = getAdapter().getView(i, view, this);
if (view != null) {
//為什么要傳入的是0
// *設(shè)置與此視圖相關(guān)聯(lián)的布局參數(shù)。這些供應(yīng)
//*參數(shù)刚夺,以指定此視圖應(yīng)該如何
// *設(shè)置献丑。有許多子viewgroup.layoutparams,這些
// *對應(yīng)的ViewGroup負(fù)責(zé)不同的子類
view.setLayoutParams(new ViewGroup.LayoutParams(0, 0));
// MeasureSpec.UNSPECIFIED是未指定尺寸侠姑,這種情況不多创橄,
// 一般都是父控件是AdapterView,通過measure方法傳入的模式莽红。
view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
if (view.getMeasuredWidth()>width){
width=view.getMeasuredWidth();
}
}
}
return width;
}
}
在寫代碼的時候如果我把view.setLayoutParams(new ViewGroup.LayoutParams(0, 0));給去掉了妥畏,只去view.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);我會發(fā)現(xiàn)始終測出來的值不對邦邦,但是我也沒有想通為什么要這么去設(shè)置,這個是看別人講解醉蚁,也不太明白燃辖。記得以前實現(xiàn)過一個動畫,先要去mease(0,0)网棍;然后去執(zhí)行才不會出錯黔龟,哎哎,先給個todo
4滥玷、關(guān)鍵的交互類氏身,ExtendPopupWindow詳情
1、構(gòu)造方法傳入?yún)?shù),MenuItemClickListener是內(nèi)部的接口惑畴,專門是做來點擊的事件蛋欣。
//當(dāng)擴(kuò)張到一定的地步是不是可以滑動
private boolean scroll = false;
public ExtendPopupWindow(Context context, List<PopupItem> items, MenuItemClickListener listener) {
this(context,items,false,listener);
}
public ExtendPopupWindow(Context context, List<PopupItem> items, boolean scroll, MenuItemClickListener listener) {
this.context = context;
this.items = items;
this.scroll = scroll;
this.listener = listener;
init();
}
public interface MenuItemClickListener {
void onItemClick(PopupItem item);
}
2、init()方法做什么
第一步初始化我們的rootview
if (rootView == null) {
rootView = LayoutInflater.from(context).inflate(R.layout.nim_popup_menu_layout, null);
ListView listView = (ListView) rootView.findViewById(R.id.popmenu_listview);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (listener != null) {
popWindow.dismiss();
listener.onItemClick(items.get(position));
}
}
});
adapter = new PopupMenuExtendAdapter(context, items);
listView.setAdapter(adapter);
}
// focusableInTouchMode.這個屬性的意思一如字面所述,就是在進(jìn)入觸摸輸入模式后,該控件是否還有獲得焦點的能力.
// 可以簡單的理解為,用戶一旦開始通過點擊屏幕的方式輸入,手機(jī)就進(jìn)入了"touch mode".
// focusableInTouchMode這種屬性,多半是設(shè)給EditText這種即使在TouchMode下,依然需要獲取焦點的控件
rootView.setFocusableInTouchMode(true);
//監(jiān)聽返回的按鈕如贷,然后 隱藏pop
rootView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU && popWindow.isShowing()
&& event.getAction() == KeyEvent.ACTION_DOWN) {
popWindow.dismiss();
return true;
}
return false;
}
});
以上的rootview其實是一個layout的高度為15dp陷虎,其中嵌套了一個我們封裝的popupwindow的listview,在給他設(shè)置了一個setOnKeyListener監(jiān)聽杠袱,判斷了按下rootview尚猿,判斷三種情況的成立的話pupwindow就必須的dismiss掉,KEYCODE_MENU其實就是早期的菜單鍵楣富,return true谊路;表示攔截這個操作
第二步做什么:初始化我們popwindow
//初始化我們的popuwindow
if (popWindow == null) {
popWindow = new PopupWindow(context);
popWindow.setContentView(rootView);
popWindow.setWidth(WindowManager.LayoutParams.WRAP_CONTENT);
if (scroll) {
popWindow.setHeight(context.getApplicationContext().getResources().getDisplayMetrics().heightPixels * 2 / 3);
} else {
popWindow.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
}
popWindow.setTouchable(true);
popWindow.setBackgroundDrawable(new BitmapDrawable());
popWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
@Override
public void onDismiss() {
}
});
}
以上需要說明的是: popWindow.setBackgroundDrawable(new BitmapDrawable());我們要給他一個bitmap,要不然會有問題菩彬,安卓有很多的地方有這個需求缠劝,估計理解為安卓的內(nèi)部的毛病。
假想個問題骗灶,這既然是一個交互的類惨恭,那么我們還需要做什么,好吧耙旦,我也咩想到脱羡,想到,最后看別人的代碼免都,哈哈…………
數(shù)據(jù)的交互锉罐,有adapter的對吧,so,暴露方法告訴adapter的數(shù)據(jù)變了
public void notifyData() {
adapter.notifyDataSetChanged();
}
pop不可能在初始化頁面了绕娘,就開始顯示了吧脓规,so,砸門的控制show()险领,判null,是否showing,這里有個關(guān)鍵的東西侨舆,我們設(shè)置了可以滑動秒紧,同時要判斷一下我們設(shè)備的屏幕的方向。
if (popWindow == null) {
return;
}
if (popWindow.isShowing()) {
popWindow.dismiss();
} else {
if (scroll) {// 當(dāng)可以滾動時挨下,橫豎屏切換時熔恢,重新確定高度
Configuration configuration = context.getResources().getConfiguration();
int ori = configuration.orientation;
if (ori == Configuration.ORIENTATION_LANDSCAPE) {
popWindow.setHeight(context.getApplicationContext().getResources().getDisplayMetrics().widthPixels* 2 / 3);
} else {
popWindow.setHeight(context.getApplicationContext().getResources().getDisplayMetrics().heightPixels * 2 / 3);
}
}
popWindow.setFocusable(true);
popWindow.showAsDropDown(v, -10, 0);
}
Configuration類專門描述手機(jī)設(shè)備上的配置信息,這些配置信息既包括用戶特定的配置項臭笆,也包括系統(tǒng)的動態(tài)設(shè)備配置叙淌。 int ori = configuration.orientation;判斷是否是橫豎屏,我記得沒錯的話愁铺,橫豎屏的話凿菩,我們的activity是需要重新的走生命周期,只有onnewintent的這個才不會走帜讲,是橫的話,為了顯示的更加人性化椒拗,我們要橫的2/3似将。 popWindow.showAsDropDown(v, -10, 0);顯示在哪個控件下面,這個v是我們的控件iew蚀苛,偏移了-10.y沒有動在验,這些可以動態(tài)的設(shè)置
5、Activity中的代碼如下
package com.example.popupwindow;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private ArrayList<PopupItem> menuItemList;
private ExtendPopupWindow popupMenu;
private View mImg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mImg = findViewById(R.id.img);
mImg.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
initPopuptWindow(MainActivity.this, mImg);
}
});
}
/**
* 窗體泄漏造成的原因堵未,是我們的view雖然已經(jīng)有了這個腋舌,但是我們在沒有通過點擊事件
* 或者其他的動作,去開啟這個popwindow的話渗蟹,我們的窗體就會泄漏
* 哈哈
*
*/
@Override
protected void onResume() {
super.onResume();
// initPopuptWindow(this, mImg);
}
private void initPopuptWindow(Context context, View view) {
if (popupMenu == null) {
menuItemList = new ArrayList<>();
popupMenu = new ExtendPopupWindow(context, menuItemList, listener);
}
menuItemList.clear();
menuItemList.addAll(getMoreMenuItems(context));
popupMenu.notifyData();
popupMenu.show(view);
}
private static List<PopupItem> getMoreMenuItems(Context context) {
List<PopupItem> moreMenuItems = new ArrayList<PopupItem>();
moreMenuItems.add(new PopupItem(context,
"feiji 0", 1,0));
moreMenuItems.add(new PopupItem(context,
"feiji 1", 2,0));
moreMenuItems.add(new PopupItem(context,
"feiji 2", 3,0));
moreMenuItems.add(new PopupItem(context,
"feiji 3", 4,0));
return moreMenuItems;
}
private ExtendPopupWindow.MenuItemClickListener listener = new ExtendPopupWindow.MenuItemClickListener() {
@Override
public void onItemClick(final PopupItem item) {
switch (item.getTag()) {
case 1:
Toast.makeText(MainActivity.this, "feiji 0",Toast.LENGTH_SHORT).show();
break;
case 2:
Toast.makeText(MainActivity.this, "feiji 1",Toast.LENGTH_SHORT).show();
break;
case 3:
Toast.makeText(MainActivity.this, "feiji 2",Toast.LENGTH_SHORT).show();
break;
case 4:
Toast.makeText(MainActivity.this, "feiji 3",Toast.LENGTH_SHORT).show();
break;
}
}
};
}
以上只說明一點:popupWindow块饺,不可能在初始化就顯示了,只能通過事件來顯示雌芽,要不然會報錯窗體泄漏