分享
 
 
 

Drag and Drop: New Data Transfer Capabilities in the JavaTM 2 Platform, Standard Edition (J2SETM), version 1.4

王朝java/jsp·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

New Data Transfer Capabilities

John Zukowski

November 2001

Sun is currently undergoing their third beta release of the JavaTM 2 Platform, Standard Edition (J2SETM), version 1.4. This latest release incorporates many new and interesting features into the standard library set, some of which have been available as standard extensions for some time. These would include capabilities like the Java Cryptography Extension (JCE), Java Secure Socket Extension (JSSE), Java Authentication and Authorization Service (JAAS), the Java API for XML Processing (JAXP), and JDBC extensions. All of these libraries have been available as optional libraries and work with prior versions of the Java 2 Platform. Now, they just come standard.

In addition to adding previously optional packages to the standard library set, the 1.4 release also incorporates many new features. These totally new elements include new Swing components, new I/O management, and a new assert keyword in the language. Features like these are not backward compatible and are not available as optional libraries for earlier releases of the Java 2 Platform.

One of the new capabilities now available is improved data transfer support within and between applications. Data transfer includes two tasks: cut, copy, and paste access to the user or system clipboard and drag-and-drop support. Neither of these features is totally new to the standard library set. However, what you can do with them now in the 1.4 release and how you interact with the features has totally changed. Older programs, designed for the earlier data transfer mechanism, will continue to work fine, but in many cases the source code for the task can be simplified considerably.

Before looking into how to use the drag-and-drop capabilities of the J2SE, version 1.4 release, there is some background information you need to understand.

Clipboards

First off is a look at clipboards. If you aren't familiar with what a clipboard is, it is a memory area for storing information in support of cut, copy, and paste operations. For instance, when you are using a word processor and cut a piece of text to place the text elsewhere in the document, the text that was cut is saved within the clipboard. When it becomes time to paste the text, the paste operation reads what is on the clipboard and pastes it into the document.

There are actually multiple clipboards available. There is one global clipboard that all applications have access to, and private clipboards that can be limited in scope to within a single application. By utilizing a private clipboard in an application, you don't copy into system memory what goes onto the system clipboard. Instead, it stays within the application's memory space.

Transferable Objects

While you now know what a clipboard is, you haven't learned anything about the objects that can go into the clipboard. With the Java platform, objects that can be placed within a clipboard must implement the Transferable interface, found in the java.awt.datatransfer package.

public interface Transferable {

public Object getTransferData(DataFlavor flavor)

throws UnsupportedFlavorException, IOException;

public DataFlavor[] getTransferDataFlavors();

public boolean isDataFlavorSupported(DataFlavor

flavor);

}

The interface talks about an object called DataFlavor, but what's a flavor? Quite simply, data flavors are ways of representing data. In the Web world, you might have heard the term MIME types, where MIME stands for Multipurpose Internet Mail Extensions. MIME types allow you to say that an email attachment is an image or the Web document to deliver through your web server is a text file. Data flavors are just the way of representing MIME types within your programs.

MIME types have textual representations like text/html or text/plain. Data flavors on the other hand are classes. Flavors get created by textual representations, but they are classes in the system. They are used by the data transfer mechanism so that when you place something on the clipboard, you can state what type of object it is. Then, when someone gets something off the clipboard, that someone can ask what flavors of data are available. If all the flavors are ones they don't support, they can't get the object off the clipboard.

What's this about multiple flavors? You provide data through the data transfer mechanism with multiple flavors so that you can support the widest possible audience. For instance, if you were to copy text within a word processor, you would want to retain the formatting information. However, if you were to paste the text into something that didn't support the formatting, wouldn't you at least want to be able to get the actual text content? That's how flavors work. You specify that the transferable data can be represented by one or more flavors. You provide the means to check if a particular flavor is supported, and then you provide the mechanism to get the data for a specific flavor. And that is exactly what the Transferable interface does for you.

DataFlavor itself is a class. It makes several default flavor and MIME types available through class constants and a method. Going from simplest to most complex, you get predefined flavors for plain text, strings, a file list, and images. There are also three MIME types defined for local, serialized, and remote objects. From the MIME types, you can create the flavors.

getTextPlainUnicodeFlavor

stringFlavor

javaFileListFlavor

imageFlavor

javaJVMLocalObjectMimeType

javaSerializedObjectMimeType

javaRemoteObjectMimeType

Using the word processor example, imagine if that word processor was your program and you wanted to support cut and paste of the content of your document. You might present the content in four different flavors. The first flavor would be the local object type. If cut-and-paste is happening within the same application, using the javaJVMLocalObjectMimeType allows you to just use a local memory reference for the copy operation. Going between different Java applications would allow you to create a flavor from javaSerializedObjectMimeType. Here, the content is serialized, and you would be able to preserve any formatting information. The last two are stringFlavor and getTextPlainUnicodeFlavor. Both lose all formatting information, but still permit you to copy the contents.

Transferring string objects is a common task so you'll find a StringSelection class in the java.awt.datatransfer package to help you. As it only works with strings, the class doesn't support the local/serialized types just mentioned.

Now that you have some background information, let's move on to some real code.

Buffers

The clipboard acts as an indirect buffer. When you make something available to the clipboard, you don't actually copy the data there. Instead, you copy a reference and that reference is accessed when you want to take something off the buffer. Because you may need to keep track of that external reference, the clipboard will actually notify you when something else replaces an item on the clipboard. That notification is handled by the ClipboardOwner interface:

public interface ClipboardOwner {

public void lostOwnership(Clipboard clip,

Transferable t);

}

This means that there is no built-in support for having multiple items on the clipboard. When something new is added to the clipboard, the old reference is thrown away.

To demonstrate the use of the system clipboard, take a look at the StringSelection class. It implements both the Transferable and ClipboardOwner interfaces. You use it to transfer strings to the clipboard. Adding a string to the clipboard involves creating a StringSelection item and setting the clipboard contents, through its setContents method. Here's what the code for that task looks like.

// Get String

String selection = ...;

// Convert to StringSelection

StringSelection data = new StringSelection(selection);

// Get system clipboard

Clipboard clipboard =

Toolkit.getDefaultToolkit().getSystemClipboard();

// Set contents

clipboard.setContents(data, data);

Getting the clipboard data is a little more involved, but not too difficult. There are basically three steps involved:

Get the reference to the data from the clipboard, the Transferable object.

Find a flavor you can handle that it supports, where DataFlavor.stringFlavor is for text strings.

Get the data for that flavor.

And, here's what that code looks like. There's also a bit of exception handling necessary as a UnsupportedFlavorException or IOException could be thrown.

// Get data from clipboard

Transferable clipData =

clipboard.getContents(clipboard);

// Make sure not empty

if (clipData != null) {

// Check if it supports the desired flavor

if (clipData.isDataFlavorSupported

(DataFlavor.stringFlavor)) {

// Get data

String s = (String)(clipData.getTransferData(

DataFlavor.stringFlavor));

// Use data

...

}

}

Clipboard Text Example

As shown in the following figure, the sample program that puts all these pieces together involves a text area and two buttons, Copy and Paste. When the Copy button is selected, the current selection within the text area is copied to the system clipboard. When the Paste button is selected, the current selection will be replaced with the contents of the system clipboard. If the flavor of data on the clipboard is not supported, the system beeps. Most of the code involves just the screen setup. There should be no new code as far as the clipboard access.

And, here's the complete source.

import java.awt.*;

import java.awt.event.*;

import java.awt.datatransfer.*;

import javax.swing.*;

public class ClipboardExample {

public static void main(String args[]) {

JFrame frame = new JFrame("Copy/Paste");

Container contentPane = frame.getContentPane();

final Toolkit kit = Toolkit.getDefaultToolkit();

final Clipboard clipboard =

kit.getSystemClipboard();

final JTextArea jt = new JTextArea();

JScrollPane pane = new JScrollPane(jt);

contentPane.add(pane, BorderLayout.CENTER);

JPanel bottom = new JPanel();

JButton copy = new JButton("Copy");

bottom.add(copy);

ActionListener copyListener = new

ActionListener() {

public void actionPerformed(ActionEvent e) {

String selection = jt.getSelectedText();

StringSelection data = new

StringSelection(selection);

clipboard.setContents(data, data);

}

};

copy.addActionListener(copyListener);

JButton paste = new JButton("Paste");

bottom.add(paste);

ActionListener pasteListener = new

ActionListener() {

public void actionPerformed(ActionEvent

actionEvent) {

Transferable clipData =

clipboard.getContents(clipboard);

if (clipData != null) {

try {

if (clipData.isDataFlavorSupported(

DataFlavor.stringFlavor)) {

String s =

(String)(clipData.getTransferData(

DataFlavor.stringFlavor));

jt.replaceSelection(s);

} else {

kit.beep();

}

} catch (Exception e) {

System.err.println("Problems getting data:

" + e);

}

}

}

};

paste.addActionListener(pasteListener);

contentPane.add(bottom, BorderLayout.SOUTH);

frame.setDefaultCloseOperation

(JFrame.EXIT_ON_CLOSE);

frame.setSize(300, 300);

frame.show();

}

}

When trying out the program, be sure to try to copy and paste between native applications like your word processor. Because you are using the system clipboard, the program will transfer whatever the contents of the clipboard are.

Text Actions

While this article is about explaining the data transfer mechanisms with the J2SE libraries, the code for copy and paste doesn't have to be that complicated when using the Swing text controls. They already know how to do cut, copy, and paste operations, among many other tasks. All you have to do is lookup the appropriate listener and add that instead.

The actions associated with the text controls are retrieved through the getActions method. This returns an array of Action objects, which implement the ActionListener interface. You just need to find the specific listener and attach that instead. The following code will do just that. It replaces the creation of the two ActionListener objects and their attachment in the prior example.

// get command table

Action actions[] = jt.getActions();

// Find the two wanted

int count =

0; for

(int i =

0, n = actions.length; (i<

n) && (count < 2);

i++) {

Action a = actions[i];

String name = (String)a.getValue(Action.NAME);

if (name.equals(DefaultEditorKit.copyAction)) {

copy.addActionListener(a);

count++;

} else if (name.equals

(DefaultEditorKit.pasteAction)) {

paste.addActionListener(a);

count++;

}

}

Transferring Images

Now that the basic process of transferring text has been explained, let's move on to something new to the 1.4 release of J2SE, images transfers. Prior versions of the J2SE didn't provide integrated support for transferring images between Java programs and native applications. While you could go through the manual process of providing the data in an understandable format, that task is no longer necessary. All you now have to do is specify that the flavor is of type DataFlavor.imageFlavor, provide the data to the clipboard as an AWT Image object, and you're all set.

While the process of transferring a String is done through the StringSelection class, there is no such class available for transferring images. You must create your own Transferable object that implements ClipboardOwner, too. If StringSelection is for transferring String objects, the logical name for transferring images is ImageSelection.

If you plan on creating transferable objects that will interact with Swing components, you don't have to implement the two interfaces yourself. Instead, there is a helper TransferHandler class that does most of the work for you. The handler does the interaction with the clipboard, and you just have to override four of its methods:

public int getSourceActions(JComponent c) -- Returns the supported operations. There are four constants in the TransferHandler class for the operations: COPY, COPY_OR_MOVE, MOVE, and NONE.

public boolean canImport(JComponent comp, DataFlavor flavor[]) -- Returns true if the component can support one of the data flavors, false otherwise.

public Transferable createTransferable(JComponent comp) -- Here, you need to save a reference to the data to be transferred, and return the TransferHandler (this). The component represents where the data is coming from. This is your copy operation. The handler does the actual copy to the clipboard at the appropriate time.

public boolean importData(JComponent comp, Transferable t) -- Returns true if the component supports getting one of the data flavors from the Transferable object, and successfully gets it, false otherwise. This is your paste operation. Again, the handler gets the data from the clipboard, you just have to get it from the Transferable.

Here's just such a class definition that works with all AbstractButton subclasses and the JLabel component.

import java.awt.*;

import java.awt.datatransfer.*;

import java.io.*;

import javax.swing.*;

public class ImageSelection extends TransferHandler

implements Transferable {

private static final DataFlavor flavors[] =

{DataFlavor.imageFlavor};

private Image image;

public int getSourceActions(JComponent c) {

return TransferHandler.COPY;

}

public boolean canImport(JComponent comp, DataFlavor

flavor[]) {

if (!(comp instanceof JLabel) ||

(comp instanceof AbstractButton)) {

return false;

}

for (int i=0, n=flavor.length; i<n; i++) {

if (flavor[i].equals(flavors[0])) {

return true;

}

}

return false;

}

public Transferable createTransferable(JComponent

comp) {

// Clear

image = null;

Icon icon = null;

if (comp instanceof JLabel) {

JLabel label = (JLabel)comp;

icon = label.getIcon();

} else if (comp instanceof AbstractButton) {

AbstractButton button = (AbstractButton)comp;

icon = button.getIcon();

}

if (icon instanceof ImageIcon) {

image = ((ImageIcon)icon).getImage();

return this;

}

return null;

}

public boolean importData(JComponent comp,

Transferable t) {

ImageIcon icon = null;

try {

if (t.isDataFlavorSupported(flavors[0])) {

image = (Image)t.getTransferData(flavors[0]);

icon = new ImageIcon(image);

}

if (comp instanceof JLabel) {

JLabel label = (JLabel)comp;

label.setIcon(icon);

return true;

} else if (comp instanceof AbstractButton) {

AbstractButton button = (AbstractButton)comp;

button.setIcon(icon);

return true;

}

} catch (UnsupportedFlavorException ignored) {

} catch (IOException ignored) {

}

return false;

}

// Transferable

public Object getTransferData(DataFlavor flavor) {

if (isDataFlavorSupported(flavor)) {

return image;

}

return null;

}

public DataFlavor[] getTransferDataFlavors() {

return flavors;

}

public boolean isDataFlavorSupported(DataFlavor

flavor) {

return flavor.equals(flavors[0]);

}

}

The following program demonstrates the ImageSelection class. There will be both a JLabel and JButton that uses the ImageSelection as its transfer handler. In the case of the label, three buttons will support different tasks: copying the image on the label to the clipboard, pasting an image on the clipboard to the label, and clearing the label. For the button, selection will act as a paste operation.

Associating an ImageSelection as the transfer handler for a component isn't sufficient for the behavior part of the program to work. You must create an ActionListener to associate to the button and call the necessary TransferHandler method to move the data. As previously mentioned, exportToClipboard will copy the image off the component, and importData will paste it. Like with pasting strings, you still need to get the Transferable off the clipboard though.

TransferHandler handler = label.getTransferHandler();

// Copy

handler.exportToClipboard(label, clipboard,

TransferHandler.COPY);

// Paste

Transferable clipData =

clipboard.getContents(clipboard);

if (clipData != null) {

if (clipData.isDataFlavorSupported

(DataFlavor.imageFlavor)) {

handler.importData(label, clipData);

}

}

To support pasting with the button component, you could do the same thing, but it isn't necessary. If the component that is to trigger the transfer is the source of the event, all you have to do is get the ActionListener right from the TransferHandler. There are three static methods for just such an operation: getCopyAction, getCutAction, and getPasteAction. Here's all the code that is necessary to have button selection trigger pasting of the clipboard contents to the button, setting its icon label.

JButton pasteB = new JButton("Paste");

pasteB.setTransferHandler(new ImageSelection());

pasteB.addActionListener

(TransferHandler.getPasteAction());

Putting all this together becomes a complete test program:

import java.awt.*;

import java.awt.event.*;

import java.awt.datatransfer.*;

import javax.swing.*;

import java.io.IOException;

public class ImageCopy {

public static void main(String args[]) {

JFrame frame = new JFrame("Copy Image");

frame.setDefaultCloseOperation

(JFrame.EXIT_ON_CLOSE);

Container contentPane = frame.getContentPane();

Toolkit kit = Toolkit.getDefaultToolkit();

final Clipboard clipboard =

kit.getSystemClipboard();

Icon icon = new ImageIcon("scott.jpg");

final JLabel label = new JLabel(icon);

label.setTransferHandler(new ImageSelection());

JScrollPane pane = new JScrollPane(label);

contentPane.add(pane, BorderLayout.CENTER);

JButton copy = new JButton("Label Copy");

copy.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent e) {

TransferHandler handler =

label.getTransferHandler();

handler.exportToClipboard(label, clipboard,

TransferHandler.COPY);

}

});

JButton clear = new JButton("Label Clear");

clear.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent

actionEvent) {

label.setIcon(null);

}

});

JButton paste = new JButton("Label Paste");

paste.addActionListener(new ActionListener() {

public void actionPerformed(ActionEvent

actionEvent) {

Transferable clipData =

clipboard.getContents(clipboard);

if (clipData != null) {

if (clipData.isDataFlavorSupported

(DataFlavor.imageFlavor)) {

TransferHandler handler =

label.getTransferHandler();

handler.importData(label, clipData);

}

}

}

});

JPanel p = new JPanel();

p.add(copy);

p.add(clear);

p.add(paste);

contentPane.add(p, BorderLayout.NORTH);

JPanel pasteP = new JPanel();

JButton pasteB = new JButton("Paste");

pasteB.setTransferHandler(new ImageSelection());

pasteB.addActionListener

(TransferHandler.getPasteAction());

pasteP.add(pasteB);

contentPane.add(pasteB, BorderLayout.SOUTH);

frame.setSize(400, 400);

frame.show();

}

}

When run, the program will look like the following screen, however, your images will probably differ. Try to copy only small images to the button as it isn't in a JScrollPane. Only the label supports large images.

Drag & Drop

This leads us up to how the 1.4 release of the J2SE supports drag-and-drop operations. The drag-and-drop operation is the transferring of data between objects, triggered by a gesture of the mouse or other pointing device. Prior versions of the platform required intricate work to get drag-and-drop going. For the new release, times have changed considerably. In some cases, all you have to do is call the setDragEnabled method of the component. In other cases, you need to associate a TransferHandler to the component. In all cases, the data to transfer must implement the Transferable interface, but now in many cases that part is already done.

For instance, if you want to support drag-and-drop operations within a JTextField, all you have to do is call the setDragEnabled method of the class, and the contents of the field are draggable.

JTextField tf = new JTextField();

tf.setDragEnabled(true);

This default behavior is available with many of the Swing components:

JColorChooser

JEditorPane

JFileChooser

JFormattedTextField

JList

JPasswordField

JTable

JTextArea

JTextField

JTextPane

JTree

Here's a simple program to demonstrate how easy drag-and-drop operations are with the new release of J2SE. It provides a JTextField and a JTree, both of which have drag enabled. The JTextField also supports drop operations, so you can drag something from the tree to the text field, but you must do additional work to enable drop on a JTree component.

import java.awt.*;

import java.awt.datatransfer.*;

import javax.swing.*;

public class DragOne {

public static void main(String args[]) {

JFrame frame = new JFrame("First Drag");

frame.setDefaultCloseOperation

(JFrame.EXIT_ON_CLOSE);

Container contentPane = frame.getContentPane();

JTree tree = new JTree();

JScrollPane pane = new JScrollPane(tree);

contentPane.add(pane, BorderLayout.CENTER);

tree.setDragEnabled(true);

JTextField tf = new JTextField();

tf.setDragEnabled(true);

contentPane.add(tf, BorderLayout.NORTH);

frame.setSize(300, 300);

frame.show();

}

}

In order to drag a node from the tree, you must select the icon associated with the specific node. Also, be sure to try to drag multiple selected nodes from the JTree component. This will drag the entries as an ordered HTML list.

While some components have built-in support for drag-and-drop operations, not all do. For instance, if you wanted to support dragging the image on a JLabel, you would have to initiate the drag operation yourself when the mouse was pressed over the component.

MouseListener mouseListener = new MouseAdapter() {

public void mousePressed(MouseEvent e) {

JComponent comp = (JComponent)e.getSource();

TransferHandler handler =

comp.getTransferHandler();

handler.exportAsDrag(comp, e,

TransferHandler.COPY);

}

};

label.addMouseListener(mouseListener);

As long as you associate the earlier ImageSelection handler as the TransferHandler for the component, you can drag off the image to a native application that accepts image input. It is that easy.

The ImageSelection handler is a little special in the sense that it doesn't transfer a property of the button/label. Instead it gets the Image from the Icon property of the component. When the transferable object actually is a property of the component, the creation of the handler can be done much more easily. The TransferHandler class has a constructor that accepts a property name as the constructor argument. Then, when you associate the handler with a Swing component, it knows how to acquire the appropriate content.

For instance, if you wanted to support the dragging of the text label for a JLabel, the following would work to create the TransferHandler for the label and associate it with the component:

TransferHandler handler = new TransferHandler("text");

JLabel label = new JLabel("Welcome");

label.setTransferHandler(handler);

You would still need to add the MouseListener to the component, but you don't have to do anything special to create the handler for the component.

If a Swing component doesn't provide built in drop support, you have to add that in yourself. The code to get the data being transferred via the drag-and-drop operation is identical to getting the data from the clipboard. You only need to associate the behavior with some triggering action, like when the mouse is released over a component:

MouseListener releaseListener = new MouseAdapter() {

public void mouseReleased(MouseEvent e) {

Transferable clipData = clipboard.getContents

(clipboard);

if (clipData != null) {

if (clipData.isDataFlavorSupported

(DataFlavor.imageFlavor)) {

TransferHandler handler =

component.getTransferHandler();

handler.importData(component, clipData);

}

}

}

};

The following sample program combines all of these operations. There is one draggable text label, one draggable image label, one droppable image label, and a text field for dropping the text.

import java.awt.*;

import java.awt.event.*;

import java.awt.datatransfer.*;

import javax.swing.*;

public class DragTwo {

public static void main(String args[]) {

JFrame frame = new JFrame("Second Drag");

frame.setDefaultCloseOperation

(JFrame.EXIT_ON_CLOSE);

Container contentPane = frame.getContentPane();

Toolkit kit = Toolkit.getDefaultToolkit();

final Clipboard clipboard =

kit.getSystemClipboard();

JTextField tf = new JTextField();

contentPane.add(tf, BorderLayout.NORTH);

Icon icon = new ImageIcon("scott.jpg");

JLabel label1 = new JLabel(icon);

label1.setTransferHandler(new ImageSelection());

MouseListener pressListener = new MouseAdapter() {

public void mousePressed(MouseEvent e) {

JComponent comp = (JComponent)e.getSource();

TransferHandler handler =

comp.getTransferHandler();

handler.exportAsDrag

(comp, e, TransferHandler.COPY);

}

};

label1.addMouseListener(pressListener);

TransferHandler handler = new

TransferHandler("text");

JLabel label2 = new JLabel("Drag Me");

label2.setTransferHandler(handler);

label2.addMouseListener(pressListener);

JPanel panel = new JPanel();

panel.add(label1);

panel.add(label2);

contentPane.add(panel, BorderLayout.SOUTH);

final JLabel dropZone = new JLabel();

dropZone.setTransferHandler(new ImageSelection());

MouseListener releaseListener =

new MouseAdapter() {

public void mouseReleased(MouseEvent e) {

Transferable clipData = clipboard.getContents

(clipboard);

if (clipData != null) {

if (clipData.isDataFlavorSupported

(DataFlavor.imageFlavor)) {

TransferHandler handler =

dropZone.getTransferHandler();

handler.importData(dropZone, clipData);

}

}

}

};

dropZone.addMouseListener(releaseListener);

JScrollPane pane = new JScrollPane(dropZone);

contentPane.add(pane, BorderLayout.CENTER);

frame.setSize(400, 400);

frame.show();

}

}

As shown here, the new J2SE, version 1.4 release makes adding drag-and-drop support to your applications very, very simple. With the added support for transferring images through either the system clipboard or through drag-and-drop operations, what more could you ask for? Throw in all the other new features being added to the 1.4 release and it is sure to be a success.

RESOURCES

New Swing Data Transfer Summary

Example of the older Drag-and-Drop Capabilities

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有