- 浏览: 131586 次
- 性别:
- 来自: 广州
文章分类
最新评论
-
jingjieyiman:
引用
[url][/url][flash=200,200][ ...
Java 学习入门到高深 -
Technoboy:
...
AOP 的利器:ASM 3.0 介绍 -
kingkan:
非常支持,我有意愿加入。。。如果可以,请给我短消息,je我每天 ...
【征集】做一个管理人际关系的应用 -
夜之son:
csuzm0613 写道能否直接做成Android应用程序呢? ...
【征集】做一个管理人际关系的应用 -
gdwrx_winson:
谢谢异常哥的回复,目前的想法是做成一个web应用抛出异常的爱 ...
【征集】做一个管理人际关系的应用
转自http://www.blogjava.net/javagui/archive/2007/10/23/155271.html
SWT自定义组件之Slider
曾经介绍过用SWT实现MSN风格的下拉框,SWT虽然没有Swing那么强大,尤其是在打造专业外观上,不支持L&F,但是通过自定义组件,同样可以达到用户要求。下面就向大家介绍本人实现的一个具备专业外观的Slider控件。
首先来参考一下组件的实际运行效果,并和SWT原生组件进行一下对比。
可以看出,经过自定义的组件在外观上要比SWT直接调用本地组件显得更加专业。当用户托拽滑动块时,还会出现一个虚拟的滑动块用来标识将要移动到的位置。演示就到此为止,下面详细介绍这个很Cool的组件是如何通过SWT实现的。
基本设计思想:与其他自定义组件一样,是通过继承org.eclipse.swt.widgets.Composite来实现,定义该类为Slider,另外滑动块(thumb)也是Composite,并放在Slider之上,当鼠标移动thumb时,调用setBounds方法定位在Slider在父组件(Slider)上的位置,从而达到拖拽thumb的目的。此外通过实现PaintListener接口进行自定义绘制,绘制的对象包括组件边框、被填充的格子、未被填充的格子、虚拟滑块。
接触过GUI编程的程序员都应该知道像Scroll、Slider、ProgressBar这样的控件都有setMaxValue、setMinValue、setValue这样的方法,除了鼠标拖拽thumb来改变当前数值外,可直接调用setValue来设置当前值。此外这些控件还有水平(Horizontal)、垂直(Vertical)两种布局,对于事件处理一般都要有一个从java.util.EventObject继承而来的事件类,还要编写事件监听器(Listener)接口,因此在开始编写Slider控件之前先定义3个类,代码都不是很长,如果你熟悉AWT、Swing的事件处理机制,相信你能轻松跳过。
public enum SliderOrientation {
HORIZONTAL, VERTICAL;
}
public class SliderEvent extends EventObject {
private int value;
public SliderEvent(Object source, int value) {
super(source);
this.value = value;
}
public int getValue() {
return value;
}
}
public interface SliderListener {
public void valueChanged(SliderEvent event);
}
接下来着重介绍Slider。首先是继承Composite并实现ControlListener、PaintListener、MouseListener,、MouseMoveListener,、MouseTrackListener,然后自动生成接口方法代码,通过Eclipse可以轻松实现,需要注意的是MouseListener,有java.awt.event.MouseListener和org.eclipse.swt.events.MouseListener两种,不要混淆,否则错误很难找到。然后是要采集一些数据信息,分别是:边框颜色、已有数据部分的填充颜色(上图中组件的绿色部分)、未达到数据部分的填充颜色(上图中组件的白色部分)、被禁用时的填充颜色、水平滑块的图标(正常、托拽中两种)、垂直滑块图标(正常、托拽中两种)、水平、垂直虚拟滑块图标。以上这些数据对应的常量声明如下:
private final Color BORDER_COLOR = new Color(Display.getCurrent(), 180, 188, 203);
private final Color FILL_COLOR = new Color(Display.getCurrent(), 147, 217, 72);
private final Color BLANK_COLOR = new Color(Display.getCurrent(), 254, 254, 254);
private final Color DISABLE_COLOR = new Color(Display.getCurrent(), 192, 192, 192);
private final Image THUMB_ICON_V = new Image(Display.getDefault(), "slider_up_v.png");
private final Image THUMB_OVER_ICON_V = new Image(Display.getDefault(), "slider_over_v.png");
private final Image THUMB_ICON_H = new Image(Display.getDefault(), "slider_up_h.png");
private final Image THUMB_OVER_ICON_H = new Image(Display.getDefault(), "slider_over_h.png");
private final Image TEMP_H = new Image(Display.getDefault(), "temp_h.png");
private final Image TEMP_V = new Image(Display.getDefault(), "temp_v.png");
除了这些常量,还应该声明默认最大值的常量,private final int DEFAULT_MAX_VALUE = 100;
接下来定义当前数值和最大值,
private int value;
private int maxValue = DEFAULT_MAX_VALUE;
并生成以上两个成员属性的get方法
然后定义滑动块和布局
private SliderOrientation orientation;
private Composite thumb;
要处理数值变化,需要实现一组监听器,添加如下代码
private List<sliderlistener> listeners = new ArrayList<sliderlistener>();
public void addSliderListener(SliderListener sliderListener) {
listeners.add(sliderListener);
}
public void removeSliderListener(SliderListener sliderListener) {
listeners.remove(sliderListener);
}
接下来定义2个辅助方法,实现valuepelsLength转换。其中value是当前的数值,由具体业务来决定,下文中称其业务值。例如一个音量控制器,音量范围在0~500,那么从业务上来讲可以将数值设置在0~500之间的任何数,而pelsLength则由控件的长/高度来决定,单位是像素。但是value与pelsLength之间存在着一个比例关系式:value/maxValue=pelsLength/控件长度或高度。这样不难得出两个函数的定义。
private int valueToPels(int value) {
float widgetLength = (orientation == SliderOrientation.HORIZONTAL) ? getBounds().width
: getBounds().height;
return (int) (widgetLength * (float) value / (float) maxValue);
}
private int pelsToValue(int pels) {
float widgetLength = (orientation == SliderOrientation.HORIZONTAL) ? getBounds().width
: getBounds().height;
return (int) ((float) pels * (float) maxValue / (float) widgetLength);
}
最后定义构造器。代码如下
public Slider(Composite parent, SliderOrientation orientation) {
super(parent, SWT.FLAT);
this.orientation = orientation;
thumb = new Composite(this, SWT.FLAT);
thumb
.setBackgroundImage(orientation == SliderOrientation.VERTICAL ? THUMB_ICON_V
: THUMB_ICON_H);
addControlListener(this);
addPaintListener(this);
thumb.addMouseListener(this);
thumb.addMouseMoveListener(this);
thumb.addMouseTrackListener(this);
}
在构造器中,注入布局对象,然后在控件上创建滑动块组件thumb,并添加鼠标处理等。
到此为止,基本的成员和方法的定义完毕,下面循序渐进讨论如何实现这一Slider。
一、绘制边框
由于是绘制操作,所以一切绘制代码均在paintControl方法内实现,先将如下代码拷贝到paintControl内
int w = getBounds().width;
int h = getBounds().height;
int fillLength = valueToPels(getValue());
GC gc = e.gc;
switch (orientation) {
case HORIZONTAL:
break;
case VERTICAL:
break;
}
分析如下
首先获取控件的长度与高度,因为接下来的绘制要经常用到这两个变量。
“int fillLength = valueToPels(getValue());”这一行代码稍后作解释,然后是获得绘制上下文对象,下一步是根据布局不同采用不同的处理,除了paintControl函数,在其他很多地方都对布局进行判断,但是简单起见,只对水平布局进行介绍,垂直部分参考完整程序。
接下来的绘制操作均在case HORIZONTAL中进行,首先将颜色设置为边框的颜色
gc.setForeground(BORDER_COLOR);然后绘制一个矩形gc.drawRectangle(0, 2, w - 1, h - 5);
关于为什么要偏移2像素、长度为什么减1、高度为什么减5,请参考有关绘图的基本知识,上一篇也有简单的介绍。
现在,你就可以编写测试程序来验证结果了,看看边框是否与示例的效果一样。
二、托拽thumb的实现
桌面GUI编程领域技术深浅的度量衡通常有4项指标:皮肤(外观,swing组件体系称其L&F)、绘图、自定义组件布局(Layout)、自定义组件。而托拽是实现自定义组件和绘图不可或缺的技术,也是难点之一,因此掌握的深浅是衡量桌面编程水平的标志。
虽然作为难点,但是也有章可循,其基本实现简单到只监听鼠标事件这么简单,基本流程是:当鼠标在thumb上按下时,记住这个位置,然后按住鼠标左键托拽,最后松开鼠标计算两个位置之间的距离(位移),根据位移量移动thumb的位置并换算出等价的value增量(可能为负值)进行业务逻辑处理。下面通过代码循序渐进完成。
定义一个位置变量用来存储鼠标单击的位置,private Point controlPoint;然后实现public void mouseDown(MouseEvent e)和public void mouseUp(MouseEvent e)两个方法。
public void mouseDown(MouseEvent e) {
controlPoint = new Point(e.x, e.y);
thumb
.setBackgroundImage(orientation == SliderOrientation.VERTICAL ? THUMB_OVER_ICON_V
: THUMB_OVER_ICON_H);
}
public void mouseUp(MouseEvent e) {
try {
thumb
.setBackgroundImage(orientation == SliderOrientation.VERTICAL ? THUMB_ICON_V
: THUMB_ICON_H);
countValue(e);
} finally {
controlPoint = null;
}
}
“controlPoint = new Point(e.x, e.y);”这一行实现记住鼠标点的位置,注意,这个位置是相对滑块thumb的,因为是thumb监听的鼠标事件。接下来是设置滑动块背景,不难理解当鼠标松开时,应该将背景恢复。然后进行非常重要的换算工作,通过countValue方法实现,最后务必要把鼠标位置清空,用try-finally是有必要这样的。之所以要在方法结束的时候清空controlPoint,是因为在鼠标移动的时候需要对controlPoint进行更加复杂的计算。稍后讲解mouseMove实现的时候再作解释,接下来着重分析countValue方法。
如前所述,countValue完成计算鼠标按下、松开的位移量,换算成与业务相关的数据(value)。
private void countValue(MouseEvent e) {
switch (orientation) {
case HORIZONTAL:
int movedX = e.x - controlPoint.x;
setValue(getValue() + pelsToValue(movedX));
break;
case VERTICAL:
......
}
}
“int movedX = e.x - controlPoint.x;”实现了位移量的计算,保存到movedX,调用pelsToValue方法将movedX转换成业务值的增量,然后调用setValue重新赋值,注意pelsToValue得到的是增量,需要与原值(getValue()得到)叠加。接下来分析setValue方法。
public void setValue(int value) {
if (value getMaxValue()) {
this.value = getMaxValue();
} else {
this.value = value;
}
try {
moveThumb();
redraw();
} finally {
for (SliderListener listener : listeners) {
try {
SliderEvent event = new SliderEvent(this, getValue());
listener.valueChanged(event);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
方法开始处的一系列if语句对value进行验证后再赋值,然后是调用moveThumb实现滑块移动、调用redraw对组件重新绘制,最后是处理具体的业务,可以看出处理业务是setValue方法的关键,所以要用try-finally,谁也不敢确保moveThumb、redraw不出问题。对于实现业务是遍历监听器列表然后执行每个监听器的valueChanged方法,这种事件源-监听器模型也是Java2以后的GUI事件实现模型。
private void moveThumb() {
Image icon = thumb.getBackgroundImage();
int iconw = (icon != null) ? icon.getBounds().width : 0;
int iconh = (icon != null) ? icon.getBounds().height : 0;
switch (orientation) {
case HORIZONTAL:
int x = valueToPels(getValue()) - iconw / 2;
if (x getBounds().width - iconw) {
x = getBounds().width - iconw;
}
thumb.setBounds(x, 0, iconw, iconh);
break;
case VERTICAL:
...... }
}
不难理解,moveThumb的任务就是根据业务值value来将滑块移动到正确的位置。
以上代码声明滑块的位置是“x”,通过转换函数获得,但是还要减去滑块的一半,因为具体坐标应该落到滑动块的中间,仔细想想不难得出。if-else是对x进行验证,最后通过setBound来定位thumb。
对于redraw,他的作用是触发paintControl方法进行重绘,因为重绘出了边框还要根据value绘制填充格子,而value已经在redraw方法调用前被赋了值,所以这时候应该进行重绘。
现在你可以托拽thumb了,美中不足的是滑动块不能随时跟随鼠标的轨迹移动,这个稍后会实现。
三、填充格子
现在的组件外观只是绘制了边框,现在进行格子填充。在讲述边框绘制的时候提到了一行代码“int fillLength = valueToPels(getValue());”现在不难理解吧,就是将当前业务值value转换成实际的长度。在绘制边框之后,加入如下代码:
if (getEnabled()) {
gc.setBackground(FILL_COLOR);
for (int i = 2; i fillLength) {
gc.setBackground(BLANK_COLOR);
}
gc.fillRectangle(i, 4, 3, h - 8);
}
} else {
gc.setBackground(DISABLE_COLOR);
gc.fillRectangle(1, 4, w - 1, h - 8);
}
首先判断是否是enable,然后设置填充颜色FILL_COLOR,然后在for循环中执行正方形格子的填充,递增量“i”从2开始是空开2像素间隔,同理i也不能超过w-2(对称性),i+=4是相邻两个格子左边坐标间距4像素,然后“gc.fillRectangle(i, 4, 3, h - 8);”这一行进行填充绘制正方形。留意,x坐标是i,y坐标是从4开始画的,出于对称高度也要“h-8”,长度之所以是“3”是保持相邻两个格子之间保持1像素的间隔(想想“i+=4”就不难得出答案)。此外还要对fillLength进行判断以便决定颜色采用绿色还是白色以示区分。对于绘图操作来说,千万不要埋怨考虑细节过多,事实上,GUI编程过程中“坐标系”这个概念是需要经常被考虑的,试想如果上述代码for循环中把“i+=4”,写成“i+=5”,只是一个像素之差绘制效果差之千里,如果想知道笔者是如何得到这些坐标数据的,实话说,是靠多次调试得出的结果。
现在你运行程序,应该能根据value进行格子填充了。到此为止,绝大多数的功能已经实现,但是人性化的界面设计应该在托拽时出现一个虚拟的滑动块用来标识将要移动到的位置。好,继续实现这一功能。
定义一个int变量纪录thumb的临时位置,private int tempLocation;然后在paintControl添加如下红色代码
case HORIZONTAL:
gc.setForeground(BORDER_COLOR);
gc.drawRectangle(0, 2, w - 1, h - 5);
if (getEnabled()) {
gc.setBackground(FILL_COLOR);
for (int i = 2; i fillLength) {
gc.setBackground(BLANK_COLOR);
}
gc.fillRectangle(i, 4, 3, h - 8);
}
if (controlPoint != null) {
gc.drawImage(TEMP_H, tempLocation, 0);
}
} else {
gc.setBackground(DISABLE_COLOR);
gc.fillRectangle(1, 4, w - 1, h - 8);
}
break;
很直观,对于水平布局就是在(tempLocation,0)画出“TEMP_H”图标。外面的if很重要,还记得mouseDown方法中的语句“controlPoint = new Point(e.x, e.y);”和mouseUp方法中的语句“controlPoint = null;”吗,当鼠标按下托拽开始时,为controlPoint赋值,当鼠标完成托拽松开时,将controlPoint置null,在这个托拽过程中controlPoint一直保持非null状态,所以paintControl方法才将图标画出。如果现在就着急运行程序则会发现,鼠标托拽时候虚拟图标总停留在最左边(如果垂直布局停留在最上边,因为并没有为tempLocation赋值默认是0),不会跟随鼠标移动。如果要实现真正的托拽那么必须在鼠标移动时反复执行paintControl,下面花大量的笔墨详细地介绍mouseMove方法的实现,并简单介绍绘图操作的一些原理和流程。
开门见山,直接将mouseMove函数的全部代码列出。
public void mouseMove(MouseEvent e) {
if (controlPoint == null) {
return;
}
int maxLength;
int maxLocator;
switch (orientation) {
case HORIZONTAL:
maxLength = valueToPels(getMaxValue());
maxLocator = maxLength - TEMP_H.getBounds().width;
int movedX = e.x - controlPoint.x;
redraw(tempLocation, 0, TEMP_H.getBounds().width,
TEMP_H.getBounds().height, false);
tempLocation = valueToPels(getValue()) + movedX
- TEMP_H.getBounds().width / 2;
if (tempLocation maxLocator) {
tempLocation = maxLocator;
}
break;
case VERTICAL:
......
break;
}
}
最前面的if语句表明,只有鼠标按下时移动鼠标才算托拽,道理前面已经阐明了。maxLength代表最大值转换得到的像素,maxLocator是虚拟图标左端(上端)最大坐标,movedX代表托拽的位移量。最下面的if-else if目的很明了。整个mouseMove函数中
redraw(tempLocation, 0, TEMP_H.getBounds().width,
TEMP_H.getBounds().height, false);
tempLocation = valueToPels(getValue()) + movedX
- TEMP_H.getBounds().width / 2;
是整个托拽操作最难懂也是技术含量最高的两条语句。简单起见暂时用下面的语句代替
tempLocation = valueToPels(getValue()) + movedX
- TEMP_H.getBounds().width / 2;
if (tempLocation maxLocator) {
tempLocation = maxLocator;
}
redraw();
其中“tempLocation”的赋值语句不变,变化的是redraw函数的调用位置和参数。这样的变化使得意思就不难理解了,首先确定tempLocation的值,等号右边的计算结果也不难理解,然后调用redraw方法重画组件。如果这时候你运行程序,托拽时确实虚拟光标会跟随鼠标移动,但是也会发现组件闪烁得很厉害!具体程度取决于用户计算机的性能,关于“组件重绘时闪烁”的问题是绘图操作的一个常见问题,不仅仅是Java,任何支持绘图的计算机语言都可以暴露这样的问题,当年在大学用MFC、VB的编写过画图板的人应该熟悉这类问题。
在具体讨论之前先简单讲述鼠标监听器中的mouseMove操作
一旦为GUI组件添加鼠标移动监听器,当鼠标光标在组件上移动时便调用监听器接口的mouseMove(MouseEvent e)方法,mouseMove调用的频率与鼠标移动的快慢有关,移动越快mouseMove被调用的次数就越少,反之就越多。假设鼠标从组件上的A点移动到B点,如果鼠标移动得足够快,那么就可以理解为从A直接到B而不经过中间的任何一个点,那么mouseMove函数仅仅调用一次。反之鼠标慢慢从A移动到B,那么中间可能会经过C、D、E、F......一系列的点,而mouseMove也会被执行多次。总之当鼠标在组件上移动时,mouseMove会频繁被调用,之所以闪烁问题出在redraw方法,如果redraw方法不加任何参数那么将对组件全部重绘,对于鼠标托拽这种操作鼠标每移动一次就要对组件全部重绘,性能的代价可想而知,不闪才怪呢。解决的办法就是只重绘变化的部分。
如何做到这一点,要先了解必要的绘图机制,在SWT中(Swing绘图原理与其类似)redraw其实会包含2个含义,擦除、绘制,所以得名于redraw,意思就是重绘。首先是根据传入redraw方法的参数确认需要擦除的范围,如果没有则擦除全部,然后底层会创建一个绘制请求交给操作系统去处理,但是这个请求不会在redraw方法调用完毕后立即被处理,即重绘操作不会立即执行,它是被送进底层的事件队列中。处理时的绘制操作是由paintControl完成,所以redraw的调用会导致paintControl的执行。
再将那两行代码列出来。
redraw(tempLocation, 0, TEMP_H.getBounds().width,
TEMP_H.getBounds().height, false);
tempLocation = valueToPels(getValue()) + movedX
- TEMP_H.getBounds().width / 2;
如果光标从A移到B处,redraw方法所做的事情是通知重绘光标在A点时虚拟图标范围内的图像,然后立即创建一个绘制请求送到系统事件队列,然后更新tempLocation的值。现在再将绘制的那部分代码列出
if (controlPoint != null) {
gc.drawImage(TEMP_H, tempLocation, 0);
}
将这两部分代码列出来做个比较,还有非常重要的一点需要指出,托拽时虚拟光标能呈连续性移动,这一功能之所以能得以实现非常重要的一点是:从底层请求送入事件队列到请求被执行存在时间差,利用这个时间差(时间非常短)可以执行一些“更新操作”,比如上面的更新tempLocation。简单的理解是redraw方法调用会以异步方式执行擦除、绘制。当执行绘制操作“gc.drawImage(TEMP_H, tempLocation, 0);”时,tempLocation已经是更新后的值了,而redraw表明擦除旧区域的图像,因为当paintControl执行时这部分图像区域根据计算结果已经不再是虚拟滑块了。下面做一个试验。
添加下面红色代码
redraw(tempLocation, 0, TEMP_H.getBounds().width,
TEMP_H.getBounds().height, false);
try {
Thread.sleep(1000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
tempLocation = valueToPels(getValue()) + movedX
- TEMP_H.getBounds().width / 2;
这样在绘制执行时,tempLocation还没有得到更新,效果运行下知晓。
现在,你可以运行完整的程序了,看一下托拽时的效果。
完整的程序这里下载
posted on 2007-10-23 13:35 sun_java_studio@yahoo.com.cn(电玩) 阅读(3387) 评论(11) 编辑 收藏 所属分类: NetBeans 、SWT
评论
# re: SWT自定义组件之Slider 2007-10-23 13:42 BeanSoft
我来顶一下! 回复 更多评论
# re: SWT自定义组件之Slider 2007-10-30 11:22 Matthew Chen
redraw(tempLocation, 0, TEMP_H.getBounds().width,TEMP_H.getBounds().height, false);
是只重绘temp thumb原来的区域,因为slider本身添加自己为PaintListener,故重画的时候paintControl被调用,那么paintControl方法作用的是整个图形区域还是temp thumb原来的区域?如果是后者,那新的temp thumb所在的区域由谁在何时绘制? 如果是前者,重绘的不还是整体而不是thumb的区域吗? 回复 更多评论
# re: SWT自定义组件之Slider 2007-10-30 12:47 Matthew Chen
进一步观察发现,lz的代码运行时虚拟滑块的边缘往往缺失,结合上一条评论谈到的,我想移动滑块时重
绘的是移动操作的上一次tempLocation指出的旧滑块的区域,而我们想要看到的新滑块的区域,并没有全部被纳入重画的范围,修改的方法:
redraw(tempLocation, 0, TEMP_H.getBounds().width,TEMP_H.getBounds().height, false);
tempLocation = valueToPels(getValue()) + movedX - TEMP_H.getBounds().width / 2;
redraw(tempLocation, 0, TEMP_H.getBounds().width,TEMP_H.getBounds().height, false);
还没想到更好的方法。 回复 更多评论
# re: SWT自定义组件之Slider 2007-10-30 15:47 sun_java_studio@yahoo.com.cn(电玩)
@Matthew Chen
paintControl方法作用的是整个图形区域,也就是说画是整个区域的重画,但是擦除如果是整个区域擦除的话那屏幕就会闪了,你可以将redraw(tempLocation, 0, TEMP_H.getBounds().width,TEMP_H.getBounds().height, false); 这行代码理解为“擦除”,擦除原来区域的图像(在执行擦除前,原来区域的区域的图像是旧虚拟滑块,等到操作系统执行绘制时,那部分区域已不是虚拟滑块了),新的temp thumb绘制是在paintControl方法完成的。
画是整个区域的重画,擦是部分部分被擦。 回复 更多评论
# re: SWT自定义组件之Slider 2007-10-30 15:51 sun_java_studio@yahoo.com.cn(电玩)
@Matthew Chen
大可不必调用redraw(tempLocation, 0, TEMP_H.getBounds().width,TEMP_H.getBounds().height, false)两次。
开始我也是这么写的,后来改进只在tempLocation赋值后调用,我运行程序的时候没发现边缘缺失的现象。 回复 更多评论
# re: SWT自定义组件之Slider 2007-10-30 19:39 Matthew Chen
我截了图
http://www.blogjava.net/Files/djsl6071/桌面.rar
第一张是正常情况,第2,3张分别显示向上、下拖动时出现的缺失。
你可以用windows自带的图片查看器打开,并放大,仔细看虚拟滑块的边缘。 回复 更多评论
# re: SWT自定义组件之Slider 2007-10-30 22:25 sun_java_studio@yahoo.com.cn(电玩)
@Matthew Chen
虚拟滑块的图片本来就是圆角矩形,轮廓是用虚线勾出来的,你替换其他图片试试,不应该是程序的原因。 回复 更多评论
# re: SWT自定义组件之Slider 2007-11-20 11:14 黑巧克力
LZ的虚拟滑块确似有缺失的现象,当朝一个方向滑动时,那个方向最边缘的虚线显示不出来。当垂直方向移动一下鼠标便可以显示出来。 回复 更多评论
# re: SWT自定义组件之Slider 2007-11-20 16:59 电玩
@黑巧克力
My god.代码实现中 水平 和垂直 是互逆的,怎么会出现垂直正常,水平有缺失的情况呢。如果缺失的话不妨采用+1像素长度试试,只能靠微调了。 回复 更多评论
# re: SWT自定义组件之Slider 2007-11-20 18:07 黑巧克力
redraw(tempLocation-1, 0, TEMP_H.getBounds).width+2,TEMP_H.getBounds().height, false);
似乎在两边多加一个象素就可以画全了
回复 更多评论
# re: SWT自定义组件之Slider 2007-11-20 18:08 黑巧克力
@电玩
我说的垂直是垂直于slider的方向,大概是运动的时候redraw少画的象素。 回复 更多评论
</sliderlistener></sliderlistener>
发表评论
-
暂存的多少钱
2010-08-20 15:34 940与配置管理工具集成 企业级大型项目,往往需要可靠稳 ... -
获取Eclipse平台插件路径
2010-04-19 19:26 1568很多时候,我们需要获取Eclipse平台中相关插件的信息,如位 ... -
Object CLass
2010-03-10 11:01 901Eclipse 中扩展点可能会需要Object Class的东 ... -
Eclipse Preferences 扩展点
2010-02-25 11:29 1002Preferences in the Eclipse Work ... -
Nature
2010-02-24 13:50 1531Nature和Builder的使用介绍 http://liu ... -
Mylyn简介
2010-02-21 17:46 1837Mylyn简介——一个不错的eclipse工具 ... -
GMF
2010-01-06 20:15 1291转自 http://blog.csdn.net/wu_07/a ... -
GMF context 菜单工具栏
2010-01-06 20:14 1395转自http://blog.csdn.net/wu_07/ar ... -
RCP构建
2009-11-29 20:02 918转自http://blog.csdn.net/xiaoxi ... -
Eclipse Nebula 部件入门2
2009-11-24 23:51 2086Eclipse Nebula 部件入门 Ec ... -
Eclipse Nebula 部件入门1
2009-11-24 23:49 1839转自https://www6.software ... -
Eclipse Nebula 部件入门
2009-11-24 23:47 2136转自https://www6.software.ibm.com ... -
用SWT实现MSN风格的下拉框
2009-11-24 00:08 1048转自http://www.blogjava.net/j ... -
SWT控件的CompositeSize
2009-11-23 16:37 957在Window上所有的元件都是用畫出來的,所以redraw的觀 ... -
自定义控件资料
2009-11-23 12:42 844Eclipse官网对自定义控件 http://www.ecl ... -
开发Eclipse自定义控件
2009-11-23 12:40 903转自 http://www.ibm.com/developer ... -
GMF同一模型,多个Editor
2009-11-12 09:00 790转自http://yiliner.iteye.co ... -
Eclipse3.4版本以上打包
2009-11-11 18:22 1778我现在在编写一个JAVA工程,是提供给客户调用的API,但是我 ... -
RCP构建
2009-11-11 13:27 1148想分析插件对Eclipse本身插件的依赖,可以使用Deb ... -
Eclipse常见界面
2009-11-02 09:26 820Section的创建 Section input ...
相关推荐
3. **自定义组件**:SWT允许开发者通过组合现有组件或者创建新的绘图表面来创建自定义组件。这通常涉及到对`Canvas`组件的使用,开发者可以在其上绘制自己的图形,实现特定的功能。 4. **滚动条(ScrollBar)**:当...
小程序 自定义slider组件滑块小程序 自定义slider组件
总之,“slider多滑块拖动条”是一个强大且灵活的UI组件,它的自定义能力和双滑块设计为开发者提供了丰富的交互可能性。通过理解和利用这些特性,开发者可以创建出更符合用户需求、更具吸引力的交互界面。同时,提供...
本文将深入探讨如何创建一个具有音乐播放、slider滑块控制、时间显示和音频图片播放中动画等功能的自定义组件。首先,我们需要了解微信小程序的基础架构,包括其组件系统、API调用以及页面生命周期。 1. **微信小...
总之,本压缩包"slider.zip"提供了一个自定义微信小程序`slider`样式的实例,通过`view`组件和CSS样式实现了一种灵活的定制方法。这种技巧对于那些希望在小程序中创建独特界面设计的开发者来说非常有帮助。通过深入...
文件名"sliderForColorChoose"暗示了这是一个实现颜色选取功能的滑块组件。在代码中,这个文件可能包含自定义滑块视图的类定义,以及相关的手势处理和颜色映射逻辑。通过深入分析和调试这个文件,开发者可以更好地...
综上所述,微信小程序中的`slider`组件是实现双向滑动功能的重要工具,开发者可以通过设置其属性、绑定事件以及自定义样式,灵活地应用于各种场景,提升用户体验。在实际项目开发中,务必根据需求和用户习惯来优化`...
在iOS开发中,创建自定义UI组件是一种常见的需求,能够为应用带来独特的用户体验。本教程主要探讨如何在iOS项目中实现一个自定义的双向Slider,用于实现文字区间的选择并显示。这种滑动控件通常被用作数值选择器或者...
本文源码
在`vue-easy-slider-master`这个项目中,你可以找到一个预封装好的slider组件,便于在自己的Vue项目中快速集成和自定义。通过学习和理解这个组件的实现,你不仅可以提升Vue.js技能,还能为自己的项目带来更丰富的...
7. **气泡组件**:这类组件通常用于提示信息或指导,可能包含动画效果、可自定义的图标和内容,以及灵活的定位选项。 `HandyControl-master`这个文件名暗示这是一个版本控制系统中的主分支,通常包含项目的最新开发...
总的来说,"double-sided-slider-master.zip" 提供的组件是微信小程序开发中一种实用的交互工具,它涉及到小程序的自定义组件开发、事件处理、数据绑定、样式定制以及用户体验优化等多个核心知识点。通过深入理解和...
上面两篇文章介绍了一下自定义的progress 和 slider 组件,这篇文章介绍一下slider组件应用的实例 代码可在文末下载 这里的音频播放用的是后台音频播放的API: wx.getBackgroundAudioManager() 在js文件中初始化这...
在Qt编程环境中,QSlider是常用的用户界面组件,用于让用户通过拖动滑块来选择一个数值范围内的值。然而,标准的QSlider组件并不直接显示滑块正上方的当前值,这在某些需要实时反馈当前选择值的应用场景下可能会不够...
vue-slider组件的优点在于它的配置简单,这意味着开发者可以快速地定制和集成到他们的项目中,而不需要编写大量的自定义代码。它还支持自适应和全屏模式,这使得组件能够适应不同设备的屏幕尺寸,包括移动设备和桌面...
React Native组件,用于从一系列值中选择一个值。 入门 yarn add @react-native-community/slider或npm install @react-native-community/slider --save 如果使用iOS,请安装cocoapods: npx pod-install 在...
本文将深入探讨如何自定义`ProgressBar`(进度条)和`Slider`(滑块)控件,以适应不同设计需求。 首先,`ProgressBar`控件通常用于显示任务的进度或百分比完成情况。在WPF中,我们可以利用模板来改变其默认外观。...
通过以上自定义方式,我们可以将普通的`Slider`转变为具有高度视觉吸引力的组件,从而提升整个应用的美感和用户满意度。记住,自定义UI元素不仅要美观,更重要的是要易于理解和操作,以符合用户习惯和无障碍设计原则...
"Swift 自定义双向 Slider.zip" 是一个开源项目,它提供了一个自定义的范围滑块(NHRangeSlider),适用于iOS应用开发。这个滑块允许用户通过两个独立的滑块来选择一个数值范围,而不是传统的单个滑点。以下是对这个...
该源码案例是一款开源自定义Slider/Stepper组件,源码SnappingSlider,SnappingSlider可以取代你的slider或者step控制,用户通过左滑或右滑来调整数值,调整完成之后滑块滑回中间。 测试环境:Xcode 6.2,iOS 8.0...