- 浏览: 3119679 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (372)
- diy (4)
- linux (19)
- 杂项 (30)
- Swing (21)
- Java2D (21)
- Java3D (5)
- JavaIO (9)
- Java通讯 (5)
- Java设计模式 (3)
- Java多媒体 (0)
- Java算法 (7)
- Spring&EJB (29)
- Javaoffice (4)
- web前端 (23)
- javascript (1)
- php基础 (1)
- eclipse (3)
- 网站相关 (3)
- Apache (4)
- seo (12)
- db (28)
- server (3)
- api (4)
- 异常 (12)
- 计算机网络词汇表 (3)
- 随想录 (52)
- 收藏 (17)
- 犹太人的智慧 (3)
- 多线程 (1)
- jfreechart (7)
- Test (1)
- SorLib (30)
- ruby on rails (1)
最新评论
-
houyutao:
二三四都是错的空字符串也被匹配,*应该改成+
Java中判断字符串是否为数字的五种方法 -
mingyun:
但是 java.util.ArrayList 实现了 remo ...
java.lang.UnsupportedOperationException 解决方案 -
mingyun:
1.因为 Arrays.asList 返回的是 Arrays内 ...
java.lang.UnsupportedOperationException 解决方案 -
leolu007:
用java8新特性String testStr = " ...
java.lang.UnsupportedOperationException 解决方案 -
zhaohuaxishiwzw:
我之前所在的项目就是日本一家证券公司的项目。完全使用的是j2e ...
抛弃EJB(EJB2.0,EJB3.0,EJB4.0)
When you hear comparisons between AWT and Swing components, one of the first points mentioned is that Swing is lightweight. What this essentially means is that there are no real native controls "behind" Swing buttons, internal frames, and menus. Everything is controlled in pure Java, including rendering and event handling. While this provides a much more flexible way to create truly platform-independent components, the task of creating a custom Swing component that has a consistent look across all platforms and look-and-feels is not an easy one. This article walks through the process of creating a new Swing component of medium complexity and highlights the important points, steps, and pitfalls along the way.
Basic Building Blocks
The Swing architecture overview provides an excellent high-level overview of the architectural decisions that were made during the development of Swing. Although it will take slightly more work to create a new component following the rules outlined in this article, the resulting code will be much easier to maintain, since it will adhere to the core Swing principles without reinventing the wheel. At the first look you might be tempted to throw everything together in one single class that will provide the external API, the model handling (state and notifications), event handling, layout, and painting. All these, however, belong in separate classes that follow the modified MVC (model-view-controller) architecture that will make your component codebase much easier to maintain and extend in the long run.
The main building blocks of all core Swing components are:
* The component class itself, which provides an API for creating, changing, and querying the component basic state.
* The model interface and the model default implementation(s) that handle the component business logic and change notifications.
* The UI delegate that handles component layout, event handling (mouse and keyboard), and painting.
This article will illustrate the process of creating a custom component that is based on the new view slider from Microsoft Windows Vista OS Explorer (see Figure 1). While this component looks very much like a slider embedded in a pop-up menu, it has new features that are not available on a regular JSlider. First, it has control points that have associated icons and labels. In addition, while some ranges are contiguous (like Small Icons-Medium Icons) and allow continuous resizing of the file icons, other ranges are discrete (like Tiles-Details). When the value is in one of these ranges, the slider thumb can be only at control points, and not inside the range.
Figure 1. View slider in Microsoft Windows Vista OS
The Component Class: UI Delegate Plumbing
The first class for a custom component is the component API itself. The API should be as simple as possible and delegate most of the business logic to the model (see the next section). In addition to the API, you should add the boilerplate code for setting the proper UI delegate (described in detail in the " Enhancing Swing Applications" article). At the barest level, this code should look like this:
It is very important to provide a fallback UI delegate that will handle the component painting, layout, and event handling when the currently installed look and feel doesn't provide a special UI delegate.
The Model Interface
This is, perhaps, the most important class for a custom component. It represents the business side of your component. The model interface should not contain any painting-related methods (such as setFont or getPreferredSize). For our specific component, we follow the LinearGradientPaint API and define the model as a sequence of ranges:
The model API to set and query the model ranges:
In addition, the model should provide the API to set and get the current value. The Value class points inside a Range:
And the model API provides the getter and setter for the current value:
The last piece of model interface contains methods for adding and removing ChangeListeners. This follows the model interfaces from core Swing components (see BoundedRangeModel):
The Model Implementation
The model implementation is pretty straightforward and follows the DefaultBoundedRangeModel. The change listeners are stored as an EventListenerList. The change to the model value results in a ChangeEvent being fired:
Note that as with all core Swing components, we iterate over the listener list from the end. This way, if a listener decides to remove itself inside the stateChanged implementation, we'll still iterate over all registered listeners exactly once. The implementation of the value-related methods is very straightforward with checking of the validity of the passed value and defensive copying of the slider ranges array (so that changes in malicious application code won't affect the model).
The Model Unit Tests
While unit testing the UI components can be difficult, the model should be thoroughly tested--remember that the loss of a pixel is nothing compared to the loss of a trailing zero in the model, especially if that zero multiplies an automatic payment by 10. Be sure to test your default model implementation with such simple tests as:
The Component Class: API
Going back to the component class, we can add the missing APIs for creation of the control itself and getting its model. In addition, some of the UI-related configuration (such as icons and label text) is stored in the component class itself (this does not belong in the model). The component constructor follows that of LinearGradientPaint, throwing exceptions on null or non-matching parameters:
The implementation is quite straightforward. First, it checks that the arrays are not null and are of matching lengths. Then, it creates a DefaultFlexiRangeModel and sets its ranges. Then, it defensively copies the icons and text arrays, and finally calls the updateUI method that installs and initializes the look-and-feel delegate. The additional APIs that are implemented in this class are:
Note that the last two are simply syntactic sugar and pass the calls to the underlying model. These are provided as convenience methods only. The first three methods are mainly for the UI delegate, but can be used by the application code as well.
UI Delegate
If the model interface is the most important part of writing a custom control, the UI delegate is in most cases the most difficult. The main question is: how do you write painting logic that will produce consistent results under all (existing and future) target look and feels? Sometimes, this will be impossible to do without writing custom UI delegates for each one of the target look and feels (as is done for many components in the SwingX project). However, in some cases you'll find that you can emulate the visual part of your custom component by combining smaller building blocks that reuse existing core components. In the latter case, the UI delegates of the core components will take care of platform/LAF-specific settings such as colors, fonts, and anti-aliasing.
The " Enhancing Swing Applications" article describes the boilerplate code that you should put in the basic implementation of the UI delegate so that it can be easily extended by custom look and feels. Start off by creating the install* and uninstall* methods, even if you're not planning to use them; a third-party look and feel may decide to add some extra functionality on top of your basic functionality (for example, adding mouse-wheel scrolling of the slider).
Now, in our specific example, we can see that the target custom component contains a slider and a collection of labels (with icons and text). Since every JComponent is also a Container, we can easily emulate the visual appearance of the target component by adding a JSlider and JLabels (one for each control point) in our installComponents (don't forget to remove them in the uninstallComponents). By reusing core Swing components, we ensure that the visual appearance of our custom component under both core and third-party LAFs will be consistent with the rest of the application.
Since we are adding sub-components, we'll need to implement a custom LayoutManager that will position them on creation and resizing. This is a fairly straightforward (and a little tedious) task: the discrete ranges result in labels being placed right next to each other, while contiguous ranges take the extra vertical space according to their relative weight. The slider itself takes the entire vertical space and thus is aligned with the first and the last control points.
Note that the alternative implementation (when there is no possible way to reuse existing components) would be much more difficult and perhaps next to impossible to do with a single UI delegate. For example, some core LAFs use native APIs to draw the respective controls (such as slider track and slider thumb), while some third-party LAFs may not respect the UIManager settings and provide hard-coded colors and custom theming APIs.
Going back to our implementation (which uses JSlider), we are facing an interesting problem: a core slider can be either discrete (snapToTicks or not). The snapping behavior is controlled inside a mouse motion listener installed in the BasicSliderUI delegate. What can we do? One option would be to remove this listener and install our own, while another would be to provide a custom implementation of BoundedRangeModel that changes the value when it's set in the discrete range. The first approach is not the best one--you can't rely on what is done in the SliderUI delegate of the specific (core or third-party) LAF, as the specific implementation may not call the super code at all. The second approach is much better, but we have decided to implement yet another approach for the reason described below.
Our implementation treats the sub-component slider as a cell renderer, just as with lists, trees, and tables. The slider is used only for rendering and does not get any events at all (see CellRendererPane for more details). This allows us to benefit from LAF-consistent painting and providing custom handling of mouse events. In our specific case, if the user clicks the mouse outside the slider thumb, instead of block scrolling towards the mouse click, the matching range value is set directly. This is why we did not use the second approach outlined above: our custom mouse listener translates the mouse click correctly for both contiguous and discrete ranges and sets the value. Since this is the only listener installed on the component (the slider is "rubber stamp" only), we can be sure that no other listener (unless explicitly set in a third-party UI delegate) will interfere with our code.
The resulting layout is shown in Figure 2. The blue outlines represent the bounds of the control point labels, while the red outline represents the bounds of the cell renderer pane:
Figure 2. Component layout
Since we are using the cell renderer pane, we need to override the paint method and paint the actual slider. Note that we don't paint the control point labels explicitly since they are "real" children of our component. In addition, note that the slider painting is done in a separate protected method. This allows third-party LAFs to replace the slider painting without changing the entire painting logic.
Test Application
Now that we have a fully functioning custom slider, it's time to test it. The test application creates a slider with few discrete and contiguous ranges and registers a change listener on this slider. On a change event, we compute the scale size for painting an icon (the icon is converted from the Tango Desktop Project icons using the SVG-to-Java2D converter described in the " Transcoding SVG to Pure Java2D code" entry). Figure 3 shows the application under different icon sizes:
Figure 3. Custom slider with different values selected
Figure 4 shows the same slider under different look and feels. From left to right, the LAFs are: Windows (core), Metal (core), Motif (core), Liquid (third party), and Napkin (third party). As you can see, the new component provides an appearance consistent with the set LAF:
Figure 4. Custom slider under different look and feels
Conclusion
Where to go now? Read the code for core Swing components, download and study the code for open source components such as SwingX or Flamingo, and start hacking away on that dream component of yours.
Basic Building Blocks
The Swing architecture overview provides an excellent high-level overview of the architectural decisions that were made during the development of Swing. Although it will take slightly more work to create a new component following the rules outlined in this article, the resulting code will be much easier to maintain, since it will adhere to the core Swing principles without reinventing the wheel. At the first look you might be tempted to throw everything together in one single class that will provide the external API, the model handling (state and notifications), event handling, layout, and painting. All these, however, belong in separate classes that follow the modified MVC (model-view-controller) architecture that will make your component codebase much easier to maintain and extend in the long run.
The main building blocks of all core Swing components are:
* The component class itself, which provides an API for creating, changing, and querying the component basic state.
* The model interface and the model default implementation(s) that handle the component business logic and change notifications.
* The UI delegate that handles component layout, event handling (mouse and keyboard), and painting.
This article will illustrate the process of creating a custom component that is based on the new view slider from Microsoft Windows Vista OS Explorer (see Figure 1). While this component looks very much like a slider embedded in a pop-up menu, it has new features that are not available on a regular JSlider. First, it has control points that have associated icons and labels. In addition, while some ranges are contiguous (like Small Icons-Medium Icons) and allow continuous resizing of the file icons, other ranges are discrete (like Tiles-Details). When the value is in one of these ranges, the slider thumb can be only at control points, and not inside the range.
Figure 1. View slider in Microsoft Windows Vista OS
The Component Class: UI Delegate Plumbing
The first class for a custom component is the component API itself. The API should be as simple as possible and delegate most of the business logic to the model (see the next section). In addition to the API, you should add the boilerplate code for setting the proper UI delegate (described in detail in the " Enhancing Swing Applications" article). At the barest level, this code should look like this:
private static final String uiClassID = "FlexiSliderUI"; public void setUI(FlexiSliderUI ui) { super.setUI(ui); } public void updateUI() { if (UIManager.get(getUIClassID()) != null) { setUI((FlexiSliderUI) UIManager.getUI(this)); } else { setUI(new BasicFlexiSliderUI()); } } public FlexiSliderUI getUI() { return (FlexiSliderUI) ui; } public String getUIClassID() { return uiClassID; }
It is very important to provide a fallback UI delegate that will handle the component painting, layout, and event handling when the currently installed look and feel doesn't provide a special UI delegate.
The Model Interface
This is, perhaps, the most important class for a custom component. It represents the business side of your component. The model interface should not contain any painting-related methods (such as setFont or getPreferredSize). For our specific component, we follow the LinearGradientPaint API and define the model as a sequence of ranges:
public static class Range { private boolean isDiscrete; private double weight; public Range(boolean isDiscrete, double weight) { this.isDiscrete = isDiscrete; this.weight = weight; } ... }
The model API to set and query the model ranges:
public void setRanges(Range... range); public int getRangeCount(); public Range getRange(int rangeIndex);
In addition, the model should provide the API to set and get the current value. The Value class points inside a Range:
public static class Value { public Range range; public double rangeFraction; public Value(Range range, double rangeFraction) { this.range = range; this.rangeFraction = rangeFraction; } ... }
And the model API provides the getter and setter for the current value:
public Value getValue(); public void setValue(Value value);
The last piece of model interface contains methods for adding and removing ChangeListeners. This follows the model interfaces from core Swing components (see BoundedRangeModel):
void addChangeListener(ChangeListener x); void removeChangeListener(ChangeListener x);
The Model Implementation
The model implementation is pretty straightforward and follows the DefaultBoundedRangeModel. The change listeners are stored as an EventListenerList. The change to the model value results in a ChangeEvent being fired:
protected void fireStateChanged() { ChangeEvent event = new ChangeEvent(this); Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length - 2; i >= 0; i -= 2) { if (listeners[i] == ChangeListener.class) { ((ChangeListener) listeners[i + 1]).stateChanged(event); } } }
Note that as with all core Swing components, we iterate over the listener list from the end. This way, if a listener decides to remove itself inside the stateChanged implementation, we'll still iterate over all registered listeners exactly once. The implementation of the value-related methods is very straightforward with checking of the validity of the passed value and defensive copying of the slider ranges array (so that changes in malicious application code won't affect the model).
The Model Unit Tests
While unit testing the UI components can be difficult, the model should be thoroughly tested--remember that the loss of a pixel is nothing compared to the loss of a trailing zero in the model, especially if that zero multiplies an automatic payment by 10. Be sure to test your default model implementation with such simple tests as:
public void testSetValue2() { FlexiRangeModel model = new DefaultFlexiRangeModel(); FlexiRangeModel.Range range0 = new FlexiRangeModel.Range(true, 0.0); model.setRanges(range0); try { // should fail since range0 is discrete FlexiRangeModel.Value value = new FlexiRangeModel.Value(range0, 0.5); model.setValue(value); } catch (IllegalArgumentException iae) { return; } assertTrue(false); }
The Component Class: API
Going back to the component class, we can add the missing APIs for creation of the control itself and getting its model. In addition, some of the UI-related configuration (such as icons and label text) is stored in the component class itself (this does not belong in the model). The component constructor follows that of LinearGradientPaint, throwing exceptions on null or non-matching parameters:
public JFlexiSlider(Range[] ranges, Icon[] controlPointIcons, String[] controlPointTexts) throws NullPointerException, IllegalArgumentException
The implementation is quite straightforward. First, it checks that the arrays are not null and are of matching lengths. Then, it creates a DefaultFlexiRangeModel and sets its ranges. Then, it defensively copies the icons and text arrays, and finally calls the updateUI method that installs and initializes the look-and-feel delegate. The additional APIs that are implemented in this class are:
public int getControlPointCount(); public Icon getControlPointIcon(int controlPointIndex); public String getControlPointText(int controlPointIndex); public FlexiRangeModel getModel(); public FlexiRangeModel.Value getValue(); public void setValue(FlexiRangeModel.Value value);
Note that the last two are simply syntactic sugar and pass the calls to the underlying model. These are provided as convenience methods only. The first three methods are mainly for the UI delegate, but can be used by the application code as well.
UI Delegate
If the model interface is the most important part of writing a custom control, the UI delegate is in most cases the most difficult. The main question is: how do you write painting logic that will produce consistent results under all (existing and future) target look and feels? Sometimes, this will be impossible to do without writing custom UI delegates for each one of the target look and feels (as is done for many components in the SwingX project). However, in some cases you'll find that you can emulate the visual part of your custom component by combining smaller building blocks that reuse existing core components. In the latter case, the UI delegates of the core components will take care of platform/LAF-specific settings such as colors, fonts, and anti-aliasing.
The " Enhancing Swing Applications" article describes the boilerplate code that you should put in the basic implementation of the UI delegate so that it can be easily extended by custom look and feels. Start off by creating the install* and uninstall* methods, even if you're not planning to use them; a third-party look and feel may decide to add some extra functionality on top of your basic functionality (for example, adding mouse-wheel scrolling of the slider).
Now, in our specific example, we can see that the target custom component contains a slider and a collection of labels (with icons and text). Since every JComponent is also a Container, we can easily emulate the visual appearance of the target component by adding a JSlider and JLabels (one for each control point) in our installComponents (don't forget to remove them in the uninstallComponents). By reusing core Swing components, we ensure that the visual appearance of our custom component under both core and third-party LAFs will be consistent with the rest of the application.
Since we are adding sub-components, we'll need to implement a custom LayoutManager that will position them on creation and resizing. This is a fairly straightforward (and a little tedious) task: the discrete ranges result in labels being placed right next to each other, while contiguous ranges take the extra vertical space according to their relative weight. The slider itself takes the entire vertical space and thus is aligned with the first and the last control points.
Note that the alternative implementation (when there is no possible way to reuse existing components) would be much more difficult and perhaps next to impossible to do with a single UI delegate. For example, some core LAFs use native APIs to draw the respective controls (such as slider track and slider thumb), while some third-party LAFs may not respect the UIManager settings and provide hard-coded colors and custom theming APIs.
Going back to our implementation (which uses JSlider), we are facing an interesting problem: a core slider can be either discrete (snapToTicks or not). The snapping behavior is controlled inside a mouse motion listener installed in the BasicSliderUI delegate. What can we do? One option would be to remove this listener and install our own, while another would be to provide a custom implementation of BoundedRangeModel that changes the value when it's set in the discrete range. The first approach is not the best one--you can't rely on what is done in the SliderUI delegate of the specific (core or third-party) LAF, as the specific implementation may not call the super code at all. The second approach is much better, but we have decided to implement yet another approach for the reason described below.
Our implementation treats the sub-component slider as a cell renderer, just as with lists, trees, and tables. The slider is used only for rendering and does not get any events at all (see CellRendererPane for more details). This allows us to benefit from LAF-consistent painting and providing custom handling of mouse events. In our specific case, if the user clicks the mouse outside the slider thumb, instead of block scrolling towards the mouse click, the matching range value is set directly. This is why we did not use the second approach outlined above: our custom mouse listener translates the mouse click correctly for both contiguous and discrete ranges and sets the value. Since this is the only listener installed on the component (the slider is "rubber stamp" only), we can be sure that no other listener (unless explicitly set in a third-party UI delegate) will interfere with our code.
The resulting layout is shown in Figure 2. The blue outlines represent the bounds of the control point labels, while the red outline represents the bounds of the cell renderer pane:
Figure 2. Component layout
Since we are using the cell renderer pane, we need to override the paint method and paint the actual slider. Note that we don't paint the control point labels explicitly since they are "real" children of our component. In addition, note that the slider painting is done in a separate protected method. This allows third-party LAFs to replace the slider painting without changing the entire painting logic.
@Override public void paint(Graphics g, JComponent c) { super.paint(g, c); this.paintSlider(g); } protected void paintSlider(Graphics g) { Rectangle sliderBounds = sliderRendererPane.getBounds(); this.sliderRendererPane.paintComponent(g, this.slider, this.flexiSlider, sliderBounds.x, sliderBounds.y, sliderBounds.width, sliderBounds.height, true); }
Test Application
Now that we have a fully functioning custom slider, it's time to test it. The test application creates a slider with few discrete and contiguous ranges and registers a change listener on this slider. On a change event, we compute the scale size for painting an icon (the icon is converted from the Tango Desktop Project icons using the SVG-to-Java2D converter described in the " Transcoding SVG to Pure Java2D code" entry). Figure 3 shows the application under different icon sizes:
Figure 3. Custom slider with different values selected
Figure 4 shows the same slider under different look and feels. From left to right, the LAFs are: Windows (core), Metal (core), Motif (core), Liquid (third party), and Napkin (third party). As you can see, the new component provides an appearance consistent with the set LAF:
Figure 4. Custom slider under different look and feels
Conclusion
Where to go now? Read the code for core Swing components, download and study the code for open source components such as SwingX or Flamingo, and start hacking away on that dream component of yours.
发表评论
-
JFileChooser 只选(显示)文件夹
2012-06-29 11:17 3848小常识,就是总是忘,干脆整理下: JFileChooser f ... -
Swing免费皮肤Substance的使用
2010-10-25 16:22 22413关键词:Swing换肤,Substance,免费皮肤 Swi ... -
Swing 窗体关闭对话框提示
2010-10-20 17:56 6866关键词:Swing,关闭窗体,对话框提示 Swing关闭窗体 ... -
JTable表头排序的两种方法
2010-10-13 09:46 16620关键词:JTable,表头排序,线程安全 在Java6以前, ... -
基于Swing的图片倒影特效
2010-10-11 17:34 3745关键字:Swing,倒影特效,Graphics2D,Gradi ... -
怎样学习Swing
2010-09-27 16:36 3554Sun网站上的Java Tutorial就非常好,非常适合。它 ... -
Swing的第一推动力(转载)
2010-09-27 16:32 1628转载自[url]http://blog.sina. ... -
Swing页面属性的设置顺序
2010-09-15 15:35 4763这是一个细节问题,但 ... -
paint和paintComponent方法的关系
2010-09-15 14:12 15715关键词:swing,paint,paintComponent, ... -
Swing拖拽效果
2010-09-07 16:31 5202Swing的拖拽貌似挺复杂 ... -
SwingUtilities中invokeLater,invokeAndWait的区别和作用
2010-09-02 17:28 2980invokeLater,invokeAndWait是专门给Sw ... -
Swing控件(JLabel,JButton....)设置字体和大小
2010-09-02 16:57 19907关键字:Swing控件,JButton,JLabel,字体,大 ... -
Java 拖动Swing组件和图片
2010-08-17 14:07 14470在Swing中拖动一个组件或者图片,我们抛开定位布局不说,是件 ... -
SwingUtilities.convertPoint 方法的作用
2010-08-17 13:38 6759SwingUtilities类是Swing组件 ... -
JTable 行 列 单元格 操作小结
2010-07-15 10:11 11073一.创建表格 1) 调用无参构造函数. JTable tabl ... -
JTable 保存单元格内容
2010-07-14 17:25 4194在开发过程中遇到这样一个问题,我编辑JTable里的内容,编辑 ... -
Swing, JTree拖拽的例子
2010-06-10 16:47 6258一个Java(JTree)拖拽的例子,其核心思想还是去实现Ja ... -
Swing JTable工厂(table4j)
2010-06-07 16:36 5248因为Swing提供的JTable用 ... -
Java Swing Border(Swing 边框) 归纳
2010-05-27 12:32 25744对Swing Border的一个小总结: 使用方法: Titl ... -
JFrame打开居中和相关设置
2010-05-13 15:51 7164对于JFrame,在界面初始化完成时,加入以下代码: //设置 ...
相关推荐
How To Write A Dissertation
VTK 12 How to Write a Process__ Object.pdf VTK 12 How to Write a Process__ Object.pdf VTK 12 How to Write a Process__ Object.pdf
根据提供的文件信息,以下是对《How to Write and Publish a Scientific Paper》一书内容的知识点的详细阐述: 标题:《How to Write and Publish a Scientific Paper》(如何撰写和发表科学论文) 1. 科学写作的...
(How to Write a (Lisp) Interpreter (in Python))和(An ((Even Better) Lisp) Interpreter (in Python))的翻译,对解释器实现原理和函数式编程敢兴趣的可以下载看看!
MIT计算机大牛Bill Freeman教授于2014年写的报告,虽然比较久远,但非常有参考意义。How to write a good CVPR submission
How to write and publish a scientific paper ContentsChapter 1 What Is Scientific Writing? Chapter 2 Origins of Scientific Writing Chapter 3 What Is a Scientific Paper? Chapter 4 How to Prepare the ...
根据给定文件的信息,本篇知识点将围绕科研论文撰写与发表的各个方面进行详细阐述。 首先,了解什么是科学写作,科学写作的范围及其对于清晰表达的必要性。科学写作是科技、医学、工程等领域的写作形式,它要求作者...
本书《How to Write & Publish a Scientific Paper 6th Edition(nc)》是一本专为理工科学生和科研人员撰写的指南,目的是提高他们科技论文写作能力,并且帮助他们更容易地将论文发表在国际期刊上。这本书自1979年...
国外教授教你怎样写论文,文章题目为:how to write a paper, 特别好的一个资料,适用需要写毕业论文或者其它论文的人,尤其是英文论文。
使用Java的Swing库或其他第三方图表库(如JFreeChart)来绘制图表。确保图表清晰易读,能够有效地传达关键信息。 - **步骤3:集成到JMeter** 将自定义图表集成到JMeter的用户界面中,使用户能够在测试过程中轻松...
How to Write & Publish a Scientific Paper 6th Edition
教你如何书写与发表科技论文 很实用哦 How to Write & Publish a Scientific Paper 英文版
1. 在论文中适当地使用第一人称复数(we)和第三人称单数(he/she/it),如“We suggest here that…”和“They use a 3D…”。这既能展现团队工作,也能表明对前人工作的尊重。 五、写作技巧 1. 文章的逻辑性和...
google-maps-react, "How to Write a Google Maps React Component" 教程的辅助代码 Google地图响应组件教程 使用 React,延迟加载依赖,当前位置查找器和由 Fullstack反应驱动的团队测试驱动的方法,声明性的谷歌...
How to write a business memo.pdf
欢迎下载,如何写作和发表英文论文。欢迎下载欢迎下载
How to write a noticePPT教案.pptx
98年的版本,应该是第5版。经典之作。国内有第六版的影印版和翻译版。