相互关联的下拉框使用

上一篇博客《安卓可输入可选择的下拉框》写了一个可输入、可选择的下拉框,今天讲讲如何利用它关联性使用。

思路分析

实际上下面介绍的使用方法受场景的限制,这肯定不是读者想要的东西,也不是我想要分析的初衷,所以,我想说清思路,有兴趣的读者可以继续看下面的代码。

关联性

相互关联的下拉框之间存在关联性,即前面的下拉框选择会影响后面下拉框的下拉数据,那如何确定这种关联性呢?比如下面介绍的使用场景的两种关联性,一个是下拉框数据全部由关联的下拉框决定,一个是下拉框数据被关联的下拉框部分影响。

对于第一种完全关联关系,我们可以通过关联的下拉框的数据(key),拿到被关联的下拉框的数据(value),然后进行全部更新。类似于从 map 中拿数据更新

对于第二种部分关联关系,我们可以拿到关联的下拉框的数据(value1),更改被关联的下拉框的数据(value2),然后更新。类似于两个数据求交集

动态更新

因为存在关联性,所以下拉框需要动态更新下拉的数据,那么如何更新呢?

考虑一个下拉框可能涉及的东西,下拉框对象、下拉数据、列表适配器,不外乎这三样。如果我们把这三样看成一个整体,关联的下拉框发生修改,我们只要拿到这三个东西,便能动态修改。

这里要注意下拉数据(list),修改里面的数据,并不会改变这个 list 对象本身,修改完数据之后,使用列表适配器通知数据修改,就能达到动态修改下拉数据的目的。


好了,到这实际思路已经很清晰了,下面看如何实现。

设计场景

这里需要设计的是一个频点选择功能,有三个下拉框,它们之间有相互关联性,第一个下拉框用来选择频段,第二个下拉框用来选择频点,第三个下拉框也是选频点,但是第三个下拉框中可选的频点中要除去第二个下拉框选中的频点。

因为我们的下拉框还能手动输入,这里我们还要传进去频点的范围,输入不合法的时候提示错误,合法的时候因为没有确认按钮,希望它自动保存。

最终设计效果如下(部分功能去除了):

在这里插入图片描述

准备资源

因为涉及频点的选择,首先应该提供频点的范围,下面数据储存在 values - array.xml 中

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <string-array name="none">
        <item>- -</item>
    </string-array>

    <string-array name="band">
        <item>- -</item>

        <item>B01</item>
        <item>B03</item>
        <item>B05</item>
        <item>B08</item>

        <item>B38</item>
        <item>B39</item>
        <item>B40</item>
        <item>B41</item>
    </string-array>


    <integer-array name="bandMin">
        <item>0</item>

        <item>0</item>
        <item>1200</item>
        <item>2400</item>
        <item>3450</item>

        <item>37750</item>
        <item>38250</item>
        <item>38650</item>
        <item>39650</item>
    </integer-array>

    <integer-array name="bandMax">
        <item>-1</item>

        <item>599</item>
        <item>1949</item>
        <item>2649</item>
        <item>3799</item>

        <item>38249</item>
        <item>38649</item>
        <item>39649</item>
        <item>41598</item>
    </integer-array>

    <string-array name="B01">
        <item>75</item>
        <item>100</item>
        <item>350</item>
        <item>375</item>

        <item>400</item>
        <item>450</item>
        <item>500</item>
        <item>525</item>
        <item>575</item>
    </string-array>

    <string-array name="B05">
        <item>2452</item>
    </string-array>

    <string-array name="B03">
        <item>1275</item>
        <item>1300</item>
        <item>1350</item>
        <item>1375</item>
        <item>1396</item>

        <item>1400</item>
        <item>1506</item>
        <item>1524</item>
        <item>1525</item>

        <item>1650</item>
        <item>1825</item>
        <item>1850</item>
    </string-array>

    <string-array name="B08">
        <item>3590</item>

        <item>3621</item>
        <item>3641</item>
        <item>3682</item>
        <item>3683</item>

        <item>3715</item>
        <item>3725</item>
        <item>3741</item>
        <item>3768</item>
        <item>3769</item>
        <item>3770</item>
        <item>3775</item>
    </string-array>

    <string-array name="B38">
        <item>38098</item>
        <item>37900</item>
    </string-array>

    <string-array name="B39">
        <item>38350</item>
        <item>38400</item>
        <item>38496</item>
        <item>38544</item>
    </string-array>

    <string-array name="B40">
        <item>38950</item>
        <item>39148</item>
        <item>39292</item>
        <item>38850</item>
        <item>39450</item>
    </string-array>

    <string-array name="B41">
        <item>40936</item>
        <item>40340</item>
        <item>40540</item>
        <item>41134</item>
        <item>41140</item>
    </string-array>

</resources>

这里只是部分常用频点,供下拉的时候选择,其中 bandMin、bandMax 是各个频段的上下界,在我们判断输入频点的时候需要使用到。

代码使用

    //这里本来有三组的,我简化了一下啊
	private EditableSpinner band1Spinner, band2Spinner, band3Spinner;
	
	//频段数据
	private List<String> bands;
	//频段上下界,对应频段数据
    private int[] bandMaxValues;
    private int[] bandMinValues;

	//储存 EditableSpinner 的列表,便于集中操作
    private List<EditableSpinner> spinnerList;
	//对应上面 EditableSpinner 中的数据列表
    private List<List<String>> spinnerContents;
	//对应上面 EditableSpinner 中的适配器
    private List<ArrayAdapter<String>> spinnerAdapters;
    //储存的数据 band 频段 - mFreq 主频点 - aFreq 副频点
    private String[] bfData = {"- -","- -","- -"};
    //private String[][] bfData = {{"- -","- -","- -"},{"- -","- -","- -"},{"- -","- -","- -"}};

	@Override
    protected void onCreate(Bundle savedInstanceState) {
        
        ...
            
     	//初始化数组数据
        initBandRange();
        
        //初始化下拉列表
        initSpinnerList();
        
        //设置下拉框事件
        setBandAndFreqChangeAction();
    }
	
	//初始化数组数据
    private void initBandRange() {
        bands = Arrays.asList(getResources().getStringArray(getArrayId("band")));
        bandMaxValues = getResources().getIntArray(getArrayId("bandMax"));
        bandMinValues = getResources().getIntArray(getArrayId("bandMin"));
    }

	//通过名称获取数组数据id
    private int getArrayId(String select){
        try {
            int id;
            ApplicationInfo appInfo = MyApplication.getContext().getApplicationInfo();
            id = MyApplication.getContext().getResources().getIdentifier(select, "array", appInfo.packageName);
            return id == 0 ? R.array.none : id;
        }catch (Exception e){
            return R.array.B01;
        }
    }

	//初始化下拉列表
	private void initSpinnerList() {

        spinnerList = Arrays.asList(    //注意生成的是视图,不可修改
                band1Spinner, mFreq1Spinner, aFreq1Spinner,
                //band2Spinner, mFreq2Spinner, aFreq2Spinner,
                //band3Spinner, mFreq3Spinner, aFreq3Spinner
        );

        spinnerContents = new ArrayList<>();
        spinnerAdapters = new ArrayList<>();

        //设置初始数据
        for (int i = 0; i < spinnerList.size(); i++) {
            EditableSpinner spinner = spinnerList.get(i);

            List<String> contentList = new ArrayList<>();
            if ((i % 3) == 0) {
                contentList.addAll(bands);
            }else {
                //无选中时数据
                contentList.add("- -");
            }

            //下拉框列表适配器
            ArrayAdapter<String> arrayAdapter =
                    new ArrayAdapter<>(mActivity, android.R.layout.simple_spinner_item, contentList);
            spinner.setAdapter(arrayAdapter);

            //更新数据显示
            arrayAdapter.notifyDataSetChanged();

            //添加到全局变量去,后面要用到,靠这动态修改下拉列表
            spinnerContents.add(contentList);
            spinnerAdapters.add(arrayAdapter);
        }
    }

	//设置下拉框事件
	private void setBandAndFreqChangeAction() {
        for (final EditableSpinner spinner : spinnerList) {
            //下拉列表点击
            spinner.setOnItemClickListener(position -> {
                //下拉框文字数据
                String selectStr = spinner.getEditString();
                //下拉框序号,0 频段、1 主频点,2 副频点
                int index = spinnerList.indexOf(spinner);
                switch (index % 3) {
                    case 0:
                        //选择频段,需要修改主频点下拉数据
                        setAllowedMainFreq(index, selectStr);
                        break;
                    case 1:
                        //选择主频点,需要修改副频点下拉数据
                        setAllowedAssiFreq(index);
                        break;
                    case 2:
                        break;
                    default:
                }
            });

            //文字输入变化
            if ((spinnerList.indexOf(spinner) % 3) != 0) {
                spinner.enableTextChangeListener(() -> {
                    int spinnerIndex = spinnerList.indexOf(spinner);
                    if (TextUtils.isEmpty(spinner.getEditString())) return;
                    //输入数据符合情况,保存数据
                    onSaveBandAndFreq(spinnerIndex, spinner.getEditString());
                });
            }
        }
    }

	//保存数据
    private void onSaveBandAndFreq(int spinnerIndex, String value) {
		bfData[spinnerIndex] = value;
        //如果有多组可以按下面保存
        //int kind = spinnerIndex % 3;
        //int index = spinnerIndex / 3;
        //bfData[kind][index] = value;
    }

	//选择频段,需要修改主频点下拉数据
	private void setAllowedMainFreq(int index, String band) {
        EditableSpinner spinner = spinnerList.get(index + 1);
        ArrayAdapter<String> adapter = spinnerAdapters.get(index + 1);
        List<String> contents = spinnerContents.get(index + 1);

        //默认第一个为"- -",表示不选
        contents.clear();
        contents.add("- -");
        spinner.setEditString("- -");
        //添加对应频段所有下拉数据
        if (!band.equals("- -")) {
            contents.addAll(getMFreqArrayFromBand(band));
        }
        adapter.notifyDataSetChanged();

        //设置频段可输入范围
        spinner.setMaxAndMinValue(getBandMinValue(band), getBandMaxValue(band));
    }

 	//选择主频点,需要修改副频点下拉数据
    private void setAllowedAssiFreq(int index) {
        //注意此处 index 是对应主频点的 index
        //频段
        String band = spinnerList.get(index - 1).getEditString();
        //主频点
        String mFreq = spinnerList.get(index).getEditString();

        EditableSpinner spinner = spinnerList.get(index + 1);
        ArrayAdapter<String> adapter = spinnerAdapters.get(index + 1);
        List<String> contents = spinnerContents.get(index + 1);

        //默认第一个为"- -",表示不选
        contents.clear();
        contents.add("- -");
        spinner.setEditString("- -");
        //添加对应频段所有下拉数据
        if (!band.equals("- -")) {
            //添加去除主频点后的数据
            contents.addAll(getAFreqArrayFromBand(band, mFreq));
        }
        adapter.notifyDataSetChanged();

        //设置频段可输入范围
        spinner.setMaxAndMinValue(getBandMinValue(band), getBandMaxValue(band));
    }

    private int getBandMaxValue(String band) {
        int index = getIndexOfList(band, bands);
        return bandMaxValues[index];
    }

    private int getBandMinValue(String band) {
        int index = getIndexOfList(band, bands);
        return bandMinValues[index];
    }

	//获取主频段的数组数据
    private ArrayList<String> getMFreqArrayFromBand(String band) {
        return new ArrayList<>(Arrays.asList(getResources().getStringArray(getArrayId(band))));
    }

	//获取副频段的数组数据,即要去除选中的主频点
    private List<String> getAFreqArrayFromBand(String band, String mFreq) {
        List<String> aFreq = getMFreqArrayFromBand(band);
        Iterator<String> iterator = aFreq.iterator();
        while (iterator.hasNext()) {
            String freq = iterator.next();
            if (freq.equals(mFreq)) {
                iterator.remove();
            }
        }
        return aFreq;
    }

	//获取String列表中mStr的下标
    private int getIndexOfList(String mStr, List<String> lsit) {
        int index = 0, i = 0;
        for (String temp : lsit) {
            if (temp.equals(mStr)) {
                index = i;
            }
            i++;
        }
        return index;
    }   

代码比较多,注意关注下 onCreate 内的三个函数,其他都是在里面调用的。

结语

上面的代码和业务相关,设计的不是很好,但可以提供一个思路,特别是当时我开发的时候要求是三组、六频点选择,真是花了一番功夫。

希望上面代码对读者有所帮助把!

end

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值