前一段時間接到一個需求,點擊彈出一個底部彈窗,彈窗的內容以列表的方式來呈現,同時包含選擇條目和自適應以及固定高度,需求不難,就是一些細節的東西繞了一些彎路,現在做下彙總。
國際慣例,先看下效果圖。
下面說下實現方法。
1.首先新建一個dialog的style。
<!--底部彈窗-->
<style name="DialogStyle" parent="android:style/Theme.Dialog">
<!--背景透明-->
<item name="android:windowBackground">@android:color/white</item>
<item name="android:windowContentOverlay">@null</item>
<!--浮於Activity之上-->
<item name="android:windowIsFloating">true</item>
<!--邊框-->
<item name="android:windowFrame">@null</item>
<!--Dialog以外的區域模糊效果-->
<item name="android:backgroundDimEnabled">true</item>
<!--無標題-->
<item name="android:windowNoTitle">true</item>
<!--半透明-->
<item name="android:windowIsTranslucent">true</item>
</style>
1.首先新建一個繼承Dialog的dialog文件,生成一個構造函數,把上面新建dialog的style引進去。
public ListViewDialog(Context context) {
super(context, R.style.AppTheme);
}
2.在構造方法中通過initViews()配置組件,主要是引入含listview的xml文件,並引用列表控件設置adapter適配器。
/**
* 配置組件
*/
private void initViews() {
View view= LayoutInflater.from(context).inflate(R.layout.dialog_main, null);
listview= view.findViewById(R.id.main_listview);
adapter= new MainAdapter(context, new ArrayList<MainBean>());
listview.setAdapter(adapter);
}
adapter適配器不復雜,item條目只有一個顯示title的TextView,完整代碼如下
/**
* 列表的adapter
*/
private class MainAdapter extends BaseAdapter {
private final Context context;
private List<MainBean> list;
private TextView titleTv;
public MainAdapter(Context context, List<MainBean> list) {
this.context= context;
this.list= list;
}
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
View contentView= LayoutInflater.from(context).inflate(R.layout.item_main, null);
titleTv= contentView.findViewById(R.id.main_titleTv);
titleTv.setText(list.get(i).getTitle());
return contentView;
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int i) {
return list.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
/**
* 設置新數據
* @param list
*/
public void setNewList(List<MainBean> list){
this.list= list;
notifyDataSetChanged();
}
}
3.在視圖Activity裏啓動dialog,爲了節省資源,我們做一個非空判斷,不用每次都new一個dialog。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (dialog== null){
dialog= new ListViewDialog(MainActivity.this);
}
dialog.show();
}
4.啓動app,數據應該出來了。
根據需求,此時我們把彈窗移到屏幕底部,並且調整一下寬度充滿屏幕。
在構造函數調用一下如下方法。
/**
* 設置寬高度
*/
private void setHeight() {
Window window= getWindow();
DisplayMetrics metrics= context.getResources().getDisplayMetrics();
WindowManager.LayoutParams params= window.getAttributes();
params.width= metrics.widthPixels;
window.setAttributes(params);
window.setGravity(Gravity.BOTTOM);
}
安裝app看下效果:
此時我們添加更多的測試數據,就會發現:
屏幕被全佔了,原來這是自適應高度,彈窗的高度會隨着條目的數量增加而增加,這種顯示效果自然不是我們想要的,所以我們要對它的高度也做下限制,比如說最多不能高於屏幕高度的百分之六十。
回到setHeight方法中,增加以下這行
params.height= (int) (metrics.heightPixels* 0.6);
運行,看效果:
很棒吧,高度已經限制屏幕的60%了,那麼問題又來了,如果條目過少呢
看吧,如果條目過少,高度也已固定,彈窗下方就會空出一大片空白區域,因此,我們應該設定當高度不足以到達屏幕高度的百分之六十時,我們就設置其高度自適應。
回到setHeight方法,在設置高度時增加這樣的判斷條件
if (window.getDecorView().getHeight() >= (int) (metrics.heightPixels * 0.6)) {
params.height = (int) (metrics.heightPixels * 0.6);
}
運行。
條目過少時,高度自適應。
條目過多時,高度固定屏幕高度的60%。
蛤?沒生效。。。經過度娘查找,因爲高度動態改變,隨之會發生彈窗焦點失去的狀況,此時我們只需要將sehHeight()方法放在這個方法中即可。
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
setHeight(); //設置寬高度
}
運行,正常了。
接下來,我們給列表加上選擇功能,當選中某個條目時,其會變成藍色,並把選中條目的值返回。
打開列表bean,增加一個自定義字段isChecked;
private boolean isChecked;
public boolean isChecked() {
return isChecked;
}
public void setChecked(boolean checked) {
isChecked = checked;
}
打開adapter適配器,在getView()方法中增加以下代碼。
titleTv.setTextColor(list.get(i).isChecked()? Color.BLUE: Color.GRAY);
即當某個條目的iChecked爲true即選中時,文字改爲藍色,反之則爲黑色。
增加控件的點擊方法。
titleTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
for (int i = 0; i < list.size(); i++) {
list.get(i).setChecked(false);
}
list.get(i).setChecked(true);
notifyDataSetChanged();
}
});
代碼的意思就是先將所有條目都的isChecked都設爲false,再把點擊條目的isChecked設爲true,從而實現單選效果。
運行起來瞅瞅。
成功!現在我們把選中的值返回到主界面。
這裏我們通過接口回調的方式來完成。
先把adapter的數值傳回dialog中。
打開adapter,新建:
public interface onItemViewClickListener{
void onItemViewClick(String title);
}
titleTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
.........
if (listener!= null){
listener.onItemViewClick(list.get(i).getTitle());
}
}
});
同理打開dialog
public interface onItemViewClickListener{
void onItemViewClick(String title);
}
adapter = new MainAdapter(context, new ArrayList<MainBean>(), new MainAdapter.onItemViewClickListener() {
@Override
public void onItemViewClick(String title) {
if (listener!= null){
listener.onItemViewClick(title);
dismiss();
}
}
});
接下來打開視圖Activity。在xml文件中放置一個用來顯示返回文字的TextView,然後修改dialog的構造方法,通過回調獲取傳遞的值。
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (dialog== null){
dialog= new ListViewDialog(MainActivity.this, new ListViewDialog.onItemViewClickListener() {
@Override
public void onItemViewClick(String title) {
titleTv.setText(title);
}
});
}
dialog.show();
}
});
運行起來~
成功!
至此全部完成,demo附上!