Widgets

Project Url: yhyzgn/Widgets
Introduction: 封装了一些常用的自定义 View,用于快捷开发
More: Author   ReportBugs   
Tags:

996.icu LICENSE

widget是一个Android自定义控件库。包含多种常用控件。

☆ 注意 ☆

1.4.1开始,全面支持AndroidX,弃用support

效果展示

控件展示较多,更新也较频繁,不方便录制gif图片,请下载demo体验。

基本使用

  • 引入jitpack

    仓库已迁移到jitpack

    allprojects {
        repositories {
            // ...
            maven { url "https://jitpack.io" }
        }
    }
    
  • 添加依赖

    在项目主模块中添加如下依赖

    dependencies {
        implementation 'com.github.yhyzgn:Widgets:latestVersion'
    }
    

控件总览

简要罗列各种控件

使用说明

core控件

  • PreImgActivity

    点击查看大图功能

    • Application中初始化

      需要在Application中初始化,并通过ImgPreHelper设置图片加载器、图片下载器等相关配置

      ImgPreHelper.getInstance().init(this).setLoader(new ImgPreHelper.ImgLoader() {
          @Override
          public <T> void load(ImageView iv, T model, ProgressBar pbLoading) {
              Glide.with(iv.getContext()).load(model).into(iv);
          }
      }).setOnDownloadListener(new ImgPreHelper.OnDownloadListener() {
          @Override
          public void onProgress(float progress, long current, long total) {
              Log.i("ImgDownloader", "下载进度:" + (progress * 100F) + "%,总大小:" + total + " bytes, 已下载:" + current + " bytes.");
          }
      
          @Override
          public void onSuccess(File img, String msg) {
              ToastUtils.shortT(msg);
          }
      
          @Override
          public void onError(String error) {
              ToastUtils.shortT(error);
          }
      });
      
    • 设置ImgPreCfg参数

      多张图

      // 多张图
      List<String> urlList = new ArrayList<>();
      urlList.add("http://img.youguoquan.com/uploads/magazine/content/a811c176420a20f8e035fc3679f19a10_magazine_web_m.jpg");
      urlList.add("http://img.youguoquan.com/uploads/magazine/content/7b2a0fdbb23c9e63586b7ff6798dbebb_magazine_web_m.jpg");
      urlList.add("http://img.youguoquan.com/uploads/magazine/content/c9c47160b46fceab5afd24dea7f216e6_magazine_web_m.jpg");
      urlList.add("http://img.youguoquan.com/uploads/magazine/content/fd986a6e0d5fa3a4485e5ce28f40b2ad_magazine_web_m.jpg");
      // 参数 1 为点击的 ImageView;参数 3 为当前要预览的图片索引。
      ImgPreCfg cfg = new ImgPreCfg(iv, urlList, 1);
      

      一张图

      // 参数 1 为点击的 ImageView;参数 2 为当前要预览的图片地址。
      ImgPreCfg cfg = new ImgPreCfg(iv, url);
      
      // 配置为不可下载
      cfg.setDownloadable(false);
      // 设置下载按钮图标
      cfg.setDownloadIconId(R.mipmap.ic_def_download);
      
      // x, y, width, height 分别为图片 x, y 坐标和宽高度。
      ImgPreCfg cfg = new ImgPreCfg(x, y, width, height, url);
      
    • 开始预览

      PreImgActivity.preview(this, cfg);
      
  • AdvView

    广告轮播展示栏

    • 布局文件

      <com.yhy.widget.core.adv.AdvView
        android:id="@+id/av_view_multiple"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="48dp"
        android:background="#ac0"
        app:av_anim_duration="1000"
        app:av_interval="4000" />
      
    • 获取控件

      AdvView avMultiple = findViewById(R.id.av_view_multiple);
      
    • 设置数据和事件

      设置数据

      List<String> mItems = new ArrayList<>();
      mItems.add("这是第 1 个");
      mItems.add("这是第 2 个");
      mItems.add("这是很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长的第 3 个");
      mItems.add("这是第 4 个");
      mItems.add("这是第 5 个");
      mItems.add("这是第 6 个");
      mItems.add("这是第 7 个");
      
      // 第 3 个参数为每页展示广告条数,默认为 1,即单条展示
      SimpleAdvAdapter<String> avAdapter = new SimpleAdvAdapter<String>(this, mItems, 3) {
        @Override
        protected View getItemView(int position, String data) {
          TextView tv = new TextView(mCtx);
          tv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
          tv.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
          tv.setText(data);
          tv.setTextColor(Color.DKGRAY);
          tv.setTextSize(14);
          tv.setSingleLine();
          tv.setEllipsize(TextUtils.TruncateAt.END);
          return tv;
        }
      };
      
      // 设置适配器到 AdvView
      avMultiple.setAdapter(avAdapter);
      

      设置条目点击事件

      avAdapter.setOnItemClickListener(new SimpleAdvAdapter.OnItemClickListener<String>() {
        @Override
        public void onItemClick(SimpleAdvAdapter adapter, int position, String data) {
          toast("position = " + position + ", " + data);
        }
      });
      
    • 自定义属性

      | 属性 | 说明 | 默认值 | | :----------------: | :-----------: | :--------------: | | av_interval | 动画定时,单位ms | 3000 | | av_anim_duration | 动画执行时间,单位ms | 800 | | av_anim_in | 入场动画资源 | R.anim.adv_in | | av_anim_out | 出场动画资源 | R.anim.adv_out |

  • CheckedTextView

    可选中的TextView

    • 布局文件

      背景选择器bg_checked_ctv_selector

      <?xml version="1.0" encoding="utf-8"?>
      <selector xmlns:android="http://schemas.android.com/apk/res/android">
        <item android:state_checked="true">
          <shape android:shape="rectangle">
            <solid android:color="@color/colorAccent" />
            <corners android:radius="4dp" />
          </shape>
        </item>
        <item>
          <shape android:shape="rectangle">
            <solid android:color="#666" />
            <corners android:radius="4dp" />
          </shape>
        </item>
      </selector>
      

      布局

      <com.yhy.widget.core.checked.CheckedTextView
        android:id="@+id/ctv_def"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/bg_checked_ctv_selector"
        android:padding="8dp"
        android:text="阻止了 Click 和 LongClick 事件"
        android:textColor="#fff"
        android:textSize="14sp" />
        <!-- 默认阻止了 click 和 longClick 事件,如果需要添加这些事件,请添加属性:app:ctv_prevent="false" -->
      
    • 获取控件

      CheckedTextView ctvDef = findViewById(R.id.ctv_def);
      
    • 设置事件

      ctvDef.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
          toast("我的 click 被阻止了");
        }
      });
      
      ctvDef.setOnLongClickListener(new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
          toast("我的 longClick 被阻止了");
          return true;
        }
      });
      
      ctvDef.setOnCheckedChangeListener(new CheckedTextView.OnCheckedChangeListener() {
        @Override
        public void onChanged(CheckedTextView ctv, boolean isChecked) {
          toast("isChecked = " + isChecked);
        }
      });
      
    • 自定义属性

      | 属性 | 说明 | 默认值 | | :-----------: | :-----------------------: | :----: | | ctv_prevent | 是否阻止clicklongClick事件 | true |

  • ExpandTextView

    可展开收起的TextView

    • 布局文件

      <com.yhy.widget.core.exptext.ExpandTextView
        android:id="@+id/etv_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#fff"
        android:orientation="vertical"
        app:etv_anim_alpha_start="0.2"
        app:etv_anim_duration="800"
        app:etv_max_collapsed_lines="4">
      
        <!-- 显示文本内容的 TextView -->
        <TextView
          android:id="@+id/tv_content"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:layout_marginLeft="10dp"
          android:layout_marginRight="10dp"
          android:layout_marginTop="8dp"
          android:ellipsize="end"
          android:textColor="#666666"
          android:textSize="16sp"/>
      
        <!--展开和收起的点击按钮-->
        <TextView
          android:id="@+id/tv_expand"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_gravity="end|bottom"
          android:padding="16dp"
          android:text="窝草"/>
      </com.yhy.widget.core.exptext.ExpandTextView>
      
    • 获取控件

      ExpandTextView etvContent = findViewById(R.id.etv_content);
      // 配置内容控件和按钮控件
      etvContent.mapViewId(R.id.tv_content, R.id.tv_expand);
      
    • 设置数据

      etvContent.setText("哈哈哈哈哈哈啊哈哈哈哈");
      
    • 设置事件

      etvContent.setOnExpandStateChangeListener(new ExpandTextView.OnExpandStateChangeListener() {
        @Override
        public void onExpandStateChanged(TextView textView, boolean isExpanded) {
          toast(isExpanded ? "展开了" : "收起了");
        }
      });
      
    • 自定义属性

      | 属性 | 说明 | 默认值 | | :-----------------------: | :-----------: | :----: | | etv_max_collapsed_lines | 收缩时显示的最大行数 | 2 | | etv_anim_duration | 动画执行时间,单位ms | 400m | | etv_anim_alpha_start | 透明度动画开始时的值 | 0.6 |

  • SquareImageView

    正方形ImageView

    • 布局文件

      <com.yhy.widget.core.img.SquareImageView 
        android:id="@+id/siv_test"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:siv_btn_img="@drawable/ic_delete_white" />
      
    • 获取控件

      SquareImageView sivTest = findViewById(R.id.siv_test);
      
    • 设置数据

      Glide.with(ctx).load(url).into(sivTest);
      
    • 设置事件

      sivTest.setOnBtnClickListener(new SquareImageView.OnBtnClickListener() {
        @Override
        public void onClick(SquareImageView siv) {
          Toast.makeText(SquareIVActivity.this, "删除第" + position + "张图片", Toast.LENGTH_SHORT).show();
        }
      });
      
    • 自定义属性

      | 属性 | 说明 | 默认值 | | :---------------: | :-------------: | :--: | | siv_btn_img | 右上角按钮图片 | 无 | | siv_btn_size | 右上角按钮大小,单位dp | 0 | | siv_btn_padding | 右上角按钮内边距,单位dp | 2 |

  • CircleImageView

    圆形图片

    • 布局文件

      <com.yhy.widget.core.img.round.CircleImageView
        android:id="@+id/civ_avatar"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:scaleType="centerCrop"
        app:riv_border_color="#8c9eff"
        app:riv_border_width="2dp" />
      
    • 获取控件

      CircleImageView civTest = findViewById(R.id.civ_test);
      
    • 设置数据

      Glide.with(ctx).load(url).into(civTest);
      
    • 自定义属性

      | 属性 | 说明 | 默认值 | | :----------------: | :---------: | :-------: | | riv_border_width | 边框宽度,单位dp | 0 | | riv_border_color | 边框颜色 | #000000 |

  • RoundImageView

    圆角图片,如果四个角半径都相等的话,直接使用riv_radius即可

    • 布局文件

      <com.yhy.widget.core.img.round.RoundImageView
        android:id="@+id/riv_d"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_marginLeft="10dp"
        android:scaleType="centerCrop"
        app:riv_border_color="#8c9eff"
        app:riv_border_width="2dp"
        app:riv_radius_left_top="30dp"
        app:riv_radius_right_top="30dp"
        app:riv_radius_right_bottom="30dp"
        app:riv_radius_left_bottom="30dp"
        app:riv_radius="12dp" />
      
    • 获取控件

      CircleImageView rivTest = findViewById(R.id.riv_test);
      
    • 设置数据

      Glide.with(ctx).load(url).into(rivTest);
      
    • 自定义属性

      | 属性 | 说明 | 默认值 | | :-----------------------: | :---------: | :----------: | | civ_border_width | 边框宽度,单位dp | 0 | | civ_border_color | 边框颜色 | #000000 | | riv_radius | 圆角半径,单位dp | 0 | | riv_radius_left_top | 左上角圆角半径 | riv_radius | | riv_radius_right_top | 右上角圆角半径 | riv_radius | | riv_radius_right_bottom | 右下角圆角半径 | riv_radius | | riv_radius_left_bottom | 左下角圆角半径 | riv_radius |

  • HackyViewPager

    多点触摸滑动时防止内存溢出的ViewPager

    当成普通ViewPager使用即可

  • PickerView

    上下滚动数据选取控件

    • 布局文件

      <com.yhy.widget.core.picker.PickerView
        android:id="@+id/pv_test"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        app:pv_text_color="#f20"/>
      
    • 获取控件

      // // 泛型表示该控件中数据源的数据类型
      PickerView<TestEntity> pvText = findViewById(R.id.pv_test);
      
    • 设置数据及事件

      List<TestEntity> testList = new ArrayList<>();
      for (int i = 1; i <= 40; i++) {
        testList.add(new TestEntity(i, "Data " + i));
      }
      
      pvTest.setData(testList, new PickerView.ItemProvider<TestEntity>() {
        @Override
        public String getItem(TestEntity data, int position) {
          return data.name;
        }
      }).setOnSelectListener(new PickerView.OnSelectListener<TestEntity>() {
        @Override
        public void onSelect(TestEntity data) {
          toast(data.name);
        }
      });
      
    • 自定义属性

      | 属性 | 说明 | 默认值 | | :----------------: | :-----------: | :-------: | | pv_max_text_size | 字体最大尺寸,单位dp | 20 | | pv_min_text_size | 字体最小尺寸,单位dp | 14 | | pv_text_color | 字体颜色 | #e84c3d |

  • RecyclerScrollView

    用来嵌套RecyclerViewScrollView

    当成普通ScrollView使用即可

    • 设置滚动监听事件

      rsvTest.setOnScrollListener(new OnScrollListener() {
        @Override
        public void onScroll(RecyclerScrollView view, int x, int y, int oldX, int oldY){
          toast("当前滚动条 y 坐标为:" + y);
        }
      });
      
  • RvDivider

    RecyclerView的分割线

    目前只针对LinearLayoutManagerGridLayoutManager两种布局

    注意:一定要在设置适配器后添加分割线

    • 创建分割线

      RvDivider mDivider = new RvDivider.Builder(this)
        .widthDp(30)
        .color(getResources().getColor(R.color.colorPrimary))
        .type(RvDivider.DividerType.TYPE_WITH_START_END)
        .build();
      
  • LinearLayoutManager

    rvContent.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
    // 先设置适配器,再添加分割线
    rvContent.addItemDecoration(mDivider);
    
  • GridLayoutManager

    rvContent.setLayoutManager(new GridLayoutManager(this, 4));
    // 先设置适配器,再添加分割线
    rvContent.addItemDecoration(mDivider);
    
  • SettingsItemView

    常用设置布局中的条目控件

    • 布局文件

      <com.yhy.widget.core.settings.SettingsItemView
        android:id="@+id/siv_test"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:background="#fff"
        android:paddingLeft="8dp"
        android:paddingRight="8dp"
        app:siv_name="设置 1"
        app:siv_text="值 1" />
      
    • 获取控件

      SettingsItemView sivTest = findViewById(R.id.siv_test);
      
    • 设置数据

      sivTest.setName("左边文字").setText("右边文字");
      // ... 还有设置图标之类的各种方法,如下
      sivTest.setIcon(mIcon)
        .showIcon(mShowIcon)
        .setArrow(mArrow)
        .showArrow(mShowArrow)
        .setName(mName)
        .setNameWidth(mNameWidth)
        .setNameColor(mNameColor)
        .setNameSize(mNameSize)
        .setText(mText)
        .setHint(mHint)
        .setEditable(mEditable)
        .setTextColor(mTextColor)
        .setTextSize(mTextSize)
        .onSwitch(mSwitchOn)
        .showSwitch(mShowSwitch)
        .setNameGravity(mNameGravity)
        .setTextGravity(mTextGravity)
        .setCursorDrawableRes(mCursorDrawableRes);
      
      // 如果有开关控件,还需要设置开关监听事件
      sivTest.setOnSwitchStateChangeListener(new SettingsItemView.OnSwitchStateChangeListener() {
        @Override
        public void onStateChanged(SettingsItemView siv, SwitchButton sb, boolean isOn) {
          toast(siv.getName() + " :: isOn = " + isOn);
        }
      });
      
    • 自定义属性

      | 属性 | 说明 | 默认值 | | :-------------------: | :-------------------------------------: | :-------: | | siv_icon | 左边图标 | 无 | | siv_show_icon | 是否显示左边图标 | false | | siv_arrow | 右边箭头 | 无 | | siv_show_arrow | 是否显示右边箭头 | false | | siv_name | 左边文本 | 空 | | siv_name_width | 左边文本宽度,单位dp | 自适应 | | siv_name_gravity | 左边文本对其方式【leftcenterright】 | center | | siv_name_size | 左边文本大小,单位sp | 14 | | siv_name_color | 左边文本颜色 | #000000 | | siv_text | 右边内容 | 空 | | siv_text_gravity | 右边内容对齐方式【leftcenterright】 | center | | siv_hint | 右边提示文本 | 空 | | siv_text_size | 右边字体大小,单位sp | 14 | | siv_text_color | 右边字体颜色 | #000000 | | siv_switch_on | 是否打开开关 | false | | siv_switch_width | 开关控件宽度,单位dp | 48 | | siv_switch_height | 开关控件高度,单位dp | 28 | | siv_show_switch | 是否显示开关控件 | false | | siv_editable | 是否可编辑 | false | | siv_cursor_drawable | 光标颜色资源 | 系统默认 | | siv_input_type | 输入类型【textphoneemailpassword】 | text | | siv_max_length | 可输入最大长度 | 不限 |

  • TitleBar

    常用标题栏控件

    • 布局文件

      <com.yhy.widget.core.title.TitleBar
        android:id="@+id/tb_test"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#7ad"
        android:elevation="8dp"
        app:tb_title="测试标题" />
      
    • 获取控件

      TitleBar tbTest = findViewById(R.id.tb_test);
      
    • 设置数据及事件

      tbTest.setTitle("标题");
      
      // ******** 该方法已过时 ********
      // 事件,该监听器不是接口或者抽象类,只需要重写对应的方法即可
      tbTest2.setOnTitleBarListener(new TitleBar.OnTitleBarListener() {
          @Override
          public void titleClick(View view) {
              toast("点击了标题");
          }
      
          @Override
          public void leftIconClick(View view) {
              toast("返回");
              finish();
          }
      
          @Override
          public void leftTextClick(View view) {
              toast("左边文本");
          }
      
          @Override
          public void rightIconClick(View view) {
              toast("右边图标");
          }
      
          @Override
          public void rightTextClick(View view) {
              toast("右边文本");
          }
      });
      
      // 以上方法已过时,用一下方法代替
      // 点击事件
      tbTest2.setOnTitleBarClickListener(new TitleBar.OnTitleBarClickListener() {
          @Override
          public void titleClick(View view) {
              toast("点击了标题");
          }
      
          @Override
          public void leftIconClick(View view) {
              toast("返回");
              finish();
          }
      
          @Override
          public void leftTextClick(View view) {
              toast("左边文本");
          }
      
          @Override
          public void rightIconClick(View view) {
              toast("右边图标");
          }
      
          @Override
          public void rightTextClick(View view) {
              toast("右边文本");
          }
      });
      
      // 长按事件【是否在长按后再加一个短按动作(true 为不加短按,false 为加入短按)】
      tbTest2.setOnTitleBarLongClickListener(new TitleBar.OnTitleBarLongClickListener {
          public boolean titleLongClick(View view) {
              return false;
          }
      
          public boolean leftTextLongClick(View view) {
              return false;
          }
      
          public boolean rightTextLongClick(View view) {
              return false;
          }
      
          public boolean leftIconLongClick(View view) {
              return false;
          }
      
          public boolean rightIconLongClick(View view) {
              return false;
          }
      });
      
    • 自定义属性

      | 属性 | 说明 | 默认值 | | :-------------------: | :-----------: | :-------: | | tb_title | 标题文本 | 空 | | tb_left_text | 左边文本 | 空 | | tb_right_text | 右边文本 | 空 | | tb_left_icon | 左边图标 | 无 | | tb_right_icon | 右边图标 | 无 | | tb_font_color | 全部字体颜色 | #ffffff | | tb_title_color | 标题字体颜色 | #ffffff | | tb_left_text_color | 左边字体颜色 | #ffffff | | tb_right_text_color | 右边字体颜色 | #ffffff | | tb_title_size | 标题字体大小,单位sp | 18 | | tb_left_text_size | 左边字体大小,单位sp | 14 | | tb_right_text_size | 右边字体大小,单位sp | 14 |

  • SwitchButton

    常用开关控件

    • 布局文件

      <com.yhy.widget.core.toggle.SwitchButton
        android:id="@+id/switch_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"/>
      
    • 获取控件

      SwitchButton switchButton = findViewById(R.id.switch_button);
      
    • 设置数据事件

      switchButton.onOrOff(true);
      switchButton.isOn();
      switchButton.toggle();     //switch state
      switchButton.toggle(false);//switch without animation
      switchButton.setShadowEffect(true);//disable shadow effect
      switchButton.setEnabled(true);//disable button
      switchButton.setEnableEffect(false);//disable the switch animation
      
      switchButton.setOnStateChangeListener(new SwitchButton.OnStateChangeListener() {
        @Override
        public void onCheckedChanged(SwitchButton view, boolean isOn) {
          toast("isOn = " + isOn);
        }
      });
      
    • 自定义属性

      | 属性 | 说明 | 默认值 | | :--------------------: | :---------------: | :---------: | | sb_shadow_radius | 阴影半径,单位dp | 2.5 | | sb_shadow_offset | 阴影偏移值,单位dp | 1.5 | | sb_shadow_color | 阴影颜色 | #33000000 | | sb_off_color | 关闭时颜色 | #dddddd | | sb_on_color | 打开时颜色 | #51d367 | | sb_border_width | 边框宽度,单位dp | 1 | | sb_on_line_color | 打开状态中短竖线颜色 | #ffffff | | sb_on_line_width | 打开状态中短竖线宽度,单位dp | 1 | | sb_off_circle_color | 关闭状态中圆圈颜色 | #aaaaaa | | sb_off_circle_width | 关闭状态中圆圈宽度,单位dp | 1.5 | | sb_off_circle_radius | 关闭状态中圆圈半径,单位dp | 4 | | sb_on | 是否打开 | false | | sb_shadow_effect | 是否支持阴影效果 | true | | sb_effect_duration | 效果显示时间,单位ms | 300 | | sb_button_color | 按钮颜色 | #ffffff | | sb_show_indicator | 是否显示指示器 | true | | sb_background | 背景颜色 | #ffffff | | sb_enable_effect | 是否开启特效 | true |

  • StepView

    可步骤化控件,包括垂直方向和水平方向

    • 布局文件

      <ScrollView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:scrollbars="none">
      
        <com.yhy.widget.core.step.StepView
          android:id="@+id/sv_vertical"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          app:sv_complete_icon="@mipmap/ic_step_finished" />
      </ScrollView>
      
    • 获取控件

      StepView<TestStep> svVertical = $(R.id.sv_vertical);
      
    • 设置数据事件

      模拟数据

      private final List<TestStep> mStepVerticalList = new ArrayList<>();
      
      // ...
      
      mStepVerticalList.clear();
      mStepVerticalList.add(new TestStep("您已提交定单,等待系统确认"));
      mStepVerticalList.add(new TestStep("您的商品需要从外地调拨,我们会尽快处理,请耐心等待"));
      mStepVerticalList.add(new TestStep("您的订单已经进入亚洲第一仓储中心 1 号库准备出库"));
      mStepVerticalList.add(new TestStep("您的订单预计 6 月 23 日送达您的手中,618 期间促销火爆,可能影响送货时间,请您谅解,我们会第一时间送到您的手中"));
      mStepVerticalList.add(new TestStep("您的订单已打印完毕"));
      mStepVerticalList.add(new TestStep("您的订单已拣货完成"));
      mStepVerticalList.add(new TestStep("扫描员已经扫描"));
      mStepVerticalList.add(new TestStep("打包成功"));
      mStepVerticalList.add(new TestStep("您的订单在京东【华东外单分拣中心】发货完成,准备送往京东【北京通州分拣中心】"));
      mStepVerticalList.add(new TestStep("您的订单在京东【北京通州分拣中心】分拣完成"));
      mStepVerticalList.add(new TestStep("您的订单在京东【北京通州分拣中心】发货完成,准备送往京东【北京中关村大厦站】"));
      mStepVerticalList.add(new TestStep("您的订单在京东【北京中关村大厦站】验货完成,正在分配配送员"));
      // 当前状态
      mStepVerticalList.add(new TestStep("配送员【哈哈哈】已出发,联系电话【130-0000-0000】,感谢您的耐心等待,参加评价还能赢取好多礼物哦", StepAble.Status.CURRENT));
      // 默认状态
      mStepVerticalList.add(new TestStep("感谢你在京东购物,欢迎你下次光临!", StepAble.Status.DEFAULT));
      
      // 反转数据
      Collections.reverse(mStepVerticalList);
      

      设置适配器

      private class VerticalAdapter extends StepAdapter<TestStep> {
          public VerticalAdapter() {
              super(mStepVerticalList);
          }
      
          @Override
          public View getItem(StepView<TestStep> stepView, int position, TestStep data) {
              View view = LayoutInflater.from(StepActivity.this).inflate(R.layout.item_step_vertical, null);
              TextView tvTest = view.findViewById(R.id.tv_test);
              tvTest.setText(data.text);
              if (data.getStatus() == StepAble.Status.COMPLETE) {
                  tvTest.setTextColor(Color.parseColor("#00beaf"));
              } else if (data.getStatus() == StepAble.Status.CURRENT) {
                  tvTest.setTextColor(Color.parseColor("#ff7500"));
              } else {
                  tvTest.setTextColor(Color.parseColor("#dcdcdc"));
              }
              return view;
          }
      }
      
      // 设置适配器
      mVerticalAdapter = new VerticalAdapter();
      svVertical.setAdapter(mVerticalAdapter);
      

      设置条目点击事件

      svVertical.setOnItemClickListener(new StepView.OnItemClickListener<TestStep>() {
          @Override
          public void onItemClick(StepView<TestStep> parent, int position, TestStep data) {
              toast(data.text);
          }
      });
      
    • 自定义属性

      | 属性 | 说明 | 默认值 | | :-------------------: | :--------------------------------------: | :--------: | | sv_orientation | 显示方向【verticalhorizontal】 | vertical | | sv_complete_color | 完成状态节点圆的颜色 | #00beaf | | sv_current_color | 当前状态节点圆的颜色 | #ff7500 | | sv_default_color | 默认状态节点圆的颜色 | #dcdcdc | | sv_solid_line_color | 实线颜色 | #00beaf | | sv_solid_line_width | 实线宽度 | 2dp | | sv_dash_line_color | 虚线颜色 | #00beaf | | sv_dash_line_width | 虚线宽度 | 1dp | | sv_radius | 节点圆的半径 | 8dp | | sv_complete_icon | 完成状态节点的图标 | 无 | | sv_current_icon | 当前状态节点的图标 | 无 | | sv_default_icon | 默认状态节点的图标 | 无 | | sv_align_middle | 是否将节点圆与itemView的中间位置对齐,不对齐则对齐itemView的左边或者上边 | true |

  • HybridBridge

    加强版的WebView,可以直接和js交互

    这里只是Android端说明,Web端说明请参考hybrid

    • 布局文件

      <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
      
        <com.yhy.widget.core.web.HybridWebView
          android:id="@+id/hwv_content"
          android:layout_width="match_parent"
          android:layout_height="match_parent" />
      
        <Button
          android:id="@+id/btn_test"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_alignParentBottom="true"
          android:layout_centerHorizontal="true"
          android:layout_marginBottom="32dp"
          android:text="点击试试" />
      </RelativeLayout>
      
    • 获取控件

      HybridWebView hwvContent = $(R.id.hwv_content);
      Button btnTest = $(R.id.btn_test);
      
    • 设置数据

      // 注册交互桥梁
      hwvContent.register(new TestBridge());
      // 加载页面
      hwvContent.loadUrl("file:///android_asset/index.html");
      
      // ...
      
      /**
       * 交互桥梁,必须是 HybridBridge 的子类
       */
      public class TestBridge extends HybridBridge {
      
          @JavascriptInterface
          public String test(String json) {
              Toast.makeText(WebHybridActivity.this, json, Toast.LENGTH_LONG).show();
              return "Android 接收到数据啦";
          }
      }
      
    • 设置事件

      hwvContent.setOnWebEventListener(new SimpleOnWebEventListener() {
          @Override
          public boolean onJsAlert(HybridWebView view, String url, String message, final JsResult result) {
              AlertDialog.Builder builder = new AlertDialog.Builder(WebHybridActivity.this);
              builder
                  .setTitle("标题")
                  .setMessage(message)
                  .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                      @Override
                      public void onClick(DialogInterface dialog, int which) {
                          result.confirm();
                      }
                  })
                  .show();
              //返回 true 表示不再往下传递弹窗事件,即不再使用原本 WebView 的弹窗,否则会弹出两次弹窗
              return true;
          }
      });
      
      btnTest.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
              // 调用 js 中的 test 函数
              hwvContent.js("test", "小姐姐");
          }
      });
      
    • 自定义属性

      | 属性 | 说明 | 默认值 | | :------------------: | :---------------------------------------: | :--------: | | hwv_strict_mode | 是否使用严格模式(需要严格匹配url参数) | false | | hwv_url_flag_name | URL标识名称 | platform | | hwv_url_flag_value | URL标识值 | app | | hwv_bridge_name | 交互桥梁名称 | app | | hwv_cache_enable | 是否开启缓存 | true | | hwv_cache_expire | 缓存保存时间,单位:s | 一周 |

  • CheckBox

    带动画效果的多选框

    • 布局文件

      <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:gravity="center"
        android:orientation="horizontal">
      
        <com.yhy.widget.core.checked.CheckBox
          android:id="@+id/cb_cancel"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          app:cb_color_checked="@color/colorPrimary"
          app:cb_color_tick="@color/textPrimary"
          app:cb_color_unchecked="@color/windowBackground"
          app:cb_color_unchecked_stroke="@color/colorLine"
          app:cb_duration="1000"
          app:cb_stroke_width="4dp"
          app:cb_click_cancel_able="false" />
      
        <TextView
          android:id="@+id/tv_cancel"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_marginLeft="8dp"
          android:text="选中后点击我来取消"
          android:textColor="@color/textPrimary"
          android:textSize="14sp" />
      </nearLayout>
      
    • 获取控件

      CheckBox cbCancel = $(R.id.cb_cancel);
      TextView tvCancel = $(R.id.tv_cancel);
      
    • 设置事件

      cbCancel.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() {
          @Override
          public void onCheckedChanged(CheckBox checkBox, boolean isChecked) {
              toast("是否选中:" + isChecked);
          }
      });
      
      tvCancel.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View v) {
              // 切换
              cbCancel.toggle();
          }
      });
      
    • 自定义属性

      | 属性 | 说明 | 默认值 | | :-------------------------: | :----------: | :-------: | | cb_duration | 动画时长,单位:ms | 300 | | cb_stroke_width | 边框宽度,单位:dp | 0 | | cb_color_tick | 打钩图标颜色 | #ffffff | | cb_color_checked | 选中时的颜色 | #fb4846 | | cb_color_unchecked | 未选中时的颜色 | #ffffff | | cb_color_unchecked_stroke | 未选中时边框的颜色 | #dfdfdf | | cb_click_cancel_able | 是否可点击取消 | true |

  • StatusDialog

    各状态【加载中|成功|失败|错误】弹窗提示

    不用布局文件

    • Common

      所有弹窗都继承了AbsStatusDialog,提供抽象方法View statusView(),用来从子类获取具体的状态View

      【成功|失败|错误】三种弹窗继承于AbsUnableCancelStatusDialog,其内部禁用了弹窗取消事件,且也继承于AbsStatusDialog默认弹出3s后自动消失,具体时间可以在构造方法参数控制

      各弹窗都可以自定义弹窗图标部分,重写父类的View statusView()方法,返回具体的View即可

      // 顶级抽象类
      public abstract class AbsStatusDialog extends AlertDialog { ... }
      
      // 禁止取消抽象类
      public abstract class AbsUnableCancelStatusDialog extends AbsStatusDialog { ... }
      
      // 加载中状态,直接继承于顶级抽象类
      public class LoadingDialog extends AbsStatusDialog { ... }
      
      // 成功状态,继承于禁止取消抽象类
      public class SuccessDialog extends AbsUnableCancelStatusDialog { ... }
      
      // 失败状态,继承于禁止取消抽象类
      public class FailedDialog extends AbsUnableCancelStatusDialog { ... }
      
      // 错误状态,继承于禁止取消抽象类
      public class ErrorDialog extends AbsUnableCancelStatusDialog { ... }
      
    • 加载中弹窗

      重写了父类的statusView()方法,返回了默认的ProgressBar控件

      未禁止返回取消弹窗,已禁止点击外部取消弹窗

      LoadingDialog dialog = new LoadingDialog(getContext());
      dialog.setText("加载中...");
      // 或者直接构造方法这样写
      LoadingDialog dialog = new LoadingDialog(getContext(), "加载中...");
      dialog.setCancelable(true, false);
      dialog.setText("...");
      dialog.show();
      
      // 取消弹窗
      dialog.dismiss();
      
    • 【成功|失败|错误】

      这三种状态用法一致,只是要用不同的类而已

      已禁止所有的手动取消事件,默认为显示后1s后消失,该时间可自定义

      // 成功
      SuccessDialog dialog = new SuccessDialog(getContext(), "加载成功", 1000);
      // 失败
      FailedDialog dialog = new FailedDialog(getContext(), "加载失败", 1000);
      // 错误
      ErrorDialog dialog = new ErrorDialog(getContext(), "加载错误", 1000);
      
      // 设置文本
      dialog.setText("...");
      
      // 显示 3s 后自动消失
      dialog.show();
      
    • 状态弹窗管理器

      这里提供个默认的弹窗管理器StatusDialogManager,用来管理各种弹窗并处理其表现形式

      其用法如下

        // 创建管理器
        StatusDialogManager manager = StatusDialogManager.with(this)
            .loadingText("正在加载...")
            .successText("成功啦")
            .failedText("失败咯")
            .errorText("出错啦")
            .enableLoadingCancel(true)
            .duration(3000)
            .create();
      
        // 触发控制各种弹窗事件
        tvLoading.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                manager.loading();
                // 或者
                manager.loading("加载中,哈哈...");
                new Handler().postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        mManager.dismiss();
                    }
                },3000);
            }
        });
      
        tvSuccess.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                manager.success();
                // 或者
                manager.success("成功啦,哈哈");
            }
        });
      
        tvFailed.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                manager.failed();
                // 或者
                manager.failed("成功啦,哈哈");
            }
        });
      
        tvError.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                manager.error();
                // 或者
                manager.error("成功啦,哈哈");
            }
        });
      
  • InputDialogView

    输入框弹窗,比如聊天输入框,评论输入框等

    不用布局文件

    • 显示弹窗

      // 显示输入框弹窗
      // builder 可配置弹窗的各种颜色和相关字体大小等属性
      InputDialogView.Builder builder = new InputDialogView.Builder(InputDialogActivity.this);
      builder.hint(position % 2 != 0 ? "回复" + mDataList.get(position) : "说点儿什么呀...")
          .contentSize(14)
          .anchor(itemView) // 弹窗需要参考的 view,弹出后回调方法 onShow()中传回弹窗与该 view 的坐标偏移值
          .listener(new InputDialogView.OnInputDialogListener() {
              @Override
              public void onPublish(InputDialogView dialog, CharSequence content) {
                  dialog.dismiss();
                  toast(content);
              }
      
              @Override
              public void onShow(int offsetX, int offsetY, int[] position) {
                  // 点击某条评论则这条评论刚好在输入框上面,点击评论按钮则输入框刚好挡住按钮
                  rvContent.smoothScrollBy(0, offsetY, new AccelerateDecelerateInterpolator());
              }
      
              @Override
              public void onDismiss() {
              }
          });
      builder.build().show();
      
  • ConstraintImageView

    按宽高比来约束大小的ImageView,还可以设置圆角及边框等

    分为两种模式:以width或者height为基准,计算另一边大小,默认以width为准

    注意:宽高比直接设置成 规定图片的宽与高即可

    • 布局文件

      <!-- 以 width 为准,宽高比为:720:300 -->
      <com.yhy.widget.core.img.ConstraintImageView
        android:id="@+id/civ_width"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="16dp"
        android:scaleType="centerCrop"
        android:src="@mipmap/ic_avatar"
        app:civ_original_ratio="720:300"
        app:civ_reference="width" />
      
      <!-- 以 height 为准,宽高比为:200:300 -->
      <com.yhy.widget.core.img.ConstraintImageView
        android:id="@+id/civ_height"
        android:layout_width="wrap_content"
        android:layout_height="240dp"
        android:layout_marginTop="16dp"
        android:scaleType="centerCrop"
        android:src="@mipmap/ic_avatar"
        app:civ_original_ratio="200:300"
        app:civ_reference="height" />
      
      <!-- 默认以 width 为准,逐个设置各个角落的圆角半径 -->
      <com.yhy.widget.core.img.ConstraintImageView
        android:id="@+id/civ_a"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_avatar"
        app:civ_border_color="#8c9eff"
        app:civ_border_width="2dp"
        app:civ_radius_left_bottom="30dp"
        app:civ_radius_right_top="30dp"
        app:civ_ratio="0.75" />
      
      <!-- 默认以 width 为准,统一设置各个角落的圆角半径 -->
      <com.yhy.widget.core.img.ConstraintImageView
        android:id="@+id/civ_test"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:layout_marginTop="16dp"
        android:src="@mipmap/ic_avatar"
        app:civ_radius="12dp"
        app:civ_ratio="0.5"
        app:civ_reference="height" />
      
    • 获取控件

      ConstraintImageView civWidth = $(R.id.civ_width);
      ConstraintImageView civHeight = $(R.id.civ_height);
      ConstraintImageView civA = $(R.id.civ_a);
      ConstraintImageView civTest = $(R.id.civ_test);
      
    • 设置数据

      ImgUtils.load(this, civWidth, ImgUrls.getAImgUrl());
      ImgUtils.load(this, civHeight, ImgUrls.getAImgUrl());
      ImgUtils.load(this, civA, ImgUrls.getAImgUrl());
      ImgUtils.load(this, civTest, ImgUrls.getAImgUrl());
      
      // 动态设置图片真实宽高比例(三种重载方式)
      civWidth.setRatio(1.5f);
      civHeight.setRatio("250:300");
      civTest.setRatio(400, 400);
      
    • 自定义属性

      | 属性 | 说明 | 默认值 | | :-----------------------: | :---------------------------------------------------------: | :-------: | | civ_reference | 参考标准,以【widthheight】为准,计算另一方向的实际值 | width | | civ_original_ratio | 比例字符串,图片实际的宽高即可,格式:“宽:高” | 无 | | civ_ratio | 图片真实宽高比例值,格式:“0.75” | 0 | | civ_radius | 四个角半径,单位:dp | 0 | | civ_radius_left_top | 左上角半径,单位:dp | 0 | | civ_radius_right_top | 右上角半径,单位:dp | 0 | | civ_radius_right_bottom | 右下角半径,单位:dp | 0 | | civ_radius_left_bottom | 左下角半径,单位:dp | 0 | | civ_border_width | 边框宽度,单位:dp | 0 | | civ_border_color | 边框颜色 | #000000 |

  • GradientTextView

    文字带渐变动画的TextView

    • 布局文件

      <com.yhy.widget.core.text.GradientTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="默认样式"
        android:textSize="16sp" />
      
      <com.yhy.widget.core.text.GradientTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:text="自定义颜色"
        android:textSize="16sp"
        app:gtv_text_color_list="@array/color_arr_test" />
      
      <com.yhy.widget.core.text.GradientTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:text="自定义颜色和刷新速度"
        android:textSize="16sp"
        app:gtv_speed_millions="100"
        app:gtv_text_color_list="@array/color_arr_test" />
      
    • 自定义属性

      | 属性 | 说明 | 默认值 | | :-------------------: | :-----------------: | :-----------------------------------: | | gtv_speed_millions | 刷新颜色动画的时间间隔,单位:ms | 200 | | gtv_text_color_list | 需要渐变的颜色数组 | @array/color_arr_gradient_text_view |

    • 自定义颜色数组res/values/arrays.xml

      <integer-array name="color_arr_test">
          <!--integer-array,这里不能直接写 16 进制的颜色代码,否则报错-->
          <item>@color/red</item>
          <item>@color/red_light</item>
          <item>@color/orange</item>
          <item>@color/red_light</item>
          <item>@color/red</item>
      </integer-array>
      
  • LineTextView

    加各种线条的TextView,比如下划线,删除线等

    • 布局文件

      <com.yhy.widget.core.text.LineTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="默认长这个样子"
        android:textColor="@color/textPrimary"
        android:textSize="16sp" />
      
      <com.yhy.widget.core.text.LineTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:text="1dp 的下划线"
        android:textColor="@color/textPrimary"
        android:textSize="16sp"
        app:ltv_line_size="1dp" />
      
      <com.yhy.widget.core.text.LineTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:text="1dp 的中间删除线"
        android:textColor="@color/textPrimary"
        android:textSize="16sp"
        app:ltv_line_size="1dp"
        app:ltv_line_style="delete_middle" />
      
      <com.yhy.widget.core.text.LineTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:text="1dp 的对角删除线"
        android:textColor="@color/textPrimary"
        android:textSize="16sp"
        app:ltv_line_size="1dp"
        app:ltv_line_style="delete_oblique" />
      
    • 自定义属性

      | 属性 | 说明 | 默认值 | | :-----------------: | :--------------------------------------: | :-----------: | | ltv_line_size | 线条宽度,单位:dp | 0 | | ltv_line_color | 线条颜色 | #000000 | | ltv_line_interval | 线条间隔,该属性只在下划线风格中有效,单位:dp | 0 | | ltv_line_style | 线条风格【underlinedelete_middledelete_oblique】 | underline |


layout控件

  • CheckedFrameLayout

    可选中的FrameLayout

    • 布局文件

      <com.yhy.widget.layout.checked.CheckedRelativeLayout
        android:id="@+id/crl_test"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
      
        <TextView
          android:id="@+id/tv_test"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:background="@drawable/bg_checked_ctv_selector"
          android:padding="8dp"
          android:text="CheckedRelativeLayout"
          android:textColor="#fff"
          android:textSize="14sp" />
      
        <TextView
          android:layout_below="@id/tv_test"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_marginTop="48dp"
          android:background="@drawable/bg_checked_ctv_selector"
          android:padding="8dp"
          android:text="CheckedRelativeLayout"
          android:textColor="#fff"
          android:textSize="14sp" />
      </com.yhy.widget.layout.checked.CheckedRelativeLayout>
      
    • 获取控件

      CheckedRelativeLayout crlTest = $(R.id.crl_test);
      CheckedRelativeLayout tvTest = $(R.id.tv_test);
      
    • 设置事件

      tvTest.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
          toast("点击了 TextView");
        }
      });
      
      crlTest.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
          toast("点击");
        }
      });
      
      crlTest.setOnCheckedChangeListener(new CheckedRelativeLayout.OnCheckedChangeListener() {
        @Override
        public void onChanged(CheckedRelativeLayout crl, boolean isChecked) {
          toast("isChecked = " + isChecked);
        }
      });
      
  • CheckedLayout

    可选中的ViewGroup

    用法同CheckedFrameLayout

  • CheckedLinearLayout

    可选中的LinearLayout

    用法同CheckedFrameLayout

  • CheckedRelativeLayout

    可选中的RelativeLayout

    用法同CheckedFrameLayout

  • FlowLayout

    流式布局

    一般不用这个布局,只在定义其他流式布局时使用,继承FlowLayout即可,详情请参照TagFlowLayout源码

    • 布局文件

      <com.yhy.widget.layout.flow.FlowLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
      
        <TextView
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_margin="8dp"
          android:background="#666"
          android:padding="4dp"
          android:text="哈哈哈哈"
          android:textColor="#fff"
          android:textSize="14sp" />
      
        <TextView
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:layout_margin="8dp"
          android:background="#666"
          android:padding="4dp"
          android:text="呵哈呵"
          android:textColor="#fff"
          android:textSize="14sp" />
      
        <!-- 多个子控件... -->
      </com.yhy.widget.layout.flow.FlowLayout>
      
    • 自定义属性

      | 属性 | 说明 | 默认值 | | :----------: | :----: | :----: | | fl_gravity | 布局对齐方式 | left |

  • TagFlowLayout

    标签流式布局

    继承于FlowLayout

    • 布局文件

      <com.yhy.widget.layout.flow.tag.TagFlowLayout
        android:id="@+id/tfl_def"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
      
    • 获取控件

      // 泛型表示该控件中数据源的数据类型
      TagFlowLayout<TestEntity> tflDef = findViewById(R.id.tfl_def);
      
    • 设置数据和事件

      // 模拟数据
      List<TestEntity> mTestList = new ArrayList<>();
      mTestList.add(new TestEntity(0, "张三张三张三张三张三"));
      mTestList.add(new TestEntity(1, "李四"));
      mTestList.add(new TestEntity(2, "大胖子"));
      mTestList.add(new TestEntity(3, "尼古拉斯"));
      mTestList.add(new TestEntity(4, "哈"));
      mTestList.add(new TestEntity(5, "大胖子"));
      mTestList.add(new TestEntity(6, "尼古拉斯"));
      mTestList.add(new TestEntity(7, "哈"));
      
      // 设置适配器
      tflDef.setAdapter(new TagFlowAdapter<TestEntity> {
        public Adapter(List<TestEntity> dataList) {
          super(dataList);
        }
      
        @Override
        public View getView(TagFlowLayout parent, int position, TestEntity data) {
          TextView tv = (TextView) LayoutInflater.from(TagFlowActivity.this).inflate(R.layout.item_tag_flow, null);
          tv.setText(data.name);
          return tv;
        }
      });
      
      // 设置条目点击事件
      tflDef.setOnCheckChangedListener(new TagFlowLayout.OnCheckChangedListener<TestEntity>() {
        @Override
        public void onChanged(boolean checked, int position, TestEntity data, List<TestEntity> dataList) {
          toast(dataList);
          // 动态添加元素
          mTestList.add(new TestEntity(9, "嘻嘻嘻" + position));
          mAdapter.notifyDataChanged();
        }
      });
      
    • 自定义属性

      | 属性 | 说明 | 默认值 | | :-------------: | :----------------: | :-----: | | fl_gravity | 布局对齐方式 | left | | tfl_max_count | 允许选中的最大数量,-1表示无限 | -1 | | tfl_is_single | 是否是单选 | false |

  • SlideLayout

    侧滑菜单布局

    • 布局文件

      <com.yhy.widget.layout.slider.SlideLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/sl_slide"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@mipmap/bg_main"
        app:sl_anim_alpha_color="#f60"
        app:sl_main_alpha_enable="true">
      
        <LinearLayout
          android:layout_width="240dp"
          android:layout_height="match_parent"
          android:background="#66000000"
          android:gravity="center"
          android:orientation="vertical">
      
          <TextView
            android:id="@+id/tv_menu"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="菜单"
            android:textColor="#f40"
            android:textSize="24sp" />
        </LinearLayout>
      
        <LinearLayout
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:background="#fff"
          android:orientation="vertical">
      
          <android.support.v4.view.ViewPager
            android:id="@+id/vp_content"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        </LinearLayout>
      </com.yhy.widget.layout.slider.SlideLayout>
      
    • 获取控件

      SlideLayout slSlide = findViewById(R.id.sl_slide);
      TextView tvMenu = findViewById(R.id.tv_menu);
      ViewPager vpContent = findViewById(R.id.vp_content);
      
    • 设置数据和事件

      // 为 ViewPager 设置适配器
      vpContent.setAdapter(new PagerAdapter() {
        @Override
        public int getCount() {
          return 4;
        }
      
        @Override
        public boolean isViewFromObject(View view, Object object) {
          return view == object;
        }
      
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
          ImageView iv = new ImageView(SliderActivity.this);
          iv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
          iv.setScaleType(ImageView.ScaleType.CENTER_CROP);
          if (position == 0) {
            iv.setImageResource(R.mipmap.img_pager_1);
          } else if (position == 1) {
            iv.setImageResource(R.mipmap.img_pager_2);
          } else if (position == 2) {
            iv.setImageResource(R.mipmap.img_pager_3);
          } else {
            iv.setImageResource(R.mipmap.img_pager_4);
          }
          container.addView(iv);
          return iv;
        }
      
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
          container.removeView((View) object);
        }
      });
      
      // 设置事件
      // 当菜单按钮点击时关闭菜单
      tvMenu.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
          slSlide.close();
        }
      });
      
      // 监听内容,根据需要改变侧滑菜单的可用性
      slSlide.setOnSlideEnableWatcher(new SlideLayout.OnSlideEnableWatcher() {
        @Override
        public boolean shouldEnable() {
          //第二页禁用侧边栏
          return vpContent.getCurrentItem() != 1;
        }
      });
      
      // 菜单滑动状态监听
      slSlide.setOnStateChangeListener(new SlideLayout.OnStateChangeListener() {
        @Override
        public void onOpened() {
          tvMenu.setText("已打开");
        }
      
        @Override
        public void onClosed() {
          tvMenu.setText("已关闭");
        }
      
        @Override
        public void onDragging(float percent, int dx, int total) {
          tvMenu.setText("比例:" + percent);
        }
      });
      
  • StatusLayout

    状态管理页面布局【加载中,空数据,错误,成功】

    共有三种使用方法,分别是【默认,布局文件中配置,使用外部助手配置】

    多种使用方式的优先级关系为:布局文件中配置 > 使用外部助手配置 > 默认

    • 注意

      四种状态均用tag来做区分,所以必须要给各种页面设置对应的tag

      | 状态 | tag值 | | :--: | :-------: | | 加载中 | loading | | 空数据 | empty | | 错误 | error | | 成功 | success |

    • 默认方式

      • 布局文件

        <com.yhy.widget.layout.status.StatusLayout
          android:id="@+id/sl_content"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:background="#fff">
        
          <!--当只有一个子控件时,该子控件会被当作[成功]状态的界面,此时无需指定 tag 为 success-->
          <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:orientation="vertical"
            android:tag="success">
        
            <TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="Success"
              android:textColor="#2f0"
              android:textSize="20sp" />
          </LinearLayout>
        </com.yhy.widget.layout.status.StatusLayout>
        
    • 布局文件中配置

      • 布局文件

        <com.yhy.widget.layout.status.StatusLayout
          android:id="@+id/sl_content"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:background="#fff">
        
          <!-- 默认的加载中页面 -->
          <com.yhy.widget.layout.status.view.StaLoadingView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:tag="loading" />
        
          <!-- 默认的错误页面 -->
          <com.yhy.widget.layout.status.view.StaErrorView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:tag="error" />
        
          <!-- 默认的空数据页面 -->
          <com.yhy.widget.layout.status.view.StaEmptyView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:tag="empty" />
        
          <!-- 成功页面 -->
          <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:orientation="vertical"
            android:tag="success">
        
            <TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="Success"
              android:textColor="#2f0"
              android:textSize="20sp" />
          </LinearLayout>
        </com.yhy.widget.layout.status.StatusLayout>
        
    • 使用外部助手配置

      • 布局文件

        <com.yhy.widget.layout.status.StatusLayout
          android:id="@+id/sl_content"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:background="#fff">
        
          <!-- 使用外部助手配置时,也可以在布局中配置,布局中优先使用 -->
          <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:orientation="vertical"
            android:tag="error">
        
            <TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:gravity="center"
              android:tag="retry"
              android:text="布局中定义的错误页面"
              android:textColor="#246854"
              android:textSize="20sp" />
          </LinearLayout>
        
          <!-- 成功页面 -->
          <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:orientation="vertical"
            android:tag="success">
        
            <TextView
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:text="Success"
              android:textColor="#2f0"
              android:textSize="20sp" />
          </LinearLayout>
        </com.yhy.widget.layout.status.StatusLayout>
        
      • 获取控件

        StatusLayout slContent = findViewById(R.id.sl_content);
        
      • 外部助手配置

        StaLayoutHelperBuilder builder = new StaLayoutHelperBuilder.Builder(slContent).setLoadingLayout(getLoadingView()).build();
        // builder 还可以设置各种状态的 view
        slContent.setHelper(builder.getHelper());
        
        // ...
        private View getLoadingView() {
          TextView tv = new TextView(this);
          tv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
          tv.setText("Helper 中定义的加载中");
          tv.setGravity(Gravity.CENTER);
          tv.setTextSize(18);
          tv.setTextColor(Color.RED);
          // 设置 tag 为 loading
          tv.setTag(StatusLayout.Status.LOADING.getStatus());
          return tv;
        }
        
    • 切换页面状态

      // 加载中
      slContent.showLoading();
      // 成功
      slContent.showSuccess();
      // 错误
      slContent.showError();
      // 空数据
      slContent.showEmpty();
      

一些颜色

预定义的一些颜色

<resources>
    <color name="colorPrimary">#01aca8</color>
    <color name="colorAccent">#fe4365</color>
    <color name="colorLoading">#113e3c</color>
    <color name="windowBackground">#eeeeee</color>
    <color name="colorDisabled">#ababab</color>
    <color name="colorLine">#999999</color>
    <color name="textPrimary">#1d294c</color>
    <color name="textAccent">#24211c</color>
    <color name="alphaBlack">#88000000</color>
    <color name="alphaWhite">#88ffffff</color>
</resources>

That's all, enjoy it !!!

License

Copyright 2019 yhyzgn

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Apps
About Me
GitHub: Trinea
Facebook: Dev Tools