1.UIActivityIndicatorView的使用
UIActivityIndicatorView *activity=[[[UIActivityIndicatorViewalloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]autorelease];
z[activity setFrame:CGRectMake(150,150, 50, 50)];
[self.window addSubview:activity];
[activity startAnimating];
2. UISlider的使用
UISlider *slider=[[[UISlideralloc] initWithFrame:CGRectMake(100,20, 140,20)] autorelease];
slider.maximumValue=10;
slider.value=5;
[slider addTarget:selfaction:@selector(change:)forControlEvents:UIControlEventTouchUpInside];
[self.window addSubview:slider];
-(void)change:(UISlider*)slider
{
NSLog(@"the val is %.2f",slider.value);
}
3.UIPageControl
通常与UIScrollView连用,提示用户当前显示的页数
@property(nonatomic) NSInteger numberOfPages; // default is 0
@property(nonatomic) NSInteger currentPage; // default is 0. value pinned to 0..numberOfPages-1
@property(nonatomic) BOOL hidesForSinglePage; // hide the the indicator if there is only one page. default is NO
sample code:
UIPageControl *pageControl=[[[UIPageControl alloc] initWithFrame:CGRectMake(0, 50, 329, 30)] autorelease]; pageControl.numberOfPages=10; pageControl.currentPage=3; pageControl.backgroundColor=[UIColor grayColor]; [pageControl addTarget:self action:@selector(clicked:) forControlEvents:UIControlEventValueChanged]; [self.window addSubview:pageControl]; -(void)clicked:(id)sender { UIPageControl *pageControl=(UIPageControl*)sender; NSLog(@"curent val is %d",pageControl.currentPage); }
4.UISegmentedControl
NSArray *array=[NSArray arrayWithObjects:@"hello",@"what",@"search",nil]; UISegmentedControl *segmentControl=[[[UISegmentedControl alloc] initWithItems:array] autorelease]; segmentControl.frame=CGRectMake(0, 40, 300, 40); segmentControl.segmentedControlStyle=UISegmentedControlStyleBordered; segmentControl.selectedSegmentIndex=2; [segmentControl addTarget:self action:@selector(clicked:) forControlEvents:UIControlEventValueChanged]; [self.window addSubview:segmentControl];
/** * Copyright (C) 2010 Square, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.squareup.util; import com.squareup.Square; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.nio.channels.FileChannel; import java.util.NoSuchElementException; /** * A reliable, efficient, file-based, FIFO queue. Additions and removals are * O(1). All operations are atomic. Writes are synchronous; data will be * written to disk before an operation returns. The underlying file is * structured to survive process and even system crashes. If an I/O exception * is thrown during a mutating change, the change is aborted. It is safe to * continue to use a {@code QueueFile} instance after an exception. * * <p>All operations are synchronized. In a traditional queue, the remove * operation returns an element. In this queue, {@link #peek} and {@link * #remove} are used in conjunction. Use {@code peek} to retrieve the first * element, and then {@code remove} to remove it after successful processing. * If the system crashes after {@code peek} and during processing, the element * will remain in the queue, to be processed when the system restarts. * * <p><b><font color="red">NOTE:</font></b> The current implementation is * built for file systems that support atomic segment writes (like YAFFS). * Most conventional file systems don't support this; if the power goes out * while writing a segment, the segment will contain garbage and the file will * be corrupt. We'll add journaling support so this class can be used with * more file systems later. * * @author Bob Lee (bob@squareup.com) */ public class QueueFile { /** Initial file size in bytes. */ private static final int INITIAL_LENGTH = 4096; // one file system block /** Length of header in bytes. */ static final int HEADER_LENGTH = 16; /** * The underlying file. Uses a ring buffer to store entries. Designed so * that a modification isn't committed or visible until we write the header. * The header is much smaller than a segment. So long as the underlying file * system supports atomic segment writes, changes to the queue are atomic. * Storing the file length ensures we can recover from a failed expansion * (i.e. if setting the file length succeeds but the process dies before the * data can be copied). * * <pre> * Format: * Header (16 bytes) * Element Ring Buffer (File Length - 16 bytes) * * Header: * File Length (4 bytes) * Element Count (4 bytes) * First Element Position (4 bytes, =0 if null) * Last Element Position (4 bytes, =0 if null) * * Element: * Length (4 bytes) * Data (Length bytes) * </pre> */ private final RandomAccessFile raf; /** Cached file length. Always a power of 2. */ int fileLength; /** Number of elements. */ private int elementCount; /** Pointer to first (or eldest) element. */ private Element first; /** Pointer to last (or newest) element. */ private Element last; /** In-memory buffer. Big enough to hold the header. */ private final byte[] buffer = new byte[16]; /** * Constructs a new queue backed by the given file. Only one {@code QueueFile} * instance should access a given file at a time. */ public QueueFile(File file) throws IOException { if (!file.exists()) initialize(file); raf = open(file); readHeader(); } /** For testing. */ QueueFile(RandomAccessFile raf) throws IOException { this.raf = raf; readHeader(); } /** * Stores int in buffer. The behavior is equivalent to calling * {@link RandomAccessFile#writeInt}. */ private static void writeInt(byte[] buffer, int offset, int value) { buffer[offset] = (byte) (value >> 24); buffer[offset + 1] = (byte) (value >> 16); buffer[offset + 2] = (byte) (value >> 8); buffer[offset + 3] = (byte) value; } /** * Stores int values in buffer. The behavior is equivalent to calling * {@link RandomAccessFile#writeInt} for each value. */ private static void writeInts(byte[] buffer, int... values) { int offset = 0; for (int value : values) { writeInt(buffer, offset, value); offset += 4; } } /** * Reads an int from a byte[]. */ private static int readInt(byte[] buffer, int offset) { return ((buffer[offset] & 0xff) << 24) + ((buffer[offset + 1] & 0xff) << 16) + ((buffer[offset + 2] & 0xff) << 8) + (buffer[offset + 3] & 0xff); } /** * Reads the header. */ private void readHeader() throws IOException { raf.seek(0); raf.readFully(buffer); fileLength = readInt(buffer, 0); elementCount = readInt(buffer, 4); int firstOffset = readInt(buffer, 8); int lastOffset = readInt(buffer, 12); first = readElement(firstOffset); last = readElement(lastOffset); } /** * Writes header atomically. The arguments contain the updated values. The * class member fields should not have changed yet. This only updates the * state in the file. It's up to the caller to update the class member * variables *after* this call succeeds. Assumes segment writes are atomic * in the underlying file system. */ private void writeHeader(int fileLength, int elementCount, int firstPosition, int lastPosition) throws IOException { writeInts(buffer, fileLength, elementCount, firstPosition, lastPosition); raf.seek(0); raf.write(buffer); } /** * Returns the Element for the given offset. */ private Element readElement(int position) throws IOException { if (position == 0) return Element.NULL; raf.seek(position); return new Element(position, raf.readInt()); } /** Atomically initializes a new file. */ private static void initialize(File file) throws IOException { // Use a temp file so we don't leave a partially-initialized file. File tempFile = new File(file.getPath() + ".tmp"); RandomAccessFile raf = open(tempFile); try { raf.setLength(INITIAL_LENGTH); raf.seek(0); byte[] headerBuffer = new byte[16]; writeInts(headerBuffer, INITIAL_LENGTH, 0, 0, 0); raf.write(headerBuffer); } finally { raf.close(); } // A rename is atomic. if (!tempFile.renameTo(file)) throw new IOException("Rename failed!"); } /** * Opens a random access file that writes synchronously. */ private static RandomAccessFile open(File file) throws FileNotFoundException { return new RandomAccessFile(file, "rwd"); } /** * Wraps the position if it exceeds the end of the file. */ private int wrapPosition(int position) { return position < fileLength ? position : HEADER_LENGTH + position - fileLength; } /** * Writes count bytes from buffer to position in file. Automatically wraps * write if position is past the end of the file or if buffer overlaps it. * * @param position in file to write to * @param buffer to write from * @param count # of bytes to write */ private void ringWrite(int position, byte[] buffer, int offset, int count) throws IOException { position = wrapPosition(position); if (position + count <= fileLength) { raf.seek(position); raf.write(buffer, offset, count); } else { // The write overlaps the EOF. // # of bytes to write before the EOF. int beforeEof = fileLength - position; raf.seek(position); raf.write(buffer, offset, beforeEof); raf.seek(HEADER_LENGTH); raf.write(buffer, offset + beforeEof, count - beforeEof); } } /** * Reads count bytes into buffer from file. Wraps if necessary. * * @param position in file to read from * @param buffer to read into * @param count # of bytes to read */ private void ringRead(int position, byte[] buffer, int offset, int count) throws IOException { position = wrapPosition(position); if (position + count <= fileLength) { raf.seek(position); raf.readFully(buffer, 0, count); } else { // The read overlaps the EOF. // # of bytes to read before the EOF. int beforeEof = fileLength - position; raf.seek(position); raf.readFully(buffer, offset, beforeEof); raf.seek(HEADER_LENGTH); raf.readFully(buffer, offset + beforeEof, count - beforeEof); } } /** * Adds an element to the end of the queue. * * @param data to copy bytes from */ public void add(byte[] data) throws IOException { add(data, 0, data.length); } /** * Adds an element to the end of the queue. * * @param data to copy bytes from * @param offset to start from in buffer * @param count number of bytes to copy * * @throws IndexOutOfBoundsException if {@code offset < 0} or * {@code count < 0}, or if {@code offset + count} is bigger than the length * of {@code buffer}. */ public synchronized void add(byte[] data, int offset, int count) throws IOException { Objects.nonNull(data, "buffer"); if ((offset | count) < 0 || count > data.length - offset) { throw new IndexOutOfBoundsException(); } expandIfNecessary(count); // Insert a new element after the current last element. boolean wasEmpty = isEmpty(); int position = wasEmpty ? HEADER_LENGTH : wrapPosition( last.position + Element.HEADER_LENGTH + last.length); Element newLast = new Element(position, count); // Write length. writeInt(buffer, 0, count); ringWrite(newLast.position, buffer, 0, Element.HEADER_LENGTH); // Write data. ringWrite(newLast.position + Element.HEADER_LENGTH, data, offset, count); // Commit the addition. If wasEmpty, first == last. int firstPosition = wasEmpty ? newLast.position : first.position; writeHeader(fileLength, elementCount + 1, firstPosition, newLast.position); last = newLast; elementCount++; if (wasEmpty) first = last; // first element } /** * Returns the number of used bytes. */ private int usedBytes() { if (elementCount == 0) return HEADER_LENGTH; if (last.position >= first.position) { // Contiguous queue. return (last.position - first.position) // all but last entry + Element.HEADER_LENGTH + last.length // last entry + HEADER_LENGTH; } else { // tail < head. The queue wraps. return last.position // buffer front + header + Element.HEADER_LENGTH + last.length // last entry + fileLength - first.position; // buffer end } } /** * Returns number of unused bytes. */ private int remainingBytes() { return fileLength - usedBytes(); } /** * Returns true if this queue contains no entries. */ public synchronized boolean isEmpty() { return elementCount == 0; } /** * If necessary, expands the file to accommodate an additional element of the * given length. * * @param dataLength length of data being added */ private void expandIfNecessary(int dataLength) throws IOException { int elementLength = Element.HEADER_LENGTH + dataLength; int remainingBytes = remainingBytes(); if (remainingBytes >= elementLength) return; // Expand. int previousLength = fileLength; int newLength; // Double the length until we can fit the new data. do { remainingBytes += previousLength; newLength = previousLength << 1; previousLength = newLength; } while (remainingBytes < elementLength); raf.setLength(newLength); // If the buffer is split, we need to make it contiguous. if (last.position < first.position) { FileChannel channel = raf.getChannel(); channel.position(fileLength); // destination position int count = last.position + Element.HEADER_LENGTH + last.length - HEADER_LENGTH; if (channel.transferTo(HEADER_LENGTH, count, channel) != count) { throw new AssertionError("Copied insufficient number of bytes!"); } // Commit the expansion. int newLastPosition = fileLength + last.position - HEADER_LENGTH; writeHeader(newLength, elementCount, first.position, newLastPosition); last = new Element(newLastPosition, last.length); } else { writeHeader(newLength, elementCount, first.position, last.position); } fileLength = newLength; } /** * Reads the eldest element. Returns null if the queue is empty. */ public synchronized byte[] peek() throws IOException { if (isEmpty()) return null; int length = first.length; byte[] data = new byte[length]; ringRead(first.position + Element.HEADER_LENGTH, data, 0, length); return data; } /** * Invokes reader with the eldest element, if an element is available. */ public synchronized void peek(ElementReader reader) throws IOException { if (elementCount > 0) { reader.read(new ElementInputStream(first), first.length); } } /** * Invokes the given reader once for each element in the queue, from * eldest to most recently added. */ public synchronized void forEach(ElementReader reader) throws IOException { int position = first.position; for (int i = 0; i < elementCount; i++) { Element current = readElement(position); reader.read(new ElementInputStream(current), current.length); position = wrapPosition(current.position + Element.HEADER_LENGTH + current.length); } } /** * Reads a single element. */ private class ElementInputStream extends InputStream { private int position; private int remaining; private ElementInputStream(Element element) { position = wrapPosition(element.position + Element.HEADER_LENGTH); remaining = element.length; } @Override public int read(byte[] buffer, int offset, int length) throws IOException { Objects.nonNull(buffer, "buffer"); if ((offset | length) < 0 || length > buffer.length - offset) { throw new ArrayIndexOutOfBoundsException(); } if (length > remaining) length = remaining; ringRead(position, buffer, offset, length); position = wrapPosition(position + length); remaining -= length; return length; } @Override public int read() throws IOException { if (remaining == 0) return -1; raf.seek(position); int b = raf.read(); position = wrapPosition(position + 1); remaining--; return b; } } /** * Returns the number of elements in this queue. */ public synchronized int size() { return elementCount; } /** * Removes the eldest element. * * @throw NoSuchElementException if the queue is empty */ public synchronized void remove() throws IOException { if (isEmpty()) throw new NoSuchElementException(); if (elementCount == 1) { clear(); } else { // assert elementCount > 1 int newFirstPosition = wrapPosition(first.position + Element.HEADER_LENGTH + first.length); ringRead(newFirstPosition, buffer, 0, Element.HEADER_LENGTH); int length = readInt(buffer, 0); writeHeader(fileLength, elementCount - 1, newFirstPosition, last.position); elementCount--; first = new Element(newFirstPosition, length); } } /** * Clears this queue. Truncates the file to the initial size. */ public synchronized void clear() throws IOException { if (fileLength > INITIAL_LENGTH) raf.setLength(INITIAL_LENGTH); writeHeader(INITIAL_LENGTH, 0, 0, 0); elementCount = 0; first = last = Element.NULL; fileLength = INITIAL_LENGTH; } /** * Closes the underlying file. */ public synchronized void close() throws IOException { raf.close(); } @Override public String toString() { final StringBuilder builder = new StringBuilder(); builder.append(getClass().getSimpleName()).append('['); builder.append("fileLength=").append(fileLength); builder.append(", size=").append(elementCount); builder.append(", first=").append(first); builder.append(", last=").append(last); builder.append(", element lengths=["); try { forEach(new ElementReader() { boolean first = true; public void read(InputStream in, int length) throws IOException { if (first) { first = false; } else { builder.append(", "); } builder.append(length); } }); } catch (IOException e) { Square.warning(e); } builder.append("]]"); return builder.toString(); } /** A pointer to an element. */ static class Element { /** Length of element header in bytes. */ static final int HEADER_LENGTH = 4; /** Null element. */ static final Element NULL = new Element(0, 0); /** Position in file. */ final int position; /** The length of the data. */ final int length; /** * Constructs a new element. * * @param position within file * @param length of data */ Element(int position, int length) { this.position = position; this.length = length; } @Override public String toString() { return getClass().getSimpleName() + "[" + "position = " + position + ", length = " + length + "]"; } } /** * Reads queue elements. Enables partial reads as opposed to reading all * of the bytes into a byte[]. */ public interface ElementReader { /* * TODO: Support remove() call from read(). */ /** * Called once per element. * * @param in stream of element data. Reads as many bytes as requested, * unless fewer than the request number of bytes remains, in which case it * reads all the remaining bytes. * @param length of element data in bytes */ public void read(InputStream in, int length) throws IOException; } } QueueFileTest.java: package com.squareup.util; import android.test.AndroidTestCase; import com.squareup.Square; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; import junit.framework.ComparisonFailure; /** * Tests for QueueFile. * * @author Bob Lee (bob@squareup.com) */ public class QueueFileTest extends AndroidTestCase { /** * Takes up 33401 bytes in the queue (N*(N+1)/2+4*N). Picked 254 instead of * 255 so that the number of bytes isn't a multiple of 4. */ private static int N = 254; // private static byte[][] values = new byte[N][]; static { for (int i = 0; i < N; i++) { byte[] value = new byte[i]; // Example: values[3] = { 3, 2, 1 } for (int ii = 0; ii < i; ii++) value[ii] = (byte) (i - ii); values[i] = value; } } private File file; @Override protected void setUp() throws Exception { file = getContext().getFileStreamPath("test.queue"); file.delete(); } @Override protected void tearDown() throws Exception { file.delete(); } public void testAddOneElement() throws IOException { // This test ensures that we update 'first' correctly. QueueFile queue = new QueueFile(file); byte[] expected = values[253]; queue.add(expected); assertEquals(expected, queue.peek()); queue.close(); queue = new QueueFile(file); assertEquals(expected, queue.peek()); } public void testAddAndRemoveElements() throws IOException { long start = System.nanoTime(); Queue<byte[]> expected = new LinkedList<byte[]>(); for (int round = 0; round < 5; round++) { QueueFile queue = new QueueFile(file); for (int i = 0; i < N; i++) { queue.add(values[i]); expected.add(values[i]); } // Leave N elements in round N, 15 total for 5 rounds. Removing all the // elements would be like starting with an empty queue. for (int i = 0; i < N - round - 1; i++) { assertEquals(expected.remove(), queue.peek()); queue.remove(); } queue.close(); } // Remove and validate remaining 15 elements. QueueFile queue = new QueueFile(file); assertEquals(15, queue.size()); assertEquals(expected.size(), queue.size()); while (!expected.isEmpty()) { assertEquals(expected.remove(), queue.peek()); queue.remove(); } queue.close(); // length() returns 0, but I checked the size w/ 'ls', and it is correct. // assertEquals(65536, file.length()); Square.debug("Ran in " + ((System.nanoTime() - start) / 1000000) + "ms."); } /** * Tests queue expansion when the data crosses EOF. */ public void testSplitExpansion() throws IOException { // This should result in 3560 bytes. int max = 80; Queue<byte[]> expected = new LinkedList<byte[]>(); QueueFile queue = new QueueFile(file); for (int i = 0; i < max; i++) { expected.add(values[i]); queue.add(values[i]); } // Remove all but 1. for (int i = 1; i < max; i++) { assertEquals(expected.remove(), queue.peek()); queue.remove(); } // This should wrap around before expanding. for (int i = 0; i < N; i++) { expected.add(values[i]); queue.add(values[i]); } while (!expected.isEmpty()) { assertEquals(expected.remove(), queue.peek()); queue.remove(); } queue.close(); } public void testFailedAdd() throws IOException { QueueFile queueFile = new QueueFile(file); queueFile.add(values[253]); queueFile.close(); final BrokenRandomAccessFile braf = new BrokenRandomAccessFile(file, "rwd"); queueFile = new QueueFile(braf); try { queueFile.add(values[252]); fail(); } catch (IOException e) { /* expected */ } braf.rejectCommit = false; // Allow a subsequent add to succeed. queueFile.add(values[251]); queueFile.close(); queueFile = new QueueFile(file); assertEquals(2, queueFile.size()); assertEquals(values[253], queueFile.peek()); queueFile.remove(); assertEquals(values[251], queueFile.peek()); } public void testFailedRemoval() throws IOException { QueueFile queueFile = new QueueFile(file); queueFile.add(values[253]); queueFile.close(); final BrokenRandomAccessFile braf = new BrokenRandomAccessFile(file, "rwd"); queueFile = new QueueFile(braf); try { queueFile.remove(); fail(); } catch (IOException e) { /* expected */ } queueFile.close(); queueFile = new QueueFile(file); assertEquals(1, queueFile.size()); assertEquals(values[253], queueFile.peek()); queueFile.add(values[99]); queueFile.remove(); assertEquals(values[99], queueFile.peek()); } public void testFailedExpansion() throws IOException { QueueFile queueFile = new QueueFile(file); queueFile.add(values[253]); queueFile.close(); final BrokenRandomAccessFile braf = new BrokenRandomAccessFile(file, "rwd"); queueFile = new QueueFile(braf); try { // This should trigger an expansion which should fail. queueFile.add(new byte[8000]); fail(); } catch (IOException e) { /* expected */ } queueFile.close(); queueFile = new QueueFile(file); assertEquals(1, queueFile.size()); assertEquals(values[253], queueFile.peek()); assertEquals(4096, queueFile.fileLength); queueFile.add(values[99]); queueFile.remove(); assertEquals(values[99], queueFile.peek()); } public void testPeakWithElementReader() throws IOException { QueueFile queueFile = new QueueFile(file); final byte[] a = { 1, 2 }; queueFile.add(a); final byte[] b = { 3, 4, 5 }; queueFile.add(b); queueFile.peek(new QueueFile.ElementReader() { public void read(InputStream in, int length) throws IOException { assertEquals(length, 2); byte[] actual = new byte[length]; in.read(actual); assertEquals(a, actual); } }); queueFile.peek(new QueueFile.ElementReader() { public void read(InputStream in, int length) throws IOException { assertEquals(length, 2); assertEquals(1, in.read()); assertEquals(2, in.read()); assertEquals(-1, in.read()); } }); queueFile.remove(); queueFile.peek(new QueueFile.ElementReader() { public void read(InputStream in, int length) throws IOException { assertEquals(length, 3); byte[] actual = new byte[length]; in.read(actual); assertEquals(b, actual); } }); assertEquals(b, queueFile.peek()); assertEquals(1, queueFile.size()); } public void testForEach() throws IOException { QueueFile queueFile = new QueueFile(file); final byte[] a = { 1, 2 }; queueFile.add(a); final byte[] b = { 3, 4, 5 }; queueFile.add(b); final int[] iteration = new int[] { 0 }; QueueFile.ElementReader elementReader = new QueueFile.ElementReader() { public void read(InputStream in, int length) throws IOException { if (iteration[0] == 0) { assertEquals(length, 2); byte[] actual = new byte[length]; in.read(actual); assertEquals(a, actual); } else if (iteration[0] == 1) { assertEquals(length, 3); byte[] actual = new byte[length]; in.read(actual); assertEquals(b, actual); } else { fail(); } iteration[0]++; } }; queueFile.forEach(elementReader); assertEquals(a, queueFile.peek()); assertEquals(2, iteration[0]); } /** * Compares two byte[]s for equality. */ private static void assertEquals(byte[] expected, byte[] actual) { if (!Arrays.equals(expected, actual)) { throw new ComparisonFailure(null, Arrays.toString(expected), Arrays.toString(actual)); } } /** * A RandomAccessFile that can break when you go to write the COMMITTED * status. */ static class BrokenRandomAccessFile extends RandomAccessFile { boolean rejectCommit = true; BrokenRandomAccessFile(File file, String mode) throws FileNotFoundException { super(file, mode); } @Override public void write(byte[] buffer) throws IOException { if (rejectCommit && getFilePointer() == 0) { throw new IOException("No commit for you!"); } super.write(buffer); } } }
又到周末了,尝试实现一个图标拖拽的小程序;主要实现的功能有:
1.两个部件的图标拖动转移
2.可以切换图标查看模式,有大图标和小图标模式两种
3.可以删除图标,添加也应该不难,所以就没实现。
4.可以框选多个图标
5.改变部件大小可以自动重新布局图标
这里初始化左边有十几个图标,右边的部件是空的。
主窗口class MainWindow:public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = NULL); ~MainWindow(); private: Splitter *m_pSplitter; MainWidget *m_pLeftWidget; MainWidget *m_pRightWidget; QScrollArea *m_pSplitterArea; };
MainWindow::MainWindow( QWidget *parent /*= NULL*/ ):QMainWindow(parent) { this->setWindowTitle("IconDemo"); m_pSplitter = new Splitter(this); m_pLeftWidget = new MainWidget(this); m_pLeftWidget->init(); m_pRightWidget = new MainWidget(this); QSize size = m_pRightWidget->size(); m_pSplitterArea = new QScrollArea(this); m_pSplitterArea->setWidget(m_pSplitter); m_pSplitter->addWidget(m_pLeftWidget); m_pSplitter->addWidget(m_pRightWidget); m_pSplitter->setMinimumSize(QSize(700,700)); m_pSplitterArea->setMinimumSize(QSize(700,600)); m_pSplitter->setCollapsible(0,false); m_pSplitter->setCollapsible(1,false); this->setStyleSheet(" QFrame{background-image: url(/blog_article/_/DragIcon/Resources/51.jpg) }"); this->setFixedSize(QSize(700,550)); connect(m_pLeftWidget,SIGNAL(heightChangeSignal(int)),m_pSplitter,SLOT(heightChangeSlot(int ))); connect(m_pRightWidget,SIGNAL(heightChangeSignal(int)),m_pSplitter,SLOT(heightChangeSlot(int ))); } MainWindow::~MainWindow() { }
分割器
class Splitter:public QSplitter { Q_OBJECT public: Splitter(QWidget *parent = NULL); ~Splitter(); protected slots: void heightChangeSlot(int height); private: };
Splitter::Splitter( QWidget *parent /*= NULL*/ ):QSplitter(parent) { } Splitter::~Splitter() { } void Splitter::heightChangeSlot( int height ) { this->setMinimumHeight(height); }
enum {BigIcon_Mode = 0, SmallIcon_Mode}; struct IconItemData { QImage image; QString name; int mode ; IconItemData() { mode = SmallIcon_Mode; } }; class Controller:public QObject { Q_OBJECT public: Controller(QObject *parent = NULL); ~Controller(); void init(); void addItem(IconItemData* pItem); void deleteItem(int index); void deleteItem(IconItemData* pItem); void changeIconMode(int mode); size_t getSize() { return m_IconDataVec.size(); } IconItemData* getItem(int index); protected: private: vector<IconItemData*> m_IconDataVec; }; class ItemMimeData:public QMimeData { Q_OBJECT public: ItemMimeData():QMimeData() { m_pDragItemList = NULL; } ~ItemMimeData() { if (m_pDragItemList) { delete m_pDragItemList; } } void SetDragDatas(QString mimeType , QList<IconItemData*> *pItemList) { m_format<<mimeType; m_pDragItemList = pItemList; } QStringList formats() const { return m_format; } const QList<IconItemData*>* DragItemData() const { return m_pDragItemList; } protected: QVariant retrieveData(const QString &mimetype, QVariant::Type preferredType) const { if (mimetype == "ItemMimeData") { return m_pDragItemList; } else { return QMimeData::retrieveData(mimetype, preferredType); } } private: QStringList m_format; const QList<IconItemData*> *m_pDragItemList; };
void Controller::addItem( IconItemData* pItem ) { if (pItem) { m_IconDataVec.push_back(pItem); } } void Controller::deleteItem( int index ) { if ((index > 0) && (index < m_IconDataVec.size())) { vector<IconItemData*>::iterator it = m_IconDataVec.begin(); it = it + index; m_IconDataVec.erase(it); } } void Controller::deleteItem( IconItemData* pItem ) { if (pItem) { vector<IconItemData*>::iterator it = m_IconDataVec.begin(); for (it; it != m_IconDataVec.end(); ++it) { if (pItem == *it) { m_IconDataVec.erase(it); break; } } } } IconItemData* Controller::getItem( int index ) { if ((index >= 0) && (index < m_IconDataVec.size())) { return m_IconDataVec[index]; } } void Controller::init() { for (int i = 0; i < 20; ++i) { IconItemData *pItem = new IconItemData; pItem->name = QString::number(i); QString iconPath = QString(":/DragIcon/Resources/%1.jpg").arg(i); QFile Iconfile(iconPath); if (Iconfile.exists()) { pItem->image = QImage(iconPath); m_IconDataVec.push_back(pItem); } else { delete pItem; } } } Controller::Controller( QObject *parent /*= NULL*/ ):QObject(parent) { //init(); } Controller::~Controller() { } void Controller::changeIconMode( int mode ) { for (int i = 0; i < m_IconDataVec.size(); ++i) { m_IconDataVec[i]->mode = mode; } }
图标部件
class ItemNameLineEdit:public QLineEdit { Q_OBJECT public: ItemNameLineEdit(QWidget *parent = NULL); ~ItemNameLineEdit(); protected: void mouseDoubleClickEvent ( QMouseEvent * event ); private slots: void finishEditSlot(); }; class IconItemWidget:public QWidget { Q_OBJECT public: IconItemWidget(IconItemData *pData,QWidget *parent = NULL); ~IconItemWidget(); void unSelectedItem() { m_selected = false; } void SelectItem() { m_selected = true; } IconItemData *getData() { return m_pItemData; } protected: void resizeEvent ( QResizeEvent * event ) ; void paintEvent ( QPaintEvent * event ) ; private: QLabel *m_pImage; ItemNameLineEdit *m_pNameLEdit; IconItemData *m_pItemData; bool m_selected; QPoint m_mousePressPos; };
IconItemWidget::IconItemWidget( IconItemData *pData,QWidget *parent /*= NULL*/ ):QWidget(parent),m_pItemData(pData) { m_selected = false; m_pImage = new QLabel(this); m_pNameLEdit = new ItemNameLineEdit(this); m_pNameLEdit->setAlignment(Qt::AlignCenter); QSize imageSize; QPixmap pixmap ; if (m_pItemData->mode == BigIcon_Mode) { pixmap = QPixmap::fromImage(m_pItemData->image).scaled(QSize(100,100),Qt::KeepAspectRatio); imageSize = pixmap.size(); m_pImage->setPixmap(pixmap); m_pNameLEdit->setText(m_pItemData->name); const int spacing = 5; int nameheight = 15; m_pImage->setGeometry(spacing,spacing,imageSize.width(),imageSize.height()); m_pNameLEdit->setGeometry(spacing,spacing + imageSize.height(),imageSize.width(),nameheight); this->setFixedSize(QSize(imageSize.width() + 10,imageSize.height() + 25)); } else if (m_pItemData->mode == SmallIcon_Mode) { pixmap = QPixmap::fromImage(m_pItemData->image).scaled(QSize(80,80),Qt::KeepAspectRatio); imageSize = pixmap.size(); m_pImage->setPixmap(pixmap); m_pNameLEdit->setText(m_pItemData->name); const int spacing = 5; int nameheight = 15; m_pImage->setGeometry(spacing,spacing,imageSize.width(),imageSize.height()); m_pNameLEdit->setGeometry(spacing,spacing + imageSize.height(),imageSize.width(),nameheight); this->setFixedSize(QSize(imageSize.width() + 10,imageSize.height() + 25)); } } IconItemWidget::~IconItemWidget() { } void IconItemWidget::resizeEvent( QResizeEvent * event ) { QWidget::resizeEvent(event); } void IconItemWidget::paintEvent( QPaintEvent * event ) { if (m_selected) { //添加选中样式边框 this->setStyleSheet("QLabel{border-width: 2px;border-style: solid;border-color: blue;}"); } else { //取消选中样式 this->setStyleSheet(""); } QWidget::paintEvent(event); } void ItemNameLineEdit::mouseDoubleClickEvent( QMouseEvent * event ) { this->setReadOnly(false); } ItemNameLineEdit::ItemNameLineEdit( QWidget *parent /*= NULL*/ ):QLineEdit(parent) { this->setContextMenuPolicy(Qt::NoContextMenu); //禁用默认右键菜单 this->setReadOnly(true); connect(this ,SIGNAL(editingFinished ()),this,SLOT(finishEditSlot())); } ItemNameLineEdit::~ItemNameLineEdit() { } void ItemNameLineEdit::finishEditSlot() { this->setReadOnly(true); }
布局部件
class MainWidget:public QWidget { Q_OBJECT public: MainWidget(QWidget *parent = NULL); ~MainWidget(); void init(); private slots: void bigModeSlot(); void smallModeSlot(); void reLayoutIconSlot(); void deleteItemSlot(); protected: void mousePressEvent ( QMouseEvent * event ); void mouseMoveEvent ( QMouseEvent * event ); void mouseReleaseEvent(QMouseEvent *event); void dragEnterEvent ( QDragEnterEvent * event ); void dragLeaveEvent ( QDragLeaveEvent * event ); void dragMoveEvent ( QDragMoveEvent * event ) ; void dropEvent(QDropEvent *event); void resizeEvent ( QResizeEvent * event ); void paintEvent ( QPaintEvent * event ); QRect GetItemRect( int index); void clear(); void performDrag(); void SelectItem(); bool HitTest(QMouseEvent * event); bool isInVector(IconItemWidget* pItem); signals: void heightChangeSignal(int height); private: int iconMode; Controller *m_pController; vector<IconItemWidget*> m_ItemVec; QPushButton *m_pAddButton; QPoint m_mousePressPos; vector<IconItemWidget*> m_selectItemVec; QPoint m_mousePos; };
MainWidget::MainWidget( QWidget *parent /*= NULL*/ ):QWidget(parent),iconMode(SmallIcon_Mode) { this->setAcceptDrops(true); m_pController = new Controller(this); //init(); this->setMinimumWidth(100); } MainWidget::~MainWidget() { } void MainWidget::init() { m_pController->init(); for (int i = 0; i < m_pController->getSize(); ++i) { IconItemWidget *pItemWidget = new IconItemWidget(m_pController->getItem(i),this); m_ItemVec.push_back(pItemWidget); } } //获取每个图标应该布局的位置 QRect MainWidget::GetItemRect( int index ) { if (index < 0 || index > m_ItemVec.size()) { return QRect(); } const int spacing = 5; int width = this->width(); int height = this->height(); int itemWidth = m_ItemVec[index]->width(); int itemHeight = m_ItemVec[index]->height(); int colCount = width / (itemWidth + spacing ); int rowCount = height / (itemHeight + spacing); int row = index / colCount; int col = index % colCount; int xPos = col * (itemWidth + spacing ); int yPos = row * (itemHeight + spacing); return QRect(xPos,yPos,itemWidth,itemHeight); } void MainWidget::resizeEvent( QResizeEvent * event ) { //改变大小了要重新布局 for (int i = 0; i < m_ItemVec.size(); ++i) { QRect rect = GetItemRect(i); if (i == m_ItemVec.size() - 1) { this->setMinimumHeight(rect.y() + 20); emit heightChangeSignal(this->height()); } m_ItemVec[i]->setGeometry(rect); } QWidget::resizeEvent(event); } void MainWidget::paintEvent( QPaintEvent * event ) { if (m_mousePos.x() == 0 && m_mousePos.y() == 0) { return; } //画红色选框 QRect rect(m_mousePressPos,m_mousePos); QPainter painter(this); painter.setPen(Qt::red); painter.drawRect(rect); update(); QWidget::paintEvent(event); } void MainWidget::mousePressEvent( QMouseEvent * event ) { m_mousePressPos = event->pos(); //点击空白处则情况选中, //如果m_selectItemVec大小为1,则也要清空下面再另外选中 //右键单击也要清空m_selectItemVec if (!HitTest(event) || (m_selectItemVec.size() == 1) ||Qt::RightButton == event->button()) { for (int i = 0; i < m_selectItemVec.size(); ++i) { m_selectItemVec[i]->unSelectedItem(); } m_selectItemVec.clear(); } for (int i = 0; i < m_ItemVec.size(); ++i) { QRect rect = GetItemRect(i); if (rect.contains(event->pos()) && (!isInVector(m_ItemVec[i]))) //图标尚未被选中则添加到m_selectItemVec { m_selectItemVec.push_back(m_ItemVec[i]); m_ItemVec[i]->SelectItem(); } } QWidget *pWidget = QApplication::focusWidget(); //如果正在编辑名称,点击别的地方可以结束编辑 if (pWidget) { pWidget->clearFocus(); } //右键菜单 if (Qt::RightButton == event->button()) { QMenu *pMenu = new QMenu(this); QAction *pBigModeAct = new QAction(tr("大图标"),pMenu); QAction *pSmallModeAct = new QAction(tr("小图标"),pMenu); QAction *pDeleteAct = new QAction(tr("删除"),pMenu); if (m_selectItemVec.size() > 0) //有选中的则弹出删除菜单 { pMenu->addAction(pDeleteAct); connect(pDeleteAct,SIGNAL(triggered()),this,SLOT(deleteItemSlot())); } else { //点击空白处则可切换图标模式 pMenu->addAction(pBigModeAct); pMenu->addAction(pSmallModeAct); connect(pBigModeAct,SIGNAL(triggered()),this,SLOT(bigModeSlot())); connect(pSmallModeAct,SIGNAL(triggered()),this,SLOT(smallModeSlot())); } pMenu->exec(event->globalPos()); delete pMenu; } QWidget::mousePressEvent(event); } //大图标模式 void MainWidget::bigModeSlot() { m_pController->changeIconMode(BigIcon_Mode); reLayoutIconSlot(); this->update(); } //小图标模式 void MainWidget::smallModeSlot() { m_pController->changeIconMode(SmallIcon_Mode); reLayoutIconSlot(); this->update(); } void MainWidget::reLayoutIconSlot() { clear(); //先清除 for (int i = 0; i < m_pController->getSize(); ++i) { //重新生成图标 IconItemWidget *pItemWidget = new IconItemWidget(m_pController->getItem(i),this); m_ItemVec.push_back(pItemWidget); } for (int i = 0; i < m_ItemVec.size(); ++i) { QRect rect = GetItemRect(i); m_ItemVec[i]->setGeometry(rect); m_ItemVec[i]->show(); //重新生成,布局图标必须show才能显示 } this->repaint(); } void MainWidget::clear() { qDeleteAll(m_ItemVec); m_ItemVec.clear(); } void MainWidget::mouseMoveEvent( QMouseEvent * event ) { if (event->buttons() & Qt::LeftButton && (m_selectItemVec.size() > 0)) { if (( m_mousePressPos - event->pos()).manhattanLength() > QApplication::startDragDistance()) { performDrag(); } } else { m_mousePos = event->pos(); } QWidget::mouseMoveEvent(event); } void MainWidget::mouseReleaseEvent( QMouseEvent *event ) { if (m_mousePos.x() == 0 && m_mousePos.y() == 0) { return; } else { //release鼠标再进行选择 SelectItem(); } m_mousePos = QPoint(); QWidget::mouseReleaseEvent(event); } void MainWidget::dragEnterEvent( QDragEnterEvent * event ) { const ItemMimeData *pMimeData = (const ItemMimeData*)event->mimeData(); const QList<IconItemData*>* plist = pMimeData->DragItemData(); if (plist) { event->accept(); } } void MainWidget::dragLeaveEvent( QDragLeaveEvent * event ) { } void MainWidget::dragMoveEvent( QDragMoveEvent * event ) { event->accept(); } void MainWidget::performDrag() { QDrag *pDrag = new QDrag(this); ItemMimeData *pData = new ItemMimeData; QList<IconItemData*> *plist = new QList<IconItemData*>; for (int i = 0; i < m_selectItemVec.size(); ++i) { plist->append(m_selectItemVec[i]->getData()); } pData->SetDragDatas("ItemMimeData",plist); pDrag->setMimeData(pData); QPixmap pixmap = QPixmap::fromImage(m_selectItemVec[0]->getData()->image).scaled(50,50,Qt::KeepAspectRatio); pDrag->setPixmap(pixmap); pDrag->exec(Qt::CopyAction); //delete m_selectItemVec[0]; } void MainWidget::dropEvent( QDropEvent *event ) { const ItemMimeData *pMimeData = (const ItemMimeData*)event->mimeData(); const QList<IconItemData*>* plist = pMimeData->DragItemData(); if (plist) { for (int i = 0; i < plist->size(); ++i) { m_pController->addItem(plist->at(i)); } reLayoutIconSlot(); event->accept(); } } void MainWidget::deleteItemSlot() { //删除再重新布局 for (int i = 0; i < m_selectItemVec.size(); ++i) { m_pController->deleteItem(m_selectItemVec[i]->getData()); } reLayoutIconSlot(); } //框选多个图标 void MainWidget::SelectItem() { QRect rect(m_mousePressPos,m_mousePos); for (int i = 0; i < m_ItemVec.size(); ++i) { QPoint centerPos = GetItemRect(i).center(); if (rect.contains(centerPos)) { m_ItemVec[i]->SelectItem(); m_selectItemVec.push_back(m_ItemVec[i]); } } } //有点击图标则返回true,否则返回false bool MainWidget::HitTest( QMouseEvent * event ) { for (int i = 0; i < m_ItemVec.size(); ++i) { QRect rect = GetItemRect(i); if (rect.contains(event->pos())) { return true; } } return false; } //检查图标是否已经被选中 bool MainWidget::isInVector(IconItemWidget* pItem ) { for (int i = 0; i < m_selectItemVec.size(); ++i) { if (m_selectItemVec[i] == pItem) { return true; } } return false; }
结果截图;