手机当服务器建网站/近期时政热点新闻20条
在RankFragment首次显示时,要通过网络从后台获取数据。这点无可厚非。然后切换到其他Fragment再切换回来,这次就不想再去从后台获取数据,想用首次获取到的数据。保存首次获取到的数据很简单,在RankFragment类中定义实例变量videoList。切换到其他Fragment再切换回来,实例变量videoList的数值是不会自动丢失的。以上都没问题。讲下遇到的问题吧:第2次展现videoList需要创建类型为FixGridLayout的实例,在创建FixGridLayout要求获取到其ParentView的宽度,因为FixGridLayout的宽度定义为MATCH_PARENT。问题就出在这里,获取到ParentView的宽度是0。实际上,ParentView的宽度并非是0,只是还没有计算出来而已。
以下是日志中获取到的宽度都为0
11-17 18:07:58.654: V/RankFragment(7183): @onCreateView. create view for RankFragment.
11-17 18:07:58.654: V/RankFragment(7183): @onViewCreated. width >> 0
11-17 18:07:58.654: V/RankFragment(7183): @onActivityCreated. videoList is not null. invoke addRankVideos immediately.
11-17 18:07:58.654: V/RankFragment(7183): @onActivityCreated. width >> 0
11-17 18:07:58.654: V/RankFragment(7183): @onStart. width >> 0
其中第2,4,5行分别是在onViewCreated,onActivityCreated,onStart方法中获取。
在Activity的onWindowFocusChanged方法中获取到View的宽度和高度不为0,请见:
http://truesea.blog.51cto.com/4129819/1316760
但在Fragment中没有这个方法,怎么办
分别在百度和谷歌上查找资料,结果显示在谷歌上找到的东西更有用,请看链接:
http://stackoverflow.com/questions/15674770/when-should-i-get-width-view-in-fragment
http://stackoverflow.com/questions/18337772/android-getwidth-in-fragment-returns-0
参考以上的文章进行修改,主要体现在(1)在RankFragment中添加boolean类型的实例变量widthReady,表示宽度是否准备好,即是否已经有值(2)在onCreateView中调用addOnLayoutChangeListener,如下所示(3)在onActivityCreated方法中,通过while循环等待widthReady变为true。
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {Logger.v(TAG, "@onCreateView. create view for RankFragment.");View view = inflater.inflate(R.layout.fragment_rank, container, false);contentLayout = (LinearLayout) view.findViewById(R.id.contentLayout);initListener();return view;
}
private void initListener() {widthReady = false;contentLayout.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {@Overridepublic void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight,int oldBottom) {Logger.v(TAG, "oldLeft:"+oldLeft+", oldRight:"+oldRight+", oldTop:"+oldTop+", oldBottom:"+oldBottom);Logger.v(TAG, "left:"+left+", right:"+right+", top:"+top+", bottom:"+bottom);widthReady = true;}});
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);if(videoList == null) {Logger.v(TAG, "@onActivityCreated. get rank data from URL >> " + TvieURL.TODAY_RANK_URL);get(REQUEST_GET_RANK, TodayRankResult.class, this, TvieURL.TODAY_RANK_URL, null);} else {Logger.v(TAG, "@onActivityCreated. videoList is not null. invoke addRankVideos immediately.");Logger.v(TAG, "@onActivityCreated. width >> " + contentLayout.getMeasuredWidth());long start = System.currentTimeMillis();while(!widthReady) {try {Thread.sleep(10);} catch (InterruptedException e) { }}long end = System.currentTimeMillis();Logger.v(TAG, "@onActivityCreated. after " + (end - start) + " ms. width is ready.");Logger.v(TAG, "@onActivityCreated. width >> " + contentLayout.getMeasuredWidth());addRankVideos(videoList);}
}
结果并不理想,程序进入等待状态,一直在等待。
接下来新建一个线程,将while循环放到线程中
先看下打印出来的日志
11-17 18:58:50.114: V/RankFragment(12656): @onCreateView. create view for RankFragment.
11-17 18:58:50.114: V/RankFragment(12656): @onViewCreated. width >> 0
11-17 18:58:50.114: V/RankFragment(12656): @onActivityCreated. videoList is not null. invoke addRankVideos immediately.
11-17 18:58:50.114: V/RankFragment(12656): @onActivityCreated. width >> 0
11-17 18:58:50.114: V/RankFragment(12656): @onStart. width >> 0
11-17 18:58:50.124: V/RankFragment(12656): oldLeft:0, oldRight:0, oldTop:0, oldBottom:0
11-17 18:58:50.124: V/RankFragment(12656): left:0, right:1129, top:0, bottom:0
11-17 18:58:50.134: V/RankFragment(12656): @onActivityCreated. after 0 ms. width is ready.
11-17 18:58:50.134: V/RankFragment(12656): @onActivityCreated. width >> 1129
11-17 18:58:50.144: V/RankFragment(12656): @addRankVideos. videoList.size >> 10
11-17 18:58:50.144: V/RankFragment(12656): @createRankGrid. width >> 1129
日志中显示:onLayoutChange方法的执行在onStart方法之后,并且时间很短。
以下是修改后正确的代码
@Override
public void onActivityCreated(Bundle savedInstanceState) {super.onActivityCreated(savedInstanceState);if(videoList == null) {Logger.v(TAG, "@onActivityCreated. get rank data from URL >> " + TvieURL.TODAY_RANK_URL);get(REQUEST_GET_RANK, TodayRankResult.class, this, TvieURL.TODAY_RANK_URL, null);} else {Logger.v(TAG, "@onActivityCreated. videoList is not null. invoke addRankVideos immediately.");Logger.v(TAG, "@onActivityCreated. width >> " + contentLayout.getMeasuredWidth());handler.post(new Runnable() {@Overridepublic void run() {long start = System.currentTimeMillis();while(!widthReady) {try {Thread.sleep(1);} catch (InterruptedException e) { }}long end = System.currentTimeMillis();Logger.v(TAG, "@onActivityCreated. after " + (end - start) + " ms. width is ready.");Logger.v(TAG, "@onActivityCreated. width >> " + contentLayout.getMeasuredWidth());handler.sendEmptyMessage(1);}});}
}
Handler handler = new Handler(){public void handleMessage(android.os.Message msg) {switch(msg.what) {case 1:addRankVideos(videoList);break;}}
};
在上面的场景调用handler貌似不是新建一个线程。
在上述代码中在执行handler.post方法之前就记录下时间,如下所示
Logger.v(TAG, "@onActivityCreated. videoList is not null. invoke addRankVideos immediately.");
Logger.v(TAG, "@onActivityCreated. width >> " + contentLayout.getMeasuredWidth());
final long start = System.currentTimeMillis();
handler.post(new Runnable() {@Overridepublic void run() {long run = System.currentTimeMillis();Logger.v(TAG, "@onActivityCreated. after " + (run - start) + " ms. invoke run().");while(!widthReady) {try {Thread.sleep(1);} catch (InterruptedException e) { }}long end = System.currentTimeMillis();Logger.v(TAG, "@onActivityCreated. after " + (end - start) + " ms. width is ready.");Logger.v(TAG, "@onActivityCreated. width >> " + contentLayout.getMeasuredWidth());handler.sendEmptyMessage(1);}
});
打印出来的日志显示run()方法真正的调用要在11ms之后。
这个结果说明handler.post不是要去创建一个线程,但是也巧合满足我们当前的需求
11-18 09:57:49.611: V/RankFragment(30389): @onCreateView. create view for RankFragment.
11-18 09:57:49.611: V/RankFragment(30389): @onActivityCreated. videoList is not null. invoke addRankVideos immediately.
11-18 09:57:49.611: V/RankFragment(30389): @onActivityCreated. width >> 0
11-18 09:57:49.611: V/RankFragment(30389): oldLeft:0, oldRight:0, oldTop:0, oldBottom:0
11-18 09:57:49.611: V/RankFragment(30389): left:0, right:1129, top:0, bottom:0
11-18 09:57:49.621: V/RankFragment(30389): @onActivityCreated. after 11 ms. invoke run().
11-18 09:57:49.621: V/RankFragment(30389): @onActivityCreated. after 11 ms. width is ready.
11-18 09:57:49.621: V/RankFragment(30389): @onActivityCreated. width >> 1129
11-18 09:57:49.631: V/RankFragment(30389): @addRankVideos. videoList.size >> 10
11-18 09:57:49.631: V/RankFragment(30389): @createRankGrid. width >> 1129
继续修改上面的代码片段,不再使用handler.post方法,改为使用new Thread(){...}.start();
Logger.v(TAG, "@onActivityCreated. videoList is not null. invoke addRankVideos immediately.");
Logger.v(TAG, "@onActivityCreated. width >> " + contentLayout.getMeasuredWidth());
final long start = System.currentTimeMillis();
new Thread() {@Overridepublic void run() {long run = System.currentTimeMillis();Logger.v(TAG, "@onActivityCreated. after " + (run - start) + " ms. invoke run().");while(!widthReady) {try {Thread.sleep(1);} catch (InterruptedException e) { }}long end = System.currentTimeMillis();Logger.v(TAG, "@onActivityCreated. after " + (end - start) + " ms. width is ready.");Logger.v(TAG, "@onActivityCreated. width >> " + contentLayout.getMeasuredWidth());handler.sendEmptyMessage(1);}
}.start();
打印出来的日志显示新建线程的run()方法是立即执行的,然后在while循环中等待widthReady变为true,这正好满足我们的需求
11-18 10:12:00.721: V/RankFragment(31655): @onCreateView. create view for RankFragment.
11-18 10:12:00.731: V/RankFragment(31655): @onActivityCreated. videoList is not null. invoke addRankVideos immediately.
11-18 10:12:00.731: V/RankFragment(31655): @onActivityCreated. width >> 0
11-18 10:12:00.731: V/RankFragment(31655): @onActivityCreated. after 1 ms. invoke run().
11-18 10:12:00.731: V/RankFragment(31655): oldLeft:0, oldRight:0, oldTop:0, oldBottom:0
11-18 10:12:00.731: V/RankFragment(31655): left:0, right:1129, top:0, bottom:0
11-18 10:12:00.741: V/RankFragment(31655): @onActivityCreated. after 9 ms. width is ready.
11-18 10:12:00.741: V/RankFragment(31655): @onActivityCreated. width >> 1129
11-18 10:12:00.741: V/RankFragment(31655): @addRankVideos. videoList.size >> 10
11-18 10:12:00.741: V/RankFragment(31655): @createRankGrid. width >> 1129
转载于:https://blog.51cto.com/truesea/1327654