写点什么

Android 实现无序树形结构图,类似思维导图和级联分层图 (无序,随机位置)

作者:芝麻粒儿
  • 2022 年 7 月 17 日
  • 本文字数:4878 字

    阅读完需:约 16 分钟

Android实现无序树形结构图,类似思维导图和级联分层图(无序,随机位置)

👉关于作者

众所周知,人生是一个漫长的流程,不断克服困难,不断反思前进的过程。在这个过程中会产生很多对于人生的质疑和思考,于是我决定将自己的思考,经验和故事全部分享出来,以此寻找共鸣!!!专注于 Android/Unity 和各种游戏开发技巧,以及各种资源分享(网站、工具、素材、源码、游戏等)


👉即将学会

不使用什么框架,就用基础的代码,实现层级导图。

👉背景

今天七夕快乐呀,小芝🙎和小空🙈今天一起吃了汉堡,很好吃哦!

👉实践过程

最近公司的医疗项目需要用到这个,需求是病例之间的状态跳转,医学方面比如急救的时候有一些比较成熟的操作流程和步骤,紧急情况下是可以略过的,所以软件就要模拟出来状态的跳转。而项目展示的状态就像亲属关系图类似。



先看下我们的效果吧



刚开始的时候,小空脑子也是空空的,毫无头绪,在咨询了同事其他端实现的思路后,转为 Android 的实现技术。

大致思路如下:

  1. 如何添加这些控件

  2. 如何位置随机

  3. 画线和画不封闭的箭头

  4. 扩展性

有了这些想法,我们就要实际动手操作了:这些仍逃不出自定义的范围:

public class BLzgView extends RelativeLayout {	private Button blzg_btn;	private TextView blzg_title_tv, blzg_describe_tv; 	public BLzgView(Context context) {		this(context,null);	} 	public BLzgView(Context context, AttributeSet attrs) {		super(context, attrs);		LayoutInflater.from(context).inflate(R.layout.view_blzg, this, true);		blzg_describe_tv=(TextView) findViewById(R.id.blzg_describe_tv);		blzg_title_tv=(TextView) findViewById(R.id.blzg_title_tv);		blzg_btn=(Button) findViewById(R.id.blzg_btn);	}     public void setTitleText(String tString){    	 blzg_title_tv.setText(tString);     }      public void setDecribeText(String string){    	 blzg_describe_tv.setText(string);     }     public void setBtnClickListener(OnClickListener onClickListener){    	 if (onClickListener!=null) {			blzg_btn.setOnClickListener(onClickListener);		}     }     //更改btn背景          //如果需要其他的需求再接着写}
复制代码

下面是我们的布局文件,1 个 Button 和 2 个 TextView:

<?xml version="1.0" encoding="utf-8"?><merge xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="120dp"    android:layout_height="70dp"    android:layout_gravity="center_horizontal">     <LinearLayout        android:layout_width="match_parent"        android:layout_height="70dp"        android:layout_gravity="center_horizontal"        android:orientation="vertical">        <Button            android:id="@+id/blzg_btn"            android:layout_width="120dp"            android:layout_height="70dp"            android:layout_gravity="center_horizontal"            android:background="@mipmap/xxk_n" />    </LinearLayout>     <TextView        android:id="@+id/blzg_title_tv"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginTop="4dp"        android:gravity="center_horizontal"        android:maxLines="1"        android:text="@string/blzg_title"        android:textColor="#ffffff" />     <TextView        android:id="@+id/blzg_describe_tv"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginTop="24dp"        android:gravity="center_horizontal"        android:maxLines="2"        android:text="内容描述"        android:textColor="@color/black" /> </merge>
复制代码

有了这些基础控件之后,就开始考虑不断创建添加进去的问题了,其实不管是位置随机还是固定位置都可以实现,具体看项目的需求了,小空做的项目应为要和其他端保持一致,所以给定了具体的画布大小和位置。

public class UnOrderTree extends Activity {    private DrawGeometryView line_view[] = new DrawGeometryView[30];    private RelativeLayout.LayoutParams[] layoutParams = new RelativeLayout.LayoutParams[15];    private RelativeLayout.LayoutParams[] layoutParams1 = new RelativeLayout.LayoutParams[15];    private BLzgView[] bLzgViews = new BLzgView[15];    private RelativeLayout insertLayout;     @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_unordertree);        insertLayout = (RelativeLayout) findViewById(R.id.layout_zone);        initData();//初始化数据    }     private int start_line_x = 0, start_line_y = 0;    private int topMargin = 0, leftMargin = 0;     private void initData() {        for (int i = 0; i < 6; i++) { // 开始绘制            topMargin = new Random().nextInt(20) * 30;            leftMargin = new Random().nextInt(10) * 40;            initUnOrder(start_line_x, start_line_y, topMargin, leftMargin, i, 0, 0, 2, 1, "");            start_line_x = leftMargin;            start_line_y = topMargin;        }    }     private void initUnOrder(int start_x, int start_y, int topMargin, int leftMargin, int i,                             int line_start_x, int line_start_y, int tree_tier, int isleft, String data) {        bLzgViews[i] = new BLzgView(this);        bLzgViews[i].setBtnClickListener(new View.OnClickListener() {             @Override            public void onClick(View v) {                Toast.makeText(UnOrderTree.this, "功能快速开发中,敬请期待", Toast.LENGTH_SHORT).show();            }         });         bLzgViews[i].setTitleText("标题" + i);        bLzgViews[i].setDecribeText("内容" + i);        ScaleAnimation animation = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, Animation.RELATIVE_TO_SELF, 0.5f,                Animation.RELATIVE_TO_SELF, 0.5f);        animation.setInterpolator(new BounceInterpolator());        animation.setStartOffset(100);// 动画秒数。        animation.setFillAfter(true);        animation.setDuration(700);        bLzgViews[i].startAnimation(animation);        layoutParams[i] = new RelativeLayout.LayoutParams(120, 70); // 大小        layoutParams[i].topMargin = topMargin;        layoutParams[i].leftMargin = leftMargin; // 设置的按钮位置        insertLayout.addView(bLzgViews[i], layoutParams[i]);        if (i != 0) {   //第一个不用画线(画线方式为:当前的坐标去找上一个坐标,之后连线)            line_view[i] = new DrawGeometryView(this, start_x + 60, start_y + 70,                    leftMargin + 40, topMargin, "线条", isleft);            layoutParams1[i] = new RelativeLayout.LayoutParams(800, 800);            line_view[i].invalidate();            layoutParams1[i].topMargin = 0;// line_y-600;//Math.min(line_y+100,button_y+100            layoutParams1[i].leftMargin = 0;// line_x+300;            insertLayout.addView(line_view[i], layoutParams1[i]);        }    }}
复制代码

从上段代码可以看出,不管是添加状态还是连线都是靠的基本的 addView,然后利用数组初始多个最开始咱们自定义的那个 View,坐标的位置就是 topMargin 和 leftMargin 的值(相对于屏幕左上角,即给的坐标)。思路确实是挺另类的。



接着就是划线了,当走到这的时候,我们就已经知道了各个自定义 View 的坐标位置,毕竟靠的是上面的 addView,那么画线的起点和终点我们稍加计算也就很明确了。

public class DrawGeometryView extends View {    private int beginx = 0;    private int beginy = 0;    private int stopx = 100;    private int stopy = 100;     /**     * @param context     * @param attrs     */    public DrawGeometryView(Context context, AttributeSet attrs) {        super(context, attrs);    }     /**     * @param context     */    public DrawGeometryView(Context context, int beginx, int beginy, int stopx, int stopy, String word, int isleft) {        super(context);        this.beginx = beginx;        this.beginy = beginy;        this.stopx = stopx;        this.stopy = stopy;    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        Paint redPaint = new Paint(); // 红色画笔        redPaint.setAntiAlias(true); // 抗锯齿效果,显得绘图平滑        redPaint.setColor(Color.BLACK); // 设置画笔颜色        redPaint.setStrokeWidth(2.0f);// 设置笔触宽度        redPaint.setStyle(Style.STROKE);// 设置画笔的填充类型(完全填充)        redPaint.setTextSize(50);// 字体        Path mPath = new Path();        mPath.reset();        // 起点        mPath.moveTo(beginx, beginy);        // 贝塞尔曲线        // mPath.cubicTo(beginx+80, beginy, beginx+80, stopy,stopx-100, stopy);        mPath.cubicTo(beginx, beginy + 30, stopx, stopy - 50, stopx, stopy);        // // 画直线        // mPath.lineTo(stopx, stopy);        // 画path        canvas.drawPath(mPath, redPaint);        //下面是画箭头线的        Paint left_paint = new Paint();        Paint right_paint = new Paint();        Path left_path = new Path();        Path right_path = new Path();        left_path.reset();        right_path.reset();        left_paint.setAntiAlias(true);        left_paint.setColor(Color.BLACK);        right_paint.setAntiAlias(true);        right_paint.setColor(Color.BLACK);        left_paint.setStrokeWidth(2.0f);        right_paint.setStrokeWidth(2.0f);        left_paint.setStyle(Style.STROKE);        right_paint.setStyle(Style.STROKE);        left_path.moveTo(stopx, stopy);        right_path.moveTo(stopx, stopy);        left_path.quadTo(stopx - 3, stopy - 3, stopx - 6, stopy - 6);        right_path.quadTo(stopx + 3, stopy - 3, stopx + 6, stopy - 6);        canvas.drawPath(left_path, left_paint);        canvas.drawPath(right_path, right_paint);    }}
复制代码

画线使用自定义 View 的 canvas,drawPath 这些就好了,为了让线条看起来更加的丝滑性感,小空加上了贝塞尔曲线,还有线头(其实只要在起点重点坐标点延长出两个线段就搞定了)

大致的效果出来了;恩 需求解决!若您有相近的需求或解决思路,欢迎在下方留下地址!

另外,既然是自定义的 View,那加动画肯定没问题;除了这些,还能更完善:比如箭头的平滑,大控件的各种事件等等,暂时待续.....

👉其他

📢作者:小空和小芝中的小空

📢转载说明-务必注明来源:https://www.infoq.cn/profile/DB2492B85795C4/publish

📢这位道友请留步☁️,我观你气度不凡,谈吐间隐隐有王者霸气💚,日后定有一番大作为📝!!!旁边有点赞👍收藏🌟今日传你,点了吧,未来你成功☀️,我分文不取,若不成功⚡️,也好回来找我。

发布于: 刚刚阅读数: 3
用户头像

芝麻粒儿

关注

尺有所短;寸有所长。 2020.08.29 加入

👑CSDN博客专家-华为云享专家-Android/Unity领域优质作者 🏅目前在模拟医学行业做Android/Unity双端开发 🏆微信公众号:【空名先生】 🏆QQ交流群:204918251或877807592

评论

发布
暂无评论
Android实现无序树形结构图,类似思维导图和级联分层图(无序,随机位置)_android_芝麻粒儿_InfoQ写作社区