Home > Articles > Programming > Java

Like this article? We recommend

Like this article? We recommend

Animation Library Tour

The JGoodies animation library provides a powerful framework for making animations part of the user interface. This framework relies on several concepts that are presented below:

  • An animation is the time-based change of some attribute (property) of an animation target (object) over a specific duration (length of time). For example, the radius (attribute) of a circle (animation target) expands from 10 pixels to 100 pixels over 20 seconds (duration).
  • Each animation defines an animation function that maps time to target attribute values. During the animation, the animation function is periodically evaluated to obtain the attribute’s next value. Because animation functions are continuous over time, an animation maintains the same overall appearance when rendered at different frame rates (the number of animation frames presented per second).
  • A composed animation comprises multiple animations and applies the animation effect (a combination of all animation functions with timing and anything else related to a composed animation...for each non-negative time, the animation effect describes all attributes values for all targets) to multiple animation targets and their attributes. For example, a circle’s radius can grow from 10 to 100 pixels (1 animation) and then stay at 100 pixels while its fill-color cycles through all shades of red (a second animation) during the first 15 seconds of this composed animation. The animations that form a composed animation can run sequentially or run in parallel.

The animation library is contained in animation-1.1.3.jar. That Jar file organizes its classes and interfaces into several packages, with com.jgoodies.animation serving as the top-level package. That package contains the animation library’s core types, including the Animation and AnimationListener interfaces, and the AbstractAnimation, AnimationEvent, and Animator classes:

  • The Animation interface describes time-based animations. Its public void animate(long time) method performs the animation at the given time by applying the animation effect to the animation target. It also fires an appropriate event when the animation starts or stops, and resets the animation effect if the animation’s duration has been exceeded. There is also a public void addAnimationListener(AnimationListener listener) method for registering an object to receive animation start and stop events.
  • The AnimationListener interface describes animation listener objects in terms of two methods: public void animationStarted(AnimationEvent evt) is invoked when the animation starts and public void animationStopped(AnimationEvent evt) is invoked when the animation stops.
  • The AbstractAnimation class minimizes the effort required to implement Animation. It provides a protected AbstractAnimation(long duration, boolean frozen) constructor for specifying the animation’s duration and whether or not the animation effect is frozen when the animation completes. A true value means that the animation effect is retained (frozen) once the animation finishes; a false value means that the animation effect is reset to whatever it would be at time 0. A simpler protected AbstractAnimation(long duration) constructor is also provided, which invokes the former constructor with false being passed to frozen.

    In addition to the aforementioned constructors, AbstractAnimation provides a protected abstract void applyEffect(long time) method to apply the animation effect at the given time to the animation target—and also maintains a list of animation listeners.

  • The AnimationEvent class describes animation start and stop events. You might want to call its public com.jgoodies.animation.AnimationEvent.Type type() method to return the event type—AnimationEvent.STARTED or AnimationEvent.STOPPED.
  • The Animator class starts and stops an animation at a given frame rate (the number of frames per second). After invoking its public Animator(Animation animation, int framesPerSecond) constructor to identify the animation to animate, and the desired frame rate, invoke Animator’s public void start() method to start the animation and (if necessary) Animator’s public void stop() method to stop the animation.

    Behind the scenes, Animator’s constructor creates a javax.swing.Timer object to take care of timing. That timer is started in response to invoking start(). Once per time period (as determined by the frame rate), the timer invokes Animator’s public void actionPerformed(ActionEvent e) method on the event-dispatching thread. In turn, that method invokes the animation’s animate(long time) method with the elapsed time as the value of time. If you were to examine AbstractAnimation’s overridden animate() method’s source code, you would discover that method invoking the applyEffect() method. It’s within applyEffect() where the animation code resides.

Let’s put these interfaces and classes into context by employing them in a simple animation application. That application’s source code appears in Listing 1.

Listing 1 AnimDemo1.java

// AnimDemo1.java

import com.jgoodies.animation.*;

public class AnimDemo1
{
  final static int DEFAULT_RATE = 30;
  final static int DURATION = 10000;

  public static void main (String [] args)
  {
   int fps = DEFAULT_RATE;

   if (args.length > 0)
     try
     {
       fps = Integer.parseInt (args [0]);
     }
     catch (NumberFormatException e)
     {
       System.err.println ("Unable to parse the frame rate");
       return;
     }

   System.out.println ("fps = " + fps);

   Animation animation = new SimpleAnimation (DURATION);

   AnimationListener al;
   al = new AnimationListener ()
      {
        public void animationStarted (AnimationEvent e)
        {
         System.out.println ("Started");
        }

        public void animationStopped (AnimationEvent e)
        {
         System.out.println ("Stopped");

         synchronized ("A")
         {
           "A".notify ();
         }
        }
      };
   animation.addAnimationListener (al);
                 
   Animator animator = new Animator (animation, fps);
   animator.start ();

   synchronized ("A")
   {
     try
     {
       "A".wait ();
     }
     catch (InterruptedException e)
     {
     }
   }
  }

  static class SimpleAnimation extends AbstractAnimation
  {
   SimpleAnimation (long duration)
   {
     super (duration);
   }

   protected void applyEffect (long time)
   {
     System.out.println ("It is now " + time + " milliseconds.");
   }
  }
} 

Listing 1’s public static void main(String [] args) method first establishes the frame rate (which is either the default setting or a command-line value). It next creates an animation object from the SimpleAnimation class, with a 10000-millisecond (10-second) duration. That class’s constructor forwards this duration to its AbstractAnimation superclass. Continuing, main() creates an animation listener—for detecting when an animation starts and stops—and attaches that listener to the animation object. Finally, main() creates and starts an Animator to execute the animation at the specified frame rate.

If the thread executing the main() method exits before the internal timer’s thread is started, the animation will not occur. Rather than force the main thread to sleep for a specific number of seconds (the value possibly needing to be changed on different platforms), I elected to have the main thread wait until it receives notification, via the event-dispatching thread, from within the animationStopped() method.

If you run AnimDemo1 with no command-line arguments, you might expect a default frame rate of 30 frames per second. Given a 10-second duration, you might also expect the applyEffect() method to be called 300 times, every 33 and 1/3 milliseconds. This isn’t quite what happens, as the output below reveals:

fps = 30
Started
It is now 3730 milliseconds.
It is now 3840 milliseconds.
It is now 3900 milliseconds.
It is now 3950 milliseconds.
It is now 4010 milliseconds.
It is now 4060 milliseconds.
It is now 4120 milliseconds.
It is now 4170 milliseconds.
It is now 4230 milliseconds.
It is now 4280 milliseconds.
It is now 4340 milliseconds.
It is now 4390 milliseconds.
It is now 4450 milliseconds.
It is now 4500 milliseconds.
It is now 4560 milliseconds.
It is now 4610 milliseconds.
It is now 4670 milliseconds.
It is now 4720 milliseconds.
It is now 4780 milliseconds.
It is now 4830 milliseconds.
It is now 4890 milliseconds.
It is now 4940 milliseconds.
It is now 5000 milliseconds.
It is now 5050 milliseconds.
It is now 5110 milliseconds.
It is now 5160 milliseconds.
It is now 5220 milliseconds.
It is now 5270 milliseconds.
It is now 5330 milliseconds.
It is now 5380 milliseconds.
It is now 5440 milliseconds.
It is now 5490 milliseconds.
It is now 5550 milliseconds.
It is now 5600 milliseconds.
It is now 5650 milliseconds.
It is now 5710 milliseconds.
It is now 5760 milliseconds.
It is now 5820 milliseconds.
It is now 5870 milliseconds.
It is now 5930 milliseconds.
It is now 5980 milliseconds.
It is now 6040 milliseconds.
It is now 6090 milliseconds.
It is now 6150 milliseconds.
It is now 6200 milliseconds.
It is now 6260 milliseconds.
It is now 6310 milliseconds.
It is now 6370 milliseconds.
It is now 6420 milliseconds.
It is now 6480 milliseconds.
It is now 6530 milliseconds.
It is now 6590 milliseconds.
It is now 6640 milliseconds.
It is now 6700 milliseconds.
It is now 6750 milliseconds.
It is now 6810 milliseconds.
It is now 6860 milliseconds.
It is now 6920 milliseconds.
It is now 6970 milliseconds.
It is now 7030 milliseconds.
It is now 7080 milliseconds.
It is now 7140 milliseconds.
It is now 7190 milliseconds.
It is now 7250 milliseconds.
It is now 7300 milliseconds.
It is now 7360 milliseconds.
It is now 7410 milliseconds.
It is now 7470 milliseconds.
It is now 7520 milliseconds.
It is now 7580 milliseconds.
It is now 7630 milliseconds.
It is now 7690 milliseconds.
It is now 7740 milliseconds.
It is now 7800 milliseconds.
It is now 7850 milliseconds.
It is now 7910 milliseconds.
It is now 7960 milliseconds.
It is now 8020 milliseconds.
It is now 8070 milliseconds.
It is now 8130 milliseconds.
It is now 8180 milliseconds.
It is now 8240 milliseconds.
It is now 8290 milliseconds.
It is now 8350 milliseconds.
It is now 8400 milliseconds.
It is now 8460 milliseconds.
It is now 8510 milliseconds.
It is now 8570 milliseconds.
It is now 8620 milliseconds.
It is now 8680 milliseconds.
It is now 8730 milliseconds.
It is now 8790 milliseconds.
It is now 8840 milliseconds.
It is now 8900 milliseconds.
It is now 8950 milliseconds.
It is now 9010 milliseconds.
It is now 9060 milliseconds.
It is now 9120 milliseconds.
It is now 9170 milliseconds.
It is now 9230 milliseconds.
It is now 9280 milliseconds.
It is now 9330 milliseconds.
It is now 9390 milliseconds.
It is now 9440 milliseconds.
It is now 9500 milliseconds.
It is now 9550 milliseconds.
It is now 9610 milliseconds.
It is now 9660 milliseconds.
It is now 9720 milliseconds.
It is now 9770 milliseconds.
It is now 9830 milliseconds.
It is now 9880 milliseconds.
It is now 9940 milliseconds.
It is now 9990 milliseconds.
It is now 0 milliseconds.
Stopped

The output (that I consistently produced during several AnimDemo1 executions) reveals the first time applyEffect() is called to be 3730 milliseconds—not 0 milliseconds. Furthermore, there are only 115 lines of output between the Started and Stopped messages—not 300. Finally, the number of milliseconds between successive method invocations varies (50 milliseconds one time; 110 milliseconds another time).

These numbers are not surprising when you consider that Java’s threading model often relies on the underlying platform’s threading mechanism, that it can take time to start the internal timer thread running, and that thread scheduling affects the order in which threads execute. You will probably notice different numbers on your platform. Just keep in mind that the actual frame rate might be lower than the frame rate you specify, and that you might not want to rely on the actual time values passed to applyEffect().

Earlier, I introduced the concept of and stated that each animation defines an animation function. Looking back at Listing 1, you might be wondering what animation function is being used. For this simple application, think of the animation function as being implicit—the act of printing out the time value is the animation function. But for more practical applications, the animation library’s com.jgoodies.animation package contains the AnimationFunction interface and the AbstractAnimationFunction class for creating explicit animation functions.

AnimationFunction describes a time-based animation function in terms of that function’s duration and a mapping from time to an animation effect (such as a fade or dissolve effect). The public long duration() method returns the duration of the effect; the public Object valueAt(long time) method returns an Object that represents the value of the effect at time time. For a text-fade animation function, valueAt() could return an Object representing text that is less faded at a smaller time value and an Object representing text that is more faded at a larger time value. To save you from having to implement AnimationFunction, the animation library supplies AbstractAnimationFunction for you to subclass.

You might never need to subclass AbstractAnimationFunction. Instead, you are more likely to make use of com.jgoodies.animation.AnimationFunctions. That class provides various static methods that each return a specific kind of AnimationFunction. For example, public static AnimationFunction discrete(long duration, Object[] values) creates and returns a noninterpolating animation function for a given duration—and a given values array whose entries are equally distributed over the duration.

To demonstrate an animation function (specifically, the AnimationFunction returned by the discrete() method), I created a Swing application that animates a bullet moving in a sinusoidal fashion from left to right on a panel. Listing 2 presents that application’s source code.

Listing 2 AnimDemo2.java

// AnimDemo2.java

import com.jgoodies.animation.*;
import java.awt.*;
import java.util.*;
import javax.swing.*;

public class AnimDemo2 extends JFrame
{
  final static int DURATION = 30000;
  final static int FRAME_RATE = 30;
  final static int HEIGHT = 200;
  final static int WIDTH = 300;

  AnimPanel ap;

  public AnimDemo2 (String title)
  {
   super (title);

   setDefaultCloseOperation (EXIT_ON_CLOSE);

   getContentPane ().add (ap = new AnimPanel (WIDTH, HEIGHT));

   Animation animation = new SimpleAnimation (DURATION);
   Animator animator = new Animator (animation, FRAME_RATE);
   animator.start ();

   pack ();

   setVisible (true);
  }

  public static void main (String [] args)
  {
   new AnimDemo2 ("Animation Demo");
  }

  class AnimPanel extends JPanel
  {
   Image imBullet;
   int height, width, xPos, yPos;

   Vector<Point> trail;

   AnimPanel (int width, int height)
   {
     this.width = width;
     this.height = height;

     xPos = 0;
     yPos = height / 2;

     imBullet = Toolkit.getDefaultToolkit ().getImage ("bullet.gif");

     trail = new Vector<Point> ();
   }

   public Dimension getPreferredSize ()
   {
     return new Dimension (width, height);
   }

   public void paintComponent (Graphics g)
   {
     super.paintComponent (g);

     for (Point pt : trail)
       g.drawLine (pt.x, pt.y, pt.x, pt.y);
      
     g.drawImage (imBullet, xPos++, yPos, this);

     if (xPos >= width)
       xPos = 0;
   }

   void setPosition (int pos)
   {
     yPos = pos;
     trail.add (new Point (xPos, yPos));
     repaint ();
   }
  }

  class SimpleAnimation extends AbstractAnimation
  {
   AnimationFunction af;

   SimpleAnimation (long duration)
   {
     super (duration);

     Integer [] positions = new Integer [3600];
     for (int degree = 0; degree < positions.length; degree++)
       positions [degree] =
        new Integer (HEIGHT / 2 +
               (int) (Math.sin (Math.toRadians (degree)) * 50));

     af = AnimationFunctions.discrete (DURATION, positions);
   }

   protected void applyEffect (long time)
   {
     Integer position = (Integer) af.valueAt (time);
     ap.setPosition (position.intValue ());
   }
  }
} 

Listing 2 establishes a GUI consisting of a single animation panel—an AnimPanel instance. That class’s AnimPanel(int width, int height) constructor establishes the animation panel’s width and height, loads the image of a bullet, and establishes a data structure to hold the trail the bullet makes as it moves across the panel. Whenever the bullet must be moved, void setPosition(int pos) is invoked to move the bullet’s image one position to the right, and either upward (if pos is positive) or downward (if pos is negative).

The setPosition() method is invoked from within SimpleAnimation’s applyEffect() method. Each invocation receives an argument value obtained by calling an animation function’s valueAt() method. This method returns one of the sinusoidal values that were precomputed in SimpleAnimation’s constructor.

Figure 1 illustrates the bullet’s sinusoidal movement in terms of the trail that follows the bullet. The noise in the lower part of the leftmost portion of the trail is due to the initial delay before the first call to applyEffect() and other issues related to the underlying platform’s threading mechanism.

Figure 1

Figure 1 A bullet moves in a sinusoidal path.

You might never need to work directly with animation functions. Instead, you will probably work at a higher level with predefined animations and components to which those animations apply. Behind the scenes, these animations make use of animation functions.

The com.jgoodies.animation.animations package supplies several animation classes, including the BasicTextAnimation and FanAnimation classes. Similarly, the com.jgoodies.animation.components package supplies several specially designed Swing component classes, such as BasicTextLabel and FanComponent, which are referenced by the animation classes. To illustrate these classes for you, I created an application that presents a splash screen. Check out Listing 3 for the source code.

Listing 3 AnimDemo3.java

// AnimDemo3.java

import com.jgoodies.animation.*;
import com.jgoodies.animation.animations.*;
import com.jgoodies.animation.components.*;
import java.awt.*;
import javax.swing.*;

public class AnimDemo3 extends JWindow
{
  final static int DURATION = 30000;
  final static int FRAME_RATE = 30;

  int duration;

  BasicTextLabel label;

  FanComponent fan;
  
  public void showSplashScreen ()
  {
   JPanel content = (JPanel) getContentPane ();
   content.setLayout (new GridLayout (2, 1));
   content.setBackground (Color.white);
   content.setBorder (BorderFactory.createLineBorder (Color.blue, 10));

   int width = 500;
   int height= 200;
   Dimension screen = Toolkit.getDefaultToolkit ().getScreenSize ();
   int x = (screen.width - width) / 2;
   int y = (screen.height -height) / 2;
   setBounds (x, y, width, height);

   label = new BasicTextLabel ("");
   label.setFont (new Font ("Tahoma", Font.BOLD, 18));
   content.add (label);

   fan = new FanComponent (10, Color.green);
   content.add (fan);

   setVisible (true);

   Animation animation = createAnimation ();

   AnimationListener al;
   al = new AnimationListener ()
      {
        public void animationStarted (AnimationEvent e)
        {
        }

        public void animationStopped (AnimationEvent e)
        {
         synchronized ("A")
         {
           "A".notify ();
         }
        }
      };
   animation.addAnimationListener (al);
                 
   Animator animator = new Animator (animation, 30);
   animator.start ();

   synchronized ("A")
   {
     try
     {
       "A".wait ();
     }
     catch (InterruptedException e)
     {
     }
   }
  }

  private Animation createAnimation ()
  {
   Animation a1 = BasicTextAnimation.defaultFade (label, 10000,
                           "Welcome To",
                           Color.orange);

   Animation a2 = BasicTextAnimation.defaultScale (label, 10000,
                           "The JGoodies",
                           Color.cyan);

   Animation a3 = BasicTextAnimation.defaultSpace (label, 10000,
                           "Animation Demonstration!",
                            Color.magenta);

   Animation allSeq = Animations.sequential (new Animation []
                        {
                          Animations.pause (1000),
                          a1,
                          Animations.pause (1000),
                          a2,
                          Animations.pause (1000),
                          a3,
                          Animations.pause (1000),
                        });

   Animation a4 = FanAnimation.defaultFan (fan, 33000);

   return Animations.parallel (allSeq, a4);
  }

  public static void main (String [] args)
  {
   new AnimDemo3 ().showSplashScreen ();
   System.exit (0);
  }
}

Listing 3 creates and displays a splash screen. The splash screen is based on a JWindow so that there are no distracting decorations (such as a minimize box). After building the splash screen, which includes the creation of BasicTextLabel and FanComponent components, the showSplashScreen() method takes care of animation setup.

From an animation perspective, Listing 3 is similar to Listing 1, but with one important exception: the createAnimation() method. That method creates a composed animation from several animations, where some animations apply to a BasicTextLabel component, and another animation applies to a FanComponent. For example, Animation a1 = BasicTextAnimation.defaultFade (label, 10000, "Welcome To", Color.orange); returns a text-fade Animation, where label identifies the BasicTextLabel animation target, 10000 identifies the duration of this animation, Welcome To is the text to fade, and Color.orange serves as the base color for the text fade.

You will notice calls to Animationspublic static Animation pause(long duration), public static Animation sequential(Animation[] animations), and public static Animation parallel(Animation animation1, Animation animation2) methods. The pause() method creates a pausing animation that does not affect the animation effect, but exists only to provide a delay between two other animations. The sequential() method concatenates several animations into a single animation, where the component animations execute in sequence. Finally, the parallel() method returns an animation whose two component animations execute in parallel. Hence, a fan can rotate concurrently with a sequence of text animations. One frame from this composed animation is shown in Figure 2.

Figure 2

Figure 2 A splash screen with two concurrent animations

InformIT Promotional Mailings & Special Offers

I would like to receive exclusive offers and hear about products from InformIT and its family of brands. I can unsubscribe at any time.

Overview


Pearson Education, Inc., 221 River Street, Hoboken, New Jersey 07030, (Pearson) presents this site to provide information about products and services that can be purchased through this site.

This privacy notice provides an overview of our commitment to privacy and describes how we collect, protect, use and share personal information collected through this site. Please note that other Pearson websites and online products and services have their own separate privacy policies.

Collection and Use of Information


To conduct business and deliver products and services, Pearson collects and uses personal information in several ways in connection with this site, including:

Questions and Inquiries

For inquiries and questions, we collect the inquiry or question, together with name, contact details (email address, phone number and mailing address) and any other additional information voluntarily submitted to us through a Contact Us form or an email. We use this information to address the inquiry and respond to the question.

Online Store

For orders and purchases placed through our online store on this site, we collect order details, name, institution name and address (if applicable), email address, phone number, shipping and billing addresses, credit/debit card information, shipping options and any instructions. We use this information to complete transactions, fulfill orders, communicate with individuals placing orders or visiting the online store, and for related purposes.

Surveys

Pearson may offer opportunities to provide feedback or participate in surveys, including surveys evaluating Pearson products, services or sites. Participation is voluntary. Pearson collects information requested in the survey questions and uses the information to evaluate, support, maintain and improve products, services or sites, develop new products and services, conduct educational research and for other purposes specified in the survey.

Contests and Drawings

Occasionally, we may sponsor a contest or drawing. Participation is optional. Pearson collects name, contact information and other information specified on the entry form for the contest or drawing to conduct the contest or drawing. Pearson may collect additional personal information from the winners of a contest or drawing in order to award the prize and for tax reporting purposes, as required by law.

Newsletters

If you have elected to receive email newsletters or promotional mailings and special offers but want to unsubscribe, simply email information@informit.com.

Service Announcements

On rare occasions it is necessary to send out a strictly service related announcement. For instance, if our service is temporarily suspended for maintenance we might send users an email. Generally, users may not opt-out of these communications, though they can deactivate their account information. However, these communications are not promotional in nature.

Customer Service

We communicate with users on a regular basis to provide requested services and in regard to issues relating to their account we reply via email or phone in accordance with the users' wishes when a user submits their information through our Contact Us form.

Other Collection and Use of Information


Application and System Logs

Pearson automatically collects log data to help ensure the delivery, availability and security of this site. Log data may include technical information about how a user or visitor connected to this site, such as browser type, type of computer/device, operating system, internet service provider and IP address. We use this information for support purposes and to monitor the health of the site, identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents and appropriately scale computing resources.

Web Analytics

Pearson may use third party web trend analytical services, including Google Analytics, to collect visitor information, such as IP addresses, browser types, referring pages, pages visited and time spent on a particular site. While these analytical services collect and report information on an anonymous basis, they may use cookies to gather web trend information. The information gathered may enable Pearson (but not the third party web trend services) to link information with application and system log data. Pearson uses this information for system administration and to identify problems, improve service, detect unauthorized access and fraudulent activity, prevent and respond to security incidents, appropriately scale computing resources and otherwise support and deliver this site and its services.

Cookies and Related Technologies

This site uses cookies and similar technologies to personalize content, measure traffic patterns, control security, track use and access of information on this site, and provide interest-based messages and advertising. Users can manage and block the use of cookies through their browser. Disabling or blocking certain cookies may limit the functionality of this site.

Do Not Track

This site currently does not respond to Do Not Track signals.

Security


Pearson uses appropriate physical, administrative and technical security measures to protect personal information from unauthorized access, use and disclosure.

Children


This site is not directed to children under the age of 13.

Marketing


Pearson may send or direct marketing communications to users, provided that

  • Pearson will not use personal information collected or processed as a K-12 school service provider for the purpose of directed or targeted advertising.
  • Such marketing is consistent with applicable law and Pearson's legal obligations.
  • Pearson will not knowingly direct or send marketing communications to an individual who has expressed a preference not to receive marketing.
  • Where required by applicable law, express or implied consent to marketing exists and has not been withdrawn.

Pearson may provide personal information to a third party service provider on a restricted basis to provide marketing solely on behalf of Pearson or an affiliate or customer for whom Pearson is a service provider. Marketing preferences may be changed at any time.

Correcting/Updating Personal Information


If a user's personally identifiable information changes (such as your postal address or email address), we provide a way to correct or update that user's personal data provided to us. This can be done on the Account page. If a user no longer desires our service and desires to delete his or her account, please contact us at customer-service@informit.com and we will process the deletion of a user's account.

Choice/Opt-out


Users can always make an informed choice as to whether they should proceed with certain services offered by InformIT. If you choose to remove yourself from our mailing list(s) simply visit the following page and uncheck any communication you no longer want to receive: www.informit.com/u.aspx.

Sale of Personal Information


Pearson does not rent or sell personal information in exchange for any payment of money.

While Pearson does not sell personal information, as defined in Nevada law, Nevada residents may email a request for no sale of their personal information to NevadaDesignatedRequest@pearson.com.

Supplemental Privacy Statement for California Residents


California residents should read our Supplemental privacy statement for California residents in conjunction with this Privacy Notice. The Supplemental privacy statement for California residents explains Pearson's commitment to comply with California law and applies to personal information of California residents collected in connection with this site and the Services.

Sharing and Disclosure


Pearson may disclose personal information, as follows:

  • As required by law.
  • With the consent of the individual (or their parent, if the individual is a minor)
  • In response to a subpoena, court order or legal process, to the extent permitted or required by law
  • To protect the security and safety of individuals, data, assets and systems, consistent with applicable law
  • In connection the sale, joint venture or other transfer of some or all of its company or assets, subject to the provisions of this Privacy Notice
  • To investigate or address actual or suspected fraud or other illegal activities
  • To exercise its legal rights, including enforcement of the Terms of Use for this site or another contract
  • To affiliated Pearson companies and other companies and organizations who perform work for Pearson and are obligated to protect the privacy of personal information consistent with this Privacy Notice
  • To a school, organization, company or government agency, where Pearson collects or processes the personal information in a school setting or on behalf of such organization, company or government agency.

Links


This web site contains links to other sites. Please be aware that we are not responsible for the privacy practices of such other sites. We encourage our users to be aware when they leave our site and to read the privacy statements of each and every web site that collects Personal Information. This privacy statement applies solely to information collected by this web site.

Requests and Contact


Please contact us about this Privacy Notice or if you have any requests or questions relating to the privacy of your personal information.

Changes to this Privacy Notice


We may revise this Privacy Notice through an updated posting. We will identify the effective date of the revision in the posting. Often, updates are made to provide greater clarity or to comply with changes in regulatory requirements. If the updates involve material changes to the collection, protection, use or disclosure of Personal Information, Pearson will provide notice of the change through a conspicuous notice on this site or other appropriate way. Continued use of the site after the effective date of a posted revision evidences acceptance. Please contact us if you have questions or concerns about the Privacy Notice or any objection to any revisions.

Last Update: November 17, 2020