2019年10月

Android选择器PickerView
https://github.com/Bigkoo/Android-PickerView

注意事项、详请使用方式、更新日志等,请查看 Wiki文档

Wiki文档,Wiki文档,Wiki文档 !~ 重要的事情说三遍

介绍

这是一款仿iOS的PickerView控件,有时间选择器和选项选择器,新版本的详细特性如下:

——TimePickerView 时间选择器,支持年月日时分,年月日,年月,时分等格式。
——OptionsPickerView 选项选择器,支持一,二,三级选项选择,并且可以设置是否联动 。

  • 支持三级联动
  • 设置是否联动
  • 设置循环模式
  • 支持自定义布局。
  • 支持item的分隔线设置。
  • 支持item间距设置。
  • 时间选择器支持起始和终止日期设定。
  • 支持“年,月,日,时,分,秒”,“省,市,区”等选项的单位(label)显示、隐藏和自定义。
  • 支持自定义文字、颜色、文字大小等属性
  • Item的文字长度过长时,文字会自适应缩放到Item的长度,避免显示不完全的问题
  • 支持Dialog 模式。
  • 支持自定义设置容器。
  • 实时回调。

TimePicker.gif
TimePickerNight.gif
lunar.gif
XOffset.png
Province.gif
CustomLayout.gif

有兴趣研究3D滚轮效果的实现机制,希望把源码研究透彻的可以看看这篇博客:

Android-PickerView系列之源码解析篇(二)

使用注意事项

  • 注意:当我们进行设置时间的启始位置时,需要特别注意月份的设定
  • 原因:Calendar组件内部的月份,是从0开始的,即0-11代表1-12月份
  • 错误使用案例:
    startDate.set(2013,1,1);
    endDate.set(2020,12,1);
  • 正确使用案例:
    startDate.set(2013,0,1);
    endDate.set(2020,11,1);

    #### V4.1.9 版本更新说明(2019-10-20)

    • 修复: 农历 day 偶现越界的问题。
    • 优化: 显示布局中英文默认大写问题。
    • 新增: 最大可见项的数目提供API给开发者设置。(setItemVisibleCount())
    • 新增: 滚轮从中间到两边透明度渐变,提供开关API设置。(isAlphaGradient(true))
    • 新增: 选中项圆形分割线样式。(DividerType.CIRCLE)

    #### V4.1.8 版本更新说明(2019-4-24)

    • 更新gradle版本, wheelview基础库由 compile 改为 api 依赖,避免gradle 5.0+版本无法引入。
    • 修复 setTextXOffset 赋值问题。

    #### V4.1.7 版本更新说明(2019-1-10)

    • 修复 WheelView在初始化时,数据为空导致height=0,造成一直显示不出来的问题。
    • 新增取消按钮的点击事件监听入口。
    • 参数注解添加,规范数据类型。
    • 废弃setBackgroundId方法, 更新方法命名为 setOutSideColor。

更多历史版本详情,请查阅:更新日志(4.x版本)

方法名与参数请查阅:方法名与参数说明文档


如何使用:

Android-PickerView 库使用示例:

1.添加Jcenter仓库 Gradle依赖:

compile 'com.contrarywind:Android-PickerView:4.1.8'

或者

Maven

<dependency>
<groupId>com.contrarywind</groupId>
<artifactId>Android-PickerView</artifactId>
<version>4.1.8</version>
<type>pom</type>
</dependency>

2.在项目中添加如下代码:

//时间选择器
TimePickerView pvTime = new TimePickerBuilder(MainActivity.this, new OnTimeSelectListener() {
                           @Override
                           public void onTimeSelect(Date date, View v) {
                               Toast.makeText(MainActivity.this, getTime(date), Toast.LENGTH_SHORT).show();
                           }
                       }).build();
//条件选择器
 OptionsPickerView pvOptions = new OptionsPickerBuilder(MainActivity.this, new OnOptionsSelectListener() {
            @Override
            public void onOptionsSelect(int options1, int option2, int options3 ,View v) {
                //返回的分别是三个级别的选中位置
                String tx = options1Items.get(options1).getPickerViewText()
                        + options2Items.get(options1).get(option2)
                        + options3Items.get(options1).get(option2).get(options3).getPickerViewText();
                tvOptions.setText(tx);
            }
        }).build();
 pvOptions.setPicker(options1Items, options2Items, options3Items);
 pvOptions.show(); 

大功告成~

3.如果默认样式不符合你的口味,可以自定义各种属性:

 Calendar selectedDate = Calendar.getInstance();
 Calendar startDate = Calendar.getInstance();
 //startDate.set(2013,1,1);
 Calendar endDate = Calendar.getInstance();
 //endDate.set(2020,1,1);
 
  //正确设置方式 原因:注意事项有说明
  startDate.set(2013,0,1);
  endDate.set(2020,11,31);

 pvTime = new TimePickerBuilder(this, new OnTimeSelectListener() {
            @Override
            public void onTimeSelect(Date date,View v) {//选中事件回调
                tvTime.setText(getTime(date));
            }
        })
                .setType(new boolean[]{true, true, true, true, true, true})// 默认全部显示
                .setCancelText("Cancel")//取消按钮文字
                .setSubmitText("Sure")//确认按钮文字
                .setContentSize(18)//滚轮文字大小
                .setTitleSize(20)//标题文字大小
                .setTitleText("Title")//标题文字
                .setOutSideCancelable(false)//点击屏幕,点在控件外部范围时,是否取消显示
                .isCyclic(true)//是否循环滚动
                .setTitleColor(Color.BLACK)//标题文字颜色
                .setSubmitColor(Color.BLUE)//确定按钮文字颜色
                .setCancelColor(Color.BLUE)//取消按钮文字颜色
                .setTitleBgColor(0xFF666666)//标题背景颜色 Night mode
                .setBgColor(0xFF333333)//滚轮背景颜色 Night mode
                .setDate(selectedDate)// 如果不设置的话,默认是系统时间*/
                .setRangDate(startDate,endDate)//起始终止年月日设定
                .setLabel("年","月","日","时","分","秒")//默认设置为年月日时分秒
                .isCenterLabel(false) //是否只显示中间选中项的label文字,false则每项item全部都带有label。
                .isDialog(true)//是否显示为对话框样式
                .build();
pvOptions = new  OptionsPickerBuilder(this, new OptionsPickerView.OnOptionsSelectListener() {
            @Override
            public void onOptionsSelect(int options1, int option2, int options3 ,View v) {
                //返回的分别是三个级别的选中位置
                String tx = options1Items.get(options1).getPickerViewText()
                        + options2Items.get(options1).get(option2)
                        + options3Items.get(options1).get(option2).get(options3).getPickerViewText();
                tvOptions.setText(tx);
            }
        }) .setOptionsSelectChangeListener(new OnOptionsSelectChangeListener() {
                              @Override
                              public void onOptionsSelectChanged(int options1, int options2, int options3) {
                                  String str = "options1: " + options1 + "\noptions2: " + options2 + "\noptions3: " + options3;
                                  Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
                              }
                          })
                .setSubmitText("确定")//确定按钮文字
                .setCancelText("取消")//取消按钮文字
                .setTitleText("城市选择")//标题
                .setSubCalSize(18)//确定和取消文字大小
                .setTitleSize(20)//标题文字大小
                .setTitleColor(Color.BLACK)//标题文字颜色
                .setSubmitColor(Color.BLUE)//确定按钮文字颜色
                .setCancelColor(Color.BLUE)//取消按钮文字颜色
                .setTitleBgColor(0xFF333333)//标题背景颜色 Night mode
                .setBgColor(0xFF000000)//滚轮背景颜色 Night mode
                .setContentTextSize(18)//滚轮文字大小
                .setLinkage(false)//设置是否联动,默认true
                .setLabels("省", "市", "区")//设置选择的三级单位
                .isCenterLabel(false) //是否只显示中间选中项的label文字,false则每项item全部都带有label。
                .setCyclic(false, false, false)//循环与否
                .setSelectOptions(1, 1, 1)  //设置默认选中项
                .setOutSideCancelable(false)//点击外部dismiss default true
                .isDialog(true)//是否显示为对话框样式
                .isRestoreItem(true)//切换时是否还原,设置默认选中第一项。
                .build();

        pvOptions.setPicker(options1Items, options2Items, options3Items);//添加数据源

4.如果需要自定义布局:

        // 注意:自定义布局中,id为 optionspicker 或者 timepicker 的布局以及其子控件必须要有,否则会报空指针
        // 具体可参考demo 里面的两个自定义布局
        pvCustomOptions = new OptionsPickerBuilder(this, new OptionsPickerView.OnOptionsSelectListener() {
            @Override
            public void onOptionsSelect(int options1, int option2, int options3, View v) {
                //返回的分别是三个级别的选中位置
                String tx = cardItem.get(options1).getPickerViewText();
                btn_CustomOptions.setText(tx);
            }
        })
                .setLayoutRes(R.layout.pickerview_custom_options, new CustomListener() {
                    @Override
                    public void customLayout(View v) {
                        //自定义布局中的控件初始化及事件处理
                        final TextView tvSubmit = (TextView) v.findViewById(R.id.tv_finish);
                        final TextView tvAdd = (TextView) v.findViewById(R.id.tv_add);
                        ImageView ivCancel = (ImageView) v.findViewById(R.id.iv_cancel);
                        tvSubmit.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                pvCustomOptions.returnData(tvSubmit);
                            }
                        });
                        ivCancel.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                pvCustomOptions.dismiss();
                            }
                        });

                        tvAdd.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                getData();
                                pvCustomOptions.setPicker(cardItem);
                            }
                        });

                    }
                })
                .build();
        pvCustomOptions.setPicker(cardItem);//添加数据

5.对使用还有疑问的话,可参考demo代码

请戳我查看demo代码

6.若只需要WheelView基础控件自行扩展实现逻辑,可直接添加基础控件库,Gradle 依赖:

compile 'com.contrarywind:wheelview:4.0.9'

WheelView 使用代码示例:

xml布局:

 <com.contrarywind.view.WheelView
            android:id="@+id/wheelview"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

Java 代码:

WheelView wheelView = findViewById(R.id.wheelview);

        wheelView.setCyclic(false);

        final List<String> mOptionsItems = new ArrayList<>();
        mOptionsItems.add("item0");
        mOptionsItems.add("item1");
        mOptionsItems.add("item2");
  
        wheelView.setAdapter(new ArrayWheelAdapter(mOptionsItems));
        wheelView.setOnItemSelectedListener(new OnItemSelectedListener() {
            @Override
            public void onItemSelected(int index) {
                Toast.makeText(MainActivity.this, "" + mOptionsItems.get(index), Toast.LENGTH_SHORT).show();
            }
        });

效果图(招行信用卡的“掌上生活”里面条件选择器他们用的就是我这个库,大家可以当实际项目参考)

https://github.com/luxiangqiang/Data-Structure-Coding

数据结构与算法必会代码实现

数组

1、数组实现增、删、改、查 (Java 实现)

2、实现一个支持动态扩容的数组 (Java 实现)

3、实现一个大小固定的有序数组,支持动态增删改操作 (Java 实现)

4、两个有序数组的合并 (Java 实现)

链表

1、单链表的插入、删除、查找 (JavaScript 实现 | Java 实现)

2、双链表的插入、删除 (JavaScript 实现)

3、循环链表的插入、查找、删除 (JavaScript 实现)

4、两个有序链表的合并 (JavaScript 实现 | Java 实现)

5、删除倒数第 K 个结点 (JavaScript 实现 | Java 实现)

6、反转链表 (JavaScript 实现 | Java 实现)

7、链表环的检测 (JavaScript 实现 | Java 实现)

8、求链表的中间结点 (JavaScript 实现 | Java 实现)

1、实现一个基于数组的顺序栈([Java 实现]())

2、实现一个基于链表的链式栈 (Java 实现)

队列

1、实现一个基于数组的顺序队列 (Java 实现)

2、实现一个循环队列 (Java 实现)

1、实现二叉树的增、删、查、(前|中|后)遍历 (JavaScript 实现 | Java 实现)

1、堆的插入与删除 (Java 实现)

2、堆排序 (JavaScript 实现 | Java 实现)

Trie(字典树)

1、实现一个字典树 (JavaScript 实现 | Java 实现)

排序

1、冒泡排序 (JavaScript 实现 | Java 实现)

2、插入排序 (JavaScript 实现 | Java 实现)

3、选择排序 (JavaScript 实现 | Java 实现)

4、希尔排序 (JavaScript 实现)

5、归并排序 (JavaScript 实现 | Java 实现)

6、快速排序 (JavaScript 实现 | Java 实现)

7、求第 K 大元素 (JavaScript 实现))

查找

1、最简单的二分查找 (JavaScript 实现 | Java 实现)

2、二分查找的四个扩展 (JavaScript 实现 | Java 实现)

遍历

1、深度优先遍历 (JavaScript 实现| Java 实现)

2、广度优先遍历 (JavaScript 实现 | Java 实现)

tp5备份mysql数据库(备忘)

//数据库备份
    public function backups()
    {
        //1.获取数据库信息
        $info = Db::getConfig();
        $dbname = $info['database'];
        //2.获取数据库所有表
        $tables = Db::query("show tables"); 
        //3、组装头部信息
        header("Content-type:text/html;charset=utf-8");
        $path = ROOT_PATH.'/data/';
        $database = $dbname;   //获取当前数据库
        $info  = "-- ----------------------------\r\n";
        $info .= "-- 日期:".date("Y-m-d H:i:s",time())."\r\n";
        $info .= "-- MySQL - 5.5.52-MariaDB : Database - ".$database."\r\n";
        $info .= "-- ----------------------------\r\n\r\n";
        $info .= "SET NAMES utf8;\r\nSET FOREIGN_KEY_CHECKS = 0;\r\n\r\n";
        //4、检查目录是否存在
        if (is_dir($path)) {
            if (is_writable($path)) {

            } else {
                echo '目录不可写'; exit();
            }
        } else {
            mkdir($path,0777,true);
        }
        //5、保存的文件名称
        $file_name = $path.$database.'_'.date('Ymd_His').'.sql';
        file_put_contents($file_name, $info, FILE_APPEND);
        //6、循环表,写入数据
        foreach ($tables as $k => $v) {
            $val = $v["Tables_in_$database"];
            $sql = "SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='$val' AND TABLE_SCHEMA='$dbname'";
            $res = Db::query($sql);
            $max_num = Db::table("$val")->order('id desc')->value('id');
            //查询表结构
            $info_table = "-- ----------------------------\r\n";
            $info_table .= "-- Table structure for `$val`\r\n";
            $info_table .= "-- ----------------------------\r\n\r\n";
            $info_table .= "DROP TABLE IF EXISTS `$val`;\r\n";

            if (count($res) < 1) {
                continue;
            }

            $info_table .= "CREATE TABLE `$val` (\n\r\t";
            foreach ($res as $kk => $vv) {
                   $info_table .= " `".$vv['COLUMN_NAME']."` ";
                   $info_table .= $vv['COLUMN_TYPE'];
                   //是否允许空值
                   if ($vv['IS_NULLABLE'] == 'NO') {
                       $info_table .= " NOT NULL ";
                   }
                   //判断主键
                   if ($vv['EXTRA']) {
                       $info_table .= " AUTO_INCREMENT ";
                       $key = $vv['COLUMN_NAME'];
                   }
                   //编码
                   if ($vv['CHARACTER_SET_NAME']) {
                       $info_table .= " CHARACTER SET ".$vv['CHARACTER_SET_NAME'];
                   }
                   //字符集
                   if ($vv['COLLATION_NAME']) {
                       $info_table .= " COLLATE ".$vv['COLLATION_NAME'];
                   }
                   //默认数值
                   if ($vv['COLUMN_DEFAULT']) {
                       $info_table .= " DEFAULT ".$vv['COLUMN_DEFAULT'];
                   }
                   //注释
                   if ($vv['COLUMN_COMMENT']) {
                       $info_table .= " COMMENT '".$vv['COLUMN_COMMENT']."',\n\r\t";
                   }
               }
               $info_table .= " PRIMARY KEY (`$key`) USING BTREE";
               $info_table .= "\n\r) ENGINE = MyISAM AUTO_INCREMENT $max_num CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;\r\n\r\n";
            

            //查询表数据
            $info_table .= "-- ----------------------------\r\n";
            $info_table .= "-- Data for the table `$val`\r\n";
            $info_table .= "-- ----------------------------\r\n\r\n";

            file_put_contents($file_name,$info_table,FILE_APPEND);
            $sql_data = "select * from $val";
            $data = Db::query($sql_data);

            $count= count($data);
            if ($count < 1) {
                continue;
            }
            foreach ($data as $key => $value) {
                $sqlStr = "INSERT INTO `$val` VALUES (";
                foreach($value as $v_d){
                    $v_d = str_replace("'","\'",$v_d);
                    $sqlStr .= "'".$v_d."', ";
                }
                //需要特别注意对数据的单引号进行转义处理
                //去掉最后一个逗号和空格
                $sqlStr = substr($sqlStr,0,strlen($sqlStr)-2);
                $sqlStr .= ");\r\n";
                file_put_contents($file_name,$sqlStr,FILE_APPEND);
            }
            $info = "\r\n";
            file_put_contents($file_name,$info,FILE_APPEND);
        }

        //7、下载数据到本地
        ob_end_clean(); 
        header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); 
        header('Content-Description: File Transfer'); 
        header('Content-Type: application/octet-stream'); 
        header('Content-Length: ' . filesize($file_name)); 
        header('Content-Disposition: attachment; filename=' . basename($file_name)); 
        readfile($file_name); 
        DownloadFile($path.$file_name); 

        $this->success("数据已备份");
    }

前序

前序遍历(先序):前序遍历可以记为根左右,若二叉树为空,则结束返回。

前序遍历的规则:

(1)访问根节点

(2)前序遍历左子树

(3)前序遍历右子树

这里需要注意:在完成第2,3步的时候,也是要按照前序遍历二叉树的规则完成。

前序遍历的输出结果:ABDECF

中序

中序遍历:中序遍历可以记为左根右,也就是说在二叉树的遍历过程中,首先要遍历二叉树的左子树,接着遍历根节点,最后遍历右子树。

同样,在二叉树为空的时候,结束返回。

中序遍历的规则:

(1)中序遍历左子树

(2)访问根节点

(3)中序遍历右子树

注意:在完成第1,3步的时候,要按照中序遍历的规则来完成。

中序遍历的输出结果:DBEAFC

后序

后序遍历:后序遍历可以记为左右根,也就是说在二叉树的遍历过程中,首先按照后序遍历的规则遍历左子树,接着按照后序遍历的规则遍历右子树,最后访问根节点。

在二叉树为空的时候,结束返回。

后序遍历二叉树的规则:

(1)后序遍历左子树

(2)后序遍历右子树

(3)访问根节点

注意:在完成1,2步的时候,依然要按照后序遍历的规则来完成。

后序遍历的输出顺序:DEBFCA