首页 文章 精选 留言 我的

精选列表

搜索[Rust编程],共10000篇文章
优秀的个人博客,低调大师

《Java编程思想》读书笔记(6)

JButton这一章,Brucke只给出了一个简单的例子,代码如下: import java.applet.Applet; import java.awt.*; import javax.swing.*; public class Test extends JApplet { private JButton btn1; private JButton btn2; /** * @param args */ public void init() { this.btn1 = new JButton("确定"); this.btn2 = new JButton("重置"); Container con = this.getContentPane(); con.setLayout(new FlowLayout()); con.add(btn1); con.add(btn2); } public static void main(String[] args) { Test t1 = new Test(); Console.run(t1,300,300); } } 这一节没有过多的东西可以思考,但布局管理器的知识值得去深究。 Java语言中提供的布局管理器种类有:边界式布局、卡片式布局、流式布局和网格式布局等,各有不同的特点,可根据实际 需要选用;但有最大自由设计空间的是“无布局管理器”——即不使用任何布局格式,而通过手工方式添加组件到页面布局的绝对位置上。因此我采用“无布局管理器”将他的代码改写如下: import java.applet.Applet; import java.awt.*; import javax.swing.*; public class Test extends JApplet { private JButton btn1; private JButton btn2; /** * @param args */ public void init() { this.btn1 = new JButton("确定"); this.btn2 = new JButton("重置"); Container con = this.getContentPane(); //con.setLayout(new FlowLayout()); con.setLayout(null); btn1.reshape(30,30,100,100); btn2.reshape(150,150,100,100); con.add(btn1); con.add(btn2); } public static void main(String[] args) { Test t1 = new Test(); Console.run(t1,600,600); } } 1. GridLayout GridLayout类是布局管理器之一,它将容器中的组件在放置在一个矩形网格中。容器被划分为相同大小的矩形,而每个组件就放置在一个单独的矩形中。 import java.awt.*; import javax.swing.*; public class GridForm extends JFrame { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub GridForm gf = new GridForm(); gf.setLayout(new GridLayout(3,2)); Container con = gf.getContentPane(); con.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); con.add(new JButton("1")); con.add(new JButton("2")); con.add(new JButton("3")); con.add(new JButton("4")); con.add(new JButton("5")); con.add(new JButton("6")); gf.setTitle("GridLayout例子"); gf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); gf.setSize(200,200); gf.setVisible(true); } } 如果容器的ComponentOrientation属性是水平的并且从左到右,上面例子产生如下图1: 如果容器的ComponentOrientation属性是horizontal 和 right-to-left,结果就如下图2: 当行数和列数都被设置为非零值(不管是通过构造函数或者通过setRows和setColumns方法来设置),指定的列数就会被忽略掉。例如说,如果指定是3行3列,而将布局中放置9个组件,它们就会显示为3行3列。 2.BorderLayout 边界布局会把容器看成五个:部分:north, south, east, west,和 center。每个区域最多包含一个组件,重要的五个常量:NORTH, SOUTH, EAST, WEST和 CENTER; Panel p = new Panel(); p.setLayout(new BorderLayout()); p.add(new Button("Okay"), BorderLayout.SOUTH); ================================================== ================================================== Panel p2 = new Panel(); p2.setLayout(new BorderLayout()); p2.add(new TextArea()); // Same as p.add(new TextArea(), BorderLayout.CENTER); <!--[endif]--> BorderLayout 支持相对定位常量, PAGE_START, PAGE_END, LINE_START, and LINE_END. 在一个容器中,如果它的 ComponentOrientation 被设置为ComponentOrientation.LEFT_TO_RIGHT,这些常量就分别映射为 NORTH, SOUTH, WEST, and EAST 为了与以前兼容,BorderLayout也好汉相对定位常量 BEFORE_FIRST_LINE, AFTER_LAST_LINE, BEFORE_LINE_BEGINS and AFTER_LINE_ENDS. 他们分别和PAGE_START, PAGE_END, LINE_START and LINE_END等价. 如果同时使用绝对定位和相对定位,就会产生不可预测的结果。例如,如果你在容器中同时使 NORTH 和 PAGE_START 常量 来添加组件,而容器的 方向是 LEFT_TO_RIGHT, 那么就只有PAGE_START被会布局。 import java.awt.*; import javax.swing.*; public class GridForm extends JFrame { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub GridForm gf = new GridForm(); gf.setLayout(new BorderLayout()); Container con = gf.getContentPane(); con.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); con.add(new JButton("North"),BorderLayout.NORTH); con.add(new JButton("SOUTH"),BorderLayout.SOUTH); con.add(new JButton("EAST"),BorderLayout.EAST); con.add(new JButton("WEST"),BorderLayout.WEST); con.add(new JButton("CENTER"),BorderLayout.CENTER); gf.setTitle("BorderLayout例子"); gf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); gf.setSize(200,200); gf.setVisible(true); } } 3. FlowLayout FlowLayout会将组件从左到右进排列,就象文章中的文本行一样。流布局一般用在面板中的按纽的放置。它将会将按纽以从左至右的方式排放,直到在同一行中再也放不下更多的按纽了。 import java.awt.*; import javax.swing.*; public class GridForm extends JFrame { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub GridForm gf = new GridForm(); //gf.setLayout(new BorderLayout()); gf.setLayout(new FlowLayout()); Container con = gf.getContentPane(); con.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT); con.add(new JButton("North")); con.add(new JButton("SOUTH")); con.add(new JButton("EAST")); con.add(new JButton("WEST")); con.add(new JButton("CENTER")); gf.setTitle("FlowLayout例子"); gf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); gf.setSize(200,200); gf.setVisible(true); } } 4.BoxLayout BoxLayout能让你在垂直和水平两个方向上控制组件的摆放,它用一些被称为支柱(struts)和胶水(glue)的东西来控制组件间的距离。 package com.vitamin.UI; import javax.swing.*; import java.awt.*; import com.vitamin.*; import com.vitamin.Console.console; public class BoxLayout1 extends JApplet { public void init() { JPanel jpv = new JPanel(); jpv.setLayout(new BoxLayout(jpv, BoxLayout.Y_AXIS)); for(int i = 0; i < 5; i++) jpv.add(new JButton("jpv " + i)); JPanel jph = new JPanel(); jph.setLayout(new BoxLayout(jph, BoxLayout.X_AXIS)); for(int i = 0; i < 5; i++) jph.add(new JButton("jph " + i)); Container cp = getContentPane(); cp.add(BorderLayout.EAST, jpv); cp.add(BorderLayout.SOUTH, jph); } public static void main(String[] args) { console.run(new BoxLayout1(), 450, 200); } } 本文转自Phinecos(洞庭散人)博客园博客,原文链接:http://www.cnblogs.com/phinecos/archive/2006/06/01/415342.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

《Java编程思想》读书笔记(10)

package com.vitamin.UI; import javax.swing.*; import javax.swing.filechooser.FileFilter; import java.awt.BorderLayout; import java.awt.Container; import java.awt.GridLayout; import java.awt.event.*; import java.io.File; import java.io.FileWriter; import java.io.IOException; import com.vitamin.Console.*; public class FileTest extends JFrame { private JButton btnOpen = new JButton("Open"); private JButton btnSave = new JButton("Save"); private JPanel p = null; private JTextField tfDir = new JTextField(); private JTextField tfFileName = new JTextField(); private JScrollPane sp = new JScrollPane(); private JEditorPane ep = new JEditorPane(); private File file = null; public FileTest() { super(); // TODO Auto-generated constructor stub this.initForm(); } public void initForm() { Container con = this.getContentPane(); con.setLayout(new BorderLayout()); this.tfDir.setEditable(false); this.tfFileName.setEditable(false); this.btnSave.setEnabled(false); this.btnOpen.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { JFileChooser fc = new JFileChooser(); fc.setFileFilter(new TextFileFilter() ); int returnVal = fc.showOpenDialog(FileTest.this); if(returnVal == JFileChooser.APPROVE_OPTION) { file = fc.getSelectedFile(); try { ep.setPage(file.toURL()); tfFileName.setText(file.getAbsolutePath()); btnSave.setEnabled(true); } catch(IOException ex) { throw new RuntimeException(ex); } } else if(returnVal == JFileChooser.CANCEL_OPTION) { tfFileName.setText("你已经取消了文件的选择"); } } }); this.btnSave.addActionListener(new ActionListener(){ public void actionPerformed(ActionEvent e) { try { FileWriter fw = new FileWriter(file); ep.write(fw); JOptionPane.showMessageDialog(FileTest.this,"保存成功"); } catch (IOException ioe) { throw new RuntimeException(ioe); } } }); sp.getViewport().add(ep); p = new JPanel(); p.add(this.btnOpen); p.add(this.btnSave); con.add(p,BorderLayout.SOUTH); con.add(sp,BorderLayout.CENTER); p = new JPanel(); p.setLayout(new GridLayout(2,1)); p.add(this.tfDir); p.add(this.tfFileName); con.add(p,BorderLayout.NORTH); console.run(this,400,400); } public class TextFileFilter extends javax.swing.filechooser.FileFilter { public boolean accept(File f) { return f.getName().endsWith(".txt") || f.isDirectory(); } public String getDescription() { return "Text Files (*.txt)"; } } /** * @param args */ public static void main(String[] args) { FileTest ft = new FileTest(); } } 做了个读写文件的小东西,功能很烂,有时间做个完整的记事本吧。 本文转自Phinecos(洞庭散人)博客园博客,原文链接:http://www.cnblogs.com/phinecos/archive/2006/06/08/420130.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

《Java编程思想》读书笔记(8)

前面的几篇笔记都写得太古板了,今天就换个新的写作风格来试试,暂且叫“案例导向式”吧,希望能对自己和别人的学习有所帮助。 第一部分 问题场景描述 某日,小菜跑来问我:“phinecos,我真是快被java逼疯了,你可得帮帮我呀!!“ 我转过头,“怎么了,小菜?“。 “是这样的,我现在在学习swing,每次用一个控件吧,如果想知道它能处理的事件的话,就得去查jdk文档,可sun那该死的文档总是把人搞得头晕,我只不过是想看看有什么事件可以处理而已,它那乱七八糟的文档反而把我快逼疯了,你给我出出主意吧。。。“小菜着急地摇着我的手说。 “哦,原来是这样啊,恩,可为什么你不用eclipse哪?它有提供你所需要的功能啊。”我不解的问。 “。。。这个,我不是不想用啊,可听人说,高手都是只用记事本和jdk的,我想成为高手,所以不敢用呀。”小菜支支唔晤的说。 “。。。。。。” 第二部分 分析与设计 需求是软件的关键。碰到上面小菜的问题了,我们就开始进行分析吧。小菜到底有什么需求哪?他是想查看一个swing控件类能够处理的事件列表,也就是它的所有addXXXListener方法。怎么办哪?看来只有两个解决方案了:一,劝小菜用eclipse,这样就可以直接利用eclipse的功能满足他的需求。(可这个已经被否定了,因为小菜要成为“高手”,^o^).二,那就只有为他开发一个小实用工具,让他可以查看各个Swing控件能够处理的事件,也就是把它的所有addXXXListener方法都列出来。 好了,东西比较小,那就先从GUI界面开始,至于核心的事件处理先不管,如下图: 6-6-1.GIF 用户界面代码如下: package com.vitamin.UI; import java.awt.ActiveEvent; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Event; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.*; import com.vitamin.Console.console; public class ShowAddListeners extends JFrame { private JLabel lbName = null; private JTextField tfName = null; private JTextArea tResult = null; private JPanel pName = null; public ShowAddListeners() { super(); this.initForm(); } public ShowAddListeners(String title) { super(title); this.initForm(); } private void initForm() { this.lbName = new JLabel("请输入Swing控件的类名:"); this.tfName = new JTextField(20); this.tResult = new JTextArea(40,50); this.pName = new JPanel(); pName.add(this.lbName); pName.add(this.tfName); Container con = this.getContentPane(); con.add(BorderLayout.NORTH,pName); con.add(new JScrollPane(this.tResult)); tfListener tf1 = new tfListener(); this.tfName.addActionListener(tf1); console.run(this,600,600); } class tfListener implements ActionListener { public void actionPerformed(ActionEvent e) {//核心处理代码处 if(tfName.getText().length()==0) { JOptionPane.showMessageDialog((JTextField)e.getSource(),"请输入控件类名称!!!","错误信息",JOptionPane.DEFAULT_OPTION); return; } JOptionPane.showMessageDialog((JTextField)e.getSource(),"OK","信息",JOptionPane.DEFAULT_OPTION); } } /** * @param args */ public static void main(String[] args) { ShowAddListeners sl = new ShowAddListeners("事件查看器"); } } 在这里我对用户引发的键盘事件还只是简单地进行一个测试用的显示处理,还没有添加进用来完成所需功能的代码,但提早让用户体验到模型,哪怕是一个没有具体功能的虚模型也是一件好事情。 恩,问题来了,接收到用户输入的swing控件类名以后,该怎么去读取它所有的方法,并且能把adeeXXXListener()这样类型的方法给显示在TextArea控件里给用户哪?我第一反应是:要想获取一个类的所有方法,应该用RTTI,先用类名构造一个Class,再去尝试获取其方法。至于想从读出来的所有方法中只选出adeeXXXListener()类型的方法,应该要使用正则表达式来进行模式匹配吧。现有的思路是这样的,可对RTTI, 正则表达式,我都不熟悉,该怎么办哪?看来得靠jdk文档和google了。 好吧,打开jdk文档开始看Class的知识吧: public static Class<?> forName(String className) throws ClassNotFoundException 返回与带有给定字符串名的类或接口相关联的 Class 对象。调用此方法等效于: Class.forName(className, true, currentLoader) 其中 currentLoader 表示此类的定义类加载器。 例如,以下代码片段返回 java.lang.Thread 类的运行时 Class 描述符。 Class t = Class.forName("java.lang.Thread") 调用 forName("X") 将导致名为 X 的类被初始化。 恩,正是我要的,仿上面给的例子,我应该这样做:先获取用户输入的swing控件名称,如JButton,然后:] Class t= Class.forName("java.swing."+name); 现在有了运行时的类了,接下来就是获取它的所有方法了 那我就应该这样:Method[] mt = t. getMethod();这样就把所有的方法读取到数组mt中了。 修改后的效果: 6-6-3.GIF 修改后的事件处理代码: class tfListener implements ActionListener { public void actionPerformed(ActionEvent e) { String name = tfName.getText().trim(); Class clName = null; if(name.length()==0) { JOptionPane.showMessageDialog((JTextField)e.getSource(),"请输入控件类名称!!!","错误信息",JOptionPane.DEFAULT_OPTION); return; } else { try { clName = Class.forName("javax.swing."+name); Method[] mt = clName.getMethods(); for(int i=0;i<mt.length;i++) { tResult.append(mt[i].toString()+"\n"); } } catch(ClassNotFoundException ex) { tResult.setText("没有找到匹配的方法名!!!"); } } } } 现在已经初具雏形了,可获取的是全部方法,并不是我们原本想要的东西,还得从这个方法列表中进行一些筛选工作才行。 要选出只含有addXXXListener的方法,就应该尝试构造一个能够识别这样方法的正则表达式。翻阅jdk文档后,我写了这样一个正则表达式: private static String StrReg = "add\\w+?Listener";其中\\w+?表示一个或多个字符 为了学习如何编写识别一个简单模式的程序,我写了一个简单的测试程序: package com.vitamin.Console; import java.util.regex.*; public class regTest { public static void main(String[] args) { Pattern p = null; Matcher m = null; p = Pattern.compile("a*b"); m = p.matcher("aaaaab"); if(m.matches()==true) { System.out.println("模式匹配成功"); } else { System.out.println("匹配失败"); } } } 现在就来完成剩余的工作,代码如下: package com.vitamin.UI; import java.awt.ActiveEvent; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Event; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.lang.reflect.Method; import java.util.regex.*; import javax.swing.*; import com.vitamin.Console.console; public class ShowAddListeners extends JFrame { private JLabel lbName = null; private JTextField tfName = null; private JTextArea tResult = null; private JPanel pName = null; private static String StrReg = "add\\w+?Listener"; private static Pattern pt = Pattern.compile(StrReg); private static Matcher mc = null; public ShowAddListeners() { super(); this.initForm(); } public ShowAddListeners(String title) { super(title); this.initForm(); } private void initForm() { this.lbName = new JLabel("请输入Swing控件的类名:"); this.tfName = new JTextField(20); this.tResult = new JTextArea(40,50); this.pName = new JPanel(); pName.add(this.lbName); pName.add(this.tfName); Container con = this.getContentPane(); con.add(BorderLayout.NORTH,pName); con.add(new JScrollPane(this.tResult)); tfListener tf1 = new tfListener(); this.tfName.addActionListener(tf1); console.run(this,600,600); } class tfListener implements ActionListener { public void actionPerformed(ActionEvent e) { String name = tfName.getText().trim(); Class clName = null; if(name.length()==0) { JOptionPane.showMessageDialog((JTextField)e.getSource(),"请输入控件类名称!!!","错误信息",JOptionPane.DEFAULT_OPTION); return; } else { try { clName = Class.forName("javax.swing."+name); Method[] mt = clName.getMethods(); for(int i=0;i<mt.length;i++) { mc = pt.matcher(mt[i].toString()); if(mc.find()) { tResult.append(mt[i].toString()+"\n"); } } } catch(ClassNotFoundException ex) { tResult.setText("没有找到匹配的方法名!!!"); } } } } /** * @param args */ public static void main(String[] args) { ShowAddListeners sl = new ShowAddListeners("事件查看器"); } } 结果如下,虽然还有很多地方不成熟,但也算是我的一个小作品吧,^o^ 本文转自Phinecos(洞庭散人)博客园博客,原文链接:http://www.cnblogs.com/phinecos/archive/2006/06/06/418261.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

《Java编程思想》读书笔记(14)

程序是根据具体情况在不断地创建新对象,而这些情况又只有在运行时才能够确定,因此我们就不知道到底需要多少对象,什么类型的对象,所以要能够创建任何对象并且能保持住这些对象的引用。Java就提供了很多容器类来持有和操纵对象。 数组是比较特别的一个容器,怎么说呢,它可以持有象int等基本类型数据,能够随机存储和访问,而且访问效率很高,但高效率是以牺牲灵活为代价的,当你创建了一个数组后,它的容量就确定了,并且在其生命周期内不能改变。而与它相对应的ArrayList就恰好相反,它可以灵活地增加容量,但效率就明显低于数组。而与List,Map,Set等泛型容器相比,数组在创建时就指明了它持有的对象类型,因此在编译时就会做类型检查,从而防止插入错误类型的对象。而泛型容器把它里面的元素都看成是Object,因此能容纳所有类型的对象(这就郁闷了,当你要往里面插入基本类型的数据时,就不得不先用对应的包装类将基本类型包装为Object型),并且泛型容器也没有编译时的类型检查,因此安全性得不到保证。 本文转自Phinecos(洞庭散人)博客园博客,原文链接:http://www.cnblogs.com/phinecos/archive/2006/06/17/428251.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

《Java编程思想》读书笔记(9)

一些listene接口中只定义了一个方法,因此要实现这种接口的工作量并不大,因为只要写完这一个方法,接口也就实现了。但是如果要使用有多个方法的listener的话,就会比较麻烦了,因为必须要实现接口中定义的所有方法,而实际上很多方法我们并不需要。举例来说,如果要捕捉鼠标点击的话,那就必须写一个mouseClicked( )方法。但是由于MouseListener是一个interface,所以即使MouseListener定义的其他方法我们不使用,也得实现其所有的方法。 为了解决这个问题,有些(但不是全部)多方法的listener接口提供了适配器(adapter)。适配器会为接口提供默认的空方法。这样,你只要继承适配器,根据需要覆写方法就可以了。 例如: package com.vitamin.UI; import java.awt.BorderLayout; import java.awt.Container; import java.awt.Event; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.*; import javax.swing.*; public class HelloForm extends JFrame { private JLabel lbInfo = null; private JButton btnOK = null; public HelloForm() { super(); } public HelloForm(String title) { super(title); this.initForm(); } private void initForm() { this.lbInfo = new JLabel(); this.btnOK = new JButton("确定"); this.btnOK.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { lbInfo.setText("hello,world"); } }); Container con = this.getContentPane(); con.setLayout(new BorderLayout()); con.add(this.btnOK,BorderLayout.SOUTH); con.add(this.lbInfo,BorderLayout.NORTH); this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); this.setSize(300,300); this.setVisible(true); } /** * @param args */ public static void main(String[] args) { HelloForm hf = new HelloForm("内部匿名类测试程序"); } } 在附上<<TIJ>>中给出的一段处理多个事件的代码: package com.vitamin.UI; import javax.swing.*; import java.awt.*; import java.awt.event.*; import java.util.*; import com.vitamin.Console.console; public class TrackEvent extends JApplet { private HashMap h = new HashMap(); private String[] event = { "focusGained", "focusLost", "keyPressed", "keyReleased", "keyTyped", "mouseClicked", "mouseEntered", "mouseExited", "mousePressed", "mouseReleased", "mouseDragged", "mouseMoved" }; private MyButton b1 = new MyButton(Color.BLUE, "test1"), b2 = new MyButton(Color.RED, "test2"); class MyButton extends JButton { void report(String field, String msg) { ((JTextField)h.get(field)).setText(msg); } FocusListener fl = new FocusListener() { public void focusGained(FocusEvent e) { report("focusGained", e.paramString()); } public void focusLost(FocusEvent e) { report("focusLost", e.paramString()); } }; KeyListener kl = new KeyListener() { public void keyPressed(KeyEvent e) { report("keyPressed", e.paramString()); } public void keyReleased(KeyEvent e) { report("keyReleased", e.paramString()); } public void keyTyped(KeyEvent e) { report("keyTyped", e.paramString()); } }; MouseListener ml = new MouseListener() { public void mouseClicked(MouseEvent e) { report("mouseClicked", e.paramString()); } public void mouseEntered(MouseEvent e) { report("mouseEntered", e.paramString()); } public void mouseExited(MouseEvent e) { report("mouseExited", e.paramString()); } public void mousePressed(MouseEvent e) { report("mousePressed", e.paramString()); } public void mouseReleased(MouseEvent e) { report("mouseReleased", e.paramString()); } }; MouseMotionListener mml = new MouseMotionListener() { public void mouseDragged(MouseEvent e) { report("mouseDragged", e.paramString()); } public void mouseMoved(MouseEvent e) { report("mouseMoved", e.paramString()); } }; public MyButton(Color color, String label) { super(label); setBackground(color); addFocusListener(fl); addKeyListener(kl); addMouseListener(ml); addMouseMotionListener(mml); } } public void init() { Container c = getContentPane(); c.setLayout(new GridLayout(event.length + 1, 2)); for(int i = 0; i < event.length; i++) { JTextField t = new JTextField(); t.setEditable(false); c.add(new JLabel(event[i], JLabel.RIGHT)); c.add(t); h.put(event[i], t); } c.add(b1); c.add(b2); } public static void main(String[] args) { console.run(new TrackEvent(), 700, 500); } } 另外,今天还尝试了下手工打包可执行的jar文件,可没有成功,找不到原因,郁闷。。。 本文转自Phinecos(洞庭散人)博客园博客,原文链接:http://www.cnblogs.com/phinecos/archive/2006/06/07/419356.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android开发实践:多线程编程小结

我们知道,Android系统为了提高程序的实时响应能力,不允许在UI线程中进行耗时的操作,否则会出现ANR异常,因此必须将耗时的任务放到非UI线程中执行。Android/Java提供了很多类来帮助大家完成异步操作,比如:Thread类,Timer类,AsyncTask类,HandlerThread类,以及Executor接口。这些类都分别在什么场合下使用呢? 本文简单地总结一下Android开发中常见的多线程类型和解决方案,并比较和分析了各个方案的区别,以便更好地理解和应用这些API接口。 1. 单项异步任务 [场景]:下载一个APK文件,拷贝一个大文件。 [方案]:Thread类,AsyncTask类 [比较]: AsyncTask提供了onProgressUpdate和onPostExecute通知调用者任务执行的进度和结果,在函数内可以直接执行UI操作。 而Thread中不能直接操作UI界面,而要通过Activity.runOnUiThread或者借助Handler来完成UI的更新。 所以Thread更适合执行一些不需要跟UI频繁交互的单项任务,而AsyncTask相反。 2. 定时/循环执行的任务 [场景]:定时刷新UI(如:秒表),保持TCP心跳连接。 [方案]:“Thread + sleep”,定时器Timer [比较]: “Thread + sleep” 方案示例: 1 2 3 4 5 6 7 8 9 new Thread( new Runnable(){ @Override public void run(){ while (!EXIT){ Thread.sleep( 1000 ); mListener.onTimeArrived(); } } }).start(); 两者都不能在循环中执行UI更新操作,而必须借助Activity.runOnUiThread或者Handler来完成UI的更新。 由于Thread方案中onTimeArrived()占用了部分时间,所以这种方案的定时并不准确,而Timer是由系统创建异步通知的定时器,会更加准确,所以推荐使用Timer来完成定时任务。 3. 工作线程 工作线程启动后处于一种等待“命令”/“消息”的休眠状态,当接收到“命令”/“消息”后,将它放入“命令”/“消息”队列,然后唤醒线程依次串行或者并行执行。 [场景]:“生产者--消费者”模式,TCP Server端命令处理程序 [方案]:“Thread + condition/lock” ,HandlerThread类,线程池Executor [比较]: HandlerThread类是Android系统提供了封装好了Loop循环的Thread类,可以更加便捷地完成CallerThread和WorkThread的命令/消息交互。当然,我们也可以用Thread和Condition/Lock方式实现同样的效果,只不过要自己实现更多的代码。 前面的两种方式,都是“串行”的方式在执行“命令”,如果希望提供并发性,同时开启和管理多个线程来执行任务,则可以考虑使用Executor。 本文转自 Jhuster 51CTO博客,原文链接:http://blog.51cto.com/ticktick/1564055,如需转载请自行联系原作者

优秀的个人博客,低调大师

【Android进阶学习】Http编程之HttpClient

在Android开发中,Android SDK附带了Apache的HttpClient,它是一个完善的客户端。它提供了对HTTP协议的全面支持,可以使用HttpClient的对象来执行HTTP GET和HTTP POST调用。 HTTP工作原理: 1.客户端(一般是指浏览器,这里是指自己写的程序)与服务器建立连接 2.建立连接后,客户端向服务器发送请求 3.服务器接收到请求后,向客户端发送响应信息 4.客户端与服务器断开连接 HttpClient的一般使用步骤: 1.使用DefaultHttpClient类实例化HttpClient对象 2.创建HttpGet或HttpPost对象,将要请求的URL通过构造方法传入HttpGet或HttpPost对象。 3.调用execute方法发送HTTP GET或HTTP POST请求,并返回HttpResponse对象。 4.通过HttpResponse接口的getEntity方法返回响应信息,并进行相应的处理。 最后记得要在AndroidManifest.xml文件添加网络权限 <uses-permission android:name="android.permission.INTERNET" /> 下面是具体的例子: 1.使用HttpClient来执行GET调用 在LogCat窗口就能看到输出的信息 packagecom.lingdududu.http; importjava.io.InputStream; importorg.apache.http.HttpResponse; importorg.apache.http.HttpStatus; importorg.apache.http.client.HttpClient; importorg.apache.http.client.methods.HttpGet; importorg.apache.http.impl.client.DefaultHttpClient; importandroid.app.Activity; importandroid.os.Bundle; importandroid.util.Log; publicclassHttpGetActivityextendsActivity{ Stringuri="http://developer.android.com/"; finalStringTAG_STRING="TAG"; @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); try{ //得到HttpClient对象 HttpClientgetClient=newDefaultHttpClient(); //得到HttpGet对象 HttpGetrequest=newHttpGet(uri); //客户端使用GET方式执行请教,获得服务器端的回应response HttpResponseresponse=getClient.execute(request); //判断请求是否成功 if(response.getStatusLine().getStatusCode()==HttpStatus.SC_OK){ Log.i(TAG_STRING,"请求服务器端成功"); //获得输入流 InputStreaminStrem=response.getEntity().getContent(); intresult=inStrem.read(); while(result!=-1){ System.out.print((char)result); result=inStrem.read(); } //关闭输入流 inStrem.close(); }else{ Log.i(TAG_STRING,"请求服务器端失败"); } }catch(Exceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } } } 使用HTTP GET调用有一个缺点就是,请求的参数作为URL一部分来传递,以这种方式传递的时候,URL的长度应该在2048个字符之内。如果超出这个这范围,就要使用到HTTP POST调用。 2.使用HttpClient来执行POST调用 使用POST调用进行参数传递时,需要使用NameValuePair来保存要传递的参数。NameValuePair封装了一个键/值组合。另外,还需要设置所使用的字符集。 packagecom.androidbook.services.httppost; importjava.io.BufferedReader; importjava.io.IOException; importjava.io.InputStreamReader; importjava.util.ArrayList; importjava.util.List; importorg.apache.http.HttpResponse; importorg.apache.http.NameValuePair; importorg.apache.http.client.HttpClient; importorg.apache.http.client.entity.UrlEncodedFormEntity; importorg.apache.http.client.methods.HttpPost; importorg.apache.http.impl.client.DefaultHttpClient; importorg.apache.http.message.BasicNameValuePair; importandroid.app.Activity; importandroid.os.Bundle; publicclassHttpPostActivityextendsActivity{ Stringuri="http://developer.android.com/"; @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); BufferedReaderin=null; try{ HttpClientclient=newDefaultHttpClient(); HttpPostrequest=newHttpPost("http://code.google.com/android/"); //使用NameValuePair来保存要传递的Post参数 List<NameValuePair>postParameters=newArrayList<NameValuePair>(); //添加要传递的参数 postParameters.add(newBasicNameValuePair("id","12345")); postParameters.add(newBasicNameValuePair("username","dave")); //实例化UrlEncodedFormEntity对象 UrlEncodedFormEntityformEntity=newUrlEncodedFormEntity( postParameters); //使用HttpPost对象来设置UrlEncodedFormEntity的Entity request.setEntity(formEntity); HttpResponseresponse=client.execute(request); in=newBufferedReader( newInputStreamReader( response.getEntity().getContent())); StringBufferstring=newStringBuffer(""); StringlineStr=""; while((lineStr=in.readLine())!=null){ string.append(lineStr+"\n"); } in.close(); StringresultStr=string.toString(); System.out.println(resultStr); }catch(Exceptione){ //Dosomethingaboutexceptions }finally{ if(in!=null){ try{ in.close(); }catch(IOExceptione){ e.printStackTrace(); } } } } } 本文转自 lingdududu 51CTO博客,原文链接: http://blog.51cto.com/liangruijun/803097

优秀的个人博客,低调大师

【Android进阶学习】Http编程之HttpConnection

HttpURLConnection是继承于URLConnection类,二者都是抽象类。其对象主要通过URL的openConnection方法获得的。 下面是具体的代码: packagecom.llingdududu.url; importjava.io.BufferedReader; importjava.io.InputStreamReader; importjava.net.HttpURLConnection; importjava.net.URL; importandroid.app.Activity; importandroid.os.Bundle; publicclassURLConnectionActivityextendsActivity{ StringurlStr="http://developer.android.com/"; /**Calledwhentheactivityisfirstcreated.*/ @Override publicvoidonCreate(BundlesavedInstanceState){ super.onCreate(savedInstanceState); setContentView(R.layout.main); try{ //实例化URL URLurl=newURL(urlStr); //使用HttpURLConnection打开连接 HttpURLConnectionhttpConnection=(HttpURLConnection)url.openConnection(); //得到输入流对象 InputStreamReaderin=newInputStreamReader(httpConnection.getInputStream()); BufferedReaderbufReader=newBufferedReader(in); StringlineStr=""; StringresultStr=""; while((lineStr=bufReader.readLine())!=null){ resultStr+=lineStr+"\n"; } System.out.println(resultStr); //关闭输入流 in.close(); //关闭连接 httpConnection.disconnect(); }catch(Exceptione){ //TODOAuto-generatedcatchblock e.printStackTrace(); } } } 本文转自 lingdududu 51CTO博客,原文链接:http://blog.51cto.com/liangruijun/803159

优秀的个人博客,低调大师

Android编程规范不完全指南

命名规则 1). 类名,接口名: 以大写开头,如果一个类的类名由多个单词组成,所有单词的首字母必须大写,单词尽量写全称,不要简写,除非约定俗成的名字,例如:URL,RTMP,RTSP 这些广泛使用的专有名词,可以全部大写,也可以首字母大写。 例如 HttpRequest,CourseActivity 2). 局部变量,类的成员变量,类的成员函数,函数参数: 以小写字母开头其他的单词首字母大写,变量名不建议使用下划线分隔单词,建议使用驼峰命名法,Android的系统类都采用此方法。 例如 toString() onCreateView(Bundle savedInstanceState) 3). 静态常量:全部大写,单词之间使用下划线分开,常量单词全部大写,所以单词之间使用下划线分隔。 例如 WHAT_EMPTY_CONTENT 4). 控件变量的命名,控件的ID命名: 建议:xml布局文件中的控件的id的命名与*.java的代码文件中的控件对象的命名一致。 classMyActivityextendsActivity{ TextViewtxtUserName; … protectedvoidonCreate(BundlesavedInstanceState){ txtUserName=(TextView)findViewById(R.id.txtUserName); } } 5). 常用控件以及类对象命名的规范说明(红色部分为建议的前缀或者后缀): 类名 变量名 类名 变量名 TextView txtDescription ProgressBar progressDescription Button btnDescription SeekBar seekBarDescription ImageButton imgBtnDescription VideoView vvDescription ImageView imgDescription Spinner spinDescription RadioButton rbDescription WebView webViewDescription EditText editDescription ListView listViewDescription ScrollView scrollDescription GridView gridDescription Handler descriptionHandler RatingBar ratingBarDescription PullToRefreshListView pullRefreshViewDescription Adapter descriptionAdapter Fragment descriptionFragment Activity descriptionActivity List<T> descriptionList Map<> mapDescription SlidingMenu slidMenuDescription ViewPager viewPagerDescription CheckBox chBoxDescription View viewDescription RadioGroup rgDescription ExpandableListView expDescription FrameLayout frameLayDescription SharedPreferences spDescription LinearLayout lineLayDescription RelativeLayout relativeLayDescription startActivityForResult(requestCode) REQUEST_CODE_DESCRIPTION msg.what WHAT_DESCRIPTION 6). 资源命名: layout资源文件的命名(全部小写,下划线分隔): activity的资源文件:activity_description1_description2.xml fragment的资源文件:fragment_description1_description2.xml listview列表项的资源文件:list_item_description1_description2.xml 可复用(被include)的组件资源文件: control_description1_description2.xml drawable资源: controlName_description1_description2_selector.xml controlName表示该资源要用在什么类型的控件上面,例如如果是按钮的图片切换则 应该这么定义 button_bg_sendmessage_selector.xml selector表示该资源的形式,例如还有shape等 图片资源的名字:同上 颜色值的命名: color_description 以color为前缀,全部小写,下划线分隔。description既可以是该颜色值使用的功能描述,也可以是该颜色值的英文描述,也可以是具体的颜色值,例如: <colorname="color_white">#ffffff</color> <colorname="color_grey_ccc">#cccccc</color> <colorname="color_grey_ddd">#dddddd</color> 因为grey可能有很多等级,有时候需要不同等级的灰色,没有那么多英文名可以区分,所以名字中可以直接使用颜色值 <color name=”color_button_pressed”>#4c4c4c</color> 根据功能定义description,表示该颜色用于按钮被按下 注:不允许出现毫无意义的命名,例如textview1,textview2 关于字面常量 代码中不允许出现直接硬编码的字面常量,如果是控件上面显示的文本,必须放在strings.xml资源文件中。 如果是代码中用到常量字符串,必须定义成 public static final String类型的常量值,在代码中使用该定义的常量值。这样做的好处是以后需要修改该常量值,只需要修改一个地方。如果是硬编码在代码中则要修改所有使 用它的地方,而且拷贝容易出错。在Activity之间传递参数的时候,intent.putExtra 的key值也要命名规范,并且统一定义为静态常量,不能直接硬编码在代码中,否则想要修改的时候很麻烦。某一个Activity在被启动的时候需要接受参 数,那么这些参数的key定义就应该放在该Activity中。 JSON解析 Android中调用服务端的接口一般返回的是json数据,在解析json的时候,无论是使用原始的手工解析方式,还是使用javabean的解 析方式,解析出来的结果在使用的时候必须都进行判空处理。不允许因为服务端的json出问题,导致app在解析json的时候出现崩溃。 类成员初始化 所有类的成员变量一定要赋初始值,不允许只定义,不赋值。 Int类型常量 函数返回的时候,如果返回的int类型的数据并不是真实的实用的数据值(例如表示高度,宽度,大小等值),仅仅表示函数执行成功、失败、异常的状态值,并且这些值是有限的几个值,必须要将这些值使用静态常量描述,或者使用枚举,例如: intGetJsonString() 该函数返回-1表示获取解析json数据异常,返回0表示成功,返回1表示网络连接异常,返回2表示json内容中的数据部分为空。那么在函数内部的代码里不要直接使用这些字面值,这些字面值对于程序员来说是毫无意义的,代码可阅读性很差,建议做成下面的模式: publicstaticfinalintRESULT_PARSE_JSON_EXCEPTION=-1; publicstaticfinalintRESULT_SUCCESS=0; publicstaticfinalintRESULT_NETWORK_EXCEPTION=1; publicstaticfinalintRESULT_NO_DATA=2; 使用这些符号常量值代替字面值的好处是,符号常量值是由大写的英文单词组成,是有意义的,可以帮助程序员更好的理解函数返回值的意义,而且符号常量值对应的具体的赋值在后期是很方便修改的。 Activity接受参数与模块化 如果一个Activity可能在多个地方被打开,或者一个Fragment可能在多个地方被用到。那么在设计该Activity和Fragment 的时候一定要考虑低耦合,对外提供统一的参数接口,启 动Activity的过程封装在该Activity类的静态成员方法里面,类似如下: classMyActivityextendsActivity{ ... publicstaticvoidstartActivity(Contextcontext,Paramsparam){ Intentintent=newIntent(context,MyActivity.class); intent.putExtra("param",param); startActivity(intent); } publicstaticvoidstartActivityForResult(Contextcontext,Paramsparam){ Intentintent=newIntent(context,MyActivity.class); intent.putExtra("param",param); startActivityForResult(intent,REQUEST_CODE); } } 参数的传递最好是封装在一个Model实体类中,避免使用Map这种方式进行参数传递。建议该实体类实现为对应的Activity的静态可序列化的内部类。 AndroidStudio工程目录组织 AndroidStudio中的项目的包结构应该根据工程各个部分的功能来组织。 Handler的封装 每一个Activity里面几乎都会定义一个Handler内部类,但是很多Activity里面的Handler都使用了重复的消息类型,这里面 是有冗余代码的,所以应该把这些Activity都使用到的Handler类的消息部分,提取成一个公用的Handler类。然后在各个Activity 里面使用继承的方式,来提供该Activity特有的Handler消息类型的Handler类实现。 另外Handler发送消息应该使用Handler类的成员函数,不应该直接使用handler.obtainMessage(xxx).sendToTarget();这 种原始的发送消息的方式,这样不利于降低耦合,这种细节应该隐藏在Handler内的里面。Handler的消息类型应该定义为Handler类里面的静 态常量,而该常量不应是public的,对外部不可见。也就是说使用handler对象发送消息的细节不应该暴露给外部。 List的数据更新 封装ListView的数据更新,在handlerMessage中更新数据,避免出现 java.lang.IllegalStateException 问题 Activity与Fragment之间传递参数 Activity与Fragment的数据传递采用interface的方式,这样可以降低耦合,有利于Fragment的复用: 网络请求数据模块化 一般在Activity中我们通过网络请求服务端的接口获得数据,这个过程一般是在一个线程中做的,获取到数据之后,再通过Activity中的 handler发送消息来通知Activity更新数据。该负责获取数据的线程类,我们一般都实现为一个Activity的内部类,该类可以直接访问 Activity的成员变量,例如handler,数据列表对象等。但是这样不利于该数据获取线程的复用。如果另一个Activity里面也需要获取相同 的数据,那么这个功能是不能复用的,所以这个负责数据请求的线程类,不应该与具体的Handler和Activity联系过于紧密。应该定义为一个静态 类,handler应该作为参数传递进来,而不是直接访问外部类的成员变量。 封装Log功能 Log功能应该封装成为自动将当前所在类的类名变成log输出的TAG参数,发布的app最好是能循环写日志文件到系统存储中,并且日志文件应该使用反复覆盖的方式重复利用。下面仅仅是一个不完善的例子: publicclassMyLog{ publicstaticfinalStringTAG="myapp"; publicstaticvoidv(Objecto,Stringmessage) { Log.v(TAG+o.getClass().getSimpleName(),message); } } 使用 MyLog.v(this,"hellolog"); 打印结果 V/myappMainActivity﹕hellolog 版本控制 使用自动化版本管理,自动生成版本号,使应用程序的版本与版本库上保持一致。使用hg替换工程目录下的app目录下的build.gradle文件 即可,如果manifest里面也有版本号的设置,AndroidStudio还是以build.gradle为准。不应该在每次发布的时候,在 AndroidStudio的工程设置里面手工修改版本号。 为程序添加全局异常捕获 应该为app添加全局异常捕获,app中总会有一些我们未捕获的异常,一旦用户使用过程中遇到这样的异常,程序就会崩溃,我们应该检测该类未捕获的异常信息,程序崩溃的时候通过写文件日志,或者发送邮件的方式获得异常信息,以便解决bug。 来源:51CTO

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

用户登录
用户注册