java something

不要以为......很遥远
随笔 - 23, 文章 - 1, 评论 - 2, 引用 - 0
数据加载中……

2011年3月23日

Activity生命周期

现有两个Activity:  Activity1,Activity2

先启动Activity1运行顺序为: Activity1 onCreate -> Activity1 onStart -> Activity1 onResume
用Intent从Activity1跳到Activity2运行顺序 : 
Activity1 onPause -> Activity2 onCreate -> Activity2 onStart -> Activity2 onResume ->Activity1 onStop -> Activity1  onDestroy
退出应用程序: Activity2 onResume ->Activity2 onStop -> Activity2  onDestroy

posted @ 2011-09-02 17:48 Jamie 阅读(216) | 评论 (0)编辑 收藏

控制3个线程运行顺序的Demo

本程序可以控制3个线程按顺序执行, 代码如下:

public class Test3 {

 public static void main(String[] args) throws IOException {
  final Test obj = new Test();
  
  new Thread()
  {
   public void run()
   {
    obj.m1();
   }
  }.start();
  new Thread()
  {
   public void run()
   {
    obj.m2();
   }
  }.start();
  new Thread()
  {
   public void run()
   {
    obj.m3();
   }
  }.start();
  
 }

}

class Test
{
 static int count;
 volatile int target = 1;
 synchronized void m1()
 { 
   for (int i = 0; i < 10; i++)
   {
    while (target == 2 || target == 3)
    {
     try {
      wait();
     } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
    }
    System.out.println("m1() =" + i);
    target = 2;
    notifyAll();
   }
 }
 
 synchronized void m2()
 {
  for (int i = 0; i < 10; i++)
  {
   while (target == 1 || target == 3)
   {
    try {
     wait();
    } catch (InterruptedException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
   }
   System.out.println("m2() =" + i);
   target = 3;
   notifyAll();
  }
 }
 
 synchronized void m3()
 {
  for (int i = 0; i < 10; i++)
  {
   while (target == 1 || target == 2)
   {
    try {
     wait();
    } catch (InterruptedException e) {
     // TODO Auto-generated catch block
     e.printStackTrace();
    }
   }
   System.out.println("m3() =" + i);
   target = 1;
   notifyAll();
  }
 }
}

posted @ 2011-09-02 02:27 Jamie 阅读(1759) | 评论 (2)编辑 收藏

线程的同步与共享

     摘要: 线程的同步与共享 前面程序中的线程都是独立的、异步执行的线程。但在很多情况下,多个线程需要共享数据资源,这就涉及到线程的同步与资源共享的问题。 1 资源冲突 下面的例子说明,多个线程共享资源,如果不加以控制可能会产生冲突。 程序CounterTest.java   Code highlighting produced by Actipro CodeHighlight...  阅读全文

posted @ 2011-09-02 01:38 Jamie 阅读(470) | 评论 (0)编辑 收藏

线程的状态与调度

  1,线程的生命周期

        线程从创建、运行到结束总是处于下面五个状态之一:新建状态、就绪状态、运行状态、阻塞状态及死亡状态。



    1.新建状态(New): 
        当用new操作符创建一个线程时, 例如new Thread(r),线程还没有开始运行,此时线程处在新建状态。 当一个线程处于新生状态时,程序还没有开始运行线程中的代码

     2.就绪状态(Runnable)

        一个新创建的线程并不自动开始运行,要执行线程,必须调用线程的start()方法。当线程对象调用start()方法即启动了线程,start()方法创建线程运行的系统资源,并调度线程运行run()方法。当start()方法返回后,线程就处于就绪状态。

        处于就绪状态的线程并不一定立即运行run()方法,线程还必须同其他线程竞争CPU时间,只有获得CPU时间才可以运行线程。因为在单CPU的计算机系统中,不可能同时运行多个线程,一个时刻仅有一个线程处于运行状态。因此此时可能有多个线程处于就绪状态。对多个处于就绪状态的线程是由Java运行时系统的线程调度程序(thread scheduler)来调度的。

    3.运行状态(Running)

        当线程获得CPU时间后,它才进入运行状态,真正开始执行run()方法.

    
4. 阻塞状态(Blocked)

        线程运行过程中,可能由于各种原因进入阻塞状态:
        1>线程通过调用sleep方法进入睡眠状态;
        2>线程调用一个在I/O上被阻塞的操作,即该操作在输入输出操作完成之前不会返回到它的调用者;
        3>线程试图得到一个锁,而该锁正被其他线程持有;
        4>线程在等待某个触发条件;
        ......           

        所谓阻塞状态是正在运行的线程没有运行结束,暂时让出
CPU,这时其他处于就绪状态的线程就可以获得CPU时间,进入运行状态。

    5. 死亡状态(Dead)

        有两个原因会导致线程死亡:
        1) run方法正常退出而自然死亡,
        2) 一个未捕获的异常终止了run方法而使线程猝死。
        为了确定线程在当前是否存活着(就是要么是可运行的,要么是被阻塞了),需要使用isAlive方法。如果是可运行或被阻塞,这个方法返回true; 如果线程仍旧是new状态且不是可运行的, 或者线程死亡了,则返回false.




2,  线程的优先级和调度

Java的每个线程都有一个优先级,当有多个线程处于就绪状态时,线程调度程序根据线程的优先级调度线程运行。

可以用下面方法设置和返回线程的优先级。

    · public final void setPriority(int newPriority) 设置线程的优先级。

    · public final int getPriority() 返回线程的优先级。

newPriority为线程的优先级,其取值为110之间的整数,也可以使用Thread类定义的常量来设置线程的优先级,这些常量分别为:Thread.MIN_PRIORITYThread.NORM_PRIORITYThread.MAX_PRIORITY,它们分别对应于线程优先级的1510,数值越大优先级越高。当创建Java线程时,如果没有指定它的优先级,则它从创建该线程那里继承优先级。

一般来说,只有在当前线程停止或由于某种原因被阻塞,较低优先级的线程才有机会运行。

前面说过多个线程可并发运行,然而实际上并不总是这样。由于很多计算机都是单CPU的,所以一个时刻只能有一个线程运行,多个线程的并发运行只是幻觉。在单CPU机器上多个线程的执行是按照某种顺序执行的,这称为线程的调度(scheduling)

大多数计算机仅有一个CPU,所以线程必须与其他线程共享CPU。多个线程在单个CPU是按照某种顺序执行的。实际的调度策略随系统的不同而不同,通常线程调度可以采用两种策略调度处于就绪状态的线程。

(1) 抢占式调度策略

     Java运行时系统的线程调度算法是抢占式的 (preemptive)Java运行时系统支持一种简单的固定优先级的调度算法。如果一个优先级比其他任何处于可运行状态的线程都高的线程进入就绪状态,那么运行时系统就会选择该线程运行。新的优先级较高的线程抢占(preempt)了其他线程。但是Java运行时系统并不抢占同优先级的线程。换句话说,Java运行时系统不是分时的(time-slice)。然而,基于Java Thread类的实现系统可能是支持分时的,因此编写代码时不要依赖分时。当系统中的处于就绪状态的线程都具有相同优先级时,线程调度程序采用一种简单的、非抢占式的轮转的调度顺序。

(2) 时间片轮转调度策略

    有些系统的线程调度采用时间片轮转(round-robin)调度策略。这种调度策略是从所有处于就绪状态的线程中选择优先级最高的线程分配一定的CPU时间运行。该时间过后再选择其他线程运行。只有当线程运行结束、放弃(yield)CPU或由于某种原因进入阻塞状态,低优先级的线程才有机会执行。如果有两个优先级相同的线程都在等待CPU,则调度程序以轮转的方式选择运行的线程。

 3.  线程状态的改变

一个线程在其生命周期中可以从一种状态改变到另一种状态,线程状态的变迁如图所示:

    
    
1>  控制线程的启动和结束

当一个新建的线程调用它的start()方法后即进入就绪状态,处于就绪状态的线程被线程调度程序选中就可以获得CPU时间,进入运行状态,该线程就开始运行run()方法。

控制线程的结束稍微复杂一点。如果线程的run()方法是一个确定次数的循环,则循环结束后,线程运行就结束了,线程对象即进入死亡状态。如果run()方法是一个不确定循环,早期的方法是调用线程对象的stop()方法,然而由于该方法可能导致线程死锁,因此从1.1版开始,不推荐使用该方法结束线程。一般是通过设置一个标志变量,在程序中改变标志变量的值实现结束线程。请看下面的例子:

程序 ThreadStop.java

import java.util.*;

class Timer implements Runnable{

    
boolean flag=true;
    
public void run(){
      
while(flag){
        System.out.print(
"\r\t"+new Date()+"");
        
try{
              Thread.sleep(
1000);
        }
catch(InterruptedException e){} 
      }
      System.out.println(
"\n"+Thread.currentThread().getName()+" Stop");
    }

    
public void stopRun(){
           flag 
= false;
    }
}

public class ThreadStop{
    
public static void main(String args[]){
       Timer timer 
= new Timer();
       Thread thread 
= new Thread(timer);       
       thread.setName(
"Timer");
       thread.start();

       
for(int i=0;i<100;i++){
         System.out.print(
"\r"+i);
        
try{
              Thread.sleep(
100);
        }
catch(InterruptedException e){} 
       }     
       timer.stopRun();
    }
}

该程序在Timer类中定义了一个布尔变量flag,同时定义了一个stopRun()方法,在其中将该变量设置为false。在主程序中通过调用该方法,从而改变该变量的值,使得run()方法的while循环条件不满足,从而实现结束线程的运行。

说明  Thread类中除了stop()方法被标注为不推荐(deprecated) 使用外,suspend()方法和resume()方法也被标明不推荐使用,这两个方法原来用作线程的挂起和恢复.

2>  线程阻塞条件

处于运行状态的线程除了可以进入死亡状态外,还可能进入就绪状态和阻塞状态。下面分别讨论这两种情况:

(1) 运行状态到就绪状态

处于运行状态的线程如果调用了yield()方法,那么它将放弃CPU时间,使当前正在运行的线程进入就绪状态。这时有几种可能的情况:如果没有其他的线程处于就绪状态等待运行,该线程会立即继续运行;如果有等待的线程,此时线程回到就绪状态状态与其他线程竞争CPU时间,当有比该线程优先级高的线程时,高优先级的线程进入运行状态,当没有比该线程优先级高的线程时,但有同优先级的线程,则由线程调度程序来决定哪个线程进入运行状态,因此线程调用yield()方法只能将CPU时间让给具有同优先级的或高优先级的线程而不能让给低优先级的线程。

一般来说,在调用线程的yield()方法可以使耗时的线程暂停执行一段时间,使其他线程有执行的机会。

(2) 运行状态到阻塞状态

有多种原因可使当前运行的线程进入阻塞状态,进入阻塞状态的线程当相应的事件结束或条件满足时进入就绪状态。使线程进入阻塞状态可能有多种原因:

线程调用了sleep()方法,线程进入睡眠状态,此时该线程停止执行一段时间。当时间到时该线程回到就绪状态,与其他线程竞争CPU时间。

Thread类中定义了一个interrupt()方法。一个处于睡眠中的线程若调用了interrupt()方法,该线程立即结束睡眠进入就绪状态。

如果一个线程的运行需要进行I/O操作,比如从键盘接收数据,这时程序可能需要等待用户的输入,这时如果该线程一直占用CPU,其他线程就得不到运行。这种情况称为I/O阻塞。这时该线程就会离开运行状态而进入阻塞状态。Java语言的所有I/O方法都具有这种行为。

③ 有时要求当前线程的执行在另一个线程执行结束后再继续执行,这时可以调用join()方法实现,join()方法有下面三种格式:

·         public void join() throws InterruptedException 使当前线程暂停执行,等待调用该方法的线程结束后再执行当前线程。

·         public void join(long millis) throws InterruptedException 最多等待millis毫秒后,当前线程继续执行。

·         public void join(long millis, int nanos) throws InterruptedException 可以指定多少毫秒、多少纳秒后继续执行当前线程。

上述方法使当前线程暂停执行,进入阻塞状态,当调用线程结束或指定的时间过后,当前线程线程进入就绪状态,例如执行下面代码:

t.join();

将使当前线程进入阻塞状态,当线程t执行结束后,当前线程才能继续执行。

④ 线程调用了wait()方法,等待某个条件变量,此时该线程进入阻塞状态。直到被通知(调用了notify()notifyAll()方法)结束等待后,线程回到就绪状态。

另外如果线程不能获得对象锁,也进入就绪状态。

后两种情况在下一节讨论。



















posted @ 2011-09-01 21:43 Jamie 阅读(3993) | 评论 (0)编辑 收藏

复习下java多线程

好久没搞这个了,今天把以前的笔记整理下,当复习。

Thread类和Runnable接口

多线程是一个程序中可以有多段代码同时运行,那么这些代码写在哪里,如何创建线程对象呢?

    首先,我们来看Java语言实现多线程编程的类和接口。在java.lang包中定义了Runnable接口和Thread类。

 

Runnable接口中只定义了一个方法:

·         public abstract void run()

这个方法要由实现了Runnable接口的类实现。Runnable对象称为可运行对象,一个线程的运行就是执行该对象的run()方法。


      Thread
类实现了Runnable接口,因此Thread对象也是可运行对象。同时Thread类也是线程类,该类的常用构造方法如下:

·         public Thread()

·         public Thread(Runnable target)

·         public Thread(String name)

·         public Thread(Runnable target, String name)
target为线程运行的目标对象,即线程调用start()方法启动后运行那个对象的run()方法,该对象的类型为Runnable,若没有指定目标对象,则以当前类对象为目标对象,name为线程名


 

  线程的创建 

介绍下如何创建和运行线程的两种方法。线程运行的代码就是实现了Runnable接口的类的run()方法或者是Thread类的子类的run()方法,因此构造线程体就有两种方法:
    ·         继承Thread类并覆盖它的run()方法;
    ·        
实现Runnable接口并实现它的run()方法。

  1,继承Thread类创建线程

通过继承Thread类,并覆盖run()方法,这时就可以用该类的实例作为线程的目标对象。下面的程序定义了SimpleThread类,它继承了Thread类并覆盖了run()方法。

程序SimpleThread.java

public class SimpleThread extends Thread{

  public SimpleThread(String str){

    super(str);

}

public void run(){

    for(int i=0; i<100; i++){

      System.out.println(getName()+" = "+ i);

      try{

         sleep((int)(Math.random()*100));

      }catch(InterruptedException e){}

    }

System.out.println(getName()+ " DONE");

}

}

_____________________________________________________________________________

    SimpleThread类继承了Thread类,并覆盖了run()方法,该方法就是线程体。

程序 ThreadTest.java

public class ThreadTest{

  public static void main(String args[]){

    Thread t1 = new SimpleThread("Runner A");

    Thread t2 = new SimpleThread("Runner B");

    t1.start();

    t2.start();

 }

}

_____________________________________________________________________________

ThreadTest类的main()方法中创建了两个SimpleThread类的线程对象并调用线程类的start()方法启动线程。构造线程时没有指定目标对象,所以线程启动后执行本类的run()方法。

注意,实际上ThreadTest程序中有三个线程同时运行,在应用程序的main()方法启动时,JVM就创建一个主线程,在主线程中可以创建其他线程。

  2,实现Runnable接口创建线程

可以定义一个类实现Runnable接口,然后将该类对象作为线程的目标对象。实现Runnable接口就是实现run()方法。

下面程序通过实现Runnable接口构造线程体。

程序 ThreadTest.java

class T1 implements Runnable{

  public void run(){

    for(int i=0;i<15;i++)

      System.out.println("Runner A="+i);

  }

}

class T2 implements Runnable{

  public void run(){

    for(int j=0;j<15;j++)

      System.out.println("Runner B="+j);

  }

}

public class ThreadTest{

  public static void main(String args[]){

    Thread t1=new Thread(new T1(),"Thread A");

    Thread t2=new Thread(new T2(),"Thread B");

    t1.start();

    t2.start();

  }

}

_____________________________________________________________________________




    

 



posted @ 2011-09-01 20:46 Jamie 阅读(375) | 评论 (0)编辑 收藏

android 项目下文件的作用

1, R.java 是建立项目时自动生成的,只读,用来定义该项目所有资源的索引文件。
这里面定义了很多常量, 名字与res文件夹的文件名和String.xml里的定义的常量名相同。当项目中加入了新的资源时,只需要刷新一下该项目,R.java 便自动生成了。
2, strings.xml 里面定义了字符串资源。 
    在类中可通过如下方式使用这些资源, Resource r = this.getContext().getResources(); String str = ((String) r.getString(R.string.name));
    在main.xml中可以 android:text="@string/name"
3,  mail.xml 用来写UI(布局,控件...)
    主程序继承Activity类,重写了void onCreate(Bundle savedInstanceState)方法。 在方法里通过setContentView(R.layout.main)设置Activity要显示的布局文件(\layout\main.xml)
4.  AndroidManifest.xml
    看下默认的:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.test"
      android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="8" />

    <application android:icon="@drawable/icon" android:label="@string/app_name">   //应用程序的名字
        <activity android:name=".WuActivity"   //默认启动哪个Activity
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>
</manifest>

posted @ 2011-08-24 02:33 Jamie 阅读(1018) | 评论 (0)编辑 收藏

关于ADT和SDK版本

用最新的就都用最新的, 不然有可能导致配置过程中出现一些问题。

我用SDK3.2和ADT 0.9.5配置, 结果Preferences->Android里设置路径出现问题。

posted @ 2011-08-23 17:45 Jamie 阅读(244) | 评论 (0)编辑 收藏

Content is not allowed in prolog

錯誤可能是由XML有中文格式的字符引起的。

posted @ 2011-04-20 08:43 Jamie 阅读(278) | 评论 (0)编辑 收藏

Jree

树控件很适合用来显示,导航和编辑结构化对象;但是JTree很复杂,swing中有整个一个包都是针对它的(javax.swing.tree),注意树控件是显示的,但是树数据结构是一种集合接口的实现,就如同JList和java.util.List一样,他们应用在不同层,当然你使用Jlist来显示List接口的实现者那是很般配的。
*
**
关于树的术语如根,节点,叶子,深度,路径,平衡性,边,子树;不需要我这里过多的解释,任何一本数据结构的书籍都会介绍他们。我这里主要是讲述树控件。

树遍历这个概念先提一下:遍历即逐个访问,对于树而言主要有三种:前序,后序,中序遍历树的每个节点。遍历一般是用递归算法来实现的,三种遍历法区别于先访问树的那个部分。树遍历也是比较难的一个技术,不好掌握,我在大学时用汇编实现过这三种算法,到现在还记忆犹新(完全自己找到的规律),一下来和朋友们分享一下。
对于一个有两个子节点的树(或多个子节点),请你沿着树的外围画一个轮廓线:

 

———>         >——————
        /             \
      /                 \
     /_____>____\     

       这是大致绕树行走的轮廓线,大家都知道(或许你还不知道)函数的调用时控制流的传递就是这个样子的。(控制流是线程执行方法时的形象表述)比如一下函数: main(){

     f1();
     f2();
}//该函数的控制流向是:先传给main,再由main()传给f1,之后退回到mian(),在传给f2()在由f2退回给main之后结束程序。异步方法调用时才会从这个封闭的轮廓中分出一个分支来。现在来谈你如何设计一个树遍历方法:
我们来看一个函数的几个关键部位,
       func(){
        entry入口处
          
            中间部位           

        return出口处   
      }也许你很迷惑这与树遍历算法有和关系,告诉你吧这三个特殊部位就是你在设计递归时,递归函数应该出现的位置,他们出现在不同的位置就是不同的“序”,伪码如:
先序遍历
traversTree(Node root){
   if(root !=null){
     if(root.isLeaf()){//当是叶子时,
         visit(root);//前序遍历是先遍历页节点
     }   
         Node[] children=root.getChildren();//获取所有子树
         for(Node n:children){
           traversTree(n);//递归遍历所有子树,注意子树可能为空。
          }
   }

}

中序遍历(亦称广度优先遍历,总是先遍历树的根)

traversTree(Node root){
   if(root !=null){
     //树非空
     visit(root); //这是中序遍历 visit出现与递归函数之前。
    
     Node[] children=root.getChildren();//获取所有子树
         for(Node n:children){
           traversTree(n);//递归遍历所有子树,注意子树可能为空。
          }
   }

}

后序遍历(亦称深度优先搜索):
traversTree(Node root){
   if(root !=null){
        
     Node[] children=root.getChildren();//获取所有子树
         for(Node n:children){
           traversTree(n);//递归遍历所有子树,注意子树可能为空。
          }
    
     
     visit(root); //这是后序遍历 visit出现在递归函数之后。
   }

}

以上三个算法,可能有点不正确,我没测试过,时间太久了有点忘了,总之为大家做个参考吧!
因为树结构典型的是应用了组合设计模式,所以只要涉及到树肯定涉及遍历,和递归。所以这里罗嗦一下。   所有的树都是节点                   
**
***
swing中Jtree处理树结构是通过树模型接口,它实现了TreeNode接口(在api文档中竟看不到此信息!),DefaultMutableTreeNode类实现了TreeNode接口,并提供了前,中,后序的树遍历能力。
JTree图形化的显示了树结构的每一个节点,所有的UI控件都有两个目的,显示和输入(输入包括数据的输入如JTextField和命令输入如菜单,按钮等),JTree既可以用于树结构的显示,也可以用于命令的输入,或使得我们编辑树节点。
树节点可以被选中,它由TreeSelectionModel来控制,选择涉及到维护作为TreeNode实例的树节点间的路径轨迹。树控件典型的可以激发两类事件:TreeModelEvent和TreeExpansionEvent,当然其他Awt和Swing事件也可由树控件激发(看其继承层次结构即可知)比如MouseListener可用来截取鼠标事件。JTree扩展了Scrollable接口,可被放在一个滚动面板中。

JTree的构造:可使用默认的构造方法,提供一个TreeNode作为其根节点,提供一个TreeModel包含所有其它的节点,或提供一个一维数组,向量,或对象的哈希表,对于这些集合中的单个元素,如果它又是一个集合,那么他们会被解释显示为子树,该功能由JTree的内部类DynamicUtilTreeNode完成。
***
****
TreeModel接口:
该接口的实现者为JTree提供显示的数据,如果你熟悉MVC模式,你应该明白所有的swing或awt控件中模型的作用就是为相应的控件提供数据。当模型的数据结构有所变化时它会通知视图(这里就是JTree)来更新显示。当然模型也可以添加其他的监听器如Jtree的addTreeModelListener,你可以实现该监听器,来使你自己的类接收模型变化给你的通知。如果你不熟悉MVC模式,请参考POSA卷一或其他资料,顺便提一下在我们学校GUI时都知道有MVC模式的应用,往往不知道那个Controller是什么类,其实就是视图的监听器,比如ActionListener,注意别被众多的监听器弄昏了,一类是模型需要添加的,一类是视图(比如JComponent的子类)需要添加的。控制的流向或数据的流向是相反的,视图需要添加的监听器(我们常常实现他们)才是控制器。

因为模型和视图都能够触发事件,比如视图(JTree等控件)是触发用户输入导致的事件,而模型触发的事件是因为模型中维护的数据有所变动才触发的(比如,树模型中树节点的增删,改,或树路径的变动)。而他们都使用了观察者模式,算了不多说了,到时全弄成模式了,会搞昏大家的。继续....

JTree的setModel和getModel方法是用来更换/设置和获取模型的方法。你可替换现有JTree的模型,或者你想这样用,两个模型,一个用,一个备。如果构造模型复杂耗时的话,先在后台构造好一个在换掉原先的。就如同双缓冲技术的思路那样。
****
*****
杂项:
DefultTreeModel是对TreeModel接口的默认实现类,
TreeNode接口可告诉你改实现者是否为一个叶子,一个父节点等。MutalbeTreeNode接口扩展了TreeNode接口,我们可在该实现者中存放一个我们自己的类实例(setUserObject()/getUserObject);
defaultMutableTreeNode 实现了MutableTreeNode接口,children()方法返回以一维向量形式存放的直接子节点的枚举,也可以使用getChildAt()返回特定索引位置的子节点(注意子节点完全可以是一颗子树)该类提供了前中后序访问树的能力:preorderEnumeration(),,breadthFirstEnumeration(),depthFirstEnumeration()postorderEnumeration()最后两个方法同行为,只不过是不同的称号而已。

TreePath:该类用一系列节点表示一个从树根到一个节点的路径,它是只读的,提供与其他路径比较的能力。
TreeCellRenderrer接口:渲染tree的一个单元的组件,我们自己实现该接口并用jtree的setCellRenderer()方法替换原先的渲染器,可以是树节点在选中,获取焦点,不同的树状态(叶子或父节点,展开,或收缩)等不同的状态下的外观。
DefaultTreeCellRenderer类是TreeCellRenderrer接口的默认实现,它扩展了JLabel,并基于以上描述的树状态来渲染树节点,其提供的属性包括图标,背景色,前景色等,其get和set方法是我们可以访问的,通过这些方法你当然可以换掉树节点的图标了。

CellEditor接口:定义了控制何时编辑将开始,结束,提取一个新的结果,是否编辑请求改变当前组件的选择,请参考API文档看该接口的方法。该接口在JTree和JTable中都有用到。,该接口也可以添加监听器,当编辑停止或取消时会激发ChangeEvents到其所有的注册处理器哪里。
TreeCellEditor接口扩展了CellEditor接口,jtree的setCellEditor()使得我们可以用任何一个可充当编辑器的组件替换掉原来的那个。DefaultCellEditor实现了该接口,这个编辑器允许使用JTextField,JComboBox或是JCheckBox组件来编辑数据,其保护的内部类EditorDelegate会响应getCellEditorValue()方法把当前值返回。DefaultCellEditor仅基于以上三个J控件作为编辑器,其clickCountToStart方法决定鼠标单击几次会触发编辑。默认对于JTextField是两次,JComboBox和JCheckBox是一次,changeEvents会在stopCellEditing()和cancelCellEditing()时激发。
DefaultTreeCellEditor扩展了DefaultCellEditor类并且是TreeCellEditor的默认实现类,他使用JTextField来编辑节点数据,在键入ENTER键后stopCellEditing()会被调用。对于树节点的编辑我们可添加自己的时间监听器来处理他们。默认时编辑开始于节点被单击三次或两次(时间间隔在内部会用一个定时器来决定),也可以改变他们的数目setClickCountToStart();
JTree的选择是基于行和树路径的,我们可以选择使用那个。

TreeSelectionModel接口用于树选择模型,支持三种选择,SINGLE_TREE_SELECTION,
DISCONTIGUOUS_TREE_SELECTION,CONTIGUOUS_TREE_SELECTION,set/getSelectionMode()可以访选择模型。getSelectionPath『s』()会返回一个当前选中的树路径。DefaultTreeSelectionModel默认实现了该接口,该类提供TreeSelectionlistener通知,当树路径选择发生变化时。

TreeModelListener实现者可以侦听模型变化,TreeSelectionListener用来侦听视图JTree的selection(仅有一个方法valueChanged(TreeSlectcionEvent tsEvt));
TreeExpansionListener用来对树展开收缩进行处理。
TreeW illExpandListener在树“将要”展开和收缩时得到通知,你可截获处理,ExpandVetoException异常如果抛出,那么树不会展开和收缩。

TreeModelEvent,用来通知模型的监听器,JTree的数据部分或全部发生了变化。该事件对象封装了源组件的引用,封装了一个TreePath或一个用来表示路径的数组。
TreeselectionEvent,视图会用其通知所有视图监听器TreeSelectionListeners,选择发生了变化。
TreeExpansionEvent,用来封装相应最近或可能展开或收缩的TreePath,使用getPath()方法访问树路径。
ExpandVetoException异常可由TreeWillExpandListener抛出,来否决树路径的展开和收缩。

JTree提供的现成方便的UI属性:
myJTree.putClientProperty("JTree.lineStyle", "Angled");//更改线型。
如同其他Swing组件,我们也可以改变默认的用于JTree的UI资源(全局性的):
UIManager.put("Tree.hash",
new ColorUIResource(Color.lightGray));//改变渲染节点间edges边的颜色。
UIManager.put("Tree.openIcon", new IconUIResource(
new ImageIcon("myOpenIcon.gif")));//改变一个打开的树节点的图标。同理可用于其它情况:Tree.leafIcon, Tree.expandedIcon,和Tree.closedIcon, Tree.collapsedIcon。

其他控制TreeUI显示的方法:
myTree.setRowHeight()//控制树节点的行高,
JTree的UI委托也提供了更改树外观的方法(相比于UIManager的方法,这里是局部的)。
BasicTreeUI basicTreeUI = (BasicTreeUI) myJTree.getUI();
basicTreeUI.setRightChildIndent(10);
basicTreeUI.setLeftChildIndent(8);


以上简要提及了JTree的方方面面,许多的事件,将听器模型,请仔细分析,一定要分清哪些是针对模型的那些是针对视图的。

*****
******
简单的示例,我这里仅用到了最简单的树构造方法,和一个监听器,在
以后我的自学过程中,我会继续试用其他的JTree知识,我的JTree学习
最终都是想实现那个GUI上的授权控制系统,请参考其他篇章,
至于这里用到的LAndFSysMenu类,在我的其他篇章中有该类的实现。
package jTreeDemo;

import java.awt.Container;
import javax.swing.*;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import lookAndFeelSys.*;
import userInterfaces.UIUtil;
import java.awt.*;
import java.util.Hashtable;
import java.util.Vector;
import javax.swing.tree.*;


public class JTreeTest extends JFrame{

public static void main(String[] args){
new JTreeTest("测试");
}

public JTreeTest(String title){
   super(title);
   biuldFrame();
}

   private void biuldFrame(){
    JMenuBar jmb=new JMenuBar();
   
    JMenu jm=new LAndFSysMenu();
    //JMenu jm=new JMenu("hello");
   
    jmb.add(jm);
    this.setJMenuBar(jmb);
   
    buildFrmContent();   
   
    UIUtil.SetComponentDimension(this,0.5,0.6);
    UIUtil.SetComponentToCenterOfScreen(this);
    this.setVisible(true);
    this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
   }
  
   private void buildFrmContent(){
    Container root_c=this.getContentPane();
    JTabbedPane jtp=new JTabbedPane();
    Container c = new JPanel();
    jtp.addTab("静态树组件练习",c );
    jtp.addTab("事件监听",this.treeDemo2());
     

   root_c.add(jtp);

c.setLayout(new GridLayout(2,4));
JScrollPane jsp_1=new JScrollPane();
JScrollPane jsp_2=new JScrollPane();
JScrollPane jsp_3=new JScrollPane();
JScrollPane jsp_4=new JScrollPane();


/*为JTree准备显示的模型*/
Object[] m1=new String[]{"节点1","节点2","节点3"};
Object[] m2=new String[][]{
    {"1.1","1.2","1.3"},
    {"2.1","2.2","2.3"},
    {"3.1","3.2","3.3"}
};
Vector<Object> m3=new Vector<Object>();
m3.add("1");
m3.add("2");
m3.add(m1);
m3.add(m2);
Hashtable<String,Object> m4=new Hashtable<String,Object>();
m4.put("子一","叶子");
m4.put("子二", m1);
m4.put("子三",m3);


JTree jtr_1=new JTree(m1);
jsp_1.getViewport().add(jtr_1);
JTree jtr_2=new JTree(m2);
jsp_2.getViewport().add(jtr_2);
JTree jtr_3=new JTree(m3);
jsp_3.getViewport().add(jtr_3);
JTree jtr_4=new JTree(m4);
jsp_4.getViewport().add(jtr_4);


c.add(jsp_1);
c.add(jsp_2);
c.add(jsp_3);
c.add(jsp_4);

/*jsp_1.getViewport().add(jtr_1);
c.add(jsp_1);*/
   }

   /*<<     另一组JTree实例:*/
   private JPanel treeDemo2(){
    JPanel rsltPanel=new JPanel();
    rsltPanel.setLayout(new BorderLayout());
   
    JLabel jl_msg=new JLabel("此标签用来显示树选择情况");
    JScrollPane jsp_1=new JScrollPane();
    Object[] m=new String[]{"节点1","节点2","节点3"};
    JTree jtr=new JTree(m);
    jtr.getSelectionModel()
    .addTreeSelectionListener(new MySelectionLstnr(
      jl_msg));
   
    jsp_1.getViewport().add(jtr);
   
    rsltPanel.add(jsp_1,BorderLayout.CENTER);
    rsltPanel.add(jl_msg,BorderLayout.SOUTH);
    return rsltPanel;
   }
   class MySelectionLstnr implements TreeSelectionListener{
    //该内部类实现树监听器,在树被选中后将选中的节点
    //信息打印到一个Label上
    private JLabel jl_msg=null;
   
    public MySelectionLstnr(JLabel msgLabel){
    this.jl_msg=msgLabel;
    }
@Override
public void valueChanged(TreeSelectionEvent e) {
   // 凡是树选择的处理都涉及到树路径的处理:
   TreePath path = e.getPath();
   Object[] nodes = path.getPath();
  
   //当前选中的节点是树路径上最后一个节点
     Object selectedNode=nodes[nodes.length-1 ];
     if(this.jl_msg!=null){
     this.jl_msg.setText("选中的节点上的文本是:"+
         selectedNode.toString());
     }
}


   }
   /*另一组JTree实例:>>*/
}

 

******
参考Java Swing (Manning出版社)swing hack (orelly出版社)。

posted @ 2011-03-23 09:09 Jamie| 编辑 收藏

JTree用法

import  java.awt.Dimension;
import  java.awt.Color;
import  javax.swing.JFrame;
import  javax.swing.JPanel;
import  javax.swing.JScrollPane;
import  javax.swing.JTree;
import  javax.swing.BoxLayout;
import  javax.swing.tree.TreePath;
import  javax.swing.tree.DefaultMutableTreeNode;
import  javax.swing.tree.DefaultTreeModel;
/*
JTree的构造函数:
JTree()
JTree(Hashtable value)
JTree(Object[] value)//只有这个构造函数可以创建多个根结点
JTree(TreeModel newModel)
JTree(TreeNode root)
JTree(TreeNode root, boolean asksAllowsChildren)
JTree(Vector value)

*/
public   class  JTreeDemo
{
 
public   static   void  main (String[] args)
 {


  
// 构造函数:JTree()
  JTree example1  =   new  JTree();

 

  
  
// 构造函数:JTree(Object[] value)
  Object[] letters =  { " a " " b " " c " " d " " e " };
  JTree example2 
=   new  JTree (letters);

 


  
// 构造函数:JTree(TreeNode root)(TreeNode空)
  
// 用空结点创建树
  DefaultMutableTreeNode node1  =   new  DefaultMutableTreeNode(); // 定义树结点
  JTree example3  =   new  JTree (node1); // 用此树结点做参数调用 JTree的构造函数创建含有一个根结点的树

 


  
// 构造函数:JTree(TreeNode root)(同上,只是TreeNode非空)
  
// 用一个根结点创建树
  DefaultMutableTreeNode node2  =   new  DefaultMutableTreeNode( " Color " );
  JTree example4 
=   new  JTree (node2); // 结点不可以颜色,默认为白面黑字
  example4.setBackground (Color.lightGray);

 


  
// 构造函数:JTree(TreeNode root, boolean asksAllowsChildren)(同上,只是TreeNode又有不同)
  
// 使用DefaultMutableTreeNode类先用一个根结点创建树,设置为可添加孩子结点,再添加孩子结点
  DefaultMutableTreeNode color  =   new  DefaultMutableTreeNode( " Color " true );
  DefaultMutableTreeNode gray 
=   new  DefaultMutableTreeNode ( " Gray " );
  color.add (gray);
  color.add (
new  DefaultMutableTreeNode ( " Red " ));
  gray.add (
new  DefaultMutableTreeNode ( " Lightgray " ));
  gray.add (
new  DefaultMutableTreeNode ( " Darkgray " ));
  color.add (
new  DefaultMutableTreeNode ( " Green " ));
  JTree example5 
=   new  JTree (color);
  
  
  
  
  
// 构造函数:JTree(TreeNode root)(同上,只是TreeNode非空)
  
// 通过逐个添加结点创建树
  DefaultMutableTreeNode biology  =   new  DefaultMutableTreeNode ( " Biology " );
  DefaultMutableTreeNode animal 
=   new  DefaultMutableTreeNode ( " Animal " );
  DefaultMutableTreeNode mammal 
=   new  DefaultMutableTreeNode ( " Mammal " );
  DefaultMutableTreeNode horse 
=   new  DefaultMutableTreeNode ( " Horse " );
  mammal.add (horse);
  animal.add (mammal);
  biology.add (animal);
  JTree example6 
=   new  JTree (biology);
  horse.isLeaf();
  horse.isRoot();
  
  


  
// 构造函数:JTree(TreeModel newModel)
  
// 用DefaultMutableTreeNodel类定义一个结点再用这个结点做参数定义一个用DefaultTreeMode
  
// 创建一个树的模型,再用JTree的构造函数创建一个树
  
  DefaultMutableTreeNode root 
=   new  DefaultMutableTreeNode ( " Root1 " );
  DefaultMutableTreeNode child1 
=   new  DefaultMutableTreeNode ( " Child1 " );
  DefaultMutableTreeNode child11 
=   new  DefaultMutableTreeNode ( " Child11 " );
  DefaultMutableTreeNode child111 
=   new  DefaultMutableTreeNode ( " Child111 " );
  root.add (child1); child1.add (child11); child11.add (child111);
  
  
  
  DefaultTreeModel model 
=   new  DefaultTreeModel (root);
  
  JTree example7 
=   new  JTree (model);

 

  JPanel panel 
=   new  JPanel();
  panel.setLayout (
new  BoxLayout (panel, BoxLayout.X_AXIS));
  panel.setPreferredSize (
new  Dimension ( 700 400 ));
  panel.add (
new  JScrollPane (example1)); // JTree必须放在JScrollPane上
  panel.add ( new  JScrollPane (example2));
  panel.add (
new  JScrollPane (example3));
  panel.add (
new  JScrollPane (example4));
  panel.add (
new  JScrollPane (example5));
  panel.add (
new  JScrollPane (example6));
  panel.add (
new  JScrollPane (example7));
  

 

  JFrame frame 
=   new  JFrame ( " JTreeDemo " );
  frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
  frame.setContentPane (panel);
  frame.pack();
  frame.show();
 }
}
××××××××××××××××××××××××××××××××××××××××××××××

在实际开发过程中会经常使用JTree组件,平时会遇到这样或那样的问题,在此将偶得一点经验写下来,与大家共享,希望对大家有所帮助。

private JTree jtNetDevice;//数组件申明
private JScrollPane jspTree;//滚动面板申明


1、初始化
    DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("root");
    jtNetDevice = new JTree(rootNode);
    jtNetDevice.setAutoscrolls(true);
    getTreeSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);//设置单选模式
    jspTree = new JScrollPane();
    jspTree.getViewport().add(jtNetDevice, null);

2、三个经常使用的取值函数
  private DefaultTreeModel getTreeModel(){
    return (DefaultTreeModel)jtNetDevice.getModel();
  }

  private DefaultMutableTreeNode getRootNode(){
    return (DefaultMutableTreeNode)getTreeModel().getRoot();
  }
 
  private TreeSelectionModel getTreeSelectionModel(){
    return jtNetDevice.getSelectionModel();
  }
 

3、根据node得到path:
  TreePath visiblePath = new TreePath(getTreeModel().getPathToRoot(node));

4、根据Path展开到该节点
  jtNetDevice.makeVisible(visiblePath);

5、根据path设定该节点选定
  jtNetDevice.setSelectionPath(visiblePath);

6、选中节点的方法
  首先,根据节点得到树路径,其中chosen为需要选中的节点
  TreePath visiblePath = new TreePath( ( (DefaultTreeModel) jtNetDevice.getModel()).
                                        getPathToRoot(chosen));
  然后根据Path选中该节点
  jtNetDevice.setSelectionPath(visiblePath);

7、滚动到可见位置
  jtNetDevice.scrollPathToVisible(visiblePath);

8、给JTree添加右键弹出菜单
  void jtNetDevice_mouseReleased(MouseEvent e) {
    if (e.isPopupTrigger()) {
      jPopupMenu1.show(e.getComponent(), e.getX(), e.getY());//弹出右键菜单
    }
  }

9、关于JTree的展开
   // If expand is true, expands all nodes in the tree.
   // Otherwise, collapses all nodes in the tree.
   public void expandAll(JTree tree, boolean expand) {
       TreeNode root = (TreeNode)tree.getModel().getRoot();
  
       // Traverse tree from root
       expandAll(tree, new TreePath(root), expand);
   }
   private void expandAll(JTree tree, TreePath parent, boolean expand) {
       // Traverse children
       TreeNode node = (TreeNode)parent.getLastPathComponent();
       if (node.getChildCount() >= 0) {
           for (Enumeration e=node.children(); e.hasMoreElements(); ) {
               TreeNode n = (TreeNode)e.nextElement();
               TreePath path = parent.pathByAddingChild(n);
               expandAll(tree, path, expand);
           }
       }
  
       // Expansion or collapse must be done bottom-up
       if (expand) {
           tree.expandPath(parent);
       } else {
           tree.collapsePath(parent);
       }
   }
 

10、如何遍历JTree
   // 创建树
   JTree tree = new JTree();
  
   // 添加树节点......
  
   // 遍历所有节点
   visitAllNodes(tree);
  
   // 仅遍历展开的节点
   visitAllExpandedNodes(tree);
  
   // Traverse all nodes in tree
   public void visitAllNodes(JTree tree) {
       TreeNode root = (TreeNode)tree.getModel().getRoot();
       visitAllNodes(root);
   }
   public void visitAllNodes(TreeNode node) {
       // node is visited exactly once
       process(node);
  
       if (node.getChildCount() >= 0) {
           for (Enumeration e=node.children(); e.hasMoreElements(); ) {
               TreeNode n = (TreeNode)e.nextElement();
               visitAllNodes(n);
           }
       }
   }
  
   // Traverse all expanded nodes in tree
   public void visitAllExpandedNodes(JTree tree) {
       TreeNode root = (TreeNode)tree.getModel().getRoot();
       visitAllExpandedNodes(tree, new TreePath(root));
   }
   public void visitAllExpandedNodes(JTree tree, TreePath parent) {
       // Return if node is not expanded
       if (!tree.isVisible(parent)) {
           return;
       }
  
       // node is visible and is visited exactly once
       TreeNode node = (TreeNode)parent.getLastPathComponent();
       process(node);
  
       // Visit all children
       if (node.getChildCount() >= 0) {
           for (Enumeration e=node.children(); e.hasMoreElements(); ) {
               TreeNode n = (TreeNode)e.nextElement();
               TreePath path = parent.pathByAddingChild(n);
               visitAllExpandedNodes(tree, path);
           }
       }
   }


posted on 2006-04-04 17:24 SIMONE 阅读(9202) 评论(1)  编辑  收藏 所属分类: JAVA

posted @ 2011-03-23 08:32 Jamie 阅读(1723) | 评论 (0)编辑 收藏