论坛首页 移动开发技术论坛

Implementing Copy and Paste for the Java ME TextBox

浏览 2685 次
该帖已经被评为隐藏帖
作者 正文
   发表时间:2010-01-01   最后修改:2010-01-01

转载:http://today.java.net/article/2006/12/21/implementing-copy-and-paste-java-me-textbox

 

 

 

     One of the most convenient and frequently used features of desktop-based text processing applications is copy (or cut) and paste. This is a feature that would be especially useful in Java ME applications because text entry can be quite tedious on a mobile phone. The text-handling classes in ME do not provide direct support for copy and paste, but it is not very difficult to build up such a capability. In this article we look at one approach to implementing this feature.

I assume that the reader is familiar with ME and, in order to try out the examples described here, that she has the J2ME Wireless Toolkit 2.2 installed on her computer.
The Basic Technique

The copy action has two components:
Select a part of the text.
Save it on the clipboard.

Similarly, paste needs to do the following:
Get what is saved on the clipboard.
Insert it at the desired position.

Additionally, of course, supporting activities like highlighting the selected text and undoing a paste are also needed. These functionalities are required in the case of a Java ME implementation too. Before we look at how we are going to achieve our goal, however, let us examine how we perform the basic tasks on a desktop.

In order to select a part of the text, the mouse is clicked on the left of the first character to be selected and then the mouse is dragged (i.e., moved while the left button is held down) up to the right of the last character for selection. While the dragging is being done, the part of the text being selected gets highlighted. The selection can also be done backwards; that is, from the right of the last character to the left of the first. Once selection is done, we activate the copy command (from a menu, for example, or by typing Ctrl-C). Similarly, for pasting, we position the caret at the point of insertion and choose the paste command.

Now we can look at the mobile approach. A Java ME device cannot be expected to have a mouse. So we have to move the caret by using arrow keys. As in the case of a desktop, we position the caret at one end of the text to be selected. However, no dragging action is possible which means we have to use a command to start the selection process. Then we have to move the caret to the other end of the text we want to select and, again, use a command to signal to the application that the selection process is over. Now the application has to show us what has been selected and this can be done by showing the selected portion on a screen. Once we signal, through an appropriate command, that the selection is right, the selected text can be saved in a record store.

Pasting is even simpler. All we need to do is position the caret where we want the saved text to be pasted and select the Paste command. This will cause the contents of the record store to be retrieved and inserted at the right place.
The Problem

Implementing the simple activities described above does not raise any theoretical problem. In practice though, things are different. Generally speaking, the textbox from which something has to be copied will have a number of associated commands. When we want to start copying, therefore, we'll have to pop up a menu, and therein lies our problem. To see actually what happens on some devices let's run a simple MIDlet--TryCursor--on WTK.

This MIDlet has just one screen with an Exit command and three others labeled One, Two and Three. The last three commands display an alert that shows the command selected and the caret position as read by the getCaretPosition method of the TextBox class, which is executed in the commandAction method. So let us key in some text and position the caret at any place within the text, as shown in as shown in Figure 1.

 


Figure 1. Caret positioned at index 14

If we now select the Menu option, a pop-up menu will appear. We can see this in Figure 2. Note that the caret is still at index 14.


Figure 2. Menu pops up
Now select one of the three commands on the menu. We would expect the alert to tell us where the caret had been positioned. But, as Figure 3 shows, that doesn't happen; the caret position sensed corresponds to the end of the text.


Figure 3. Caret position read as end of text string

Try this out with the caret at various positions and you'll see that no matter where you position the caret, the getCaretPosition method always returns the index of the last position of the string! So what do we do?
The Solution

The QwertyDevice on the WTK offers us a clue. When we run TryCursor on QwertyDevice, the command One is shown on the screen while the other two can be called up by clicking the Menu button. If One is selected, then the application behaves the way we expect it to, and the alert shows the correct caret position. But if we click on Menu and select one of the other commands the problem recurs. What does that tell us? When a pop-up menu hides a part (or all) of the screen, then the screen has to be redrawn afterwards. It appears that, during the process of redrawing the screen, the position of the caret (14 in the above example) is not taken into account; the screen is redrawn with the caret at the end of the text and then the position is read. So as long as we can work without having to pop up a menu we're safe.

The implication here is that the commands required for copy and paste actions must always be on the screen. Then we won't need a pop-up menu for copy/paste operations. In general, of course, this will be difficult to achieve, as our screen will require a number of other commands and we cannot always guarantee that the command required next for copy (or paste) operation will be on the screen. While this may be achieved by constantly removing a used command and adding the one that will required next (so that we always have a small number of active commands), in a generalized scenario this will mean a lot of juggling with commands and will add to the burden of the programmer writing the application. Even with all that effort, we may not succeed in eliminating a pop-up when we want to.

We therefore propose to create a dedicated set of screens that will be invoked only for copying and pasting. For example, the copy command will display a screen with the text we have entered and a maximum of two commands so that the commands are available onscreen and a pop-up menu will not be required. A similar thing will happen for pasting. This approach has three benefits:
The classes can be programmed, tested, and debugged, creating reusable code--something like a CopyNPaste API.
As the classes are dedicated ones, they will guarantee that the required commands will always be onscreen.
The application programmer will need to write only a small amount of glue code and invoke the canned classes as needed.
The Classes

Our "API" is composed of the following classes:
ClipboardCompatible: This is an abstract class that must be subclassed by all text-handling classes that use the Copy and Paste functions.
Scratchpad: This class manages all the activities required for copy and paste operations. It has a private constructor and cannot be instantiated with the new statement. There are two static methods that can be used to obtain instances of Copyboard (the dedicated screen for copying) and Pasteboard (the dedicated screen for pasting).
ClipboardCommandProcessor: This class handles all commands related to selection, copying, saving, retrieving, and pasting.
ClipboardHandler: This class handles interactions with the clipboard. That is, it saves selected text and retrieves whatever is saved on the clipboard.

In addition to the above, we need the Clipboard MIDlet to initialize the clipboard. The Clipboard MIDlet is very simple; its only function is to create a record store named clipboard:

clipboard = RecordStore.openRecordStore("clipboard", true,
            RecordStore.AUTHMODE_ANY, true);


MIDP 2.0 permits sharing of record stores among MIDlets belonging to different suites, provided such universal access is authorized at the time of creation of a record store. As shown above, the clipboard record store is created in this manner by specifying RecordStore.AUTHMODE_ANY in the parameter list of the openRecordStore method. This record store can now be opened from any MIDlet, provided its full name is used. We shall see how to do this in the next section.

Once the record store has been created, we can test our copy and paste application. To do that, we create two MIDlet suites: ClipDemo1 and ClipDemo2. Actually, the two are identical except for the names of the respective MIDlet classes. This difference has been introduced to underline the fact that the clipboard can be used from any MIDlet.

In both the demos, the MIDlets (SourceScreen and SourceScreen2) are just entry points. They instantiate the ClipboardDemo class and display it. The four classes mentioned above are present in both the demos and the ClipboardDemo class in each case extends the ClipboardCompatible class, which is really a customized TextBox. The assumption here is that the text to be copied from or pasted into will be entered into a subclass of TextBox. ClipboardCompatible contains all the items essential for implementing our copy-and-paste function such as the commands and their listeners. It also declares, creates, and uses two instances of the Scratchpad class--Copyboard, to be used for copying, and Pasteboard, to be used for pasting. There are two abstract methods whose implementations are left unspecified to meet any special requirement of the text-handling application. Finally, it has a concrete method, clearClipboard, for deleting the contents of clipboard.

The ClipboardDemo class is the one that does the actual text handling and wants to incorporate the copy-and-paste functionality. We can see that all this class needs to do is take care of its own functional requirements. As for copy-and-paste actions, it only has to implement the two abstract methods. Since ClipboardCompatible implements CommandListener, its subclass (ClipboardDemo) doesn't have to explicitly do that again. In the commandAction method, ClipboardDemo handles only the command it has added and invokes the same method in its superclass for Copy and Paste commands.

The real actions for copy and paste functions take place within the Scratchpad class working in conjunction with the ClipboardCommandProcessor--which handles all commands associated with the scratchpad instances--and the ClipboardHandler that provides the interface to the clipboard. Copyboard and Pasteboard provide the necessary sequencing of commands making sure that there are never more than two commands on the user interface.
Testing the Action

Let us now see how the copy and paste functions work. For copying text, we need to select the Copy command on ClipboardDemo (see Figure 4).


Figure 4. ClipboardDemo screen with text and pop-up menu

This displays the Copyboard, which has the text that was on ClipboardDemo. This can be seen in Figure 5. Note that the command that we'll need next--"Start selection"--is available onscreen.


Figure 5. Copyboard with the contents of ClipboardDemo

Next we need to move the caret to the left of the first character of the part to be copied and select the "Start selection" command, as shown in Figure 6.


Figure 6. Start selecting text to be copied

We can see the result in Figure 7. This command is processed by ClipboardCommandProcessor and we see an alert that tells us where the start position is.


Figure 7. Alert showing where selection has been started

The alert is displayed for two seconds, after which Copyboard is brought to the foreground. At this time, the "Start selection" command is removed from the screen and the Copy command takes its place. This is illustrated in Figure 8.


Figure 8. Copyboard after starting selection

Then we have to position the caret at the right of the last character to be copied and select the Copy command. Again ClipboardCommandProcessor takes over and shows the string that has been selected. But before that, it checks to see if the selection was done backwards and adjusts the copying action accordingly:

if(endposition<startposition)
{
    //selection has been made backwards
    //so simply interchange start and end positions
    int temp = endposition;
    endposition = startposition;
    startposition = temp;
}
               
//make the selection
displaystring = text.substring(startposition, endposition);

//show the selected string
selection.setText(displaystring);
CopyForm.setTitle("Selected text");
CopyForm.addCommand(CMD_COPY);
display.setCurrent(CopyForm);


Figure 9 shows the screen for displaying the selected text.


Figure 9. Screen showing the selected text

Once we approve the text selection through the OK command, the saveCopiedInfo method of ClipboardHandler class is invoked to save the copied text into clipboard. Of course, we have to the option to cancel the copying process at each step.

Similarly, we initiate pasting by selecting the Paste command on ClipboardDemo. This brings up the Pasteboard with the contents of ClipboardDemo. Again we position the caret where we want the saved text to be pasted and select Paste (Figure 10).


Figure 10. Caret positioned for pasting

We get an alert to inform us that the pasting operation has been done and then we see the result as in Figure 11. At this point, we can either accept or reject the pasting action.


Figure 11. Pasteboard after pasting

The one thing that we have not talked about is how the demo MIDlets get to access the common clipboard. The trick lies in knowing how to address this common record store. We have seen earlier how clipboard was created with universal access authorization by the Clipboard MIDlet. While setting up the Clipboard project on WTK, I had entered the name of a fictitious vendor in the required field of project settings. Record store names are internally constructed from the name of the MIDlet suite that create them, the name of the vendor of the MIDlet, and the name given to the record store by the MIDlet. So the full name of clipboard is a combination of "clipboard" (record store name), "V. Endor" (vendor name), and "Clipboard" (with a capital "C"--the name of the creator). Any other MIDlet can access clipboard if all these parameters are specified. This can be seen in the openRecord method of ClipboardHandler:

private void openRecord() throws Exception
{
    clipboard = RecordStore.openRecordStore("clipboard",
 "V. Endor", "Clipboard");
    //clipboard = RecordStore.openRecordStore("newclipboard",
 true);
}


Note that the significance of the commented-out line of code is explained in the concluding section of this article.

There is one feature that is definitely desirable, if not essential: clearing clipboard. In desktop applications, this usually happens automatically when we close the source. Applications on mobile phones, however, run one at a time, which means the source must always be closed before the destination can be opened. So automatic deletion is not a good option here. On the other hand, the user may not like to leave the copied matter on the clipboard if the content is confidential. That is why we display a reminder alert when the Exit command is selected. If the user doesn't want to clear the clipboard, the application is closed. If, on the other hand, she decides to delete whatever had been saved, the clearClipboard method of ClipboardCompatible is called. This call ripples through to ClipboardHandler, which saves an empty string thus deleting whatever was on clipboard. The application is then closed.

With Clipboard, ClipDemo1, and ClipDemo2 installed on a phone, you have to run Clipboard first to create clipboard. Thereafter, you can exchange text between the two demo applications by copying from one and pasting into the other.
One Final Issue

A close look at Scratchpad and ClipboardCommandProcessor will reveal that as a command gets removed and another is added to the instances of Scratchpad, there is always another screen that is sandwiched between the two actions. For example, when we select the starting position on Copyboard, the "Start selection" command is removed, the Copy command is added, and an alert is shown for two seconds, after which the Copyboard is displayed once more. While these intervening displays keep the user informed of what is happening, they do serve another purpose as well.

When a command is added to a Displayable that is "actually visible on the display, and this call affects the set of visible commands, the implementation should update the display as soon as it is feasible to do so." The quote is from ME documentation. In some devices the visual updating does not occur, while in some of them the added command does not become functional until the screen is moved to background and then made visible again. Thus the alerts and other screens that keep popping up during copying and pasting ensure smooth functioning of newly added commands by temporarily hiding Copyboard and Pasteboard.
Conclusion

We have seen how we can incorporate copy and paste functions into an ME TextBox. Along the way, we have also seen that ME implementations can vary subtly but significantly from one device to another and considerable effort is required to write, test, and debug applications that work on a wide range of devices. The application described here has been tested on the WTK22 devices (with the modification mentioned below), and also on a Nokia 6101 and on a Samsung 309. On WTK, the universal access mechanism for record stores does not seem to work. So if you want to test the basic performance on WTK, you'll need to comment out the first line of the openRecord method of ClipboardHandler shown above and uncomment the second. However, in that case, each MIDlet will have its own clipboard and copying from one to the other will not be possible. On the Nokia and Samsung phones the application works just as expected.

Along with Copy and Paste, there is another function that is used widely--Cut. This third function is actually a combination of Copy and Delete. I have not shown its implementation here because I believe it'll be an enjoyable experience for you to enhance this application by incorporating this missing function.
Resources
trycursor_src.zip: Source code for TryCursor
clipboard_pckg.zip: Source code, .jar, and .jad files for Clipboard
clipdemo1_pckg.zip: Source code, .jar, and .jad files for ClipDemo1
clipdemo2_pckg.zip: Source code, .jar, and .jad files for ClipDemo2
Beginning J2ME: From Novice to Professional: An excellent introduction to ME. Also contains valuable tips on using WTK.
"Deploy MIDlets on J2ME-enabled devices:" tutorial on downloading applications to handsets


Biswajit Sarkar is an electrical engineer with a specialization in programmable industrial automation.

 

 

 

 

论坛首页 移动开发技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics