我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android树形控件绘制方法

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android树形控件绘制方法

前言

作为一个开发者,日常会接触到很多优秀的软件,其实,或多或少会有这样的想法,我能不能开发一个自己软件,甚至办公软件都希望是Markdown的文本,为何用office?我常常想自己做一个IDE什么的。但是,很多只是想了一下就过了,一直没有实现.
我接触思维导图软件已经很久的了,开始是使用微软的思维导图软件,接着XMind,后来使用了MindMaple Lite。感觉很好用的。也想过如何去实现一个思维导图的软件,加之我特别注意软件的快捷键,我选取软件常常是,看快捷如何,快捷键差的就不要了。基于自己的实践使用思维导图。前一个月我就在github上实现了一个树形图的Android控件,这个其实是我想实现思维导图的开始。实现后,才发现并没有多大的障碍。下面我就说说我如何打造一个树形控件的。先上效果:


效果1


效果2

实现

一步一步可夺城。将自己要实现的东西肢解,那些实现得了的?那些未知的?

思路步骤概要

整个结构分为:树形,节点; 对于Android的结构有:模型(树形,节点),View;

实现树形的节点node 的Model; 实现树形Model; 实现View的绘制:1.添加View;2.确定Nodes的位置;3.连线Node;

详细步骤

看到思路步骤概要后,相信我们的思路已经很清晰了。感觉是不是很simple,是的,实现也如此。到这里了,我就开始编码。但是为了教会大家,我提几个疑问给大家:

树的遍历如何实现?(可以google,如果你学过数据结构当然simple了)
节点和节点这间使用什么关联?(next)
如何确定Node的位置?位置有什么规律?(??)
如何实现两个View之间的连线?(??)
……

其实问题还真的有一点。但是这些都不能妨碍我们的步伐,还是写好已知的代码吧 。

代码

1.树的节点。主要是一些需要的数据。父节点,值,子节点,是否对焦(对于将来用的),在树形的层……


package com.owant.drawtreeview.model;
import java.util.LinkedList;

public class TreeNode<T> {
 
 public TreeNode<T> parentNode;
 
 public T value;
 
 public LinkedList<TreeNode<T>> childNodes;
 
 public boolean focus;
 
 public int floor;
 public TreeNode(T value) {
  this.value = value;
  this.childNodes = new LinkedList<TreeNode<T>>();
//  this.focus = false;
//  this.parentNode = null;
 }
 public TreeNode<T> getParentNode() {
  return parentNode;
 }
 public void setParentNode(TreeNode<T> parentNode) {
  this.parentNode = parentNode;
 }
 public T getValue() {
  return value;
 }
 public void setValue(T value) {
  this.value = value;
 }
 public LinkedList<TreeNode<T>> getChildNodes() {
  return childNodes;
 }
 public void setChildNodes(LinkedList<TreeNode<T>> childNodes) {
  this.childNodes = childNodes;
 }
 public boolean isFocus() {
  return focus;
 }
 public void setFocus(boolean focus) {
  this.focus = focus;
 }
 public int getFloor() {
  return floor;
 }
 public void setFloor(int floor) {
  this.floor = floor;
 }
}

2.树形。根节点,添加节点,遍历,上一个节点,下一个节点,基于点拆分的上下节点集合。


package com.owant.drawtreeview.model;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;
import java.util.Stack;

public class Tree<T> {
 
 public TreeNode<T> rootNode;
 public Tree(TreeNode<T> rootNode) {
  this.rootNode = rootNode;
 }
 
 public void addNode(TreeNode<T> start, TreeNode<T>... nodes) {
  int index = 1;
  TreeNode<T> temp = start;
  if (temp.getParentNode() != null) {
   index = temp.getParentNode().floor;
  }
  for (TreeNode<T> t : nodes) {
   t.setParentNode(start);
   t.setFloor(index);
   start.getChildNodes().add(t);
  }
 }
 public boolean remvoeNode(TreeNode<T> starNode, TreeNode<T> deleteNote) {
  boolean rm = false;
  int size = starNode.getChildNodes().size();
  if (size > 0) {
   rm = starNode.getChildNodes().remove(deleteNote);
  }
  return rm;
 }
 public TreeNode<T> getRootNode() {
  return rootNode;
 }
 public void setRootNode(TreeNode<T> rootNode) {
  this.rootNode = rootNode;
 }
 
 public TreeNode<T> getLowNode(TreeNode<T> midPreNode) {
  TreeNode<T> find = null;
  TreeNode<T> parentNode = midPreNode.getParentNode();
  if (parentNode != null && parentNode.getChildNodes().size() >= 2) {
   Deque<TreeNode<T>> queue = new ArrayDeque<>();
   TreeNode<T> rootNode = parentNode;
   queue.add(rootNode);
   boolean up = false;
   while (!queue.isEmpty()) {
    rootNode = (TreeNode<T>) queue.poll();
    if (up) {
     if (rootNode.getFloor() == midPreNode.getFloor()) {
      find = rootNode;
     }
     break;
    }
    //到了该元素
    if (rootNode == midPreNode) up = true;
    LinkedList<TreeNode<T>> childNodes = rootNode.getChildNodes();
    if (childNodes.size() > 0) {
     for (TreeNode<T> item : childNodes) {
      queue.add(item);
     }
    }
   }
  }
  return find;
 }
 public TreeNode<T> getPreNode(TreeNode<T> midPreNode) {
  TreeNode<T> parentNode = midPreNode.getParentNode();
  TreeNode<T> find = null;
  if (parentNode != null && parentNode.getChildNodes().size() > 0) {
   Deque<TreeNode<T>> queue = new ArrayDeque<>();
   TreeNode<T> rootNode = parentNode;
   queue.add(rootNode);
   while (!queue.isEmpty()) {
    rootNode = (TreeNode<T>) queue.poll();
    //到了该元素
    if (rootNode == midPreNode) {
     //返回之前的值
     break;
    }
    find = rootNode;
    LinkedList<TreeNode<T>> childNodes = rootNode.getChildNodes();
    if (childNodes.size() > 0) {
     for (TreeNode<T> item : childNodes) {
      queue.add(item);
     }
    }
   }
   if (find != null && find.getFloor() != midPreNode.getFloor()) {
    find = null;
   }
  }
  return find;
 }
 public ArrayList<TreeNode<T>> getAllLowNodes(TreeNode<T> addNode) {
  ArrayList<TreeNode<T>> array = new ArrayList<>();
  TreeNode<T> parentNode = addNode.getParentNode();
  while (parentNode != null) {
   TreeNode<T> lowNode = getLowNode(parentNode);
   while (lowNode != null) {
    array.add(lowNode);
    lowNode = getLowNode(lowNode);
   }
   parentNode = parentNode.getParentNode();
  }
  return array;
 }
 public ArrayList<TreeNode<T>> getAllPreNodes(TreeNode<T> addNode) {
  ArrayList<TreeNode<T>> array = new ArrayList<>();
  TreeNode<T> parentNode = addNode.getParentNode();
  while (parentNode != null) {
   TreeNode<T> lowNode = getPreNode(parentNode);
   while (lowNode != null) {
    array.add(lowNode);
    lowNode = getPreNode(lowNode);
   }
   parentNode = parentNode.getParentNode();
  }
  return array;
 }
 public LinkedList<TreeNode<T>> getNodeChildNodes(TreeNode<T> node) {
  return node.getChildNodes();
 }
 public void printTree() {
  Stack<TreeNode<T>> stack = new Stack<>();
  TreeNode<T> rootNode = getRootNode();
  stack.add(rootNode);
  while (!stack.isEmpty()) {
   TreeNode<T> pop = stack.pop();
   System.out.println(pop.getValue().toString());
   LinkedList<TreeNode<T>> childNodes = pop.getChildNodes();
   for (TreeNode<T> item : childNodes) {
    stack.add(item);
   }
  }
 }
 public void printTree2() {
  Deque<TreeNode<T>> queue = new ArrayDeque<>();
  TreeNode<T> rootNode = getRootNode();
  queue.add(rootNode);
  while (!queue.isEmpty()) {
   rootNode = (TreeNode<T>) queue.poll();
   System.out.println(rootNode.getValue().toString());
   LinkedList<TreeNode<T>> childNodes = rootNode.getChildNodes();
   if (childNodes.size() > 0) {
    for (TreeNode<T> item : childNodes) {
     queue.add(item);
    }
   }
  }
 }
}

3.测试模型 当我们实现了模型后,要写一些列子来测试模型是否正确,进行打印,遍历等测试,这是很重要的。对于树形的node的上一个node和下一个node的理解等。 


4.树形的View


package com.owant.drawtreeview.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import com.owant.drawtreeview.R;
import com.owant.drawtreeview.model.Tree;
import com.owant.drawtreeview.model.TreeNode;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.LinkedList;

public class SuperTreeView extends ViewGroup {
  
  private int mDx;
  private int mDy;
  private int mWith;
  private int mHeight;
  private Context mContext;
  private Tree<String> mTree;
  private ArrayList<NodeView> mNodesViews;
  public SuperTreeView(Context context) {
    this(context, null, 0);
  }
  public SuperTreeView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
  }
  public SuperTreeView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    mContext = context;
    mNodesViews = new ArrayList<>();
    mContext = context;
    mDx = dp2px(mContext, 26);
    mDy = dp2px(mContext, 22);
  }
  
  private void onAddNodeViews() {
    if (mTree != null) {
      TreeNode<String> rootNode = mTree.getRootNode();
      Deque<TreeNode<String>> deque = new ArrayDeque<>();
      deque.add(rootNode);
      while (!deque.isEmpty()) {
        TreeNode<String> poll = deque.poll();
        NodeView nodeView = new NodeView(mContext);
        nodeView.setTreeNode(poll);
        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        nodeView.setLayoutParams(lp);
        this.addView(nodeView);
        mNodesViews.add(nodeView);
        LinkedList<TreeNode<String>> childNodes = poll.getChildNodes();
        for (TreeNode<String> ch : childNodes) {
          deque.push(ch);
        }
      }
    }
  }
  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    final int size = getChildCount();
    for (int i = 0; i < size; i++) {
      measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
    }
  }
  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {
    mHeight = getMeasuredHeight();
    mWith = getMeasuredWidth();
    if (mTree != null) {
      NodeView rootView = findTreeNodeView(mTree.getRootNode());
      if (rootView != null) {
        //root的位置
        rootTreeViewLayout(rootView);
        //标准位置
        for (NodeView nv : mNodesViews) {
          standardTreeChildLayout(nv);
        }
        //基于父子的移动
        for (NodeView nv : mNodesViews) {
          fatherChildCorrect(nv);
        }
      }
    }
  }
  private void rootTreeViewLayout(NodeView rootView) {
    int lr = mDy;
    int tr = mHeight / 2 - rootView.getMeasuredHeight() / 2;
    int rr = lr + rootView.getMeasuredWidth();
    int br = tr + rootView.getMeasuredHeight();
    rootView.layout(lr, tr, rr, br);
  }
  @Override
  protected void dispatchDraw(Canvas canvas) {
    if (mTree != null) {
      drawTreeLine(canvas, mTree.getRootNode());
    }
    super.dispatchDraw(canvas);
  }
  
  private void standardTreeChildLayout(NodeView rootView) {
    TreeNode<String> treeNode = rootView.getTreeNode();
    if (treeNode != null) {
      //所有的子节点
      LinkedList<TreeNode<String>> childNodes = treeNode.getChildNodes();
      int size = childNodes.size();
      int mid = size / 2;
      int r = size % 2;
      //基线
      //    b
      //  a-------
      //    c
      //
      int left = rootView.getRight() + mDx;
      int top = rootView.getTop() + rootView.getMeasuredHeight() / 2;
      int right = 0;
      int bottom = 0;
      if (size == 0) {
        return;
      } else if (size == 1) {
        NodeView midChildNodeView = findTreeNodeView(childNodes.get(0));
        top = top - midChildNodeView.getMeasuredHeight() / 2;
        right = left + midChildNodeView.getMeasuredWidth();
        bottom = top + midChildNodeView.getMeasuredHeight();
        midChildNodeView.layout(left, top, right, bottom);
      } else {
        int topLeft = left;
        int topTop = top;
        int topRight = 0;
        int topBottom = 0;
        int bottomLeft = left;
        int bottomTop = top;
        int bottomRight = 0;
        int bottomBottom = 0;
        if (r == 0) {//偶数
          for (int i = mid - 1; i >= 0; i--) {
            NodeView topView = findTreeNodeView(childNodes.get(i));
            NodeView bottomView = findTreeNodeView(childNodes.get(size - i - 1));
            if (i == mid - 1) {
              topTop = topTop - mDy / 2 - topView.getMeasuredHeight();
              topRight = topLeft + topView.getMeasuredWidth();
              topBottom = topTop + topView.getMeasuredHeight();
              bottomTop = bottomTop + mDy / 2;
              bottomRight = bottomLeft + bottomView.getMeasuredWidth();
              bottomBottom = bottomTop + bottomView.getMeasuredHeight();
            } else {
              topTop = topTop - mDy - topView.getMeasuredHeight();
              topRight = topLeft + topView.getMeasuredWidth();
              topBottom = topTop + topView.getMeasuredHeight();
              bottomTop = bottomTop + mDy;
              bottomRight = bottomLeft + bottomView.getMeasuredWidth();
              bottomBottom = bottomTop + bottomView.getMeasuredHeight();
            }
            topView.layout(topLeft, topTop, topRight, topBottom);
            bottomView.layout(bottomLeft, bottomTop, bottomRight, bottomBottom);
            bottomTop = bottomView.getBottom();
          }
        } else {
          NodeView midView = findTreeNodeView(childNodes.get(mid));
          midView.layout(left, top - midView.getMeasuredHeight() / 2, left + midView.getMeasuredWidth(),
              top - midView.getMeasuredHeight() / 2 + midView.getMeasuredHeight());
          topTop = midView.getTop();
          bottomTop = midView.getBottom();
          for (int i = mid - 1; i >= 0; i--) {
            NodeView topView = findTreeNodeView(childNodes.get(i));
            NodeView bottomView = findTreeNodeView(childNodes.get(size - i - 1));
            topTop = topTop - mDy - topView.getMeasuredHeight();
            topRight = topLeft + topView.getMeasuredWidth();
            topBottom = topTop + topView.getMeasuredHeight();
            bottomTop = bottomTop + mDy;
            bottomRight = bottomLeft + bottomView.getMeasuredWidth();
            bottomBottom = bottomTop + bottomView.getMeasuredHeight();
            topView.layout(topLeft, topTop, topRight, topBottom);
            bottomView.layout(bottomLeft, bottomTop, bottomRight, bottomBottom);
            bottomTop = bottomView.getBottom();
          }
        }
      }
    }
  }
  
  private void moveNodeLayout(NodeView rootView, int dy) {
    Deque<TreeNode<String>> queue = new ArrayDeque<>();
    TreeNode<String> rootNode = rootView.getTreeNode();
    queue.add(rootNode);
    while (!queue.isEmpty()) {
      rootNode = (TreeNode<String>) queue.poll();
      rootView = findTreeNodeView(rootNode);
      int l = rootView.getLeft();
      int t = rootView.getTop() + dy;
      rootView.layout(l, t, l + rootView.getMeasuredWidth(), t + rootView.getMeasuredHeight());
      LinkedList<TreeNode<String>> childNodes = rootNode.getChildNodes();
      for (TreeNode<String> item : childNodes) {
        queue.add(item);
      }
    }
  }
  private void fatherChildCorrect(NodeView nv) {
    int count = nv.getTreeNode().getChildNodes().size();
    if (nv.getParent() != null && count >= 2) {
      TreeNode<String> tn = nv.getTreeNode().getChildNodes().get(0);
      TreeNode<String> bn = nv.getTreeNode().getChildNodes().get(count - 1);
      Log.i("see fc", nv.getTreeNode().getValue() + ":" + tn.getValue() + "," + bn.getValue());
      int topDr = nv.getTop() - findTreeNodeView(tn).getBottom() + mDy;
      int bnDr = findTreeNodeView(bn).getTop() - nv.getBottom() + mDy;
      //上移动
      ArrayList<TreeNode<String>> allLowNodes = mTree.getAllLowNodes(bn);
      ArrayList<TreeNode<String>> allPreNodes = mTree.getAllPreNodes(tn);
      for (TreeNode<String> low : allLowNodes) {
        NodeView view = findTreeNodeView(low);
        moveNodeLayout(view, bnDr);
      }
      for (TreeNode<String> pre : allPreNodes) {
        NodeView view = findTreeNodeView(pre);
        moveNodeLayout(view, -topDr);
      }
    }
  }
  
  private void drawTreeLine(Canvas canvas, TreeNode<String> root) {
    NodeView fatherView = findTreeNodeView(root);
    if (fatherView != null) {
      LinkedList<TreeNode<String>> childNodes = root.getChildNodes();
      for (TreeNode<String> node : childNodes) {
        drawLineToView(canvas, fatherView, findTreeNodeView(node));
        drawTreeLine(canvas, node);
      }
    }
  }
  
  private void drawLineToView(Canvas canvas, View from, View to) {
    Paint paint = new Paint();
    paint.setAntiAlias(true);
    paint.setStyle(Paint.Style.STROKE);
    float width = 2f;
    paint.setStrokeWidth(dp2px(mContext, width));
    paint.setColor(mContext.getResources().getColor(R.color.chelsea_cucumber));
    int top = from.getTop();
    int formY = top + from.getMeasuredHeight() / 2;
    int formX = from.getRight();
    int top1 = to.getTop();
    int toY = top1 + to.getMeasuredHeight() / 2;
    int toX = to.getLeft();
    Path path = new Path();
    path.moveTo(formX, formY);
    path.quadTo(toX - dp2px(mContext, 15), toY, toX, toY);
    canvas.drawPath(path, paint);
  }
  private NodeView findTreeNodeView(TreeNode<String> node) {
    NodeView v = null;
    for (NodeView view : mNodesViews) {
      if (view.getTreeNode() == node) {
        v = view;
        continue;
      }
    }
    return v;
  }
  public int dp2px(Context context, float dpVal) {
    int result = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, context.getResources()
        .getDisplayMetrics());
    return result;
  }
  public void setTree(Tree<String> tree) {
    this.mTree = tree;
    onAddNodeViews();
  }
  public Tree<String> getTree() {
    return mTree;
  }
}

粘贴代码后发现,没有什么好说了,对于读者来说,应该是一脸懵逼的。毕竟,那个位置是如何确定的呀,view和view的连线呀…… 对于整个View来说这是最难的,我也是探索了好久才得出结论的。首先,对于一个只有两层的树形来说,如图:

PQRS是基于F来计算的,之后分布。之后我就得到的方法如下:



  private void standardTreeChildLayout(NodeView rootView) {
    TreeNode<String> treeNode = rootView.getTreeNode();
    if (treeNode != null) {
      //所有的子节点
      LinkedList<TreeNode<String>> childNodes = treeNode.getChildNodes();
      int size = childNodes.size();
      int mid = size / 2;
      int r = size % 2;
      //基线
      //    b
      //  a-------
      //    c
      //
      int left = rootView.getRight() + mDx;
      int top = rootView.getTop() + rootView.getMeasuredHeight() / 2;
      int right = 0;
      int bottom = 0;
      if (size == 0) {
        return;
      } else if (size == 1) {
        NodeView midChildNodeView = findTreeNodeView(childNodes.get(0));
        top = top - midChildNodeView.getMeasuredHeight() / 2;
        right = left + midChildNodeView.getMeasuredWidth();
        bottom = top + midChildNodeView.getMeasuredHeight();
        midChildNodeView.layout(left, top, right, bottom);
      } else {
        int topLeft = left;
        int topTop = top;
        int topRight = 0;
        int topBottom = 0;
        int bottomLeft = left;
        int bottomTop = top;
        int bottomRight = 0;
        int bottomBottom = 0;
        if (r == 0) {//偶数
          for (int i = mid - 1; i >= 0; i--) {
            NodeView topView = findTreeNodeView(childNodes.get(i));
            NodeView bottomView = findTreeNodeView(childNodes.get(size - i - 1));
            if (i == mid - 1) {
              topTop = topTop - mDy / 2 - topView.getMeasuredHeight();
              topRight = topLeft + topView.getMeasuredWidth();
              topBottom = topTop + topView.getMeasuredHeight();
              bottomTop = bottomTop + mDy / 2;
              bottomRight = bottomLeft + bottomView.getMeasuredWidth();
              bottomBottom = bottomTop + bottomView.getMeasuredHeight();
            } else {
              topTop = topTop - mDy - topView.getMeasuredHeight();
              topRight = topLeft + topView.getMeasuredWidth();
              topBottom = topTop + topView.getMeasuredHeight();
              bottomTop = bottomTop + mDy;
              bottomRight = bottomLeft + bottomView.getMeasuredWidth();
              bottomBottom = bottomTop + bottomView.getMeasuredHeight();
            }
            topView.layout(topLeft, topTop, topRight, topBottom);
            bottomView.layout(bottomLeft, bottomTop, bottomRight, bottomBottom);
            bottomTop = bottomView.getBottom();
          }
        } else {
          NodeView midView = findTreeNodeView(childNodes.get(mid));
          midView.layout(left, top - midView.getMeasuredHeight() / 2, left + midView.getMeasuredWidth(),
              top - midView.getMeasuredHeight() / 2 + midView.getMeasuredHeight());
          topTop = midView.getTop();
          bottomTop = midView.getBottom();
          for (int i = mid - 1; i >= 0; i--) {
            NodeView topView = findTreeNodeView(childNodes.get(i));
            NodeView bottomView = findTreeNodeView(childNodes.get(size - i - 1));
            topTop = topTop - mDy - topView.getMeasuredHeight();
            topRight = topLeft + topView.getMeasuredWidth();
            topBottom = topTop + topView.getMeasuredHeight();
            bottomTop = bottomTop + mDy;
            bottomRight = bottomLeft + bottomView.getMeasuredWidth();
            bottomBottom = bottomTop + bottomView.getMeasuredHeight();
            topView.layout(topLeft, topTop, topRight, topBottom);
            bottomView.layout(bottomLeft, bottomTop, bottomRight, bottomBottom);
            bottomTop = bottomView.getBottom();
          }
        }
      }
    }
  }

之后等到的View情况如下:

说明我们还需要纠正。下面是纠正的探索,我精简一下结构如下情况:


发现:

B需要在E的上面;
D需要在I的下面;

就是EI要撑开ID的位置。之后我们可以先写这个算法,发现基本可以了。但是还是有问题,同层的还是会重合,只有我们又进行同层的纠正。发现好像解决了,其实还是不行,测试还是发现问题,对于单伸长还有问题,之后又是修改…………

最后发现……这里就不说了,就是自己要探索啦。

总结

最后我才发现了那个完善的纠正算法,就是代码了的。大家可以在我的github中找到我之前的TreeView中的那个位置算法探索。欢迎大家支持:https://github.com/owant/TreeView

您可能感兴趣的文章:Android使用Canvas绘制圆形进度条效果Android编程开发之在Canvas中利用Path绘制基本图形(圆形,矩形,椭圆,三角形等)Android编程之canvas绘制各种图形(点,直线,弧,圆,椭圆,文字,矩形,多边形,曲线,圆角矩形)Android中使用Canvas绘制南丁格尔玫瑰图(Nightingale rose diagram)Android canvas画图操作之切割画布实现方法(clipRect)Android开发之图形图像与动画(一)Paint和Canvas类学习Android自定义控件绘制基本图形基础入门Android自定义view绘制圆环占比动画android绘制圆形图片的两种方式示例Android自定义View实现shape图形绘制Android编程实现canvas绘制饼状统计图功能示例【自动适应条目数量与大小】


免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android树形控件绘制方法

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Android树形控件绘制方法

前言 作为一个开发者,日常会接触到很多优秀的软件,其实,或多或少会有这样的想法,我能不能开发一个自己软件,甚至办公软件都希望是Markdown的文本,为何用office?我常常想自己做一个IDE什么的。但是,很多只是想了一下就过了,一直没有
2022-06-06

Android树形控件的实现方法

在PC上我们已经习惯了树形控件,因为其可以清晰的展现各个节点之间的层次结果,但是在Android平台上,系统并没有提供这样一个控件,而是只有ListView。不过通过改写与ListView绑定的Adapter可以实现这样一个效果。 一个Li
2022-06-06

Android多级树形列表控件

我们开发app过程中,经常会碰到需要 多级列表展示的效果。而Android原生sdk中根本没有3级 4级甚至更多级别的列表控件。 所以我们就要自己去实现一个类似treeListView 的控件,下面这个是我项目中的一个效果图,可支持多级列表
2022-06-06

Android自定义控件(实现视图树绘制指示器)

之前写轮播条或者指示器的时候都是UI图里面直接有,这样的效果并不好,给用户的体验比较差,所以闲暇之余自己写了个指示器,可以展现出一个优雅的效果,当手指 当手指滑动的时候小圆点会跟着一点一点的滑动,当手指停下时,小红点也跟着停下来。首先我说说
2022-06-06

Android编程绘制圆形图片的方法

本文实例讲述了Android编程绘制圆形图片的方法。分享给大家供大家参考,具体如下: 效果图如下:第一步:新建RoundView自定义控件继承Viewpackage com.rong.activity; import com.rong.te
2022-06-06

Android自定义控件绘制基本图形基础入门

本文讲述绘制Android自定义各种图形效果,为自定义控件的入门篇 相关视频链接: Android自定义控件系列 http://edu.csdn.net/course/detail/3719/65396 Android视频全系列 ht
2022-06-06

Android Flutter利用CustomPaint绘制基本图形的方法

今天小编给大家分享一下Android Flutter利用CustomPaint绘制基本图形的方法的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们
2023-07-02

VB.NET绘制GDI图形的方法

本篇内容主要讲解“VB.NET绘制GDI图形的方法”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“VB.NET绘制GDI图形的方法”吧!在向大家详细介绍用VB.NET绘制GDI图形之前,首先让大家
2023-06-17

Android应用中绘制圆形头像的方法解析

要画这种圆形带阴影的头像,个人分解成三个图层 1,先画头像边缘的渐变RadialGradient gradient = new RadialGradient(j/2,k/2,j/2,new int[]{0xff5d5d5d,0xff5d5d
2022-06-06

纯CSS绘制三角形的方法

本篇内容主要讲解“纯CSS绘制三角形的方法”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“纯CSS绘制三角形的方法”吧!CSS绘制三角形的方法:1、定义个宽高为0的标签元素;2、使用“border
2023-06-14

HTML5中canvas绘制矩形的方法

小编给大家分享一下HTML5中canvas绘制矩形的方法,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!只是一个绘制图形的容器,除
2023-06-09

Qt中树形控件Tree Widget的使用方法有哪些

本篇内容主要讲解“Qt中树形控件Tree Widget的使用方法有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Qt中树形控件Tree Widget的使用方法有哪些”吧!Tree Widge
2023-06-21

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录