我们开发app过程中,经常会碰到需要 多级列表展示的效果。而android原生sdk中根本没有3级 4级甚至更多级别的列表控件。

所以我们就要自己去实现一个类似treeListView 的控件,下面这个是我项目中的一个效果图,可支持多级列表扩展。

           

android中有ExpandListView控件,但是这个控件只支持两级列表。对于多级列表如果重写这个不是很好用。

实现这种列表 思想就是递归,构造一个子父级的关系。

话不多说 代码中体会

Activity

[java] view plaincopy print?
  1. package com.example.customtreeviewdemo;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import android.app.Activity;
  5. import android.os.Bundle;
  6. import android.view.View;
  7. import android.view.View.OnClickListener;
  8. import android.widget.Button;
  9. import android.widget.ListView;
  10. import android.widget.Toast;
  11. import com.example.customtreeviewdemo.bean.MyNodeBean;
  12. import com.example.customtreeviewdemo.tree.Node;
  13. import com.example.customtreeviewdemo.tree.TreeListViewAdapter.OnTreeNodeClickListener;
  14. public class MainActivity extends Activity {
  15. private ListView treeLv;
  16. private Button checkSwitchBtn;
  17. private MyTreeListViewAdapter<MyNodeBean> adapter;
  18. private List<MyNodeBean> mDatas = new ArrayList<MyNodeBean>();
  19. //标记是显示Checkbox还是隐藏
  20. private boolean isHide = true;
  21. @Override
  22. protected void onCreate(Bundle savedInstanceState) {
  23. super.onCreate(savedInstanceState);
  24. setContentView(R.layout.activity_main);
  25. initDatas();
  26. treeLv = (ListView) this.findViewById(R.id.tree_lv);
  27. checkSwitchBtn = (Button)this.findViewById(R.id.check_switch_btn);
  28. checkSwitchBtn.setOnClickListener(new OnClickListener(){
  29. @Override
  30. public void onClick(View v) {
  31. if(isHide){
  32. isHide = false;
  33. }else{
  34. isHide = true;
  35. }
  36. adapter.updateView(isHide);
  37. }
  38. });
  39. try {
  40. adapter = new MyTreeListViewAdapter<MyNodeBean>(treeLv, this,
  41. mDatas, 10, isHide);
  42. adapter.setOnTreeNodeClickListener(new OnTreeNodeClickListener() {
  43. @Override
  44. public void onClick(Node node, int position) {
  45. if (node.isLeaf()) {
  46. Toast.makeText(getApplicationContext(), node.getName(),
  47. Toast.LENGTH_SHORT).show();
  48. }
  49. }
  50. @Override
  51. public void onCheckChange(Node node, int position,
  52. List<Node> checkedNodes) {
  53. StringBuffer sb = new StringBuffer();
  54. for (Node n : checkedNodes) {
  55. int pos = n.getId() - 1;
  56. sb.append(mDatas.get(pos).getName()).append("---")
  57. .append(pos + 1).append(";");
  58. }
  59. Toast.makeText(getApplicationContext(), sb.toString(),
  60. Toast.LENGTH_SHORT).show();
  61. }
  62. });
  63. } catch (IllegalArgumentException e) {
  64. e.printStackTrace();
  65. } catch (IllegalAccessException e) {
  66. e.printStackTrace();
  67. }
  68. treeLv.setAdapter(adapter);
  69. }
  70. private void initDatas() {
  71. mDatas.add(new MyNodeBean(1, 0, "中国古代"));
  72. mDatas.add(new MyNodeBean(2, 1, "唐朝"));
  73. mDatas.add(new MyNodeBean(3, 1, "宋朝"));
  74. mDatas.add(new MyNodeBean(4, 1, "明朝"));
  75. mDatas.add(new MyNodeBean(5, 2, "李世民"));
  76. mDatas.add(new MyNodeBean(6, 2, "李白"));
  77. mDatas.add(new MyNodeBean(7, 3, "赵匡胤"));
  78. mDatas.add(new MyNodeBean(8, 3, "苏轼"));
  79. mDatas.add(new MyNodeBean(9, 4, "朱元璋"));
  80. mDatas.add(new MyNodeBean(10, 4, "唐伯虎"));
  81. mDatas.add(new MyNodeBean(11, 4, "文征明"));
  82. mDatas.add(new MyNodeBean(12, 7, "赵建立"));
  83. mDatas.add(new MyNodeBean(13, 8, "苏东东"));
  84. mDatas.add(new MyNodeBean(14, 10, "秋香"));
  85. }
  86. }

Adapter

这个adapter是继承了自己的定义的一个TreeListViewAdapter,核心实现都是在TreeListViewAdapter这个里面

[java] view plaincopy print?
  1. package com.example.customtreeviewdemo;
  2. import java.util.List;
  3. import android.content.Context;
  4. import android.view.View;
  5. import android.view.ViewGroup;
  6. import android.widget.CheckBox;
  7. import android.widget.CompoundButton;
  8. import android.widget.CompoundButton.OnCheckedChangeListener;
  9. import android.widget.ImageView;
  10. import android.widget.ListView;
  11. import android.widget.TextView;
  12. import com.example.customtreeviewdemo.tree.Node;
  13. import com.example.customtreeviewdemo.tree.TreeListViewAdapter;
  14. public class MyTreeListViewAdapter<T> extends TreeListViewAdapter<T> {
  15. public MyTreeListViewAdapter(ListView mTree, Context context,
  16. List<T> datas, int defaultExpandLevel,boolean isHide)
  17. throws IllegalArgumentException, IllegalAccessException {
  18. super(mTree, context, datas, defaultExpandLevel,isHide);
  19. }
  20. @SuppressWarnings("unchecked")
  21. @Override
  22. public View getConvertView(Node node, int position, View convertView,
  23. ViewGroup parent) {
  24. ViewHolder viewHolder = null;
  25. if (convertView == null)
  26. {
  27. convertView = mInflater.inflate(R.layout.list_item, parent, false);
  28. viewHolder = new ViewHolder();
  29. viewHolder.icon = (ImageView) convertView
  30. .findViewById(R.id.id_treenode_icon);
  31. viewHolder.label = (TextView) convertView
  32. .findViewById(R.id.id_treenode_name);
  33. viewHolder.checkBox = (CheckBox)convertView.findViewById(R.id.id_treeNode_check);
  34. convertView.setTag(viewHolder);
  35. } else
  36. {
  37. viewHolder = (ViewHolder) convertView.getTag();
  38. }
  39. if (node.getIcon() == -1)
  40. {
  41. viewHolder.icon.setVisibility(View.INVISIBLE);
  42. } else
  43. {
  44. viewHolder.icon.setVisibility(View.VISIBLE);
  45. viewHolder.icon.setImageResource(node.getIcon());
  46. }
  47. if(node.isHideChecked()){
  48. viewHolder.checkBox.setVisibility(View.GONE);
  49. }else{
  50. viewHolder.checkBox.setVisibility(View.VISIBLE);
  51. setCheckBoxBg(viewHolder.checkBox,node.isChecked());
  52. }
  53. viewHolder.label.setText(node.getName());
  54. return convertView;
  55. }
  56. private final class ViewHolder
  57. {
  58. ImageView icon;
  59. TextView label;
  60. CheckBox checkBox;
  61. }
  62. /**
  63. * checkbox是否显示
  64. * @param cb
  65. * @param isChecked
  66. */
  67. private void setCheckBoxBg(CheckBox cb,boolean isChecked){
  68. if(isChecked){
  69. cb.setBackgroundResource(R.drawable.check_box_bg_check);
  70. }else{
  71. cb.setBackgroundResource(R.drawable.check_box_bg);
  72. }
  73. }
  74. }

自定义TreeListViewAdapter  这个是整个树形结构的一个适配器,这里面主要是实现对Node节点的操作 点击,选中改变 更新等

[java] view plaincopy print?
  1. package com.example.customtreeviewdemo.tree;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import android.content.Context;
  5. import android.view.LayoutInflater;
  6. import android.view.View;
  7. import android.view.ViewGroup;
  8. import android.widget.AdapterView;
  9. import android.widget.AdapterView.OnItemClickListener;
  10. import android.widget.BaseAdapter;
  11. import android.widget.CheckBox;
  12. import android.widget.CompoundButton;
  13. import android.widget.CompoundButton.OnCheckedChangeListener;
  14. import android.widget.ListView;
  15. import android.widget.RelativeLayout;
  16. /**
  17. * tree适配器
  18. * @param <T>
  19. */
  20. public abstract class TreeListViewAdapter<T> extends BaseAdapter {
  21. protected Context mContext;
  22. /**
  23. * 存储所有可见的Node
  24. */
  25. protected List<Node> mNodes;
  26. protected LayoutInflater mInflater;
  27. /**
  28. * 存储所有的Node
  29. */
  30. protected List<Node> mAllNodes;
  31. /**
  32. * 点击的回调接口
  33. */
  34. private OnTreeNodeClickListener onTreeNodeClickListener;
  35. public interface OnTreeNodeClickListener {
  36. /**
  37. * 处理node click事件
  38. * @param node
  39. * @param position
  40. */
  41. void onClick(Node node, int position);
  42. /**
  43. * 处理checkbox选择改变事件
  44. * @param node
  45. * @param position
  46. * @param checkedNodes
  47. */
  48. void onCheckChange(Node node, int position,List<Node> checkedNodes);
  49. }
  50. public void setOnTreeNodeClickListener(
  51. OnTreeNodeClickListener onTreeNodeClickListener) {
  52. this.onTreeNodeClickListener = onTreeNodeClickListener;
  53. }
  54. /**
  55. *
  56. * @param mTree
  57. * @param context
  58. * @param datas
  59. * @param defaultExpandLevel
  60. *            默认展开几级树
  61. * @throws IllegalArgumentException
  62. * @throws IllegalAccessException
  63. */
  64. public TreeListViewAdapter(ListView mTree, Context context, List<T> datas,
  65. int defaultExpandLevel, boolean isHide)
  66. throws IllegalArgumentException, IllegalAccessException {
  67. mContext = context;
  68. /**
  69. * 对所有的Node进行排序
  70. */
  71. mAllNodes = TreeHelper
  72. .getSortedNodes(datas, defaultExpandLevel, isHide);
  73. /**
  74. * 过滤出可见的Node
  75. */
  76. mNodes = TreeHelper.filterVisibleNode(mAllNodes);
  77. mInflater = LayoutInflater.from(context);
  78. /**
  79. * 设置节点点击时,可以展开以及关闭;并且将ItemClick事件继续往外公布
  80. */
  81. mTree.setOnItemClickListener(new OnItemClickListener() {
  82. @Override
  83. public void onItemClick(AdapterView<?> parent, View view,
  84. int position, long id) {
  85. expandOrCollapse(position);
  86. if (onTreeNodeClickListener != null) {
  87. onTreeNodeClickListener.onClick(mNodes.get(position),
  88. position);
  89. }
  90. }
  91. });
  92. }
  93. /**
  94. * 相应ListView的点击事件 展开或关闭某节点
  95. *
  96. * @param position
  97. */
  98. public void expandOrCollapse(int position) {
  99. Node n = mNodes.get(position);
  100. if (n != null)// 排除传入参数错误异常
  101. {
  102. if (!n.isLeaf()) {
  103. n.setExpand(!n.isExpand());
  104. mNodes = TreeHelper.filterVisibleNode(mAllNodes);
  105. notifyDataSetChanged();// 刷新视图
  106. }
  107. }
  108. }
  109. @Override
  110. public int getCount() {
  111. return mNodes.size();
  112. }
  113. @Override
  114. public Object getItem(int position) {
  115. return mNodes.get(position);
  116. }
  117. @Override
  118. public long getItemId(int position) {
  119. return position;
  120. }
  121. @Override
  122. public View getView(final int position, View convertView, ViewGroup parent) {
  123. final Node node = mNodes.get(position);
  124. convertView = getConvertView(node, position, convertView, parent);
  125. // 设置内边距
  126. convertView.setPadding(node.getLevel() * 30, 3, 3, 3);
  127. if (!node.isHideChecked()) {
  128. //获取各个节点所在的父布局
  129. RelativeLayout myView = (RelativeLayout) convertView;
  130. //父布局下的CheckBox
  131. CheckBox cb = (CheckBox) myView.getChildAt(1);
  132. cb.setOnCheckedChangeListener(new OnCheckedChangeListener(){
  133. @Override
  134. public void onCheckedChanged(CompoundButton buttonView,
  135. boolean isChecked) {
  136. TreeHelper.setNodeChecked(node, isChecked);
  137. List<Node> checkedNodes = new ArrayList<Node>();
  138. for(Node n:mAllNodes){
  139. if(n.isChecked()){
  140. checkedNodes.add(n);
  141. }
  142. }
  143. onTreeNodeClickListener.onCheckChange(node,position,checkedNodes);
  144. TreeListViewAdapter.this.notifyDataSetChanged();
  145. }
  146. });
  147. }
  148. return convertView;
  149. }
  150. public abstract View getConvertView(Node node, int position,
  151. View convertView, ViewGroup parent);
  152. /**
  153. * 更新
  154. * @param isHide
  155. */
  156. public void updateView(boolean isHide){
  157. for(Node node:mAllNodes){
  158. node.setHideChecked(isHide);
  159. }
  160. this.notifyDataSetChanged();
  161. }
  162. }

node 模型类

[java] view plaincopy print?
  1. package com.example.customtreeviewdemo.bean;
  2. public class MyNodeBean {
  3. /**
  4. * 节点Id
  5. */
  6. private int id;
  7. /**
  8. * 节点父id
  9. */
  10. private int pId;
  11. /**
  12. * 节点name
  13. */
  14. private String name;
  15. /**
  16. *
  17. */
  18. private String desc;
  19. /**
  20. * 节点名字长度
  21. */
  22. private long length;
  23. public MyNodeBean(int id, int pId, String name) {
  24. super();
  25. this.id = id;
  26. this.pId = pId;
  27. this.name = name;
  28. }
  29. public int getId() {
  30. return id;
  31. }
  32. public void setId(int id) {
  33. this.id = id;
  34. }
  35. public int getPid() {
  36. return pId;
  37. }
  38. public void setPid(int pId) {
  39. this.pId = pId;
  40. }
  41. public String getName() {
  42. return name;
  43. }
  44. public void setName(String name) {
  45. this.name = name;
  46. }
  47. public String getDesc() {
  48. return desc;
  49. }
  50. public void setDesc(String desc) {
  51. this.desc = desc;
  52. }
  53. public long getLength() {
  54. return length;
  55. }
  56. public void setLength(long length) {
  57. this.length = length;
  58. }
  59. }

TreeHelper这个也是核心操作类,主要功能是将业务数据和节点数据进行匹配

[java] view plaincopy print?
  1. package com.example.customtreeviewdemo.tree;
  2. import java.lang.reflect.Field;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. import com.example.customtreeviewdemo.R;
  6. public class TreeHelper {
  7. /**
  8. * 根据所有节点获取可见节点
  9. *
  10. * @param allNodes
  11. * @return
  12. */
  13. public static List<Node> filterVisibleNode(List<Node> allNodes) {
  14. List<Node> visibleNodes = new ArrayList<Node>();
  15. for (Node node : allNodes) {
  16. // 如果为根节点,或者上层目录为展开状态
  17. if (node.isRoot() || node.isParentExpand()) {
  18. setNodeIcon(node);
  19. visibleNodes.add(node);
  20. }
  21. }
  22. return visibleNodes;
  23. }
  24. /**
  25. * 获取排序的所有节点
  26. *
  27. * @param datas
  28. * @param defaultExpandLevel
  29. * @return
  30. * @throws IllegalArgumentException
  31. * @throws IllegalAccessException
  32. */
  33. public static <T> List<Node> getSortedNodes(List<T> datas,
  34. int defaultExpandLevel, boolean isHide)
  35. throws IllegalAccessException, IllegalArgumentException {
  36. List<Node> sortedNodes = new ArrayList<Node>();
  37. // 将用户数据转化为List<Node>
  38. List<Node> nodes = convertData2Nodes(datas, isHide);
  39. // 拿到根节点
  40. List<Node> rootNodes = getRootNodes(nodes);
  41. // 排序以及设置Node间关系
  42. for (Node node : rootNodes) {
  43. addNode(sortedNodes, node, defaultExpandLevel, 1);
  44. }
  45. return sortedNodes;
  46. }
  47. /**
  48. * 把一个节点上的所有的内容都挂上去
  49. */
  50. private static void addNode(List<Node> nodes, Node node,
  51. int defaultExpandLeval, int currentLevel) {
  52. nodes.add(node);
  53. if (defaultExpandLeval >= currentLevel) {
  54. node.setExpand(true);
  55. }
  56. if (node.isLeaf())
  57. return;
  58. for (int i = 0; i < node.getChildrenNodes().size(); i++) {
  59. addNode(nodes, node.getChildrenNodes().get(i), defaultExpandLeval,
  60. currentLevel + 1);
  61. }
  62. }
  63. /**
  64. * 获取所有的根节点
  65. *
  66. * @param nodes
  67. * @return
  68. */
  69. public static List<Node> getRootNodes(List<Node> nodes) {
  70. List<Node> rootNodes = new ArrayList<Node>();
  71. for (Node node : nodes) {
  72. if (node.isRoot()) {
  73. rootNodes.add(node);
  74. }
  75. }
  76. return rootNodes;
  77. }
  78. /**
  79. * 将泛型datas转换为node
  80. *
  81. * @param datas
  82. * @return
  83. * @throws IllegalArgumentException
  84. * @throws IllegalAccessException
  85. */
  86. public static <T> List<Node> convertData2Nodes(List<T> datas, boolean isHide)
  87. throws IllegalAccessException, IllegalArgumentException {
  88. List<Node> nodes = new ArrayList<Node>();
  89. Node node = null;
  90. for (T t : datas) {
  91. int id = -1;
  92. int pId = -1;
  93. String name = null;
  94. Class<? extends Object> clazz = t.getClass();
  95. Field[] declaredFields = clazz.getDeclaredFields();
  96. /**
  97. * 与MyNodeBean实体一一对应
  98. */
  99. for (Field f : declaredFields) {
  100. if ("id".equals(f.getName())) {
  101. f.setAccessible(true);
  102. id = f.getInt(t);
  103. }
  104. if ("pId".equals(f.getName())) {
  105. f.setAccessible(true);
  106. pId = f.getInt(t);
  107. }
  108. if ("name".equals(f.getName())) {
  109. f.setAccessible(true);
  110. name = (String) f.get(t);
  111. }
  112. if ("desc".equals(f.getName())) {
  113. continue;
  114. }
  115. if ("length".equals(f.getName())) {
  116. continue;
  117. }
  118. if (id == -1 && pId == -1 && name == null) {
  119. break;
  120. }
  121. }
  122. node = new Node(id, pId, name);
  123. node.setHideChecked(isHide);
  124. nodes.add(node);
  125. }
  126. /**
  127. * 比较nodes中的所有节点,分别添加children和parent
  128. */
  129. for (int i = 0; i < nodes.size(); i++) {
  130. Node n = nodes.get(i);
  131. for (int j = i + 1; j < nodes.size(); j++) {
  132. Node m = nodes.get(j);
  133. if (n.getId() == m.getpId()) {
  134. n.getChildrenNodes().add(m);
  135. m.setParent(n);
  136. } else if (n.getpId() == m.getId()) {
  137. n.setParent(m);
  138. m.getChildrenNodes().add(n);
  139. }
  140. }
  141. }
  142. for (Node n : nodes) {
  143. setNodeIcon(n);
  144. }
  145. return nodes;
  146. }
  147. /**
  148. * 设置打开,关闭icon
  149. *
  150. * @param node
  151. */
  152. public static void setNodeIcon(Node node) {
  153. if (node.getChildrenNodes().size() > 0 && node.isExpand()) {
  154. node.setIcon(R.drawable.tree_expand);
  155. } else if (node.getChildrenNodes().size() > 0 && !node.isExpand()) {
  156. node.setIcon(R.drawable.tree_econpand);
  157. } else
  158. node.setIcon(-1);
  159. }
  160. public static void setNodeChecked(Node node, boolean isChecked) {
  161. // 自己设置是否选择
  162. node.setChecked(isChecked);
  163. /**
  164. * 非叶子节点,子节点处理
  165. */
  166. setChildrenNodeChecked(node, isChecked);
  167. /** 父节点处理 */
  168. setParentNodeChecked(node);
  169. }
  170. /**
  171. * 非叶子节点,子节点处理
  172. */
  173. private static void setChildrenNodeChecked(Node node, boolean isChecked) {
  174. node.setChecked(isChecked);
  175. if (!node.isLeaf()) {
  176. for (Node n : node.getChildrenNodes()) {
  177. // 所有子节点设置是否选择
  178. setChildrenNodeChecked(n, isChecked);
  179. }
  180. }
  181. }
  182. /**
  183. * 设置父节点选择
  184. *
  185. * @param node
  186. */
  187. private static void setParentNodeChecked(Node node) {
  188. /** 非根节点 */
  189. if (!node.isRoot()) {
  190. Node rootNode = node.getParent();
  191. boolean isAllChecked = true;
  192. for (Node n : rootNode.getChildrenNodes()) {
  193. if (!n.isChecked()) {
  194. isAllChecked = false;
  195. break;
  196. }
  197. }
  198. if (isAllChecked) {
  199. rootNode.setChecked(true);
  200. } else {
  201. rootNode.setChecked(false);
  202. }
  203. setParentNodeChecked(rootNode);
  204. }
  205. }
  206. }

核心的代码就是这些,希望对大家有帮助。

转自:http://blog.csdn.net/shaoyezhangliwei/article/details/52275130

android多级列表相关推荐

  1. Android多级列表的实现

    多级列表是ui入门,没什么特别需要注意的难点,直接上代码: 第一级列表窗口 /*** 第一级列表* 1.编辑状态下选中一个条目,会弹出底部操作栏* 2.非编辑状态下选中条目会跳转到下一级* 3.复选框 ...

  2. Android UI开发第二篇——多级列表(ExpandableListView)

    开发中很多地方使用到了多级列表,android可以使用ExpandableListView很好的实现,下面模仿了手机qq的实现,见下图. 多级列表使用了ExpandableListView,自定义了A ...

  3. Android UI 之实现多级列表TreeView

    所谓TreeView就是在Windows中常见的多级列表树,在Android中系统只默认提供了ListView和ExpandableListView两种列表,最多只支持到二级列表的实现,所以如果想要实 ...

  4. 更快实现Android多级树形选择列表

    图片demo下载地址:https://download.csdn.net/download/Imshuyuan/85464136 快速实现Android多级树形列表,这个库是在鸿洋多级树形列表demo ...

  5. android--------ExpandableListView的使用多级列表

    2019独角兽企业重金招聘Python工程师标准>>> 多级列表ExpandableListView 扩展列表能够显示一个指示在每项显示项的当前状态(状态通常是一个扩展的组,组的孩子 ...

  6. 安卓listView实现多级列表

    安卓listView实现多级列表 实现两级列表我们可以使用ExpandableListView,但对于实现多级列表,使用ExpandableListView嵌套实现起来就比较费劲,可以使用listVi ...

  7. word2010多级列表编号变成黑块的解决方案

    为什么80%的码农都做不了架构师?>>>    1.在文档最后键入多行文字,分别设置为和多级列表关联的样式.(依然显示黑块) 2.选择新键入的这部面文字,先选择多级列表的无,然后再选 ...

  8. word报错:题注或页码中不含章节编号。请使用“开始”选项卡上的“多级列表”按钮,然后选择链接到标题样式的编号方案(无法添加题注)

    这破问题还真不好解决.... 看了这个视频:WORD教学五(论文排版之论文中的图表跟随章节插入题注) 貌似知道怎么弄了..这样操作: 选择标题,然后点击多级列表 --> 定义新的多级列表 然后在 ...

  9. word里的多级列表和项目编号是什么区别?

    numbering:主要用于创建同级编号,对于正文内并列关系的段落增加编号. multilevel list:主要用于创建多层编号,还能和标题之类绑定,为不同层级的标题加编号.这个用的比较多,还能创建 ...

最新文章

  1. c语言各种编程风格 微软 gnu,编程规范-c语言的编程风格
  2. 如何在JavaScript中实现堆栈和队列?
  3. spring mvc DispatcherServlet详解之拾忆工具类utils
  4. P1476 休息中的小呆
  5. 敏捷水手——单体法到微服务之旅
  6. 中国移动MM7 API用户手册(七)
  7. android仿高德地图透明黑字,Android 仿高德地图可拉伸的BottomSheet
  8. 那些年破处倒闭的公司,都有哪些特征?打工人避雷必看
  9. jetty之嵌入式运行jetty
  10. 服务器与普通电脑的区别?
  11. Python利用SMTP发邮件
  12. 2018美赛B题总结
  13. UrlRewrite的使用
  14. modbus tcp调试助手_wemos D1 arduino项目实战1-TCP协议连接Tlink平台①
  15. html逐字显示并停止,Pr字幕如何让他逐字出现逐字消失?
  16. 计算机用通讯电压多少,通信局(站)用交流电源的质量指标要求
  17. win7 64位虚拟机安装
  18. oracle中distinct和count函数组合使用
  19. 聚英国际|【燎原计划启动周第二期AMA】Filecoin经济模型及热点争议解读
  20. WebStorm2016.2 注册码及激活,2018.6.14亲测有效

热门文章

  1. 2022交易猫系统+带聊天/安心购/已验号模板+APP双端跳转
  2. 6-禅宗的形成及其基本观念
  3. 黑白翻转棋原理及实现链接
  4. 目标检测 IoU、GloU、DloU、CloU详解
  5. 大数据开发,就要掌握哪些技术?
  6. python0.618方法
  7. 04-MongoDB集群和安全
  8. 计算机与通信网络潘书文答案,计算机与通信网络潘书文答案
  9. 《重构》第三章 - 读后感(发散、散弹、依恋)
  10. unity 在移动平台中,文件操作路径详解