《打造高质量Android应用》读书笔记

2019-11-17  本文已影响0人  crossroads

前言

最近看到一本书,是开发安卓的一些小技巧和基础知识,大家一起查漏补缺吧!只有一部分,其他的有点老了,还有一些不感兴趣,没继续看了,所以只有9个知识点哦

1. 使用weight和weightsum属性实现视图居中
<LinearLayout 
  ......
    android:gravity="center"
    android:orientation="horizontal"
    android:weightSum="1" > //

    <Button
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="0.5" //确保按钮占据50%空间
        ......
 />
</LinearLayout>
2. 在include里使用android:layout_*属性,重写include布局属性,实现复用

footer布局:

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:gravity="center"
    android:text="@string/footer_text" />

include footer:

<RelativeLayout
    <include
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="30dp"
        layout="@layout/footer" />
 ......
/RelativeLayout>
3. ViewStub实现view的延迟加载

场景:让用户自己决定是否显示地图信息
地图view

<com.google.android.maps.MapView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:clickable="true" 
   ......
/>

使用ViewStub

 <ViewStub
      android:id="@+id/map_stub"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      android:inflatedId="@+id/map_view" 
      android:layout="@layout/map" />

在Activity中调用

  private View mViewStub;
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    mViewStub = findViewById(R.id.map_stub);
  }
  public void onShowMap(View v) {
    mViewStub.setVisibility(View.VISIBLE);
  }
  ......

inflatedId是调用ViewStub的inflate()方法或setVisibility()方法是返回的ID,这个ID便是被填充的View的ID,避免了使用findViewById()方法

4.定制ViewGroup

绘制布局由两个遍历过程组成:测量过程和布局过程。测量过程由measure(int, int)方法完成,该方法从上到下遍历视图树。在递归遍历过程中,每个视图都会向下层传递尺寸和规格。当measure方法遍历结束,每个视图都保存了各自的尺寸信息。第二个过程由layout(int,int,int,int)方法完成,该方法也是由上而下遍历视图树,在遍历过程中,每个父视图通过测量过程的结果定位所有子视图的位置信息。
为了理解这个概念,下面分析ViewGroup的绘制过程,第一步是测量ViewGroup的宽度和高度,在onMeasure方法中完成这个步骤。在该方法中,ViewGroup通过遍历所有子视图计算它的大小。最后一步操作,在onLayout()方法中完成,利用上一步计算出的测量信息,布局所有子视图。

作者实现了如下布局:


1>在values下创建attrs.xml,自定义属性

<resources>
    <declare-styleable name="CascadeLayout">
        <attr name="horizontal_spacing" format="dimension" />
        <attr name="vertical_spacing" format="dimension" />
    </declare-styleable>

    <declare-styleable name="CascadeLayout_LayoutParams">
        <attr name="layout_vertical_spacing" format="dimension" />//属性名前缀是layout_,因此该属性会被添加到LayoutParams的属性表中
    </declare-styleable>
</resources>

2>布局

<FrameLayout
  xmlns:anyname="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <com.XX.view.CascadeLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        anyname:horizontal_spacing="30dp"
       anyname:vertical_spacing="20dp" >

        <View
            android:layout_width="100dp"
            android:layout_height="150dp"
           anyname:layout_vertical_spacing="90dp"
            android:background="#FF0000" />

        <View
            android:layout_width="100dp"
            android:layout_height="150dp"
            android:background="#00FF00" />

        <View
            android:layout_width="100dp"
            android:layout_height="150dp"
            android:background="#0000FF" />
    </com.XX.view.CascadeLayout>

</FrameLayout>

3>自定义CascadeLayout

public class CascadeLayout extends ViewGroup {

  private int mHorizontalSpacing;
  private int mVerticalSpacing;

  public CascadeLayout(Context context, AttributeSet attrs) {
    super(context, attrs);

    TypedArray a = context.obtainStyledAttributes(attrs,
        R.styleable.CascadeLayout);

    try {
      mHorizontalSpacing = a.getDimensionPixelSize(
          R.styleable.CascadeLayout_horizontal_spacing,
          getResources().getDimensionPixelSize(
              R.dimen.cascade_horizontal_spacing));

      mVerticalSpacing = a.getDimensionPixelSize(
          R.styleable.CascadeLayout_vertical_spacing, getResources()
              .getDimensionPixelSize(R.dimen.cascade_vertical_spacing));
    } finally {
      a.recycle();
    }

  }

  @Override
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 使用宽和高计算布局的最终大小以及子视图的x和y轴位置
    int width = getPaddingLeft();
    int height = getPaddingTop();
    int verticalSpacing;

    final int count = getChildCount();
    for (int i = 0; i < count; i++) {
      verticalSpacing = mVerticalSpacing;

      View child = getChildAt(i);
      measureChild(child, widthMeasureSpec, heightMeasureSpec); //令每个子视图测量自身

      LayoutParams lp = (LayoutParams) child.getLayoutParams();
      width = getPaddingLeft() + mHorizontalSpacing * i;

// 在LayoutParams中保存每个子视图的x、y坐标
      lp.x = width; 
      lp.y = height;

      if (lp.verticalSpacing >= 0) {
        verticalSpacing = lp.verticalSpacing;
      }

      width += child.getMeasuredWidth();
      height += verticalSpacing;
    }

    width += getPaddingRight();
    height += getChildAt(getChildCount() - 1).getMeasuredHeight()
        + getPaddingBottom();

    setMeasuredDimension(resolveSize(width, widthMeasureSpec),
        resolveSize(height, heightMeasureSpec)); // 用计算所得宽高设置整个布局的测量尺寸
  }

  @Override
  protected void onLayout(boolean changed, int l, int t, int r, int b) {

    final int count = getChildCount();
    for (int i = 0; i < count; i++) {
      View child = getChildAt(i);
      LayoutParams lp = (LayoutParams) child.getLayoutParams();

      child.layout(lp.x, lp.y, lp.x + child.getMeasuredWidth(), lp.y
          + child.getMeasuredHeight());
    }
  }

  @Override
  protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
    return p instanceof LayoutParams;
  }

  @Override
  protected LayoutParams generateDefaultLayoutParams() {
    return new LayoutParams(LayoutParams.WRAP_CONTENT,
        LayoutParams.WRAP_CONTENT);
  }

  @Override
  public LayoutParams generateLayoutParams(AttributeSet attrs) {
    return new LayoutParams(getContext(), attrs);
  }

  @Override
  protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
    return new LayoutParams(p.width, p.height);
  }

  public static class LayoutParams extends ViewGroup.LayoutParams {
    int x;
    int y;
    public int verticalSpacing;

    public LayoutParams(Context context, AttributeSet attrs) {
      super(context, attrs);

      TypedArray a = context.obtainStyledAttributes(attrs,
          R.styleable.CascadeLayout_LayoutParams);
      try {
        verticalSpacing = a
            .getDimensionPixelSize(
                R.styleable.CascadeLayout_LayoutParams_layout_vertical_spacing,
                -1);
      } finally {
        a.recycle();
      }
    }

    public LayoutParams(int w, int h) {
      super(w, h);
    }

  }
}
5. TextSwitcher和ImageSwitcher实现平滑过渡

TextSwitcher: 为文本标签添加动画效果
ImageSwitcher : 为图片切换添加动画效果

    mTextSwitcher.setFactory(new ViewFactory() {
      @Override
      public View makeView() {
        TextView t = new TextView(MainActivity.this);
        t.setGravity(Gravity.CENTER);
        return t;
      }
    });
    mTextSwitcher.setInAnimation(this, android.R.anim.fade_in);
    mTextSwitcher.setOutAnimation(this, android.R.anim.fade_out);
     mTextSwitcher.setText("hello");
6. 避免在Edittext中验证日期

在应用程序中使用Android内置资源是一个借用设备样式的好方法,如

  <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:background="@android:drawable/edit_text"
        />
7. 格式化TextView的文本,如链接、不同样式文本等

链接:

textView1.setText(Html.fromHtml("<![CDATA[Visit <a href=\"http://manning.com/\">Manning home page</a>]]>"));
textView1.setMovementMethod(LinkMovementMethod.getInstance());

不同样式


    final Spannable text2 = new SpannableString(
        getString(R.string.text2));
    text2.setSpan(new BackgroundColorSpan(Color.RED), 1, 4, 0);
    text2.setSpan(new ForegroundColorSpan(Color.BLUE), 5, 9, 0);
XX.setText(text2);
8. 横竖屏

设置android:configChanges="orientation"就不会重启Activity,而是调用onConfigurationChanged()方法

9. 移除窗口默认背景提高应用程序启动速度
<resources>
    <style name="Theme.NoBackground" parent="android:Theme">
        <item name="android:windowBackground">@null</item>
        <item name="android:windowNoTitle">true</item>
    </style>
</resources>
9.更改toast显示位置
 Toast toast = Toast.makeText(this, "Upper Left!",
        Toast.LENGTH_SHORT);
    toast.setGravity(Gravity.TOP | Gravity.LEFT, 0, 0);
    toast.show();
上一篇下一篇

猜你喜欢

热点阅读