WPF自定义控件 —— 装饰器

摘自:http://www.cnblogs.com/Curry/archive/2009/09/16/1567757.html

顾名思义就是装饰用的,也就是说不改变原有的控件结构,但可以为控件添加一些新的功能,或是为控件的显示外观增加些东西。如MSDN中的例子:

untitled

本来TextBox四角没有圆点,但是通过装饰器可以为它加上。所以可以看成在TextBox上加了层。

image

这样就“无痛”的给控件进行了装饰。当然应用不单单这样加几个点而已,修饰嘛比如拖动控件的修饰

image

 

image

而之前比较著名的层拖拽是Bea StollinitzHow can I drag and drop items between data bound ItemsControls?

 

 

一.AdornerLayer

我们说层,是覆盖在控件上的一层东西,那么控件上能不能覆盖多个层呢?

image

答案当然是可以的,而这些层自然的要放在一个容器中,这个容器就叫做AdornerLayer

然后问题又来了这个层是如何产生的?是我们人为放的,还是自动产生的(虽然自动实际上也是需要有人写的)?

我们知道AdornerLayer有个方法

public static AdornerLayer GetAdornerLayer(Visual visual);

可以得到某个Visual的所在的层,我们打开Reflector进行查看

 

public static AdornerLayer GetAdornerLayer(Visual visual)
{if (visual == null){throw new ArgumentNullException("visual");}for (Visual visual2 = VisualTreeHelper.GetParent(visual) as Visual; visual2 != null; visual2 = VisualTreeHelper.GetParent(visual2) as Visual){if (visual2 is AdornerDecorator){return ((AdornerDecorator)visual2).AdornerLayer;}if (visual2 is ScrollContentPresenter){return ((ScrollContentPresenter)visual2).AdornerLayer;}}return null;
}

很容易我们就可以看出它实际是通过可视树进行查找,然后判断元素是否为AdornerDecoratorScrollContentPresenter,如果是的话则取他们的AdornerLayer属性,也就是说AdornerLayer是由AdornerDecoratorScrollContentPresenter产生的,打开本地MSDN ,键入ScrollContentPresenter

image

 

由红框中的文字可以得知ScrollContentPresenter属于ScrollViewer,也就是说有ScrollViewer的地方就会有AdornerLayer,打开ScrollViewer的链接我们又可以了解到ScrollViewer通常需要包装Panel控件

image

那么哪些控件默认样式是用到ScrollViewer的呢,据我所知继承于ItemsControl的控件,还有Window等常用控件等,当然这里就不一一列举了。

如果实在没有ScrollViewer的地方,或者需要直接在控件上加层,我们也可以手动在控件外面包个AdornerDecorator来产生AdornerLayer。

<AdornerDecorator><TextBox Text="可以得到AdornerLayer"/>
</AdornerDecorator>

 

 

那么AdornerLayer到底是种什么概念,为什么总会在控件之上呢?

再用Reflector打开ScrollContentPresenterAdornerDecoratorGetVisualChild(int index)中应该会注意到下面的代码(下面代码在ScrollContentPresenter中获得)

private readonly AdornerLayer _adornerLayer = new AdornerLayer();protected override Visual GetVisualChild(int index)
{if (base.TemplateChild == null){throw new ArgumentOutOfRangeException("index", index, SR.Get("Visual_ArgumentOutOfRange"));}switch (index){case 0:return base.TemplateChild;case 1:return this._adornerLayer;}throw new ArgumentOutOfRangeException("index", index, SR.Get("Visual_ArgumentOutOfRange"));
}

 

这里就很明白了,index 0通常是我们需要装饰的控件,1就是AdornerLayer。我们知道系统首先会画0层的东西,再画1层 ,导致1永远都在0上。所以其实AdornerLayer也存在于可视树,可以通过VisualTreeHelper来找到。而且你不管调整控件的z-index都是无用的,人家写死了嘛。

 

二.Adorner

      有了容器,自然的要往里面添加东西,要不然不是空空如也么,有了也等于没有。而AdornerLayer规定能够加入它这个容器的只能是Adorner的派生类。在此淫威下所以我们也不得不臣服,把类继承于Adorner这个抽象类。

public class SimpleTextBlockAdorner : Adorner
{private TextBlock _textBlock;public SimpleTextBlockAdorner(UIElement adornedElement): base(adornedElement) {_textBlock = new TextBlock();_textBlock.Foreground = Brushes.Green;_textBlock.Text = "AdornerText";}protected override Visual GetVisualChild(int index){return _textBlock;}protected override int VisualChildrenCount{get{return 1;}}protected override Size ArrangeOverride(Size finalSize){//为控件指定位置和大小_textBlock.Arrange(new Rect(new Point(-10, 20), _textBlock.DesiredSize));return base.ArrangeOverride(finalSize);}
}

CS中代码如下:

public Window1()
{InitializeComponent();TextBlock textBlock = new TextBlock();textBlock.FlowDirection = FlowDirection.RightToLeft;textBlock.Text = "FlowDirection.RightToLeft";//放一个层容器AdornerDecorator adornerDecorator = new AdornerDecorator();adornerDecorator.Child = textBlock;this.Content = adornerDecorator;//得到层容器var adornerLayer = AdornerLayer.GetAdornerLayer(textBlock);//在层容器中加层adornerLayer.Add(new SimpleTextBlockAdorner(textBlock));
}

 

当改变textBlock的FlowDirection属性时,会出现如下所示的结果。也就是说被装饰的元素的FlowDirection效果对层上的元素有影响。

image image

 

探其究竟便又要用到Reflector了

image

原来是做了绑定,不过我一直未探明白的是DispatcherPriority的顺序意义是什么,到底适用在哪些场景,还望高手多多指点。

 

其次我们关心的另外一件事是,这个层有多大?是覆盖整个被装饰的控件还是整个屏幕或是整个窗口。一般MeasureOverride可以指定控件的大小,顺序是Measure->Arrange->Render(本想贴我布局中的那张图,可回头看有些地方居然是错的,不知道当时怎么想的,囧RZ)。那么来看看MeasureOverride到底干了什么?

image

从红线框中我们很容易看出他是用了装饰控件的呈现尺寸来修饰的。当然这只是默认,你也可以自己重载MeasureOverride来指定大小。

 

可要是装饰控件(本文中TextBox)的大小改变了,装饰器(本文中的SimpleTextBlockAdorner)怎么侦测的到?

实际上当我们改变装饰控件的大小时候,多是改变控件的Height或Width, 以改变Height为例,他是产生于FrameworkElement中的,定义如下

public static readonly DependencyProperty HeightProperty = DependencyProperty.Register("Height", typeof(double), _typeofThis, new FrameworkPropertyMetadata((double) 1.0 / (double) 0.0, FrameworkPropertyMetadataOptions.AffectsMeasure, new PropertyChangedCallback(FrameworkElement.OnTransformDirty)), new ValidateValueCallback(FrameworkElement.IsWidthHeightValid));

就是说改变Height的时候会触发Measure方法,而Measure方法会沿可视树向上找到父容器(在本例中是AdornerDecorator),然后调用它的OnChildDesiredSizeChanged方法,而OnChildDesiredSizeChanged中调用的是父容器本身的Measure方法,Measure方法会重新改变子容器的大小,装饰控件(TextBox)和装饰层(AdornerLayer)本来就同属于AdornerDecorator,所以在AdornerDecorator的Measure方法中会调用装饰控件和装饰层的Measure方法,装饰层又会用同样的方法刷新它的子类也就是我们的SimpleTextBlockAdorner,子调用父,父又调用子,子接着调用父?不是死循环了么?所以这里WPF用了变量MeasureInProgress和MeasureDirty来控制,如果已经在Measure中,则不需要循环调用。

 

这样下来你是不是感觉布局系统是很耗费资源的呢?^ 0 ^

image

 

 

另外对于Adorner中的GetDesiredTransform方法,其实看过AdornerLayer中的布局方法ArrangeOverride就可窥其详了

protected override Size ArrangeOverride(Size finalSize)
{DictionaryEntry[] array = new DictionaryEntry[this._zOrderMap.Count];this._zOrderMap.CopyTo(array, 0);for (int i = 0; i < array.Length; i++){ArrayList list = (ArrayList)array[i].Value;int num2 = 0;while (num2 < list.Count){AdornerInfo info = (AdornerInfo)list[num2++];if (!info.Adorner.IsArrangeValid){Point location = new Point();info.Adorner.Arrange(new Rect(location, info.Adorner.DesiredSize));
                GeneralTransform desiredTransform = info.Adorner.GetDesiredTransform(info.Transform);GeneralTransform proposedTransform = this.GetProposedTransform(info.Adorner, desiredTransform);int index = this._children.IndexOf(info.Adorner);if (index >= 0){Transform transform3 = (proposedTransform != null) ? proposedTransform.AffineTransform : null;((Adorner)this._children[index]).AdornerTransform = transform3;}}if (info.Adorner.IsClipEnabled){info.Adorner.AdornerClip = info.Clip;}else if (info.Adorner.AdornerClip != null){info.Adorner.AdornerClip = null;}}}return finalSize;
}

 

GeneralTransform desiredTransform = info.Adorner.GetDesiredTransform(info.Transform);

((Adorner)this._children[index]).AdornerTransform = transform3;

从中我们可以看出,分配的时候就是把从GetDesiredTransform得到的值又返回给Adorner的AdornerTransform属性,而AdornerTransform属性其实

image

RenderTransform属性我们总熟悉了吧,不熟悉?那看这个吧http://msdn.microsoft.com/zh-cn/library/system.windows.uielement.rendertransform.aspx

用RenderTransform可以比较肯定的说,速度要比普通布局快,因为它是在布局之后弄的,并不牵涉到反复的可视树传递引发,所以动画尽量以改变此值为主。

 

我另外标示的GeneralTransform proposedTransform = this.GetProposedTransform(info.Adorner, desiredTransform); 也一定好奇吧,做什么呢?其实就是开始说的FlowDirection问题,反转上面的控件用的。

private GeneralTransform GetProposedTransform(Adorner adorner, GeneralTransform sourceTransform)
{if (adorner.FlowDirection == base.FlowDirection){return sourceTransform;}GeneralTransformGroup group = new GeneralTransformGroup();Matrix matrix = new Matrix(-1.0, 0.0, 0.0, 1.0, adorner.RenderSize.Width, 0.0);MatrixTransform transform = new MatrixTransform(matrix);group.Children.Add(transform);if ((sourceTransform != null) && (sourceTransform != Transform.Identity)){group.Children.Add(sourceTransform);}return group;
}

 

三.默认控件的应用

 

 image      

        GridSplitter   Grid上的拖拉控件,我想大家应该不用吃惊吧,它是写了个PreviewAdorner来移动。在网上看到了这个链接http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/dfff9b89-81a8-4bfc-852d-d08ccdffe6bb 提问者改变了Window的模板,并在模板中放了Grid和GridSplitter,为什么会报错?现在我们知道了,Window默认的模板中有ScrollViewer,可以产生AdornerLayer,而改变的模板中没有AdornerLayer的容器,而且Window已经是窗口的最高层控件,沿可视树向上找也不会有其他的控件,所以GridSplitter不可能获取到AdornerLayer,因此就抛了NullReferenceException。解决办法便是在GridSplitter  外面套一层有AdornerLayer的东西,或ScrollViewer或AdornerDecorator,在链接中回答者给出的是AdornerDecorator。

 

image

       Validation 验证时候用的模板,其实你看到的这些感叹号,外框都是在层上的,他和GridSplitter 不同的是他可以在外面定义个模板,可以让用户自己指定要呈现的东西,为此他它写了个TemplatedAdorner,为什么找不到Validation 的默认模板,因为它用代码写死了。当然如果你发现惊叹号,外框不在该有地方,也容易做了——肯定层的位置有问题嘛。

private static ControlTemplate CreateDefaultErrorTemplate()
{ControlTemplate template = new ControlTemplate(typeof(Control));FrameworkElementFactory factory = new FrameworkElementFactory(typeof(Border), "Border");factory.SetValue(Border.BorderBrushProperty, Brushes.Red);factory.SetValue(Border.BorderThicknessProperty, new Thickness(1.0));FrameworkElementFactory child = new FrameworkElementFactory(typeof(AdornedElementPlaceholder), "Placeholder");factory.AppendChild(child);template.VisualTree = factory;template.Seal();return template;
}

至于AdornedElementPlaceholder这个占位符,它的大小是验证控件(TextBox)的大小,可他却是在模板中定义的,那么他如何来知道具体的验证控件是什么呢,这里它经过TemplatedAdorner中的AdornedElement来达到效果。可以说的上奇巧淫技,它使得AdornedElementPlaceholder知道具体的TemplatedAdorner,可TemplatedAdorner并不知晓具体的AdornedElementPlaceholder,但在AdornedElementPlaceholder同时观察到

image

所以他的有效作用只有一个。好的控件是能够更好的解耦,可解耦的前提是原来的控件有一定的预留,TemplatedAdorner便是预留了ReferenceElement来达到效果。

 

四.自定义个遮罩控件

 

image

说了这么多是不是技痒了,那先来做个简单的吧,有时当我们读取数据希望未显示完的列表不需要让客户操作,所以需要要这个遮罩层来档下,一方面为了不让客户操作具体控件,令一方面可以让客户看到事情进度或操作信息。那怎么做比较舒服呢?自然的,我希望遮罩只针对某个控件而已,因为其他地方并不影响,依然可以操作。在Demo上就简化了。显示信息的模板可以自定义修改,有没有感觉和刚才说的TemplatedAdorner模板有类似。所以发挥拿来主义的精神。

代码就不在这里列举了,不过要注意的是要把上面的模板控件加入可视树,要不然会穿越,就达不到阻挡的作用,同理如若需要穿越操作的话,可以不加入可视树。

对于代码有些人喜欢完全的附加属性如Validation 那样的赋值,我个人比较喜欢用类赋值,如果不喜欢可以动动手自己改掉,调用代码如下:

<ListView ItemsSource="{Binding Employees}"><ControlLibrary:MaskAttach.MaskAttach><ControlLibrary:MaskAttach x:Name="fff" DataContext="{Binding Progress}" Open="{Binding IsLoading}"><ControlLibrary:MaskAttach.Template><DataTemplate><Grid><Grid.ColumnDefinitions><ColumnDefinition /><ColumnDefinition Width="40"/></Grid.ColumnDefinitions><Rectangle Grid.ColumnSpan="2" Fill="Black" Opacity="0.7"/><TextBlock Grid.Column="1" Margin="5" Foreground="White"  HorizontalAlignment="Right"  VerticalAlignment="Center"><AccessText Text="{Binding}"/><AccessText>%</AccessText></TextBlock><ProgressBar Margin="10" Value="{Binding Mode=OneWay}"  Height="20"/></Grid></DataTemplate></ControlLibrary:MaskAttach.Template></ControlLibrary:MaskAttach></ControlLibrary:MaskAttach.MaskAttach>

在这里还要说明的是,AdornerLayer.GetAdornerLayer取得层的时候最后是放在d.Dispatcher.BeginInvoke中取,因为有时候需要等上面加载完,对于DispatcherPriority我一般选的是Render

而DependencyProperty的属性值改变侦测,如果用Binding的话则需要对象一定要从FrameworkElement派生的,但可以利用DependencyPropertyDescriptor侦测:

var dpdDataContext = DependencyPropertyDescriptor.FromProperty(MaskAttach.DataContextProperty, maskAttach.GetType());
dpdDataContext.AddValueChanged(maskAttach, delegate{d.SetValue(MaskAttach.DataContextProperty, maskAttach.DataContext);});

 

 

五.Decorator

 

   当看到Decorator让人更容易的想到Decorator模式,Decorator在我的印象中更接近一个包装器,把原有的方法放入包装类的一个同名方法中,在同名方法中再加些其他的功能罢了。

image

    如果说里面颜色块代表功能大小的化,很明显包装类的功能更强大。而且他增加功能的话又对原来的A类结构是无损的,用户通过接口来操作的话也无须知道实体类。

    就WPF而言,用户所要的是呈现效果,在外包一层改变了显示效果但是不影响原有控件的效果和功能。所以他可以操作控件的外观常用的Border控件,以及改变控件现实大小的ViewBox都是继承于Decorator类,还有就是我们之上提到的AdornerDecorator。都是对原来控件外观或控制的扩展。

image

Decorator本身只是个单容器控件,只能对一个控件进行装饰,Panel的话是多容器,可以对多控件进行装饰,控件大小的位置的改变也是装饰的一种表现形式。本想做个简单的拖拽控件,网上搜索了下发现已经有人做了。http://codeblitz.wordpress.com/2009/06/10/wpf-dragdrop-decorator-for-itemscontrol/

网页似乎被墙,我看的是快照,示例程序我也放上来了以免以后下不到。

dragdrop_decorator

对于默认拖拽的显示和做法,下面也记录下:

1.拖拽一般是两个控件之间的数据交互,就是拖拉的时候(MouseDown)把数据放到一个地方,拖拉完之后(MouseUp)再把这个数据放到另一个控件中,所以拖拽的两个控件本身要提供拖拽,继承于UIElement的控件可以直接把AllowDrop属性设置为True.

2.在鼠标点击也就是MouseDown,一般注册MouseDown事件或直接重载OnMouseDown方法,把选中的数据放到变量中,你选中的是控件?控件是数据的呈现,所以你应该能拿的到数据。除非那个控件真的没有数据,那也就不需要拉了,有数据的话,我们把数据放到一个变量_mouseDownData中。

3.在鼠标移动的过程中,我们看到鼠标的样式是会动的(鼠标下面会有小方块),而且我们传数据也需要个方法传对吧,所在鼠标移动的时候有MouseMove做下面这句

DragDrop.DoDragDrop((ItemsControl)sender, _mouseDownData, DragDropEffects.Move | DragDropEffects.Copy);

4.鼠标放开就就是MouseUp的话,把传递的数据的变量mouseDownData清空或赋值为Null.

5.拖拉有两方,假设要把A数据拖到B上,那么A调用了DragDrop.DoDragDrop方法去放,B怎么接收的呢?B控件要注册PreviewDrop事件,通过DragEventArgs e参数来获得e.Data,其中数据类型一般先e.Data.GetDataPresent(typeof(数据类型))来判断有没有,然后通过e.Data.GetData(typeof(数据类型))具体拿值,e.Effects可以用来判断操作:是否要把数据添加进B控件。

6.假定拖拉到一般要取消怎么办?控件注册PreviewQueryContinueDrag事件在QueryContinueDragEventArgs e中对e.Action进行赋值操作,可以DragAction.Cancel当然也可以DragAction.Drop或者DragAction.Continue了。

 

关于拖拽这里还有个文章:http://www.cnblogs.com/taowen/archive/2008/10/30/1323329.html

 

六.纯粹的个人感概

      

      在我学习WPF的第一个月,可以说自我感觉最良好的时候,当时认为什么都可以做了,WPF不过偶尔,凭借着以前的Winform开发思路,在OnRender中大放光彩,认为什么都能做,所以之前的控件都是自己重做的,看微软默认的不爽就重写,没有的就自己造,后来慢慢的,开始MVVM,开始大量的转变控件,虽然默认的控件大部分到最后也都是Draw出来的,但是使用默认控件拼出新控件却是团队沟通的桥梁,默认控件一般都能满足需求,自己定义的话可能效率有一定优势,开始的时候也方便,到最后做大做复杂也挺麻烦,最主要的是团队成员的样式套用就麻烦,整体效果就有影响。之前自己有个想法,就是控件加快开发进度的,所以不好用的就不用了,实际上是也没怎么认真去想怎么用。默认控件的模式和思路都是值得研究和学习的。当然如果你的控件需要一定的运行效率那就只能重做一份了。

      关于模式,面向对象开发,都是希望把责任分的更清晰,把功能切的更细,那把责任和功能切的更细的意义是什么呢,易于维护,可团队里的每个人的思维形态并不一样,你认为这样好,可人家却很难理解,难理解之后沟通是不是也难了,开发效率怎么上的去?所以利于沟通的设计才是好的设计。当团队思想慢慢的趋于一致,再发挥你的才干,你可能会得到更好的帮助和建议。有些事不必要急于一时。

      在这里感谢很多热心人的帮助,特别是周永恒,高手虽多可忙的不少,乐于解答的更少,帮你分析的少之又少。而周永恒却可以帮你具体讲解分析,在这里表示由衷的谢意。

转载于:https://www.cnblogs.com/jojinshallar/articles/3372323.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/421478.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Netty Associated -- ByteBuf

ByteBuf ByteBuf是Netty的Server与Client之间通信的数据传输载体.他提供了一个byte数组(byte[])的抽象视图 buffer创建 我们推荐通过一个Unpooled的帮助方法来创建新的buffer而不是通过调用独立的构造器来创建 随机访问索引 就像普通的原声字节数组一样, ByteBuf使用零基坐标(z…

php 根号2计算过程,根号2以及π的计算--关于无理数的畅想

[导读]这两篇文章里&#xff0c;我发现不需要那些老师教的范式也能很好地理解那些现代概念&#xff0c;并且理解的更深刻。这两篇文章里&#xff0c;我发现不需要那些老师教的范式也能很好地理解那些现代概念&#xff0c;并且理解的更深刻。我一般假设自己从来没有学过微积分&a…

任务分配与时间估计

各成员的任务分配与时间估计如下&#xff1a; 项目所需总时间为&#xff1a; 转载于:https://www.cnblogs.com/buaa-smile/p/3383374.html

linux所有用户无法登录用户名和密码错误,为什么linux登录不了啊,用户名和密码都没问题,就是出现这个,有高手指点一下。...

为什么linux登录不了啊&#xff0c;用户名和密码都没问题&#xff0c;就是出现这个&#xff0c;有高手指点一下。关注:121 答案:6 mip版解决时间 2021-02-04 23:27提问者删除记忆里的你2021-02-04 04:27最佳答案二级知识专家那年夏天2021-02-04 05:19用户名肯定是root&#x…

linux spring mvc tomcat配置,Spring MVC配置详解

一、Spring MVC处理流程1.Spring MVC将所有请求都交由DispatchServlet进行处理。2.DispatchServlet获取HandlerMapping(处理映射器)&#xff0c;然后找到对应的HandlerBean处理Controller请求&#xff0c;并返回一个ModelAndView对象。3.DispatchServlet查询一个或多个ViewReso…

如何在linux里面运行ncl,NCL基础讲解(二)——NCL安装与运行

NCL基础讲解(二)——NCL安装与运行兰溪之水2015-11-04上一期已经给大家简单介绍了NCL的基本情况&#xff0c;既然NCL在科学数据分析和可视化方面有那么多优点&#xff0c;那还等什么&#xff0c;让我们一起“用”起来吧&#xff01;这一期给大家介绍NCL的安装与运行。关于NCL的…

C#值类型和引用类型的不同

1 C#中有两种类型&#xff1a; 值类型和引用类型&#xff1a; 值类型的变量直接包含他们的数据&#xff0c;引用类型存储对他们的数据的引用&#xff0c;后者称为对象&#xff1a; 简单说:值类型直接存储其值,引用类型存储对值得引用.引用类型分为引用和引用的对象。2…

linux malloc命令,减少 curl 中内存分配操作(malloc)

今天我在 libcurl 内部又做了一个小改动&#xff0c;使其做更少的 malloc。这一次&#xff0c;泛型链表函数被转换成更少的 malloc (这才是链表函数应有的方式&#xff0c;真的)。研究 malloc几周前我开始研究内存分配。这很容易&#xff0c;因为多年前我们 curl 中就已经有内存…

flume linux 命令,flume启动命令 · Mr.Deng’s Blog

8种机械键盘轴体对比本人程序员&#xff0c;要买一个写代码的键盘&#xff0c;请问红轴和茶轴怎么选&#xff1f;突然发现拖了很久的flume启动命令了&#xff0c;今天就简单讲讲flume的启动命令吧。如何启动flumeflume目录以上是安装flume的目录&#xff0c;flume的启动主要是在…

stm32 IAP + APP ==双剑合一

&#xff08;扩展-IAP主要用于产品出厂后应用程序的更新作用&#xff0c;上一篇博文详细的对IAP 升级程序做了详细的分析http://blog.csdn.net/yx_l128125/article/details/12992773&#xff0c;考虑到出厂时要先烧写IAP 再烧写APP应用程序要烧写2次增加工人劳动力基础上写了“…

花旗linux 内核 如何调试,揭秘首个运行在Linux平台的核心银行系统

上个星期社区进行了一场直播&#xff0c;关于“新一代核心银行系统解决方案CBOD”——首个运行在Linux平台的核心银行系统。国外上线Linux核心系统的银行已经不少&#xff0c;例如花旗银行在主机系统上装载了Linux系统&#xff0c;处理所有信用卡交易和认证交易&#xff0c;但国…

360发布穿戴设备“儿童卫士”手环

10月29日下午消息&#xff0c;360今日召开发布会发布“儿童卫士”手环&#xff0c;可随时定位孩子位置&#xff0c;并具备安全区域预警、通话连接等功能。这款手环售价199元&#xff0c;将于12月开始销售。 据360透露&#xff0c;儿童卫士具备三大功能&#xff1a; 一、随时定位…

layout_gravity 和 gravity

LinearLayout有两个非常相似的属性&#xff1a; android:gravity与android:layout_gravity。 他们的区别在于&#xff1a; android:gravity 属性是对该view中内容的限定&#xff0e;比如一个button 上面的text. 你可以设置该text 相对于view的靠左&#xff0c;靠右等位置&…

如何用Jquery做图片展示效果

一. 前言 到底用JQuery做出怎样的展示效果? 让我们先来看一下&#xff01;网页加载时&#xff0c;如图所示&#xff1a; 二.本人思路 这个效果初学者看起来好像有点复杂,其实不太难&#xff0c;关键是理清思路&#xff0c;从后端的数据库中找出我们要展示的图档&#xff0c;将…