Android可筛选的弹窗控件CustomFiltControl

本文实例为大家分享了Android弹窗控件CustomFiltControl的使用方法,供大家参考,具体内容如下

效果:

起初踩的坑:

刚开始是因为项目中需要用到筛选的功能,以前也遇到过但都是其他同事做的,而我看他们的实现大多都是自己一个个的码布局,然后做事件处理很麻烦,还有的是通过网上的一些线性排列控件自己组合实现的。

如今自己遇到了我开始想的也是通过LinearLayout动态去添加选项,title部分就是也是动态添加,一个打的LinearLayout包两个小的,然后在小的里面又包很多选项,但是遇到要换行的时候又需要添加一个LinearLayout,也就是但是有个问题,布局繁琐,得不到很好的复用。

后面突然想到了GridLayout,然后又使用了LinearLayout+GridLayout,对GridLayout是可以避免在你换行的时候去计算,只要你设置好行列,它会自动换行,这是确实实现了上面的效果,但是博主写好了又发现不够完美,既然GridLayout能自动换行,又可以一个站多行多列,为什么不把title也放到GridLayout中呢,有了这个想法,又来修改,在计算行列的时候确实遇到了阻碍,不过终究是完成了,最后封装在了popuwindow中直接调用。

看看主要实现吧:

package com.zzq.mack.customfiltcontrol;

import android.content.Context;
import android.graphics.Color;
import android.support.v7.widget.GridLayout;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.TextView;
import android.widget.Toast;

import com.zzq.mack.customfiltcontrol.model.FiltModel;

import java.util.List;

/**
 * Author:xqt
 * Email:zzq1573@gmail.com
 * Date:2018/3/31 0031 11:24
 * Description:筛选弹框 版权所有转载请注明出处
 */
public class FiltPopuWindow extends PopupWindow{
  public FiltPopuWindow(Context context,View view){
    //这里可以修改popupwindow的宽高
    super(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
    setContentView(view);
    initViews();
  }
  private void initViews() {
    setAnimationStyle(R.style.popwin_anim_style);
    //setBackgroundDrawable(new ColorDrawable(0x00000000));
    setFocusable(true);
    setOutsideTouchable(true);
  }

  public static class Builder {
    private Context context;
    private List<FiltModel> listData;
    private int columnCount;
    private GridLayout rootGridLayout;
    private LinearLayout contextll;
    //背景颜色
    private int colorBg = Color.parseColor("#F8F8F8");
    private int titleTextSize = 14;//SP
    private int tabTextSize = 14;//SP
    private int titleTextColor = Color.parseColor("#333333");//标题字体颜色
    private int tabTextColor = R.color.fit_item_textcolor;//选项字体颜色
    private int tabBgDrawable = R.drawable.item_lable_bg_shape;//选项背景颜色
    //当前加载的行数
    private int row = -1;
    private FiltPopuWindow mFiltPopuWindow;

    public Builder(Context context) {
      this.context = context;
    }
    /**
     * 设置数据源
     * @return
     */
    public Builder setDataSource(List<FiltModel> listData) {
      this.listData = listData;
      return this;
    }

    public Builder setColumnCount(int columnCount){
      this.columnCount = columnCount;
      return this;
    }

    public Builder setColorBg(int color){
      colorBg = context.getResources().getColor(color);
      return this;
    }

    public Builder setTitleTextSize(int titleTextSize) {
      this.titleTextSize = titleTextSize;
      return this;
    }

    public Builder setTabTextSize(int tabTextSize) {
      this.tabTextSize = tabTextSize;
      return this;
    }

    public Builder setTitleTextColor(int titleTextColor) {
      this.titleTextColor = titleTextColor;
      return this;
    }

    public Builder setTabTextColor(int tabTextColor) {
      this.tabTextColor = tabTextColor;
      return this;
    }

    public Builder setTabBgDrawable(int tabBgDrawable) {
      this.tabBgDrawable = tabBgDrawable;
      return this;
    }

    public Builder build(){
      newItemLayout(getRowCount(),columnCount);
      for (int i = 0; i < listData.size(); i++){
        ++row;
        TextView view = new TextView(context);
        view.setText(listData.get(i).getTypeName());
        view.setTextColor(titleTextColor);
        view.setTextSize(titleTextSize);
        //配置列 第一个参数是起始列标 第二个参数是占几列 title(筛选类型)应该占满整行,so -> 总列数
        GridLayout.Spec columnSpec = GridLayout.spec(0,columnCount);
        //配置行 第一个参数是起始行标 起始行+起始列就是一个确定的位置
        GridLayout.Spec rowSpec = GridLayout.spec(row);
        //将Spec传入GridLayout.LayoutParams并设置宽高为0或者WRAP_CONTENT,必须设置宽高,否则视图异常
        GridLayout.LayoutParams lp = new GridLayout.LayoutParams(rowSpec, columnSpec);
        lp.width = GridLayout.LayoutParams.WRAP_CONTENT;
        lp.height = GridLayout.LayoutParams.WRAP_CONTENT;
        lp.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
        lp.bottomMargin = context.getResources().getDimensionPixelSize(R.dimen.dp_8);
        rootGridLayout.addView(view,lp);
        //添加选项
        addTabs(listData.get(i),i);
      }

      return this;
    }

    private void newItemLayout(int rowCount,int columnCount){
      contextll = new LinearLayout(context);
      contextll.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
      contextll.setBackgroundColor(context.getResources().getColor(R.color.color_33000000));
      contextll.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
          if (mFiltPopuWindow != null){
            mFiltPopuWindow.dismiss();
            //点击外部消失
          }
        }
      });
      rootGridLayout = new GridLayout(context);
      LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
      rootGridLayout.setOrientation(GridLayout.HORIZONTAL);
      rootGridLayout.setRowCount(rowCount);
      rootGridLayout.setColumnCount(columnCount);
      rootGridLayout.setBackgroundColor(colorBg);
      rootGridLayout.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
          return true;
        }
      });
      int pandd = context.getResources().getDimensionPixelSize(R.dimen.dp_10);
      lp.weight = 1;
      rootGridLayout.setPadding(pandd,pandd,pandd,pandd);
      contextll.addView(rootGridLayout,lp);
    }

    /**
     * 添加选项
     * @param model
     */
    private void addTabs(final FiltModel model, final int titleIndex){
      List<FiltModel.TableMode> tabs = model.getTabs();
      for (int i = 0; i < tabs.size(); i++){
        if (i % columnCount == 0){
          row ++;
        }
        final FiltModel.TableMode tab = tabs.get(i);
        final TextView lable = new TextView(context);
        lable.setTextColor(context.getResources().getColorStateList(tabTextColor));
        lable.setBackgroundDrawable(context.getResources().getDrawable(tabBgDrawable));
        lable.setSingleLine(true);
        lable.setGravity(Gravity.CENTER);
        lable.setEllipsize(TextUtils.TruncateAt.MIDDLE);
        //这里可以自行修改tab框框的大小
        int panddT = context.getResources().getDimensionPixelSize(R.dimen.dp_2);
        int panddL = context.getResources().getDimensionPixelSize(R.dimen.dp_8);
        lable.setPadding(panddL,panddT,panddL,panddT);
        lable.setTextSize(tabTextSize);
        rootGridLayout.addView(lable,getItemLayoutParams(i,row));
        lable.setText(tab.name);
        if (tabs.get(i) == model.getTab()){
          lable.setSelected(true);
        }
        lable.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
            //lable.setSelected(true);
            if (tab != model.getTab()){
              //清空上次选中
              rootGridLayout.getChildAt(getIndex(model,titleIndex)).setSelected(false);
              //设置当前选中
              model.setTab(tab);
              lable.setSelected(true);
            }
          }
        });
      }
    }

    private GridLayout.LayoutParams getItemLayoutParams(int i,int row){
      //使用Spec定义子控件的位置和比重
      GridLayout.Spec rowSpec = GridLayout.spec(row,1f);
      GridLayout.Spec columnSpec = GridLayout.spec(i%columnCount,1f);
      //将Spec传入GridLayout.LayoutParams并设置宽高为0,必须设置宽高,否则视图异常
      GridLayout.LayoutParams lp = new GridLayout.LayoutParams(rowSpec, columnSpec);
      lp.width = 0;
      lp.height = GridLayout.LayoutParams.WRAP_CONTENT;
      lp.bottomMargin = context.getResources().getDimensionPixelSize(R.dimen.dp_8);
      if(i % columnCount == 0) {//最左边
        lp.leftMargin = context.getResources().getDimensionPixelSize(R.dimen.dp_10);
        lp.rightMargin = context.getResources().getDimensionPixelSize(R.dimen.dp_20);
      }else if((i + 1) % columnCount == 0){//最右边
        lp.rightMargin = context.getResources().getDimensionPixelSize(R.dimen.dp_10);
      }else {//中间
        lp.rightMargin = context.getResources().getDimensionPixelSize(R.dimen.dp_20);
      }
      return lp;
    }

    /**
     * 获取当前选中tab的 在整个GridLayout的索引
     * @return
     */
    private int getIndex(FiltModel model,int titleIndex){
      int index = 0;
      for (int i = 0; i < titleIndex; i++){
        //计算当前类型之前的元素所占的个数 title算一个
        index += listData.get(i).getTabs().size() + 1;
      }
      //加上当前 title下的索引
      FiltModel.TableMode tableModel = model.getTab();
      index += model.getTabs().indexOf(tableModel) + 1;
      return index;
    }

    /**
     * 计算行数
     * @return
     */
    private int getRowCount(){
      int row = 0;
      for (FiltModel model : listData){
        //计算当前类型之前的元素所占的个数 title算一个
        row ++;
        int size = model.getTabs().size();
        row += (size / columnCount) + (size % columnCount > 0 ? 1 : 0) ;
      }
      return row;
    }
    public FiltPopuWindow createPop(){
      if (listData == null || listData.size() == 0){
        try {
          throw new Exception("没有筛选条件");
        } catch (Exception e) {
          Toast.makeText(context,e.getMessage(),Toast.LENGTH_SHORT).show();
          e.printStackTrace();
        }
        return null;
      }
      mFiltPopuWindow = new FiltPopuWindow(context,contextll);
      return mFiltPopuWindow;
    }

  }
}

通过设置columnCount的大小可以改变列数,测试2,3,4,5没问题,只是列数越多就越挤,这是必然的。

希望对你有帮助,有问题欢迎给我留言。

这里准备了一个demo作为参考

GitHub:CutomFiltControl

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Android开发笔记之:消息循环与Looper的详解

    Understanding LooperLooper是用于给一个线程添加一个消息队列(MessageQueue),并且循环等待,当有消息时会唤起线程来处理消息的一个工具,直到线程结束为止.通常情况下不会用到Looper,因为对于Activity,Service等系统组件,Frameworks已经为我们初始化好了线程(俗称的UI线程或主线程),在其内含有一个Looper,和由Looper创建的消息队列,所以主线程会一直运行,处理用户事件,直到某些事件(BACK)退出.如果,我们需要新建一个线程,并

  • android仿京东商品属性筛选功能

    筛选和属性选择是目前非常常用的功能模块:几乎所有的APP中都会使用: 点击筛选按钮会弹出一个自己封装好的popupWindow,实用方法非常简单:两行代码直接显示:(当然初始化数据除外) 这里和以前用到的流式布局有些不一样:流式布局 以前使用的是单个分类,而且也没有在项目中大量实用:这个筛选功能除了数据外几乎都是从项目中Copy出来的: 整个popupWindow布局就是一个自定义的ListView,这个自定义的listview主要是控制listview的高度: 如果数据少的话就是自适应,如果数

  • 可支持快速搜索筛选的Android自定义选择控件

    Android 自定义支持快速搜索筛选的选择控件使用方法,具体如下 项目中遇到选择控件选项过多,需要快速查找匹配的情况. 做了简单的Demo,效果图如下: 源码地址:https://github.com/whieenz/SearchSelect 这个控件是由Dialog+SearchView+ListView实现的.Dialog用来承载选择控件,SearchView实现输入,ListView展示结果.设计概要图如下: 一.自定义Dialog Dialog布局文件 <?xml version=&quo

  • Android实现图片循环播放的实例方法

    很多时候,我们需要展示在客户端展示图片,而且是动态显示,即不停地自行切换图片.下面我们来看一下具体的实现方法.首先,我们需要在XML...    很多时候,我们需要展示在客户端展示图片,而且是动态显示,即不停地自行切换图片.下面我们来看一下具体的实现方法. 首先,我们需要在XML文件中配置一下将要播放图片的控件(main.xml): 复制代码 代码如下: <?xml version="1.0" encoding="utf-8"?> <LinearL

  • Android中 TeaScreenPopupWindow多类型筛选弹框功能的实例代码

    Github地址 YangsBryant/TeaScreenPopupWindow (Github排版比较好,建议进入这里查看详情,如果觉得好,点个star吧!) 引入module allprojects { repositories { google() jcenter() maven { url 'https://www.jitpack.io' } } } implementation 'com.github.YangsBryant:TeaScreenPopupWindow:1.0.2' 主

  • Android实现简单下拉筛选框

    最近接到一个新的项目,项目时间比较紧张,有一个功能类似于58同城,京东的一个下拉筛选框,为了节省时间,从网上面拷贝了一份封装好的代码,进行的自己的一些修改,感觉灵活性还挺高的,分享出来给大家看一看 大致效果如下,可以自己加入自己的布局 先看一下这个ExpandTabView这个类  代码比较简单 我就不做具体介绍了 有不懂的可以私信我 public class ExpandTabView extends LinearLayout implements OnDismissListener { pr

  • Android 仿京东侧滑筛选实例代码

    简单介绍 这个demo写的是仿京东的侧滑筛选页面,点击进入筛选后进入二级筛选,两次侧滑的筛选,还包括ListView+CheckBox滑动冲突,ListView+ GridView显示一行问题解决,接口回调传递数据等 效果图 简单得代码介绍 1.首页侧滑用的是安卓官方V4包中的DrawerLayout <?xml version="1.0" encoding="utf-8"?> <android.support.v4.widget.DrawerLa

  • RecyclerView+SnapHelper实现无限循环筛选控件

    项目用到横向滑动的单项选择控件.需求如下: 1.选项由后台配置,也就是控件要动态设置宽度:2.每次滑动都要左对齐,并默认选中最左边选项:3.控件可以无限循环. 动态设置控件宽度:获取adapter的item宽度:获取adapter里装载的itemCount. 每次滑动都要左对齐:利用LinearSnapHelper中calculateDistanceToFinalSnap的方法实现: 默认选中最左item:利用RecycleView的LayoutManager中查找显示第一项的方法linearM

  • Android Handler之消息循环的深入解析

    Handler是用于操作线程内部的消息队列的类.这有点绕,没关系,我们慢慢的来讲.前面Looper一篇讲到了Looper是用于给线程创建消息队列用的,也就是说Looper可以让消息队列(MessageQueue)附属在线程之内,并让消息队列循环起来,接收并处理消息.但,我们并不直接的操作消息队列,而是用Handler来操作消息队列,给消息队列发送消息,和从消息队列中取出消息并处理.这就是Handler的职责.Handler,Looper和MessageQueue是属于一个线程内部的数据,但是它提

  • Android实现可复用的筛选页面

    本文实例为大家分享了Android实现可复用的筛选页面的具体代码,供大家参考,具体内容如下 窗口代码 /** * 筛选页面 * 1.将用户的输入转换成sql语句 * 2.涉及到精确查询和模糊查询 * 3.提交数据之后需要刷新列表 */ public class UserFilterActivity extends AppCompatActivity implements View.OnClickListener { private List<ImageView> imageViewList;

随机推荐