View,viewgroup,viewstub总结

 

 

  1. package com.example.xiutest;
  2. import java.io.File;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. publicclassMainActivityextendsActivityimplementsOnClickListener,OnTouchListener{
  6. privateContext ctx;
  7. privateBoolean isFrist=true;
  8. privateImageView dargtool;
  9. privateint lastX;
  10. privateint lastY;
  11. privateint startTop;
  12. privateint startBottom;
  13. privateint startLeft;
  14. privateint startRight;
  15. @Override
  16. protectedvoid onCreate(Bundle savedInstanceState){
  17. super.onCreate(savedInstanceState);
  18. setContentView(R.layout.activity_main);
  19. ctx =this;
  20. dargtool=(ImageView)findViewById(R.id.dargtool);
  21. dargtool.setOnTouchListener(this);
  22. }
  23. @Override
  24. publicboolean onTouch(View v,MotionEvent event){
  25. switch(event.getAction()){
  26. caseMotionEvent.ACTION_MOVE:
  27. int dx =(int)event.getRawX()- lastX;
  28. int dy =(int)event.getRawY()- lastY;
  29. int left = v.getLeft()+ dx;
  30. int top = v.getTop()+ dy;
  31. int right = v.getRight()+ dx;
  32. int bottom = v.getBottom()+ dy;
  33. if(left <0){
  34. left =0;
  35. right = left + v.getWidth();
  36. }
  37. if(right >720){
  38. right =720;
  39. left = right - v.getWidth();
  40. }
  41. if(top <0){
  42. top =0;
  43. bottom = top + v.getHeight();
  44. }
  45. if(bottom >600){
  46. bottom =600;
  47. top = bottom - v.getHeight();
  48. mHandler.sendEmptyMessage(3);
  49. }
  50. v.layout(left, top, right, bottom);
  51. lastX =(int) event.getRawX();
  52. lastY =(int) event.getRawY();
  53. break;
  54. caseMotionEvent.ACTION_DOWN:
  55. if(isFrist){
  56. startTop=dargtool.getTop();
  57. startBottom=dargtool.getBottom();
  58. startLeft=dargtool.getLeft();
  59. startRight=dargtool.getRight();
  60. isFrist=false;
  61. }
  62. lastX =(int) event.getRawX();
  63. lastY =(int) event.getRawY();
  64. break;
  65. caseMotionEvent.ACTION_UP:
  66. v.layout(startLeft, startTop, startRight, startBottom);
  67. break;
  68. default:
  69. break;
  70. }
  71. returntrue;
  72. }
  73. }

可拖动的View控件

以上代码实现控件拖动跟随然后又回弹原始位置,
1.dargtool.getTop()等获取的相对于父控件的位置
2.在onCreate,dargtool.getTop()等的值全部是0,因为没有初始化完毕
3.int dx =(int)event.getRawX()- lastX;
int dy =(int)event.getRawY()- lastY;
获取事件的绝对位置的变化, 
            int left = v.getLeft() + dx; 
            int top = v.getTop() + dy; 
            int right = v.getRight() + dx; 
            int bottom = v.getBottom() + dy; 
转移给相对于父控件的相对位置

View聚焦

在xml里面配置(聚焦才能实现跑马灯效果)
  1. <TextView
  2. android:id="@+id/mine_nickname"
  3. android:layout_width="wrap_content"
  4. android:layout_height="fill_parent"
  5. android:ellipsize="marquee"
  6. android:focusable="true"
  7. android:focusableInTouchMode="true"
  8. android:gravity="center"
  9. android:marqueeRepeatLimit="marquee_forever"
  10. android:scrollHorizontally="true"
  11. android:singleLine="true"
  12. android:text=""
  13. android:textColor="@color/hovn_index_room_name"
  14. android:textSize="@dimen/mine_info_name_size"/>
在代码类里面配置
 
setFocusable(true); 是设置能否获得焦点而已。requestFocus(),是让控件得到焦点
 

自定义的viewgroup允许添加子view

  1. //设置是否能够调用自定义的布局,false是可以
  2. setWillNotDraw(false);
  3. //优先其子类控件而获取到焦点
  4. setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);

View.requestLayout()和invalidate

requestLayout:当view确定自身已经不再适合现有的区域时,该view本身调用这个方法要求parent view重新调用他的onMeasure onLayout来对重新设置自己位置。
特别的当view的layoutparameter发生改变,并且它的值还没能应用到view上,这时候适合调用这个方法。
invalidate:View本身调用迫使view重画。

View的略缩图

  获取View的缩略图很有用,比如需要展示树形目录每个节点的内容的时候,将每个节点的布局显示通过缩略图抠取出来,通过每个布局的缩略图就可以了解每个页面的大概内容。
   实现代码如下:
方法1:
  1.  
  2. privateBitmap getViewBitmap(View view ){
  3.     view.setDrawingCacheEnabled(true);
  4.     Bitmap bitmap =null;
  5.     try{
  6.         if(null!= view.getDrawingCache()){
  7.            bitmap =Bitmap.createScaledBitmap( view.getDrawingCache(),256,192,false);
  8.         }else{
  9.             Bitmap bitmapTmp =((BitmapDrawable)( getResources().getDrawable( R.drawable.syncompdetailcontent_background ))).getBitmap();
  10.         }
  11.         }catch(OutOfMemoryError e ){
  12.             e.printStackTrace();
  13.         }finally{
  14.            view.setDrawingCacheEnabled(false);
  15.            view.destroyDrawingCache();
  16.         }
  17.         return bitmap;
  18.     }
方法2:
  1.     /**
  2.         * 把View绘制到Bitmap上
  3.         * @param view 需要绘制的View
  4.         * @param width 该View的宽度
  5.         * @param height 该View的高度
  6.         * @return 返回Bitmap对象
  7.         * add by csj 13-11-6
  8.         */ 
  9.         publicBitmap getViewBitmap(View comBitmap,int width,int height){ 
  10.             Bitmap bitmap = null; 
  11.             if(comBitmap != null){ 
  12.                 comBitmap.clearFocus(); 
  13.                 comBitmap.setPressed(false); 
  14.                 boolean willNotCache = comBitmap.willNotCacheDrawing(); 
  15.                 comBitmap.setWillNotCacheDrawing(false); 
  16.                 // Reset the drawing cache background color to fully transparent 
  17.                 // for the duration of this operation 
  18.                 int color = comBitmap.getDrawingCacheBackgroundColor(); 
  19.                 comBitmap.setDrawingCacheBackgroundColor(0); 
  20.                 float alpha = comBitmap.getAlpha(); 
  21.                 comBitmap.setAlpha(1.0f); 
  22.                 if(color !=0){ 
  23.                     comBitmap.destroyDrawingCache(); 
  24.                 } 
  25.                 int widthSpec =View.MeasureSpec.makeMeasureSpec(width,View.MeasureSpec.EXACTLY); 
  26.                 int heightSpec =View.MeasureSpec.makeMeasureSpec(height,View.MeasureSpec.EXACTLY); 
  27.                 comBitmap.measure(widthSpec, heightSpec); 
  28.                 comBitmap.layout(0,0, width, height); 
  29.                 comBitmap.buildDrawingCache(); 
  30.                 Bitmap cacheBitmap = comBitmap.getDrawingCache(); 
  31.                 if(cacheBitmap == null){ 
  32.                     Log.e("view.ProcessImageToBlur","failed getViewBitmap("+ comBitmap +")",  
  33.                             newRuntimeException()); 
  34.                     return null; 
  35.                 } 
  36.                 bitmap =Bitmap.createBitmap(cacheBitmap); 
  37.                 // Restore the view 
  38.                 comBitmap.setAlpha(alpha); 
  39.                 comBitmap.destroyDrawingCache(); 
  40.                 comBitmap.setWillNotCacheDrawing(willNotCache); 
  41.                 comBitmap.setDrawingCacheBackgroundColor(color); 
  42.             } 
  43.             return bitmap; 
  44.         } 
 
 
 
 
方法3:
 
[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1.     privateBitmap getViewBitmap(View v){   
  2.             v.clearFocus();   
  3.             v.setPressed(false);   
  4.             boolean willNotCache = v.willNotCacheDrawing();   
  5.             v.setWillNotCacheDrawing(false);   
  6.             // Reset the drawing cache background color to fully transparent   
  7.             // for the duration of this operation   
  8.             int color = v.getDrawingCacheBackgroundColor();   
  9.             v.setDrawingCacheBackgroundColor(0);   
  10.             if(color !=0){   
  11.                 v.destroyDrawingCache();   
  12.             }   
  13.             v.buildDrawingCache();   
  14.             Bitmap cacheBitmap = v.getDrawingCache();   
  15.             if(cacheBitmap == null){   
  16.                 Log.e("Folder","failed getViewBitmap("+ v +")",newRuntimeException());   
  17.                 return null;   
  18.             }   
  19.             Bitmap bitmap =Bitmap.createBitmap(cacheBitmap);   
  20.             // Restore the view   
  21.             v.destroyDrawingCache();   
  22.             v.setWillNotCacheDrawing(willNotCache);   
  23.             v.setDrawingCacheBackgroundColor(color);   
  24.             return bitmap;   
  25.         } 
方法4:
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. View view = mLauncher.getDragLayer();
  2. view.setDrawingCacheEnabled(true);
  3. Bitmap bitmap = view.getDrawingCache();
 

ViewGroup提高绘制性能

Viewgroup如果下面有很多子View。绘制的时候,需要开启其子View的绘制缓存。从而提高绘制效率。具体的代码如下
  1.     publicvoid setChildrenDrawingCacheEnabled(boolean enabled){ 
  2.          final int count = getChildCount(); 
  3.          for(int i =0; i < count; i++){ 
  4.          final View view = getChildAt(i); 
  5.         view.setDrawingCacheEnabled(true); 
  6.             // Update the drawing caches 
  7.          view.buildDrawingCache(true); 
  8.         } 
  9.          } 
另一方面也可以通过setDrawingCacheQuality(low),将缓存质量降低,减少内存。
最后结束的时候,需要通过以下代码来清空绘制缓存.
  1.     void clearChildrenCache(){ 
  2.        final int count = getChildCount(); 
  3.         for(int i =0; i < count; i++){ 
  4.             final CellLayout layout =(CellLayout) getChildAt(i); 
  5.             layout.setChildrenDrawnWithCacheEnabled(false); 
  6.         } 
  7.       } 

截屏代码实现

  1. publicclassScreenShot{
  2. // 获取指定Activity的截屏,保存到png文件
  3. publicstaticBitmap takeScreenShot(Activity activity){
  4. // View是你需要截图的View
  5. View view = activity.getWindow().getDecorView();
  6. view.setDrawingCacheEnabled(true);
  7. view.buildDrawingCache();
  8. Bitmap b1 = view.getDrawingCache();
  9. // 获取状态栏高度
  10. Rect frame =newRect();
  11. activity.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
  12. int statusBarHeight = frame.top;
  13. System.out.println(statusBarHeight);
  14. // 获取屏幕长和高
  15. int width = activity.getWindowManager().getDefaultDisplay().getWidth();
  16. int height = activity.getWindowManager().getDefaultDisplay()
  17. .getHeight();
  18. // 去掉标题栏
  19. // Bitmap b = Bitmap.createBitmap(b1, 0, 25, 320, 455);
  20. Bitmap b =Bitmap.createBitmap(b1,0, statusBarHeight, width, height
  21. - statusBarHeight);
  22. view.destroyDrawingCache();
  23. savePic(b,"/sdcard/screen_test.png");
  24. return b;
  25. }
  26. // 保存到sdcard
  27. publicstaticvoid savePic(Bitmap b,String strFileName){
  28. FileOutputStream fos =null;
  29. try{
  30. fos =newFileOutputStream(strFileName);
  31. if(null!= fos){
  32. b.compress(Bitmap.CompressFormat.PNG,90, fos);
  33. fos.flush();
  34. fos.close();
  35. }
  36. }catch(FileNotFoundException e){
  37. e.printStackTrace();
  38. }catch(IOException e){
  39. e.printStackTrace();
  40. }
  41. }
  42. /**
  43. * 把View对象转换成bitmap
  44. * */
  45. publicstaticBitmap convertViewToBitmap(View view){
  46. view.measure(MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED),
  47. MeasureSpec.makeMeasureSpec(0,MeasureSpec.UNSPECIFIED));
  48. view.layout(0,0, view.getMeasuredWidth(), view.getMeasuredHeight());
  49. view.buildDrawingCache();
  50. Bitmap bitmap = view.getDrawingCache();
  51. if(bitmap !=null){
  52. System.out.println("这不是nullde1");
  53. Log.d("nullde1","nullde1");
  54. }else{
  55. System.out.println("这nullnulllnulnlul");
  56. }
  57. return bitmap;
  58. }
  59. // 程序入口1
  60. publicstaticvoid shoot(Activity a){
  61. ScreenShot.savePic(ScreenShot.takeScreenShot(a),"/sdcard/screen_test.png");
  62. }
  63. // 程序入口2
  64. publicstaticvoid shootView(View view){
  65. ScreenShot.savePic(ScreenShot.convertViewToBitmap(view),
  66. "sdcard/xx.png");
  67. }
  68. publicstaticBitmap getViewBitmap(View v){
  69. v.clearFocus();
  70. v.setPressed(false);
  71. boolean willNotCache = v.willNotCacheDrawing();
  72. v.setWillNotCacheDrawing(false);
  73. // Reset the drawing cache background color to fully transparent
  74. // for the duration of this operation
  75. int color = v.getDrawingCacheBackgroundColor();
  76. v.setDrawingCacheBackgroundColor(0);
  77. if(color !=0){
  78. v.destroyDrawingCache();
  79. }
  80. v.buildDrawingCache();
  81. Bitmap cacheBitmap = v.getDrawingCache();
  82. if(cacheBitmap ==null){
  83. Log.e("TTTTTTTTActivity","failed getViewBitmap("+ v +")",
  84. newRuntimeException());
  85. returnnull;
  86. }
  87. Bitmap bitmap =Bitmap.createBitmap(cacheBitmap);
  88. // Restore the view
  89. v.destroyDrawingCache();
  90. v.setWillNotCacheDrawing(willNotCache);
  91. v.setDrawingCacheBackgroundColor(color);
  92. return bitmap;
  93. }
  94. /**
  95. * 截取scrollview的屏幕
  96. * **/
  97. publicstaticBitmap getBitmapByView(ScrollView scrollView){
  98. int h =0;
  99. Bitmap bitmap =null;
  100. // 获取listView实际高度
  101. for(int i =0; i < scrollView.getChildCount(); i++){
  102. h += scrollView.getChildAt(i).getHeight();
  103. scrollView.getChildAt(i).setBackgroundResource(R.drawable.bg3);
  104. }
  105. Log.d(TAG,"实际高度:"+ h);
  106. Log.d(TAG," 高度:"+ scrollView.getHeight());
  107. // 创建对应大小的bitmap
  108. bitmap =Bitmap.createBitmap(scrollView.getWidth(), h,
  109. Bitmap.Config.ARGB_8888);
  110. finalCanvas canvas =newCanvas(bitmap);
  111. scrollView.draw(canvas);
  112. // 测试输出
  113. FileOutputStream out =null;
  114. try{
  115. out =newFileOutputStream("/sdcard/screen_test.png");
  116. }catch(FileNotFoundException e){
  117. e.printStackTrace();
  118. }
  119. try{
  120. if(null!= out){
  121. bitmap.compress(Bitmap.CompressFormat.PNG,100, out);
  122. out.flush();
  123. out.close();
  124. }
  125. }catch(IOException e){
  126. // TODO: handle exception
  127. }
  128. return bitmap;
  129. }
  130. privatestaticString TAG ="Listview and ScrollView item 截图:";
  131. /**
  132. * 截图listview
  133. * **/
  134. publicstaticBitmap getbBitmap(ListView listView){
  135. int h =0;
  136. Bitmap bitmap =null;
  137. // 获取listView实际高度
  138. for(int i =0; i < listView.getChildCount(); i++){
  139. h += listView.getChildAt(i).getHeight();
  140. }
  141. Log.d(TAG,"实际高度:"+ h);
  142. Log.d(TAG,"list 高度:"+ listView.getHeight());
  143. // 创建对应大小的bitmap
  144. bitmap =Bitmap.createBitmap(listView.getWidth(), h,
  145. Bitmap.Config.ARGB_8888);
  146. finalCanvas canvas =newCanvas(bitmap);
  147. listView.draw(canvas);
  148. // 测试输出
  149. FileOutputStream out =null;
  150. try{
  151. out =newFileOutputStream("/sdcard/screen_test.png");
  152. }catch(FileNotFoundException e){
  153. e.printStackTrace();
  154. }
  155. try{
  156. if(null!= out){
  157. bitmap.compress(Bitmap.CompressFormat.PNG,100, out);
  158. out.flush();
  159. out.close();
  160. }
  161. }catch(IOException e){
  162. // TODO: handle exception
  163. }
  164. return bitmap;
  165. }
  166. }

view及其子类的LayoutParams的设置

view的params是RelativeLayout.LayoutParams
  1. LayoutParamsParam=(RelativeLayout.LayoutParams) live_broadcase.getLayoutParams();
  2. float live_broadcase_lenght=live_broadcase.getTextSize()*s[2].length();
  3. if(screen_width<live_broadcase_lenght){
  4. Param.setMargins(Param.leftMargin,Param.topMargin,(int)-live_broadcase_lenght+screen_width,Param.bottomMargin);
  5. live_broadcase.setLayoutParams(Param);
  6. }

view的ViewConfiguration

  1. ViewConfiguration config =ViewConfiguration.get(context);
  2. mTouchSlop = config.getScaledTouchSlop();
getScaledTouchSlop是一个距离,表示滑动的时候,手的移动要大于这个距离才开始移动控件。如果小于这个距离就不触发移动控件,如viewpager就是用这个距离来判断用户是否翻页

代码控制布局

http://www.cnblogs.com/vivid-stanley/archive/2012/08/21/2648790.html
  1. RelativeLayout container =newRelativeLayout(this);
  2. Button btn1 =newButton(this);
  3. btn1.setId(11);
  4. btn1.setText("上");
  5. Button btn2 =newButton(this);
  6. btn2.setId(12);
  7. btn2.setText("下");
  8. Button btn3 =newButton(this);
  9. btn3.setId(13);
  10. btn3.setText("左");
  11. Button btn4 =newButton(this);
  12. btn4.setId(14);
  13. btn4.setText("右");
  14. Button btn5 =newButton(this);
  15. btn5.setId(15);
  16. btn5.setText("中");
  17. RelativeLayout.LayoutParams lp1 =newRelativeLayout.LayoutParams(100,
  18. RelativeLayout.LayoutParams.WRAP_CONTENT);
  19. RelativeLayout.LayoutParams lp2 =newRelativeLayout.LayoutParams(lp1);
  20. RelativeLayout.LayoutParams lp3 =newRelativeLayout.LayoutParams(lp1);
  21. RelativeLayout.LayoutParams lp4 =newRelativeLayout.LayoutParams(lp1);
  22. RelativeLayout.LayoutParams lp5 =newRelativeLayout.LayoutParams(lp1);
  23. lp5.addRule(RelativeLayout.CENTER_IN_PARENT,RelativeLayout.TRUE);
  24. lp1.addRule(RelativeLayout.ABOVE, btn5.getId());
  25. lp1.addRule(RelativeLayout.ALIGN_LEFT, btn5.getId());
  26. lp2.addRule(RelativeLayout.BELOW, btn5.getId());
  27. lp2.addRule(RelativeLayout.ALIGN_LEFT, btn5.getId());
  28. lp3.addRule(RelativeLayout.LEFT_OF, btn5.getId());
  29. lp3.addRule(RelativeLayout.ALIGN_BASELINE, btn5.getId());
  30. lp4.addRule(RelativeLayout.RIGHT_OF, btn5.getId());
  31. lp4.addRule(RelativeLayout.ALIGN_TOP, btn5.getId());
  32. container.addView(btn5, lp5);
  33. container.addView(btn1, lp1);
  34. container.addView(btn2, lp2);
  35. container.addView(btn3, lp3);
  36. container.addView(btn4, lp4);
  37. return container;
 

addView的理解

参考地址http://sdywcd.iteye.com/blog/2035458

public void addView (View child, int index, ViewGroup.LayoutParams params)

这里最主要的参数就是index,标示的是希望将view增加到哪个view的下面。从刚才的布局代码片段看,以父布局relativelayout算起,

RelativeLayou index=0;

GridView index=1;

LinearLayout index=2;

你如果给index填写大于2的值将会报错。因为在当前父容器下,没有大于3的同级view

当然你还可以填写index=-1,-1的意思是,将view加到父容器中所有容器的最下面,也就是linearlayout的下面

 

所以一般: headView一般调用addView(child, 0, params);
          footView一般调用addView(child, -1, params);
源码中    
  1. public void addView(View child) {
  2.         addView(child, -1);
  3.     }
所以默认添加到当前的最后面
 

根据不同的需求加载布局到当前界面,并且保证不找错xml的情况下加载布局里面的控件

pull_to_refresh_header_horizontal.xml
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <mergexmlns:android="http://schemas.android.com/apk/res/android">
  3. <FrameLayout
  4. android:id="@+id/fl_inner"
  5. android:layout_width="wrap_content"
  6. android:layout_height="fill_parent"
  7. android:paddingBottom="@dimen/header_footer_top_bottom_padding"
  8. android:paddingLeft="@dimen/header_footer_left_right_padding"
  9. android:paddingRight="@dimen/header_footer_left_right_padding"
  10. android:paddingTop="@dimen/header_footer_top_bottom_padding">
  11. <ImageView
  12. android:id="@+id/pull_to_refresh_image"
  13. android:layout_width="wrap_content"
  14. android:layout_height="wrap_content"
  15. android:layout_gravity="center"/>
  16. <ProgressBar
  17. android:id="@+id/pull_to_refresh_progress"
  18. style="?android:attr/progressBarStyleSmall"
  19. android:layout_width="wrap_content"
  20. android:layout_height="wrap_content"
  21. android:layout_gravity="center"
  22. android:indeterminate="true"
  23. android:visibility="gone"/>
  24. </FrameLayout>
  25. </merge>
pull_to_refresh_header_vertical.xml
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <mergexmlns:android="http://schemas.android.com/apk/res/android">
  3. <FrameLayout
  4. android:id="@+id/fl_inner"
  5. android:layout_width="fill_parent"
  6. android:layout_height="wrap_content"
  7. android:paddingBottom="@dimen/header_footer_top_bottom_padding"
  8. android:paddingLeft="@dimen/header_footer_left_right_padding"
  9. android:paddingRight="@dimen/header_footer_left_right_padding"
  10. android:paddingTop="@dimen/header_footer_top_bottom_padding">
  11. <FrameLayout
  12. android:layout_width="wrap_content"
  13. android:layout_height="wrap_content"
  14. android:layout_gravity="left|center_vertical">
  15. <ImageView
  16. android:id="@+id/pull_to_refresh_image"
  17. android:layout_width="wrap_content"
  18. android:layout_height="wrap_content"
  19. android:layout_gravity="center"/>
  20. <ProgressBar
  21. android:id="@+id/pull_to_refresh_progress"
  22. style="?android:attr/progressBarStyleSmall"
  23. android:layout_width="wrap_content"
  24. android:layout_height="wrap_content"
  25. android:layout_gravity="center"
  26. android:indeterminate="true"
  27. android:visibility="gone"/>
  28. </FrameLayout>
  29. <LinearLayout
  30. android:layout_width="wrap_content"
  31. android:layout_height="wrap_content"
  32. android:layout_gravity="center"
  33. android:gravity="center_horizontal"
  34. android:orientation="vertical">
  35. <TextView
  36. android:id="@+id/pull_to_refresh_text"
  37. android:layout_width="wrap_content"
  38. android:layout_height="wrap_content"
  39. android:singleLine="true"
  40. android:textAppearance="?android:attr/textAppearance"
  41. android:textStyle="bold"/>
  42. <TextView
  43. android:id="@+id/pull_to_refresh_sub_text"
  44. android:layout_width="wrap_content"
  45. android:layout_height="wrap_content"
  46. android:singleLine="true"
  47. android:textAppearance="?android:attr/textAppearanceSmall"
  48. android:visibility="gone"/>
  49. </LinearLayout>
  50. </FrameLayout>
  51. </merge>
  1. public abstract class LoadingLayout extends FrameLayout implements ILoadingLayout {
  2. publicLoadingLayout(Context context,finalMode mode,finalOrientation scrollDirection,TypedArray attrs){
  3. super(context);
  4. mMode = mode;
  5. mScrollDirection = scrollDirection;
  6. switch(scrollDirection){
  7. case HORIZONTAL:
  8. LayoutInflater.from(context).inflate(R.layout.pull_to_refresh_header_horizontal,this);
  9. break;
  10. case VERTICAL:
  11. default:
  12. LayoutInflater.from(context).inflate(R.layout.pull_to_refresh_header_vertical,this);
  13. break;
  14. }
  15. mInnerLayout =(FrameLayout) findViewById(R.id.fl_inner);
  16. mHeaderText =(TextView) mInnerLayout.findViewById(R.id.pull_to_refresh_text);
  17. mHeaderProgress =(ProgressBar) mInnerLayout.findViewById(R.id.pull_to_refresh_progress);
  18. mSubHeaderText =(TextView) mInnerLayout.findViewById(R.id.pull_to_refresh_sub_text);
  19. mHeaderImage =(ImageView) mInnerLayout.findViewById(R.id.pull_to_refresh_image);
R.id.fl_inner不会加载错的原因在于LayoutInflater.from(context).inflate(R.layout.pull_to_refresh_header_horizontal,this);里面的this是父控件,已经在当前页面了,所以不会加载错

viewstub的理解

在xml布局文件中,view和view的子类布局在里面有一个弊端,就是无论这个布局在界面里面要不要显示,都会在加载布局的时候占用内存,这样在对内存要求很高的情况下,就会对手机性能影响很大,用viewstub,在没有对viewstub做代码操作之前,就没有占用内存,只有用到的时候才会加载进去

Android性能优化之一:ViewStub

ViewStub是Android布局优化中一个很不错的标签/控件,直接继承自View。虽然Android开发人员基本上都听说过,但是真正用的可能不多。

ViewStub可以理解成一个非常轻量级的View,与其他的控件一样,有着自己的属性及特定的方法。当ViewStub使用在布局文件中时,当程序inflate布局文件时,ViewStub本身也会被解析,且占据内存控件,但是与其他控件相比,主要区别体现在以下几点:

1.当布局文件inflate时,ViewStub控件虽然也占据内存,但是相相比于其他控件,ViewStub所占内存很小;

2.布局文件inflate时,ViewStub主要是作为一个“占位符”的性质,放置于view tree中,且ViewStub本身是不可见的。ViewStub中有一个layout属性,指向ViewStub本身可能被替换掉的布局文件,在一定时机时,通过viewStub.inflate()完成此过程;

3.ViewStub本身是不可见的,对ViewStub setVisibility(..)与其他控件不一样,ViewStub的setVisibility 成View.VISIBLE或INVISIBLE如果是首次使用,都会自动inflate其指向的布局文件,并替换ViewStub本身,再次使用则是相当于对其指向的布局文件设置可见性。

这里需要注意的是:

1.ViewStub之所以常称之为“延迟化加载”,是因为在教多数情况下,程序无需显示ViewStub所指向的布局文件,只有在特定的某些较少条件下,此时ViewStub所指向的布局文件才需要被inflate,且此布局文件直接将当前ViewStub替换掉,具体是通过viewStub.infalte()或viewStub.setVisibility(View.VISIBLE)来完成;

2.正确把握住ViewStub的应用场景非常重要,正如如1中所描述需求场景下,使用ViewStub可以优化布局;

3.对ViewStub的inflate操作只能进行一次,因为inflate的时候是将其指向的布局文件解析inflate并替换掉当前ViewStub本身(由此体现出了ViewStub“占位符”性质),一旦替换后,此时原来的布局文件中就没有ViewStub控件了,因此,如果多次对ViewStub进行infalte,会出现错误信息:ViewStub must have a non-null ViewGroup viewParent。

4.3中所讲到的ViewStub指向的布局文件解析inflate并替换掉当前ViewStub本身,并不是完全意义上的替换(与include标签还不太一样),替换时,布局文件的layout params是以ViewStub为准,其他布局属性是以布局文件自身为准。

下面看一下简单的需求场景:在listview显示列表数据时,可能会出现服务端一条数据都没有的情况,此时显示一个EmptyView,提示用户暂无数据。此时考虑到实际应用中EmptyView显示出来的机会相当小,因此,可以在布局文件中使用ViewStub站位,然后确实没有数据时才viewStub.infalte()。

相关部分代码如下:

复制代码
 1 public void showEmptyView() {
 2     listview.setVisibility(View.GONE);
 3     if (noDataView == null) {
 4         ViewStub noDataViewStub = (ViewStub) view.findViewById(R.id.no_data_viewstub);
 5         noDataView = noDataViewStub.inflate();
 6     } else {
 7         noDataView.setVisibility(View.VISIBLE);
 8     }
 9 }
1011 public void showListView(){
12     listview.setVisibility(View.VISIBLE);
13     if(noDataView != null){
14         noDataView.setVisibility(View.GONE);
15     }
16 }
复制代码

特别需要注意的是对ViewStub是否已经inflate的判断。

在Listview Item中,有时候可能遇到如下场景:在不同的列表页item的布局一部分不同,但相对于整个item布局来说又不是很多,此时最常见的有如下两种处理:

1.对不同的部分都写出来,放到一个item文件中,然后逻辑分别处理不同部分的显示与否(View.VISIBLE和View.GONE);

2.对这两种不同的item整个部分都分别区分开,完全写成两个item文件,然后结合listView显示不同布局分别做逻辑处理(通过getItemType()等方式)。

以上两种处理方式其实都可以,第一种方式逻辑清晰,非常灵活,只是在一定程度上增加了内存和资源消耗。第二种方式是的布局文件有重复(虽然相同部分可以通过include,但是逻辑上还是有重复的),包括逻辑上处理的代码实质上的重复。一般对于有较大不同的item布局推荐采用此种方式。

有时候结合需求,可以在第一种方式的基础上,结合ViewStub“占位符”可以比较好的完成此类需求。也相当于是两种方式的一种折中形式,但同时兼顾了内存和资源消耗以及不同的布局逻辑控件。

 

在代码中新建一个控件对象,和布局控件(xml中的)的位置一样的

其中liveactivity这个是整个布局的控件对象
live_gift_display是布局中的一个控件
m是新建的对象
 
没实现效果的代码(调试时候打乱了顺序)
  1. //m.setLayoutParams(lp);
  2. //RelativeLayout.LayoutParams lp=new RelativeLayout.LayoutParams(250, 250);
  3. //lp.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
  4. //lp.addRule(RelativeLayout.CENTER_HORIZONTAL, RelativeLayout.TRUE);
  5. //lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, RelativeLayout.TRUE);
  6. //m.setX(x);
  7. //int[] fromViewLocation = new int[2];
  8. //liveactivity.getLocationOnScreen(fromViewLocation);
  9. //m.setX(fromViewLocation[0]/2);
  10. //m.setY(fromViewLocation[1]/2);
正确的代码
  1. android.view.ViewGroup.LayoutParams lp= live_gift_display.getLayoutParams();
  2. ImageView m=newImageView(this);
  3. m.setLayoutParams(lp);
  4. m.setScaleType(ScaleType.FIT_CENTER);
  5. m.setImageDrawable(d);
  6. liveactivity.addView(m);
  7. m.layout(live_gift_display.getLeft(), live_gift_display.getTop(), live_gift_display.getRight(), live_gift_display.getBottom());

改变控件的高和宽

  1. RelativeLayout.LayoutParams linearParams =(RelativeLayout.LayoutParams) baseBtn.getLayoutParams();//取控件textView当前的布局参数
  2. linearParams.height = getResources().getDimensionPixelSize(R.dimen.height_20dp);// 控件的高强制设成20
  3. linearParams.width = getResources().getDimensionPixelSize(R.dimen.width_20dp);// 控件的宽强制设成30
  4. baseBtn.setLayoutParams(linearParams);

OnGlobalLayoutListener获得一个视图的高度

我们知道在oncreate中View.getWidth和View.getHeight无法获得一个view的高度和宽度,这是因为View组件布局要在onResume回调后完成。所以现在需要使用getViewTreeObserver().addOnGlobalLayoutListener()来获得宽度或者高度。这是获得一个view的宽度和高度的方法之一。
OnGlobalLayoutListener 是ViewTreeObserver的内部类,当一个视图树的布局发生改变时,可以被ViewTreeObserver监听到,这是一个注册监听视图树的观察者(observer),在视图树的全局事件改变时得到通知。ViewTreeObserver不能直接实例化,而是通过getViewTreeObserver()获得。
除了OnGlobalLayoutListener ,ViewTreeObserver还有如下内部类:
interfaceViewTreeObserver.OnGlobalFocusChangeListener
当在一个视图树中的焦点状态发生改变时,所要调用的回调函数的接口类
interfaceViewTreeObserver.OnGlobalLayoutListener
当在一个视图树中全局布局发生改变或者视图树中的某个视图的可视状态发生改变时,所要调用的回调函数的接口类
interfaceViewTreeObserver.OnPreDrawListener 
当一个视图树将要绘制时,所要调用的回调函数的接口类
interfaceViewTreeObserver.OnScrollChangedListener
当一个视图树中的一些组件发生滚动时,所要调用的回调函数的接口类
interfaceViewTreeObserver.OnTouchModeChangeListener
当一个视图树的触摸模式发生改变时,所要调用的回调函数的接口类
其中,我们可以利用OnGlobalLayoutListener来获得一个视图的真实高度。
  1. privateint mHeaderViewHeight;
  2. privateView mHeaderView;
  3. .....
  4. mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(
  5. newOnGlobalLayoutListener(){
  6. @Override
  7. publicvoid onGlobalLayout(){
  8. mHeaderViewHeight = mHeaderView.getHeight();
  9. mHeaderView.getViewTreeObserver()
  10. .removeGlobalOnLayoutListener(this);
  11. }
  12. });
但是需要注意的是OnGlobalLayoutListener可能会被多次触发,因此在得到了高度之后,要将OnGlobalLayoutListener注销掉。另外mHeaderViewHeight和mHeaderView都需要写在当前java文件类(比如Activity)的成员变量中。不能直接在onCreate中定义否则会编译不通过:
 
Cannot refer to a non-final variable sHeight inside an inner class defined in a different method
 

ViewGroup为什么不会调用onDraw

 
正常情况下,我们重写LinearLayout的onDraw方法,它是不会被调用的,这篇文章就来分析一下原因和解决方法。
一,现象
<com.test.demo.MyLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 
    android:id="@+id/ll_absolute"
 
    android:orientation="vertical"
 
    android:layout_width="fill_parent"
 
    android:layout_height="fill_parent"
 
    android:background="#FF000000">
 
</com.test.demo.MyLinearLayout>
大概的架构是,MyLinearLayout从LinearLayout派生出来,然后在程序中重载OnDraw(Canvas canvas)。但是,onDraw不会被调用。我们可能会遇到这个问题:如果不给LinearLayout设置一个背景,系统是不会调用onDraw时,也就是说,我们重写的onDraw是不会调用的。当设置一个背景后,onDraw就会被调用。
 
二,原因
造成这种现象的原因是继承自LinearLayout,而LinearLayout这是一个容器,ViewGroup嘛,它本身并没有任何可画的东西,它是一个透明的控件,因些并不会触发onDraw,但是你现在给LinearLayout设置一个背景色,其实这个背景色不管你设置成什么颜色,系统会认为,这个LinearLayout上面有东西可画了,因此会调用onDraw方法。
 
我们可以仔细分析View的源码,它有一个方法View#draw(Canvas)方法,这里面有两个地方调用onDraw,它的条件都是:
 
if (!dirtyOpaque) onDraw(canvas);
也就是说,如果dirtyOpaque是true的话,onDraw就不会调用,而dirtyOpaque的值的计算代码如下:
 
[java] view plain copy
final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE && 
                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);  
当View初始他时,它会调用一个私有方法:computeOpaqueFlags,这里面列出了不透明的三个条件:
        // Opaque if:
 
        //   - Has a background
 
        //   - Background is opaque
 
        //   - Doesn't have scrollbars or scrollbars are inside overlay
View还提供了一个重要的方法:setWillNotDraw,我们看一看它的实现:
[java] view plain copy
/**
  * If this view doesn't do any drawing on its own, set this flag to
  * allow further optimizations. By default, this flag is not set on
  * View, but could be set on some View subclasses such as ViewGroup.
  *
  * Typically, if you override {@link #onDraw} you should clear this flag.
  *
  * @param willNotDraw whether or not this View draw on its own
  */ 
 public void setWillNotDraw(boolean willNotDraw) { 
     setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK); 
 
 } 
从这个方法的注释,我们可以看出,如果你想重写onDraw的话,你应该调用这个方法来清除flag,所以如果我们想要重写LinearLayout的onDraw的话,我们也可以在其构造方法中调用setWillNotDraw方法。 在ViewGroup初始他时,它调用了一个私有方法:initViewGroup,它里面会有一句setFlags(WILL_NOT_DRAW, DRAW_MASK); 相当于调用了setWillNotDraw(true),所以说,对于ViewGroup,它就认为是透明的了。如果我们想要重写onDraw,就需要调用setWillNotDraw(false)
三,总结一下:
1)ViewGroup默认情况下,会被设置成WILL_NOT_DRAW,这是从性能考虑,这样一来,onDraw就不会被调用了。
 
2)如果我们要重要一个ViweGroup的onDraw方法,有两种方法:
 
        1,在构造函数里面,给其设置一个颜色,如#00000000。
 
        2,在构造函数里面,调用setWillNotDraw(false),去掉其WILL_NOT_DRAW flag。
 
 

onLayout的含义(ViewGroup:可布局的区域 view:布局的位置

onLayout方法是ViewGroup中子View的布局方法,用于放置子View的位置。放置子View很简单,只需在重写onLayout方法,然后获取子View的实例,调用子View的layout方法实现布局。在实际开发中,一般要配合onMeasure测量方法一起使用。
onLayout方法:
 
  1. @Override
  2. protected abstract void onLayout(boolean changed,
  3.             int l,int t,int r,int b);
 
该方法在ViewGroup中定义是抽象函数,继承该类必须实现onLayout方法,而ViewGroup的onMeasure并非必须重写的。View的放置都是根据一个矩形空间放置的,onLayout传下来的l,t,r,b分别是放置父控件的矩形可用空间(除去margin和padding的空间)的左上角的left、top以及右下角right、bottom值。
 
layout方法:
  1. publicvoid layout(int l,int t,int r,int b);
 
该方法是View的放置方法,在View类实现。调用该方法需要传入放置View的矩形空间左上角left、top值和右下角right、bottom值。这四个值是相对于父控件而言的。例如传入的是(10, 10, 100, 100),则该View在距离父控件的左上角位置(10, 10)处显示,显示的大小是宽高是90(参数r,b是相对左上角的),这有点像绝对布局。
 

 

posted @ 2016-03-19 19:27  balder_m  阅读(540)  评论(0编辑  收藏  举报