Android開發丶列表底部列表彈窗Dialog同時包含選擇和自適應高度功能

前一段時間接到一個需求,點擊彈出一個底部彈窗,彈窗的內容以列表的方式來呈現,同時包含選擇條目和自適應以及固定高度,需求不難,就是一些細節的東西繞了一些彎路,現在做下彙總。

國際慣例,先看下效果圖。

下面說下實現方法。

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附上!

資源下載

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章