在网上看到一篇介绍NSDate的博文。在它的“NSDate初始化“章节,说在使用 NSLog(@"当前时间 date = %@",[NSDate date]);时,显示出来的时间不是自己时区的时间,比我们小8个小时。因此判断该问题是时区问题。
好,我承认作者判断的很对,但是他给出的解决方法,却令人很意外。这个点对于理解[NSDate date]有很大的误导和迷惑性。
他的部分代码如下:
// 获取当前日期
NSDate *date = [NSDatedate];
// 打印结果: 当前时间date = 2013-08-16 09:00:04 +0000
NSLog(@"当前时间 date = %@",date);
// 获取从某个日期开始往前或者往后多久的日期,此处60代表60秒,如果需要获取之前的,将60改为-60即可
date = [[NSDate alloc]initWithTimeInterval:60 sinceDate:[NSDate date]];
//打印结果:当前时间 往后60s的时间date = 2013-08-16 09:01:04 +0000
NSLog(@"当前时间 往后60s的时间date = %@",date);
PS:测试时时间是下午5点,但是得到的当前时间却是上午9点,相差了8小时,是时区的问题
NSTimeZone *zone =[NSTimeZone systemTimeZone];
NSInteger interval = [zonesecondsFromGMTForDate: date];
NSDate *localDate =[date dateByAddingTimeInterval: interval];
// 打印结果 正确当前时间 localDate = 2013-08-16 17:01:04 +0000
NSLog(@"正确当前时间 localDate = %@",localDate);
因为我们在正8区,而使用NSLog显示[NSDatedate]时,显示的时间是GTM时区的时间。
作者将[NSDate date]的结果加上8×60×60,得到的时间就是我们正8区看到的时间。
好吧,不得不说这个方法得到的结果是符合要求的,显示时间数字是一致的了。
下面说这个方法的不当之处。它给人一种误导,[NSDate date]得到的时间是应该加8小时,才能正确显示符合我们时区的时间。
何必呢?不就是要[NSDate date]符合本时区的时间数字显示出来吗?何必认为修改时间数值呢?
NSDate对象的显示结果不符合,却去修改对象本身,真真是舍本逐末。
刚好在stackoverflow.com上找到一帖,可以用来解释这个问题。呵呵。
http://stackoverflow.com/questions/4547379/nsdate-is-not-returning-my-local-time-zone-default-time-zone-of-device
My local and default time zone is GMT +5 but when I get date and time byNSDate it return me GMT date and time.
For example the code and output from my code while testing on device isas, [device time zone Islamabad GMT +5]
NSTimeZone *lo = [NSTimeZonelocalTimeZone];
NSLog(@" - current local timezone is %@",lo); // GMT +5
2010-12-28 20:56:11.785 Done[484:307] - current local timezone is Local Time Zone (Asia/Karachi (GMT+05:00)offset 18000)
NSTimeZone *df = [NSTimeZonedefaultTimeZone];
NSLog(@" - current default timezone is %@",df); // GMT +5
2010-12-28 20:56:11.790 Done[484:307] - current default timezone is Asia/Karachi (GMT+05:00) offset 18000
NSDate *cDate = [NSDatedate];
NSLog(@"current date byNSDate %@",cDate); //but NSDate show GMT
2010-12-28 20:56:11.794 Done[484:307] current date by NSDate2010-12-28 15:56:11 GMT
NSDateFormatter*dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.dateFormat =@"yyyy-MM-dd'T'HH:mm";
//// NSTimeZone *gmt =[NSTimeZone ]
NSTimeZone *gmt = [NSTimeZonetimeZoneWithAbbreviation:@"GMT+05:00"];
[dateFormattersetTimeZone:gmt];
NSString *timeStamp =[dateFormatter stringFromDate:[NSDate date]];
NSLog(@" date stringobject %@" ,timeStamp); // string From Date is GMT +5
2010-12-28 20:56:11.802 Done[484:307] date string object 2010-12-28T20:56
NSDate *datef =[dateFormatter dateFromString:@"2010-12-28T20:56"];
NSLog(@" date object%@" ,datef); // the date form abovestring gives again GMT
2010-12-28 20:56:11.809 Done[484:307] **date object 2010-12-28 15:56:00 GMT**
Why is NSDate not giving local current time? Please help...
这是一个巴基斯坦人的提问,他不明白为什么会这样?您明白了吗?你知道了怎么来正确地显示时间数字了吗?
最近一直有点忙,刚刚看到一个朋友的留言提到Fragment中加载ListView的问题,这里写了一个非常简单的测试,至于对Fragment的增、删、改实现动态布局构建灵活的UI,以后有时间在讨论:
MainActivity:
package com.home.testfragment; import java.util.ArrayList; import java.util.List; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.widget.ArrayAdapter; import android.widget.ListView; public class MainActivity extends FragmentActivity { @Override protected void onCreate(Bundle arg0) { super.onCreate(arg0); setContentView(R.layout.main); ListView listView = (ListView) findViewById(R.id.fragment__first_list); List<String> list = new ArrayList<String>(); list.add("张三"); list.add("李四"); list.add("王五"); list.add("赵六"); ArrayAdapter<String> adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, list); listView.setAdapter(adapter); } }
FirstFragment:
package com.home.testfragment; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; public class FirstFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_first, container, false); } }
main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <fragment android:name="com.home.testfragment.FirstFragment" android:layout_width="match_parent" android:layout_height="wrap_content" android:tag="@+id/main_fragment_first" /> </LinearLayout>
fragment_first:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="测试fragment的简单使用" android:textSize="20sp" /> <ListView android:id="@+id/fragment__first_list" android:layout_width="match_parent" android:layout_height="wrap_content" > </ListView> </LinearLayout>
附上界面效果:
Qt的Graphics-View框架和OpenGL结合详解
演示程序下载地址:这里
程序源代码下载地址:这里
这是一篇纯技术文,介绍了这一个月来我抽时间研究的成果。
Qt中有一个非常炫的例子:Boxes,它展示了Qt能够让其Graphics–View框架和Qt的OpenGL模块结合起来,渲染出非常出色的效果。其实我私自认为凭这个程序,已经有很多游戏开发者关注Qt了,因为游戏开发一个非常常见的模块就是UI,一般情况下游戏引擎提供的UI模块比较弱,基本上都是游戏引擎+第三方GUI库进行结合的。但是Qt以其Graphics–View框架能够非常轻松地将UI控件嵌入场景中,而且能够和OpenGL底层共存,更重要的是,凭借着Qt的qss,Qt可以定制许多GUI元素,这是非常具有吸引力的。所以说,如果大家对游戏开发感兴趣,那么不妨看一下Qt。
好了,下面介绍一下前几天我制作并发布的一个demo。这个demo是对Boxes这个例子进行模仿,结合学习《OpenGL超级宝典》,制作而成的,由于最近比较忙,所以总共花了将近一个月才完成,不得不说效率有点儿低。
首先从MainWindow.cpp这个文件说起吧,一开始是要初始化MainWindow类的,这个类是继承QMainWindow的,这里重点说说它的构造函数:
MainWindow::MainWindow( QWidget* pParent ): QMainWindow( pParent ) { QGLWidget* pWidget = new QGLWidget( QGLFormat( QGL::SampleBuffers ), this ); pWidget->makeCurrent( ); // scene的内容 GraphicsScene* pScene = new GraphicsScene( this ); OpenGLView* pView = new OpenGLView( this ); pView->setViewport( pWidget ); pView->setViewportUpdateMode( QGraphicsView::FullViewportUpdate ); pView->setScene( pScene ); // 选择不同的着色器的时候进行着色器连接 connect( pScene, SIGNAL( SwitchShader( const QString& ) ), pView, SLOT( SwitchShader( const QString& ) ) ); connect( pScene, SIGNAL( SetLightPos( const QVector3D& ) ), pView, SLOT( SetLightPos( const QVector3D& ) ) ); setCentralWidget( pView ); setWindowTitle( tr( "Light for shader" ) ); resize( 640, 360 ); }
首先在我们创建了一个QWidget,然后调用makeCurrent()成员函数,其实意思是让它的rendercontext设为当前的rendercontext。随后建立的是OpenGLView,这个OpenGLView是来自于QGraphicsView的,它的初始化和其祖先的并无二致,随后一句非常重要:setViewport(),它的作用是将QGLWidget设置为OpenGLView的viewport,这样的话背景的rendercontext不再是rastercontext而是OpenGLcontext了,否则场景的背景还是需要用CPU渲染的,效率低下。接着是两段建立连接的代码。最后设置的是窗口大小和标题什么的,一开始还是非常简单的。
接下来我们看看OpenGLView是怎么定义的:
class OpenGLView: public QGraphicsView, protected QOpenGLFunctions { Q_OBJECT public: OpenGLView( QWidget* pParent = 0 ); virtual ~OpenGLView( void ); void setScene( GraphicsScene* pScene ); public slots: bool SwitchShader( const QString& shaderFileName ); void SetLightPos( const QVector3D& lightPos = QVector3D( ) ); protected: void resizeEvent( QResizeEvent* pEvent ); void mousePressEvent( QMouseEvent* pEvent ); void mouseReleaseEvent( QMouseEvent* pEvent ); void mouseMoveEvent( QMouseEvent* pEvent ); void wheelEvent( QWheelEvent* pEvent ); void drawBackground( QPainter* pPainter, const QRectF& rect ); private: void InitGL( void ); void ResizeGL( int width, int height ); void PaintGL( void ); void DrawAxis( void ); bool SetupShaders( void ); Camera m_Camera; Format3DS m_3DS; // 着色器 QOpenGLShader* m_pVertexShader; QOpenGLShaderProgram* m_pShaderProgram; };
这里我们在它的成员中添加了一个摄像机,一个3ds模型实例,还有一个顶点着色器和着色器程序类。在上次的博客中讲到了在这种情况最好使用类指针而不是类成员作为数据成员,这里我索性把着色器程序类也做成了指针成员了。
由于OpenGLView类实现比较长,这里我着重说一下其中的几个函数。下面是drawBackground()函数的实现:
void OpenGLView::drawBackground( QPainter* pPainter, const QRectF& ) { pPainter->beginNativePainting( ); glPushAttrib( GL_ALL_ATTRIB_BITS ); InitGL( ); ResizeGL( pPainter->device( )->width( ), pPainter->device( )->height( ) ); PaintGL( ); glPopAttrib( ); pPainter->endNativePainting( ); }
为什么选择drawBackground()?因为我们想要得到一种效果,OpenGL在底层绘制,上面绘制控件,其实自从QPainter有了beginNativePainting()和endNativePainting()这两个函数,我们就可以进行纯OpenGL的绘制了。这里还要注意的是,因为绘制控件也是使用OpenGL的context,这样简单调用会让OpenGL的状态混乱,所以需要将各种状态通过glPushAttrib(GL_ALL_ATTRIB_BITS);保存起来,然后初始化我们的OpenGL状态,以及绘图,最后记得glPopAttrib();还原所有状态供2D绘制。这里就不像以往的GLWidget套路了,因为GLWidget里面的initializeGL()函数只调用一次,paintGL()函数调用多次,但是在这里,只要有刷新的消息(通过update()或repaint()触发),就必须调用InitGL()函数来进行OpenGL状态的设置,否则先前设置的所有状态都消失了。
接下来看看ResizeEvent()函数:
void OpenGLView::resizeEvent( QResizeEvent* pEvent ) { scene( )->setSceneRect( 0.0, 0.0, pEvent->size( ).width( ), pEvent->size( ).height( ) ); pEvent->accept( ); }
这里由于我们已经设置了scene为GraphicsScene类的实例指针,因此scene()是非空的,我们将场景限制为view的大小,这样可以避免一些刷新的问题。
而paintGL()函数也是相当的简单。
void OpenGLView::PaintGL( void ) { glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glPushMatrix( ); m_Camera.Apply( ); m_3DS.RenderGL( ); if ( g_ShowAxis ) DrawAxis( ); glPopMatrix( ); }
接着我向大家介绍一下GraphicsScene类:
class GraphicsScene: public QGraphicsScene { Q_OBJECT public: GraphicsScene( QObject* pParent = 0 ); void SetCamera( Camera* pCamera ); signals: void SwitchShader( const QString& shaderFileName ); void SetLightPos( const QVector3D& pos ); protected: void mousePressEvent( QGraphicsSceneMouseEvent* pEvent ); void mouseMoveEvent( QGraphicsSceneMouseEvent* pEvent ); void mouseReleaseEvent( QGraphicsSceneMouseEvent* pEvent ); void wheelEvent( QGraphicsSceneWheelEvent* pEvent ); private slots: void Feedback( void ); private: // 鼠标事件需要 QPointF m_LastPos; Camera* m_pCamera; };
这里的GraphicsScene类保存的是来自view的Camera和一些信号以及事件的处理。在实现上也说一下它的构造函数吧。
GraphicsScene::GraphicsScene( QObject* pParent ): QGraphicsScene( pParent ), m_pCamera( Q_NULLPTR ) { ClickableTextItem* pTextItem = new ClickableTextItem( Q_NULLPTR ); pTextItem->setPos( 10.0, 10.0 ); pTextItem->setHtml( tr( "<font color=white>" "Made By Jiangcaiyang<br>" "Created in September<br>" "Click for feedback." "</font>" ) ); connect( pTextItem, SIGNAL( Clicked( ) ), this, SLOT( Feedback( ) ) ); addItem( pTextItem ); ShaderOptionDialog* pDialog = new ShaderOptionDialog; connect( pDialog, SIGNAL( SwitchShader( const QString& ) ), this, SIGNAL( SwitchShader( const QString& ) ) ); connect( pDialog, SIGNAL( SetLightPos( const QVector3D& ) ), this, SIGNAL( SetLightPos( const QVector3D& ) ) ); QGraphicsProxyWidget* pProxy = addWidget( pDialog, Qt::Window | Qt::WindowTitleHint ); pProxy->setPos( 100, 200 ); }
我们创建了一个ClickableTextItem类,它继承于QGraphicsTextItem,它被摆在左上角,显示三排可以点击的文字。随后添加了一个对话框,设置信号和槽完毕后就用代理放入场景中了。整个过程也是非常简单的。
我测试了下,整个程序在我的Ubuntu13.04下能够正常运行。只是由于显卡不同(Ubuntu下较难支持nvidia显卡,使用的是intel集显),specularOpt效果出不来。