当前位置: 代码迷 >> Android >> Android 自定义分层级罗选控件
  详细解决方案

Android 自定义分层级罗选控件

热度:130   发布时间:2016-04-24 11:31:51.0
Android 自定义分层级筛选控件

今天下午写了一个分层级筛选控件,效果如下
这里写图片描述
该控件由两部分组成:
1.上面一排的筛选标题按钮(就是四个toggleButton,根据筛选项的数量动态追加)
2.点击筛选按钮弹出来的筛选内容(一个Popupwindow,它包含一个Gridview和一个Button)

需求开发点:
1.单个筛选项内容视图的生成,也就是那个Popupwindow的内容的生成
2.主控件的实现,根据筛选项的数量动添加上面一排内容(这里是四个筛选项),并且关联好每一个筛选项。

1.单个弹出内容视图的生成
我说的单个视图指的是下面红框框部分
这里写图片描述
首先分析,单个视图包含什么?
1)一个Gridview
2)一个底部Button
3)可筛选的数据List<\string>指的是A学校、B学校这些
4)其实上面的标题(大学、院系等)也归结为单个弹出视图的一部分,所以也得有一个变量叫title

总结: 所以单个视图的生成至少有以上四个成员变量,所以我们的单个视图实现如下:

package com.example.expandableview;import java.util.ArrayList;import java.util.List;import com.example.expandableview.adapter.MyBaseAdapter;import com.example.expandableview.adapter.ViewHolder;import android.content.Context;import android.util.AttributeSet;import android.view.LayoutInflater;import android.view.View;import android.widget.AdapterView;import android.widget.AdapterView.OnItemClickListener;import android.widget.Button;import android.widget.GridView;import android.widget.LinearLayout;public class ExpandleItemView extends LinearLayout {    /**显示在toggleButton的标题文字*/    public String mTitle;    /** 底部按钮 */    private Button mBottomBtn;    /** 展示要筛选的数据*/    private GridView mGridView;    /** 筛选的数据内容*/    private List<String> mGridviewDatas;    public ExpandleItemView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    public ExpandleItemView(Context context, AttributeSet attrs) {        this(context, attrs, -1);    }    public ExpandleItemView(Context context) {        this(context, null);    }    public ExpandleItemView(String title, Context context,List<String> datas) {        this(context);        setTitle(title);        mGridviewDatas = datas;        init();    }    private void init() {        setBackgroundColor(getResources().getColor(android.R.color.white));        /**将布局inflate到此视图中*/        LayoutInflater.from(getContext()).inflate(R.layout.expand_item_layout, this, true);        setOrientation(LinearLayout.VERTICAL);        mGridView = (GridView) findViewById(R.id.gridview);        mBottomBtn = (Button) findViewById(R.id.btn_all);        /**自己写的通用适配器,传入数据项和layoutid,一句话就使用了*/        mGridView.setAdapter(new MyBaseAdapter<String>(mGridviewDatas, R.layout.gridview_item, getContext()) {            @Override            protected void convert(ViewHolder viewHolder, String t) {                viewHolder.setBtnText(R.id.item_text, t);            }        });        /**每一个子项回调给监听者*/        mGridView.setOnItemClickListener(new OnItemClickListener() {            @Override            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {                if(mOnExpandItemClick != null)                {                    mOnExpandItemClick.onItemClick(position);                }            }        });        /**底部按钮的点击事件回调给监听者*/        mBottomBtn.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                if(mOnExpandItemClick != null)                {                    mOnExpandItemClick.onBottomClick();                }            }        });    }    public String getTitle() {        return mTitle == null ? new String() : mTitle;    }    public void setTitle(String mTitle) {        this.mTitle = mTitle;    }    public List<String> getmGridviewDatas() {        return mGridviewDatas == null ? new ArrayList<String>() : mGridviewDatas;    }    public void setmGridviewDatas(List<String> mGridviewDatas) {        this.mGridviewDatas = mGridviewDatas;    }    /**     * 累加子类的高度作为自身的高度     */    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int cCount = getChildCount();        int desireWidth = MeasureSpec.getSize(widthMeasureSpec);        int desireHeight = 0;        for (int i = 0; i < cCount; i++) {            View child = getChildAt(i);            measureChild(child, widthMeasureSpec, heightMeasureSpec);            desireHeight += child.getMeasuredHeight();        }        setMeasuredDimension(desireWidth, desireHeight);    }    /**     * 点击item事件回调给监听者     * @author rander     */    public interface OnExpandItemClick    {        void onItemClick(int position);        void onBottomClick();    }    private OnExpandItemClick mOnExpandItemClick;    public void setOnExpandItemClick(OnExpandItemClick onExpandItemClick) {        this.mOnExpandItemClick = onExpandItemClick;    }}

该视图的布局R.layout.expand_item_layout如下:

<?xml version="1.0" encoding="utf-8"?><merge xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:orientation="vertical" >    <GridView        android:id="@+id/gridview"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:columnWidth="90dp"        android:gravity="center"        android:horizontalSpacing="10dp"        android:numColumns="auto_fit"        android:stretchMode="columnWidth"        android:descendantFocusability="blocksDescendants"        android:verticalSpacing="10dp" />    <include         layout="@layout/line" />    <Button        android:id="@+id/btn_all"        android:layout_width="match_parent"        android:layout_height="50dp"        android:background="@drawable/bottom_selector"        android:text="全部"        android:textColor="@color/grey"        android:textSize="18dp" />    <include layout="@layout/line" /></merge>

Gridview的Item布局R.layout.gridview_item,就是一个Button

<?xml version="1.0" encoding="utf-8"?><Button xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/item_text"    android:layout_width="wrap_content"    android:layout_height="40dp"    android:paddingLeft="20dp"    android:paddingRight="20dp"    android:gravity="center"    android:focusable="false"    android:clickable="false"    android:background="@drawable/btn_selctor"     ></Button>

分析好了,写起代码来还是爽歪歪吧。

2.主体控件的实现
我说的主体控件就是这个控件的功能的实现了。
首先:分析主控件包含什么
1)有多少个筛选项List<\View>,这个View就是我们上面定义的ExpandleItemView,针对父类编程,兼容性好。也有人说所有的筛选项复用一个View就行了,是可以,但是没有必要给自己找麻烦。
2)上面有一排按钮,所以需要List<\View>来保存,我这里使用的是ToggleButton,针对父类编程,用View。
3)我们点击的时候总要记住当前筛选的是哪一项,所以我定义了一个ToggleButton记录当前的筛选项mSelectToggleBtn
4)内容的弹出使用PopupWindow,当然需要一个变量
5)还有两个变量,有没有发现选中和不选中的标题颜色是不一样的,所以定义两个变量记录选中的标题颜色和不选中的标题颜色。

总结:其实主控件有这也属性就行啦,当然这里我还定义了两个变量记录了PopupWindow的宽高属性,必要性不是很大。

OK,实现代码如下:

package com.example.expandableview;import java.util.ArrayList;import java.util.List;import com.example.expandableview.ExpandleItemView.OnExpandItemClick;import android.content.Context;import android.graphics.Color;import android.graphics.drawable.BitmapDrawable;import android.util.AttributeSet;import android.view.LayoutInflater;import android.view.View;import android.widget.LinearLayout;import android.widget.PopupWindow;import android.widget.PopupWindow.OnDismissListener;import android.widget.RelativeLayout;import android.widget.ToggleButton;public class ExpandableView extends LinearLayout implements OnExpandItemClick {    /** 记录选中的ToggleButton */    private ToggleButton mSelectToggleBtn;    /** 筛选 */    private List<View> mToggleButtons = new ArrayList<>();    /** 筛选项集合 */    private List<View> mPopupviews;    /** popupwindow展示的宽 */    private int mDisplayWidth;    /** popupwindow展示的高 */    private int mDisplayHeight;    /** 筛选内容用PopupWindow弹出来 */    private PopupWindow mPopupWindow;    private Context mContext;    /** toggleButton正常的字体颜色 */    int mNormalTextColor = getResources().getColor(R.color.grey);    /** toggleButton被选中的类型字体颜色 */    int mSelectTextColor = Color.RED;    public ExpandableView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    public ExpandableView(Context context, AttributeSet attrs) {        this(context, attrs, -1);    }    public ExpandableView(Context context) {        this(context, null);    }    private void init() {        setOrientation(LinearLayout.HORIZONTAL);        mDisplayWidth = getResources().getDisplayMetrics().widthPixels;        mDisplayHeight = getResources().getDisplayMetrics().heightPixels;        mContext = getContext();        setBackgroundResource(R.drawable.choosearea_bg_right);    }    /**     * 初始化数据和布局,做的工作如下: 1.根据筛选项的数量,动态增加上面一排ToggleButton 2.设置每一个ToggleButton的监听事件     * 3.toggleButton.setTag(i)这一句非常重要,我们取View数据都是根据这个tag取的 4.     *      * @param views     */    public void initViews(List<ExpandleItemView> views) {        mPopupviews = new ArrayList<>();        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);        for (int i = 0; i < views.size(); i++) {            ExpandleItemView view = views.get(i);            view.setOnExpandItemClick(this);            final RelativeLayout r = new RelativeLayout(mContext);            RelativeLayout.LayoutParams rl = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,                    RelativeLayout.LayoutParams.WRAP_CONTENT);            r.addView(view, rl);            mPopupviews.add(r);            final ToggleButton toggleButton = (ToggleButton) inflater.inflate(R.layout.toggle_button, this, false);            toggleButton.setText(view.getTitle());            mToggleButtons.add(toggleButton);            addView(toggleButton);            toggleButton.setTag(i);            toggleButton.setOnClickListener(new OnClickListener() {                @Override                public void onClick(View v) {                    /** 记录选中的ToggleButton,有了这个什么都好办 */                    mSelectToggleBtn = toggleButton;                    showPopWindow();                }            });            /**             * 点击popupwindow外部,就隐藏popupwindow,这个r是点击事件包裹了一个ExpandleItemView             * 如果用户所点之处为ExpandleItemView所在范围,点击事件由ExpandleItemView,如果点到             * ExpandleItemView外面,则有r处理,处理方式就是收缩             */            r.setOnClickListener(new OnClickListener() {                @Override                public void onClick(View v) {                    onPressBack();                }                private void onPressBack() {                    hidePopWindow();                }            });        }    }    /**     * 隐藏popupWindow,并且重置ToggleButton字体颜色     */    private void hidePopWindow() {        if (mPopupWindow != null) {            mPopupWindow.dismiss();        }        if (mSelectToggleBtn != null) {            mSelectToggleBtn.setTextColor(mNormalTextColor);            mSelectToggleBtn.setChecked(false);        }    }    /**     * 显示popupWindow     */    private void showPopWindow() {        if (null == mPopupWindow) {            mPopupWindow = new PopupWindow(mPopupviews.get((int) mSelectToggleBtn.getTag()), mDisplayWidth,                    mDisplayHeight);            /** 监听popupWindow的收缩,并重置字体颜色 */            mPopupWindow.setOnDismissListener(new OnDismissListener() {                @Override                public void onDismiss() {                    if (mSelectToggleBtn != null) {                        mSelectToggleBtn.setTextColor(mNormalTextColor);                        mSelectToggleBtn.setChecked(false);                    }                }            });            mPopupWindow.setAnimationStyle(R.style.PopupWindowAnimation);            mPopupWindow.setFocusable(true);            mPopupWindow.setOutsideTouchable(true);            mPopupWindow.setBackgroundDrawable(new BitmapDrawable());        } else {            mPopupWindow.setContentView(mPopupviews.get((int) mSelectToggleBtn.getTag()));        }        if (mPopupWindow.isShowing()) {            hidePopWindow();        } else {            /** 显示的时候,设为选中颜色 */            mSelectToggleBtn.setTextColor(mSelectTextColor);            mPopupWindow.showAsDropDown(mToggleButtons.get(0), 0, 0);        }    }    /**     * Item项选中的回调 注意Tag的使用 筛选项视图是根据tag拿的,因为mSelectToggleBtn的tag就是视图的索引     * mSelectToggleBtn显示筛选的内容     */    @Override    public void onItemClick(int position) {        hidePopWindow();        if (null != mSelectToggleBtn) {            int selectBtnIndex = (int) mSelectToggleBtn.getTag();            mSelectToggleBtn                    .setText(((ExpandleItemView) (((RelativeLayout) mPopupviews.get(selectBtnIndex)).getChildAt(0)))                            .getmGridviewDatas().get(position));        }    }    /**     * 底部按钮点击的回调 mSelectToggleBtn显示筛选的内容     */    @Override    public void onBottomClick() {        hidePopWindow();        if (null != mSelectToggleBtn) {            int selectBtnIndex = (int) mSelectToggleBtn.getTag();            mSelectToggleBtn                    .setText((((ExpandleItemView) (((RelativeLayout) mPopupviews.get(selectBtnIndex)).getChildAt(0)))                            .getTitle()));        }    }}

上面使用的单个ToggleButton的视图R.layout.toggle_button如下:

<?xml version="1.0" encoding="utf-8"?><ToggleButton xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="40dp"    android:layout_weight="1"    android:background="@null"    android:drawablePadding="-10dp"    android:drawableRight="@drawable/index_icon_targetdown_grey"    android:gravity="center"    android:paddingRight="20dp"    android:singleLine="true"    android:textColor="@color/grey"    android:textOff="@null"    android:textOn="@null"    android:textSize="16sp"    android:textStyle="bold" ></ToggleButton>

接下来就是使用了,使用的布局如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent" >    <com.example.expandableview.ExpandableView        android:id="@+id/expandview"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:orientation="horizontal" >    </com.example.expandableview.ExpandableView></RelativeLayout>

Activity代码如下:

package com.example.expandableview;import java.util.ArrayList;import java.util.Arrays;import java.util.LinkedHashMap;import java.util.Map;import android.app.Activity;import android.os.Bundle;public class MainActivity extends Activity {    private ExpandableView mExpandableView;    private Map<String, ExpandleItemView> mExpandleItemViews;    private String[] mColleages = { "A学校", "B学校", "C学校", "D学校", "E学校", "F学校", "G学校", "H学校", "I学校", "J学校" };    private String[] mDepartments = { "A系", "B系", "C系", "D系", "E系", "F系", "G系", "H系" , "I系" };    private String[] mProfessions = { "A专业", "B专业", "C专业" , "D专业" , "E专业" , "F专业" };    private String[] mClasses = { "A班", "B班", "C班", "D班" , "E班" };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mExpandableView = (ExpandableView) findViewById(R.id.expandview);        mExpandleItemViews = new LinkedHashMap<>();        //不要问我为什么这么用,因为我想用LinkedHashMap        mExpandleItemViews.put("大学", new ExpandleItemView("大学", this, Arrays.asList(mColleages)));        mExpandleItemViews.put("院系", new ExpandleItemView("院系", this, Arrays.asList(mDepartments)));        mExpandleItemViews.put("专业", new ExpandleItemView("专业", this, Arrays.asList(mProfessions)));        mExpandleItemViews.put("班级", new ExpandleItemView("班级", this, Arrays.asList(mClasses)));        mExpandableView.initViews(new ArrayList<>(mExpandleItemViews.values()));    }}

使用起来如此easy,效果就是上面看到的效果了

源码下载地址

  相关解决方案