zoukankan      html  css  js  c++  java
  • [ZETCODE]wxWidgets教程五:布局管理

    本教程原文链接:http://zetcode.com/gui/wxwidgets/layoutmanagement/

    翻译:瓶哥

    日期:2013年12月4日星期三

    邮箱:414236069@qq.com

    主页:http://www.cnblogs.com/pingge/

    若有翻译错误或者歧义请联系我!

    一个典型的应用程序由各种各样的组件组成,这些组件被放置在容器组件内。一个程序员必须要管理应用程序的界面布局,这不是一个简单的工作,在wxWidgets里面我们有两个选择:

    1.使用绝对位置放置组件

    2.使用布局控件

    绝对位置

    程序员以像素单位去指定一个组件的位置和大小,当你使用绝对位置时,你会明白以下几点:

    1.当你缩放主窗口时,组件的位置和大小不会改变。

    2.程序在不同的平台上看起来不同(蹩脚的)。

    3.在你的程序中更改字体也许会破坏布局。

    4.如果你决定改变你的布局,你必须要完全重做你的布局,这将是单调乏味且浪费时间的工作。

    然而有些地方也许能够使用绝对位置去布局。例如,例如我的教程,我不想使我的例子变得太难理解,所以我经常使用绝对位置布局来解释一些主题。但是,在真正的应用程序中,程序员通常使用布局控件。

    在我们的例子中,我们有一个简单的文本编辑器的骨架,如果我们缩放主窗口,wxTextCtrl这个组件的大小不会像我们预期的那样去改变。

    调整大小之前

    调整大小之后

    absolute.h

    #include <wx/wx.h>
    
    class Absolute : public wxFrame
    {
    public:
        Absolute(const wxString & title);
    
        wxMenuBar * menubar;
        wxMenu * file;
        wxMenu * edit;
        wxMenu * help;
        wxTextCtrl * textctrl;
    };
    View Code

    absolute.cpp

    #include "absolute.h"
    
    Absolute::Absolute(const wxString & title)
            : wxFrame(NULL, -1, title, wxPoint(-1, -1), wxSize(250, 180))
    {
        wxPanel * panel = new wxPanel(this, wxID_ANY);
    
        menubar = new wxMenuBar;
        file = new wxMenu;
        edit = new wxMenu;
        help = new wxMenu;
    
        menubar->Append(file, _T("&File"));
        menubar->Append(edit, _T("&Edit"));
        menubar->Append(help, _T("&Help"));
    
        SetMenuBar(menubar);
    
        textctrl = new wxTextCtrl(panel, -1, _T(""), wxPoint(-1, -1), wxSize(250, 150));
    
        Centre();
    }
    View Code

    main.h

    #include <wx/wx.h>
    
    class MyApp : public wxApp
    {
    public:
        virtual bool OnInit();
    };
    View Code

    main.cpp

    #include "main.h"
    #include "absolute.h"
    
    IMPLEMENT_APP(MyApp)
    
    bool MyApp::OnInit()
    {
        Absolute * absolute = new Absolute(_T("Absolute"));
        absolute->Show(true);
    
        return true;
    }
    View Code

    这是一个使用绝对位置布局的例子,我们把一个wxTextCtrl组件放置在一个面板组件中。

    Textctrl = new wxTextCtrl(panel, -1, _T(""), wxPoint(-1, -1), wxSize(250, 150));

    我们在wxTextCtrl组件的构造函数中完成了绝对位置布局,在我们的例子中,我们把它放置在默认的位置,指定宽度250px和高度150px。

    使用布局控件

    wxWidgets里面的布局控件处理关于组件的位置的所有问题。

    我们能够在以下这些布局控件中选择:

    1.wxBoxSizer

    2.wxStaticBoxSizer

    3.wxGridSizer

    4.wxFlexGridSizer

    5.wxGridBagSizer

    调整大小之前

    调整大小之后

    sizer.h

    #include <wx/wx.h>
    
    class Sizer : public wxFrame
    {
    public:
        Sizer(const wxString & title);
    
        wxMenuBar * menubar;
        wxMenu * file;
        wxMenu * edit;
        wxMenu * help;
        wxTextCtrl * textctrl;
    };
    View Code

    sizer.cpp

    #include "sizer.h"
    
    Sizer::Sizer(const wxString & title)
         : wxFrame(NULL, -1, title, wxPoint(-1, -1), wxSize(250, 180))
    {
        menubar = new wxMenuBar;
        file = new wxMenu;
        edit = new wxMenu;
        help = new wxMenu;
    
        menubar->Append(file, _T("&File"));
        menubar->Append(edit, _T("&Edit"));
        menubar->Append(help, _T("&Help"));
        SetMenuBar(menubar);
    
        textctrl = new wxTextCtrl(this, -1, _T(""), wxPoint(-1, -1), wxSize(250, 150));
    
        Centre();
    }
    View Code

    main.h

    #include <wx/wx.h>
    
    class MyApp : public wxApp
    {
    public:
        virtual bool OnInit();
    };
    View Code

    main.cpp

    #include "main.h"
    #include "sizer.h"
    
    IMPLEMENT_APP(MyApp)
    
    bool MyApp::OnInit()
    {
        Sizer * sizer = new Sizer(_T("Sizer"));
        sizer->Show(true);
        
        return true;
    }
    View Code

    wxBoxSizer

    这个布局控件允许我们把多个组件放在一行或者一列上,我们能在一个布局控件中放入另一个布局控件。这种设计使得我们能够设计非常复杂的布局。

    wxBoxSizer(int orient)

    wxSIzerItem * Add(wxWindow * window, int proportion = 0, int flag = 0, int border = 0)

    参数orient可以是wxVERTICAL或者wxHORIZONTAL。通过Add()方法添加组件到wxBoxSizer内,为了能够更好的理解它,我们需要看它的参数。

    参数proportion定义了组件在指定的排列方向内自由缩放的比例,让我们假定有三个按钮,它们的proportion分别是0、1、2

    它们被添加进一个水平布局控件

    proportion = 0的按钮始终都不会改变,proportion = 2的按钮会比proportion = 1的按钮在水平尺寸上多缩放一倍的尺寸。

    有了flag参数你能够进一步设置wxBoxSizer内的组件的行为,我们能够控制两个组件之间的边界距离,我们可以在两个组件之间填充一些空白像素。为了显示边框,我们需要定义哪个方向上的边框需要使用。我们能够使用|运算符把它们组合起来,例如wxLEFT | wxBOTTOM,我们能够下面这些标志中选择:

    1.wxLEFT

    2.wxRIGHT

    3.wxBOTTOM

    4.wxTOP

    5.wxALL

    一个wxPanel组件周围的边框

    border.h

    #include <wx/wx.h>
    
    class Border : public wxFrame
    {
    public:
        Border(const wxString & title);
    };
    View Code

    border.cpp

    #include "border.h"
    
    Border::Border(const wxString & title)
          : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(250, 200))
    {
        wxColor col1, col2;
        col1.Set(_T("#4F5049"));
        col2.Set(_T("#EDEDED"));
    
        wxPanel * panel = new wxPanel(this, -1);
        panel->SetBackgroundColour(col1);
    
        wxPanel * midPan = new wxPanel(panel, wxID_ANY);
        midPan->SetBackgroundColour(col2);
    
        wxBoxSizer * vbox = new wxBoxSizer(wxVERTICAL);
        vbox->Add(midPan, 1, wxEXPAND | wxALL, 20);
        panel->SetSizer(vbox);
    
        Centre();
    }
    View Code

    main.h

    #include <wx/wx.h>
    
    class MyApp : public wxApp
    {
    public:
        virtual bool OnInit();
    };
    View Code

    main.cpp

    #include "main.h"
    #include "border.h"
    
    IMPLEMENT_APP(MyApp)
    
    bool MyApp::OnInit()
    {
        Border * border = new Border(_T("Border"));
        border->Show(true);
    
        return true;
    }
    View Code

    在这个例子中,我们创建了两个panels,第二个panel在其自身周围有一圈空白。

    Box->Add(midPan, 1, wxEXPAND | wxALL, 20);

    我们在midPan这个panel周围放置了宽度为20px的边框,wxALL表示边框适用于全部四个方向。如果我们使用wxEXPAND标识,这个组件会在允许的边框内扩展到最大。

    最后,我们也可以定义组件的对齐标识,我们使用以下标识去定义:

    1.wxALIGN_LEFT

    2.wxALIGN_RIGHT

    3.wxALIGN_TOP

    4.wxALIGN_BOTTOM

    5.wxALIGN_CENTER_VERTICAL

    6.wxALIGN_CENTER_HORIZONTAL

    7.wxALIGN_CENTER

    接下来我们把两个按钮放置到窗口的右下角:

    align.h

    #include <wx/wx.h>
    
    class Align : public wxFrame
    {
    public:
        Align(const wxString & title);
    };
    View Code

    align.cpp

    #include "align.h"
    
    Align::Align(const wxString & title)
         : wxFrame(NULL, -1, title, wxPoint(-1, -1), wxSize(300, 200))
    {
        wxPanel * panel = new wxPanel(this, -1);
    
        wxBoxSizer * vbox = new wxBoxSizer(wxVERTICAL);
        wxBoxSizer * hbox1 = new wxBoxSizer(wxHORIZONTAL);
        wxBoxSizer * hbox2 = new wxBoxSizer(wxHORIZONTAL);
    
        wxButton * ok = new wxButton(panel, wxID_ANY, _T("OK"));
        wxButton * cancel = new wxButton(panel, wxID_ANY, _T("Cancel"));
    
        hbox1->Add(new wxPanel(panel, wxID_ANY));
        hbox2->Add(ok);
        hbox2->Add(cancel);
    
        vbox->Add(hbox1, 1, wxEXPAND);
        vbox->Add(hbox2, 0, wxALIGN_RIGHT | wxRIGHT | wxBOTTOM, 10);
        panel->SetSizer(vbox);
    
        Centre();
    }
    View Code

    main.h

    #include <wx/wx.h>
    
    class MyApp : public wxApp
    {
    public:
        virtual bool OnInit();
    };
    View Code

    main.cpp

    #include "main.h"
    #include "align.h"
    
    IMPLEMENT_APP(MyApp)
    
    bool MyApp::OnInit()
    {
        Align * align = new Align(_T("Align"));
        align->Show(true);
    
        return true;
    }
    View Code

    我们创建了三个布局控件,一个垂直控件和两个水平控件。我们把这两个水平布局控件放置到垂直布局控件中。

    Hbox->Add(new wxPanel(panel, wxID_ANY));

    Vbox->Add(hbox, 1, wxEXPAND);

    我们把一个wxPanel放置在第一个水平控件中,我们把缩放因子设置为1并且设置了wxEXPAND标识,这样做这个布局控件就会占据除了hbox2之外的所有空间。

    Vbox->Add(hbox2, 0, wxALIGN_RIGHT | wxRIGHT | wxBOTTOM, 10);

    我们把两个按钮放置在hbox2这个控件中,在hbox2中的控件是右对齐排列的,而且我们在这两个按钮的底部和右边放置了宽度为10px的空白元素。

    Go To Class

    在接下来的例子中,我们将介绍几个重要的观念。

    gotoclass.h

    #include <wx/wx.h>
    
    class GotoClass : public wxFrame
    {
    public:
        GotoClass(const wxString & title);
    };
    View Code

    gotoclass.cpp

    #include "gotoclass.h"
    
    GotoClass::GotoClass(const wxString & title)
             : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(450, 400))
    {
        wxPanel * panel = new wxPanel(this, wxID_ANY);
    
        /**< Main sizer */
        wxBoxSizer * vbox = new wxBoxSizer(wxVERTICAL);
    
        /**< hbox1 */
        wxBoxSizer * hbox1 = new wxBoxSizer(wxHORIZONTAL);
        //(*
        wxStaticText * st1 = new wxStaticText(panel, wxID_ANY, _T("CLass Name"));
        hbox1->Add(st1, 0, wxRIGHT, 8);
        wxTextCtrl * tc = new wxTextCtrl(panel, wxID_ANY);
        hbox1->Add(tc, 1);
        vbox->Add(hbox1, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, 10);
        vbox->Add(-1, 10); // 10 pix space
        //*)
    
        /**< hbox2 */
        wxBoxSizer * hbox2 = new wxBoxSizer(wxHORIZONTAL);
        //(*
        wxStaticText * st2 = new wxStaticText(panel, wxID_ANY, _T("Matching classes"));
        hbox2->Add(st2, 0);
        vbox->Add(hbox2, 0, wxLEFT | wxTOP, 10);
        vbox->Add(-1, 10);
        //*)
    
        /**< hbox3 */
        wxBoxSizer * hbox3 = new wxBoxSizer(wxHORIZONTAL);
        //(*
        wxTextCtrl * tc2 = new wxTextCtrl(panel, wxID_ANY, _T(""),
                                          wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);
        hbox3->Add(tc2, 1, wxEXPAND);
        vbox->Add(hbox3, 1, wxLEFT | wxRIGHT | wxEXPAND, 10);
        vbox->Add(-1, 25);
        //*)
    
        /**< hbox4 */
        wxBoxSizer * hbox4 = new wxBoxSizer(wxHORIZONTAL);
        //(*
        wxCheckBox * cb1 = new wxCheckBox(panel, wxID_ANY, _T("Case Sensitive"));
        hbox4->Add(cb1);
        wxCheckBox * cb2 = new wxCheckBox(panel, wxID_ANY, _T("Nexted Classes"));
        hbox4->Add(cb2, 0, wxLEFT, 10);
        wxCheckBox * cb3 = new wxCheckBox(panel, wxID_ANY, _T("Non-Project Classes"));
        hbox4->Add(cb3, 0, wxLEFT, 10);
        vbox->Add(hbox4, 0, wxLEFT, 10);
        vbox->Add(-1, 25);
        //*)
    
        /**< hbox5 */
        wxBoxSizer * hbox5 = new wxBoxSizer(wxHORIZONTAL);
        //(*
        wxButton * btn1 = new wxButton(panel, wxID_ANY, _T("OK"));
        hbox5->Add(btn1, 0);
        wxButton * btn2 = new wxButton(panel, wxID_ANY, _T("Close"));
        hbox5->Add(btn2, 0, wxLEFT | wxBOTTOM, 5);
        vbox->Add(hbox5, 0, wxALIGN_RIGHT | wxRIGHT, 10);
        //*)
    
        panel->SetSizer(vbox);
    }
    View Code

    main.h

    #include <wx/wx.h>
    
    class MyApp : public wxApp
    {
    public:
        virtual bool OnInit();
    };
    View Code

    main.cpp

    #include "main.h"
    #include "gotoclass.h"
    
    IMPLEMENT_APP(MyApp)
    
    bool MyApp::OnInit()
    {
        GotoClass * gotoclass = new GotoClass(_T("GotoClass"));
        gotoclass->Show(true);
    
        return true;
    }
    View Code

    这是一个综合使用wxBoxSizer的例子,布局得比较紧凑,我们创建了一个垂直布局控件,然后我们把5个水平布局控件添加进去。

    Vbox->Add(hbox3, 1, wxLEFT | wxRIGHT | wxEXPAND, 10);

    Vbox->Add(-1, 25);

    我们已经知道我们可以使用flag参数和border参数控制控件之间的间距。但是有一点很不自然,在Add()方法中我们只能给所有的方向指定同一个边界宽度。在我们的例子中,我们在右和左边缘给出10px空白,但是我们不能给底部指定25px的空白,我们所能做的只是给底部10px空白或者省略wxBOTTOM给底部0px空白。所以我们需要另一种重载的Add()方法添加一些额外的空白。

    Vbox->Add(hbox5, 0, wxALIGN_RIGHT | wxRIGHT, 10);

    我们把两个按钮放置到窗体的右下角,我们是如何做到的?要达到这种目的,需要注意三样东西,proportion、align flag、wxEXPAND flag,proportion参数一定要等于0,这样两个按钮就不会改变它们的大小,当我们缩放我们的主窗口的时候我们不能指定wxEXPAND标识。最后,我们必须指定wxALIGN_RIGHT标识,这个水平布局控件会从主窗体的左边扩展到右边,所以如果我们指定wxALIGN_RIGHT标识,两个按钮会被放置在右边,这恰好是我们期望的。

    wxGridSizer

    wxGridSizer把控件布局在一个格子中,每一个格子都有相同的大小。

    wxGridSizer(int rows, int cols, int vgap, int hgap);

    在构造函数中我们指定网格的行数和列数和每个格子的垂直、水平间距。

    在我们的例子中我们建立了一个计算器的框架,这是一个介绍wxGridSizer的完美的例子。

    gridsizer.h

    #include <wx/wx.h>
    
    class GridSizer : public wxFrame
    {
    public:
        GridSizer(const wxString & title);
    
        wxBoxSizer * sizer;
        wxGridSizer * gs;
        wxTextCtrl * display;
    };
    View Code

    gridsizer.cpp

    #include "gridsizer.h"
    
    GridSizer::GridSizer(const wxString & title)
             : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(270, 220))
    {
        sizer = new wxBoxSizer(wxVERTICAL);
    
        display = new wxTextCtrl(this, wxID_ANY, _T(""), wxDefaultPosition, wxDefaultSize, wxTE_RIGHT);
    
        sizer->Add(display, 0, wxEXPAND | wxTOP | wxBOTTOM, 4);
    
        gs = new wxGridSizer(5, 4, 3, 3);
    
        gs->Add(new wxButton(this, -1, _T("Cls")), 0, wxEXPAND);
        gs->Add(new wxButton(this, -1, _T("Bck")), 0, wxEXPAND);
        gs->Add(new wxStaticText(this, -1, _T("")), 0, wxEXPAND);
        gs->Add(new wxButton(this, -1, _T("Close")), 0, wxEXPAND);
    
        gs->Add(new wxButton(this, -1, _T("7")), 0, wxEXPAND);
        gs->Add(new wxButton(this, -1, _T("8")), 0, wxEXPAND);
        gs->Add(new wxButton(this, -1, _T("9")), 0, wxEXPAND);
        gs->Add(new wxButton(this, -1, _T("/")), 0, wxEXPAND);
        gs->Add(new wxButton(this, -1, _T("4")), 0, wxEXPAND);
        gs->Add(new wxButton(this, -1, _T("5")), 0, wxEXPAND);
        gs->Add(new wxButton(this, -1, _T("6")), 0, wxEXPAND);
        gs->Add(new wxButton(this, -1, _T("*")), 0, wxEXPAND);
        gs->Add(new wxButton(this, -1, _T("1")), 0, wxEXPAND);
        gs->Add(new wxButton(this, -1, _T("2")), 0, wxEXPAND);
        gs->Add(new wxButton(this, -1, _T("3")), 0, wxEXPAND);
        gs->Add(new wxButton(this, -1, _T("-")), 0, wxEXPAND);
        gs->Add(new wxButton(this, -1, _T("0")), 0, wxEXPAND);
        gs->Add(new wxButton(this, -1, _T(".")), 0, wxEXPAND);
        gs->Add(new wxButton(this, -1, _T("=")), 0, wxEXPAND);
        gs->Add(new wxButton(this, -1, _T("+")), 0, wxEXPAND);
    
        sizer->Add(gs, 1, wxEXPAND);
        SetSizer(sizer);
    
        SetMinSize(wxSize(270, 220));
    
        Centre();
    }
    View Code

    main.h

    #include <wx/wx.h>
    
    class MyApp : public wxApp
    {
    public:
        virtual bool OnInit();
    };
    View Code

    main.cpp

    #include "main.h"
    #include "gridsizer.h"
    
    IMPLEMENT_APP(MyApp)
    
    bool MyApp::OnInit()
    {
        GridSizer * gs = new GridSizer(_T("GridSizer"));
        gs->Show(true);
    
        return true;
    }
    View Code

    在我们的例子中,我们为wxFrame建立一个垂直布局控件,我们把一个静态文本和一个网格布局控件放进垂直布局控件。

    注意我们是如何在Bck和Close按钮之间添加空白的,我们只是简单的添加了一个空的wxStaticText,这是一个很常用的技巧。

    gs->Add(new wxButton(this, -1, _T("Cls")), 0, wxEXPAND);

    我们调用Add()方法许多次,组件被顺序放置进网格布局控件,第一行被放满,第二行,第三行同样。

    wxFlexGridSizer

    这个布局控件和wxGridSizer有点相似,它同样把组件布局到有两个尺寸的格子中,但是它添加了一些灵活性,wxGridSizer的格子都是相同大小的,在wxFlexSizer中所有的格子在一行上有相同的高度,一列上有相同的宽度,但是所有的行和列不一定有相同的高度和宽度。

    wxFlexGridSize(int rows, int cols, int vgap, int hgap);

    rows和cols指定了布局控件中的行数和列数。vgap和hgap在组件之间两个方向上添加了一些空白。

    许多时候程序员需要开发一个对话框用来进行数据录入火修改,我发现wxFlexGridSIzer很适合这个任务,一个程序员可以使用这个布局控件轻松的创建一个对话框,使用wxGridSizer或许同样可以完成这个任务,但是这样会影响美观,因为每一个网格的大小都一样会显得很不自然。

    flexgridsizer.h

    #include <wx/wx.h>
    
    class FlexGridSizer : public wxFrame
    {
    public:
        FlexGridSizer(const wxString & title);
    };
    View Code

    flexgridsizer.cpp

    #include "flexgridsizer.h"
    
    FlexGridSizer::FlexGridSizer(const wxString & title)
                 : wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(270, 220))
    {
        wxPanel * panel = new wxPanel(this, wxID_ANY);
    
        wxBoxSizer * hbox = new wxBoxSizer(wxHORIZONTAL);
    
        wxFlexGridSizer * fgs = new wxFlexGridSizer(3, 2, 9, 25);
    
        wxStaticText * thetitle = new wxStaticText(panel, wxID_ANY, _T("Title"));
        wxStaticText * author   = new wxStaticText(panel, wxID_ANY, _T("Author"));
        wxStaticText * review   = new wxStaticText(panel, wxID_ANY, _T("Review"));
    
        wxTextCtrl * tc1 = new wxTextCtrl(panel, wxID_ANY);
        wxTextCtrl * tc2 = new wxTextCtrl(panel, wxID_ANY);
        wxTextCtrl * tc3 = new wxTextCtrl(panel, wxID_ANY, _T(""),
                                          wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE);
    
        fgs->Add(thetitle);
        fgs->Add(tc1, 1, wxEXPAND);
        fgs->Add(author);
        fgs->Add(tc2, 1, wxEXPAND);
        fgs->Add(review, 1, wxEXPAND);
        fgs->Add(tc3, 1, wxEXPAND);
    
        fgs->AddGrowableRow(2, 1);
        fgs->AddGrowableCol(1, 1);
    
        hbox->Add(fgs, 1, wxALL | wxEXPAND, 15);
        panel->SetSizer(hbox);
        Centre();
    }
    View Code

    main.h

    #include <wx/wx.h>
    
    class MyApp : public wxApp
    {
    public:
        virtual bool OnInit();
    };
    View Code

    main.cpp

    #include "main.h"
    #include "flexgridsizer.h"
    
    IMPLEMENT_APP(MyApp)
    
    bool MyApp::OnInit()
    {
        FlexGridSizer * fgs = new FlexGridSizer(_T("FlexGridSizer"));
        fgs->Show(true);
    
        return true;
    }
    View Code

    在我们的例子中我们创建了一个简单的对话框,它可以用来把数据插入到数据库。

    wxBoxSizer * hbox = new wxBoxSizer(wxHORIZONTAL);

    Hbox->Add(fgs, 1, wxALL | wxEXPAND, 15);

    我们创建了一个水平布局控件,用来在组件的边界制造出15px的空白。

    fgs->Add(thetitle);

    我们像使用wxGridSizer一样把组件添加进布局控件。

    fgs->AddGrowableRow(2, 1);

    fgs->AddGrowableCol(1, 1);

    我们让第三行和第二列成为可扩展的,这样当主窗口缩放时,第三个多行文本控件就可以自动扩展。前两个文本控件会在水平方向自动扩展,第三个会在两个方向自动扩展,我们必须使用wxEXPAND确保它们正常工作。

    这一章的wxWidgets教程介绍了如何管理布局。

  • 相关阅读:
    储存过程、游标与触发器
    linux系统安装mysql5.7.22
    为什么实体类要实现序列化
    jsp的语法
    jsp的原理
    转发和重定向的区别
    SpringCloud——简介,5大组件
    Java——线程,并发包(Lock、线程池)
    Spring Data JPA——基本使用
    SpringBoot
  • 原文地址:https://www.cnblogs.com/pingge/p/3458387.html
Copyright © 2011-2022 走看看