Android XML绘制图形(虚线、三角形、圆、圆角矩形等)

1、虚线

res/drawable/shape_test.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="line" >
    <stroke
        android:dashGap="3dp"
        android:dashWidth="6dp"
        android:width="1dp"
        android:color="@android:color/black" />
    <!-- 虚线的高度 -->
    <size android:height="1dp" />
</shape>

注意:如果在android 6.0上没有效果可以在代码中写

line.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

或者在布局文件中加入

android:layerType="software"

效果:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <View
        android:layout_width="match_parent"
        android:layout_height="5dp"
        android:layout_gravity="center_vertical"
        android:background="@drawable/shape_test" />

</FrameLayout>

2、三角形

res/drawable/shape_test.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <rotate
            android:fromDegrees="45"
            android:toDegrees="45"
            android:pivotX="-40%"
            android:pivotY="87%">
            <shape android:shape="rectangle">
                <solid android:color="@android:color/black" />
                <stroke
                    android:width="0.5dp"
                    android:color="@android:color/black" />
            </shape>
        </rotate>
    </item>
</layer-list>

效果:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <View
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_gravity="center"
        android:background="@drawable/shape_test" />

</FrameLayout>

3、圆形

res/drawable/shape_test.xml文件:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval"
    android:useLevel="false" >
    <solid android:color="@android:color/transparent" />
    <stroke
        android:dashGap="2dp"
        android:dashWidth="2dp"
        android:width="0.5dp"
        android:color="@android:color/black" />
</shape>

效果:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <View
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_gravity="center"
        android:background="@drawable/shape_test" />

</FrameLayout>

4、圆角矩形

res/drawable/shape_test.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >

    <!-- 实心 -->
    <solid android:color="@android:color/transparent" />
    <!-- 渐变 -->
    <gradient
        android:angle="90"
        android:endColor="@android:color/black"
        android:startColor="@android:color/white" />
    <!--   描边  -->
    <stroke
        android:width="1dp"
        android:color="@android:color/black" />
    <corners android:radius="5dp" />
    <!--可以设置边距
       <padding
        android:bottom="10dp"
        android:left="10dp"
        android:right="10dp"
        android:top="10dp" />
    -->
</shape>

效果:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <View
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_gravity="center"
        android:background="@drawable/shape_test" />

</FrameLayout>

5、按钮触发效果

res/color/color_test.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:color="@android:color/holo_blue_bright"
        android:state_focused="true"
        android:state_pressed="true"
        />
    <item
        android:color="@android:color/holo_blue_bright"
        android:state_focused="false"
        android:state_pressed="true"
        />
    <item
        android:color="@android:color/holo_blue_bright"
        android:state_focused="true"
        />
    <item
        android:color="@android:color/holo_blue_dark"
        android:state_focused="false"
        android:state_pressed="false"
        />

</selector>

效果:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="点击变色"
        android:layout_gravity="center"
        android:textColor="@color/color_test" />

</FrameLayout>

6、简单动画

<animation-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:drawable="@mipmap/loading_01"
        android:duration="200" />
    <item
        android:drawable="@mipmap/loading_02"
        android:duration="200" />
    <item
        android:drawable="@mipmap/loading_03"
        android:duration="200" />
    <item
        android:drawable="@mipmap/loading_04"
        android:duration="200" />
    <item
        android:drawable="@mipmap/loading_05"
        android:duration="200" />
    <item
        android:drawable="@mipmap/loading_06"
        android:duration="200" />
    <item
        android:drawable="@mipmap/loading_07"
        android:duration="200" />
    <item
        android:drawable="@mipmap/loading_08"
        android:duration="200" />
    <item
        android:drawable="@mipmap/loading_09"
        android:duration="200" />
    <item
        android:drawable="@mipmap/loading_10"
        android:duration="200" />
    <item
        android:drawable="@mipmap/loading_11"
        android:duration="200" />
    <item
        android:drawable="@mipmap/loading_12"
        android:duration="200" />
</animation-list>

在代码中写入

AnimationDrawable an = (AnimationDrawable) loding.getBackground();
an.start();

LinearLayout和RelativeLayout绘制过程的对比

在平时设计UI时,相信大多数人用得比较多的布局就是LinearLayout和RelativeLayout了,毕竟这两种布局能实现我们一般的需求。还记得刚开始学Android时,使用Eclipse进行开发,每次新建一个布局文件,该布局文件默认使用的布局是LinearLayout。后来改用Android Studio开发,SDK版本更新之后,每次新建的布局文件改成用RelativeLayout作为默认布局了。 很多人说这个Google团队出于性能考虑而进行的优化,的确,RelativeLayout能减少布局嵌套从而提高布局的性能,而使用Linearlayout如果不注意的话可能出现布局嵌套层次太深,从而大大降低了布局的性能。但是LinearLayout真的比不上RelativeLayout吗?答案是否定的。

View的绘制一般都是经过onMeasure,onLayout,和onDraw,LinearLayout和RelativeLayout都是继承自ViewGroup,而ViewGroup又是继承View的,所以LinearLayout和RelativeLayout一般也会经历上面所说的三个流程。但是在ViewGroup一般默认不调用onDraw,因为ViewGroup是容器,一般只用来装载控件,所以负责测量控件总共所需的宽高,并将控件放在我们指定的位置就行了。所以接下来我打算只从onMeasure和onLayout两个方面对比LinearLayout和RelativeLayout。

先看看LinearLayout的onMeasure方法:

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        if (mOrientation == VERTICAL) {
            measureVertical(widthMeasureSpec, heightMeasureSpec);
        } else {
            measureHorizontal(widthMeasureSpec, heightMeasureSpec);
        }
    }

我们都知道LinearLayout有两个方向走向,一个是竖直方向,一个是水平方向。可以看出Linearlayout在测量宽高时是根据我们设置的方向分别调用不同的测量方法,这两个测量方法大致一样,我们以measureVertical来说明LinearLayout的具体测量过程吧。因为measureVertical这个方法有点长,所以我们贴部分代码说明问题就行了。

// Determine how big this child would like to be. If this or
// previous children have given a weight, then we allow it to
// use all available space (and we will shrink things later
// if needed).
final int usedHeight = totalWeight == 0 ? mTotalLength : 0;
measureChildBeforeLayout(child, i, widthMeasureSpec, 0,
      heightMeasureSpec, usedHeight);

在measureVertical方法里面调用了这个方法,什么意思呢?我们都知道在LinearLayout中,我们可以使用layout_weight属性,当子View使用了layout_weight属性时就会调用这个方法,这个方法的大概步骤是先不限制设置了weight属性的子View的大小,只要容器剩余的空间还足够就不对该子View作处理,但凡事都有个度,所以不可能每个设置了weight属性的子View都无限大吧,所以当所有子View测量完毕后,会对设置了weight属性的子View再调用一次measure方法,如下:

// We have no limit, so make all weighted views as tall as the largest child.
// Children will have already been measured once.
if (useLargestChild && heightMode != MeasureSpec.EXACTLY) {
    for (int i = 0; i < count; i++) {
        final View child = getVirtualChildAt(i);
        if (child == null || child.getVisibility() == View.GONE) {
            continue;
        }

        final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams();

        float childExtra = lp.weight;
        if (childExtra > 0) {
            child.measure(MeasureSpec.makeMeasureSpec(child.getMeasuredWidth(), MeasureSpec.EXACTLY),
                                MeasureSpec.makeMeasureSpec(largestChildHeight, MeasureSpec.EXACTLY));
       }
   }
}

接下来说说RelativeLayout的onMeasure方法,部分源码如下:

        for (int i = 0; i < count; i++) {
            View child = views[i];
            if (child.getVisibility() != GONE) {
                LayoutParams params = (LayoutParams) child.getLayoutParams();
                int[] rules = params.getRules(layoutDirection);

                applyHorizontalSizeRules(params, myWidth, rules);
                measureChildHorizontal(child, params, myWidth, myHeight);

                if (positionChildHorizontal(child, params, myWidth, isWrapContentWidth)) {
                    offsetHorizontalAxis = true;
                }
            }
        }
        for (int i = 0; i < count; i++) {
            final View child = views[i];
            if (child.getVisibility() != GONE) {
                final LayoutParams params = (LayoutParams) child.getLayoutParams();

                applyVerticalSizeRules(params, myHeight, child.getBaseline());
                measureChild(child, params, myWidth, myHeight);
                if (positionChildVertical(child, params, myHeight, isWrapContentHeight)) {
                    offsetVerticalAxis = true;
                }

                if (isWrapContentWidth) {
                    if (isLayoutRtl()) {
                        if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
                            width = Math.max(width, myWidth - params.mLeft);
                        } else {
                            width = Math.max(width, myWidth - params.mLeft - params.leftMargin);
                        }
                    } else {
                        if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
                            width = Math.max(width, params.mRight);
                        } else {
                            width = Math.max(width, params.mRight + params.rightMargin);
                        }
                    }
                }

                if (isWrapContentHeight) {
                    if (targetSdkVersion < Build.VERSION_CODES.KITKAT) {
                        height = Math.max(height, params.mBottom);
                    } else {
                        height = Math.max(height, params.mBottom + params.bottomMargin);
                    }
                }

                if (child != ignore || verticalGravity) {
                    left = Math.min(left, params.mLeft - params.leftMargin);
                    top = Math.min(top, params.mTop - params.topMargin);
                }

                if (child != ignore || horizontalGravity) {
                    right = Math.max(right, params.mRight + params.rightMargin);
                    bottom = Math.max(bottom, params.mBottom + params.bottomMargin);
                }
            }
        }

由上面源码我们可知RelativeLayout对子View分别进行了竖直和水平方向的两次测量,这不难理解,因为RelativeLayout里面每个子View的位置都是相对的,所以要分别从水平和竖直两个方向对子View进行测量。

接下来在来看看LinearLayout和RelativeLayout的onLayout方法。同样的,先看LinearLayout的onLayout方法,如下:

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        if (mOrientation == VERTICAL) {
            layoutVertical(l, t, r, b);
        } else {
            layoutHorizontal(l, t, r, b);
        }
    }

简单分析这个方法,即先根据LinearLayout设置的方向调用不同的方法,和上面的onMeasure方法中调用的情况类似,在这两个方法做的事情也差不多,就是将子View逐个按照其大小和布局参数摆放在对应的位置。

再看看RelativeLayout中onLayout方法,如下:

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        //  The layout has actually already been performed and the positions
        //  cached.  Apply the cached values to the children.
        final int count = getChildCount();

        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            if (child.getVisibility() != GONE) {
                RelativeLayout.LayoutParams st =
                        (RelativeLayout.LayoutParams) child.getLayoutParams();
                child.layout(st.mLeft, st.mTop, st.mRight, st.mBottom);
            }
        }
    }

RelativeLayout的onLayout方法和LinearLayout的onLayout方法类似,都是遍历所有的子View,逐个逐个地将子View放在对应的位置,所以两者的onLayout方法性能相差不大。

总结:LinearLayout和RelativeLayout的性能差别主要体在onMeasure方法上,RelativeLayout始终要从竖直和水平两个方向对子View进行测量,而Linearlayout,当我们没有在子View中使用layout_weight属性时,LinearLayout只需对子View进行一次测量,反则需要对子View进行两次测量以确定最终大小,所以如果可以我们尽量少用layout_weight属性。在使用这两个布局之前,我们可以先进行衡量,如果需要实现的布局嵌套层次不深或者嵌套层次已经固定了,可以考虑用LinearLayout,相对的,如果某个布局嵌套层次很深,此时应该考虑使用RelativeLayout来减少嵌套层析从而优化布局的性能。

LayoutInflater中inflate方法的三个参数

public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)  

1.1 root不为null,attachToRoot为true

表示将resource指定的布局添加到root中,添加的过程中resource所指定的的布局的根节点的各个属性都是有效的

Activity布局:

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    xmlns:tools="http://schemas.android.com/tools"  
    android:layout_width="match_parent"  
    android:layout_height="match_parent"  
    android:orientation="vertical"  
    android:id="@+id/ll"  
    tools:context="org.sang.layoutinflater.MainActivity">  
</LinearLayout>

linearlayout.xml布局:

<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:id="@+id/ll"  
    android:layout_width="200dp"  
    android:layout_height="200dp"  
    android:background="@color/colorPrimary"  
    android:gravity="center"  
    android:orientation="vertical">  

    <Button  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content" />  
</LinearLayout>

java代码:

@Override  
protected void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.activity_main);  
    LinearLayout ll = (LinearLayout) findViewById(R.id.ll);  
    LayoutInflater inflater = LayoutInflater.from(this);  
    inflater.inflate(R.layout.linearlayout, ll,true);  
}  

这里我们都没写将inflate出来的View添加到ll中的代码,但是linearlayout布局文件就已经添加进来了。

这就是因为我第三个参数设置为了true,表示将第一个参数所指定的布局添加到第二个参数的View中。

1.2 root不为null,attachToRoot为false

如果root不为null,而attachToRoot为false的话,表示不将第一个参数所指定的View添加到root中。

那么这个时候有的小伙伴可能就有疑问了,既然不添加到root中,那我还写这么多干嘛?第二个参数直接给null不就可以了?

其实不然,这里涉及到另外一个问题:我们在开发的过程中给控件所指定的layout_width和layout_height到底是什么意思?该属性的表示一个控件在容器中的大小,就是说这个控件必须在容器中,这个属性才有意义,否则无意义。

这就意味着如果我直接将linearlayout加载进来而不给它指定一个父布局,则inflate布局的根节点的layout_width和layout_height属性将会失效(因为这个时候linearlayout将不处于任何容器中,那么它的根节点的宽高自然会失效)。

如果我想让linearlayout的根节点有效,又不想让其处于某一个容器中,那我就可以设置root不为null,而attachToRoot为false。

这样,指定root的目的也就很明确了,即root会协助linearlayout的根节点生成布局参数,只有这一个作用。

OK,还是上面的布局文件,如果我想将之添加到activity的布局中又该如何呢?

protected void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.activity_main);  
    LinearLayout ll = (LinearLayout) findViewById(R.id.ll);  
    LayoutInflater inflater = LayoutInflater.from(this);  
    View view = inflater.inflate(R.layout.linearlayout, ll, false);  
    ll.addView(view);  
}  

Fragment

静态设置

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
      android:id="@+id/left_fragment"
      android:name="com.shijiusui.LeftFragment"
      android:layout_width="0dp"
      android:layout_height="match_parent"
      android:layout_weight="1"/>

    <fragment
      android:id="@+id/right_fragment"
      android:name="com.shijiusui.RightFragment"
      android:layout_width="0dp"
      android:layout_height="match_parent"
      android:layout_weight="1"/>

</LinearLayout>

动态添加

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
      android:id="@+id/left_fragment"
      android:name="com.shijiusui.LeftFragment"
      android:layout_width="0dp"
      android:layout_height="match_parent"
      android:layout_weight="1"/>

    <FrameLayout
      android:id="@+id/right_layout"
      android:layout_width="0dp"
      android:layout_height="match_parent"
      android:layout_weight="1"/>

</LinearLayout>
private void replaceFragment(Fragment fragment){
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    transaction.replace(R.id.right_layout, fragment);
    transaction.commit();
}

动态添加Fragment步骤:

  1. 创建待添加的碎片实例
  2. 获取FragmentManager,在Activity中可以直接通过调用getSupportFragmentManager()方法得到。
  3. 开启一个事务,通过调用beginTransaction()方法开启。
  4. 向容器内添加或替换Fragment,一般使用replace()方法实现,需要传入容器的id和待添加的Fragment实例。
  5. 提交事务,调用commit()方法来完成。

返回栈

private void replaceFragment(Fragment fragment){
    FragmentManager fragmentManager = getSupportFragmentManager();
    FragmentTransaction transaction = fragmentManager.beginTransaction();
    transaction.replace(R.id.right_layout, fragment);
    transaction.addToBackStack(null);//----------->
    transaction.commit();
}

这里我们在事务提交之前调用了FragmentTransaction的addToBackStack()方法,它可以接收一个名字用于描述返回栈的状态,一般传入null即可。


Fragment和Activity之间进行通信

为了方便Fragment和Activity之间通信,FragmentManager提供了一个类似于findViewById()的方法,专门用于从布局文件中获取碎片的实例。

RightFragment rightFragment = (RightFragment) getFragmentManager()
    .findFragmentById(R.id.right_fragment);

在Fragment中可以通过调用getActivity()方法来得到和当前Fragment相关联的Activity实例。

MainActivity activity = (MainActivity)getActivity();

生命周期

onAttach(Context)

onCreate(Bundle)
onCreateView(LayoutInflater, ViewGroup, Bundle)<——-返回栈
onActivityCreated(Bundle)

onStart()
onResume()

onPause()
onStop()

onDestroyView()—–>返回栈
onDestroy()

onDetach()

RightFragment第一次显示时,依次执行
onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart()->onResume()

当被其他替换时,进入停止状态,执行的是
onPause()->onStop()->onDestroyView()
如果在替换的时候没有调用addToBackStack()方法,此时会进入销毁状态,会接着执行
—->onDestroy()->onDetach()

当在替换的时候调用了addToBackStack()方法,按返回键会回退到RightFragment,由于RightFragment重新回到了运行状态,因此会执行
onActivityCreated()->onStart()->onResume()

注意,此时onCreate()和onCreateView()方法并不会执行,因为我们借助了addToBackStack()方法使得RightFragment和它的视图并没有销毁。

还有一个方法onSaveInstanceState()方法可以保存数据。在onCreate()、onCreateView()和onActivityCreated()中可以获取保存的数据。

RecyclerView

compile 'com.android.support:recyclerview-v7:24.2.1'
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>
class XXXViewHolder extends RecyclerView.ViewHolder{
    public XXXViewHolder(View view){
        super(view);
    }
}
public class XXXAdapter extends RecyclerView.Adapter<XXXViewHolder>{

    @Override
    public XXXViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
        View view = LayoutInflater.from(parent.getContext()).inflate(..., false);
        XXXViewHolder holder = new XXXViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(XXXViewHolder holder, int position){

    }

    @Override
    public int getItemCount(){
        return ...;
    }
}
RecyclerView recyclerView = findViewById(...);
LinearLayoutManager layoutManager = new LinearLayoutManager(context);
recyclerView.setLayoutManager(layoutManager);

XXXAdapter adapter = new XXXAdapter(...);

recycler.setAdapter(adapter);

横向滚动效果

RecyclerView recyclerView = findViewById(...);
LinearLayoutManager layoutManager = new LinearLayoutManager(context);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);//------------>
recyclerView.setLayoutManager(layoutManager);

XXXAdapter adapter = new XXXAdapter(...);

recycler.setAdapter(adapter);

RecyclerView还提供了GridLayoutManager和StaggeredGridLayoutManager这两种内置的布局排列方法。

GridLayoutManager可以用于实现网格布局。
StaggeredGridLayoutManager可以用于实现瀑布流布局。

RecyclerView recyclerView = findViewById(...);
StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);

XXXAdapter adapter = new XXXAdapter(...);

recycler.setAdapter(adapter);

RecyclerView的点击事件

不同于ListView的是,RecyclerView并没有提供类似于setOnItemClickListener()这样的注册监听器方法,而是需要我们自己给子项具体的View去注册点击事件。

隐藏ActionBar

public class MainActivity extends AppCompatActivity{
  @Override
  protected void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ActionBar actionBar = getSupportActionBar();
    if(actionBar != null){
      actionBar.hide();
    }
  }
}

百分比布局

由于LinearLayout本身已经支持按比例指定控件的大小了,因此百分比布局只为FrameLayout和RelativeLayout进行功能扩展,提供了PercentFrameLayout和PercentRelativeLayout这两个全新的布局。

compile 'com.android.support:percent:24.2.1'
<android.support.percent.PercentFrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button1"
        android:text="Button1"
        android:layout_gravity="left|top"
        app:layout_widthPercent="50%"
        app:layout_heightPercent="50%"
        />

    <Button
        android:id="@+id/button2"
        android:text="Button2"
        android:layout_gravity="right|top"
        app:layout_widthPercent="50%"
        app:layout_heightPercent="50%"
        />

    <Button
        android:id="@+id/button3"
        android:text="Button3"
        android:layout_gravity="left|bottom"
        app:layout_widthPercent="50%"
        app:layout_heightPercent="50%"
        />

    <Button
        android:id="@+id/button4"
        android:text="Button4"
        android:layout_gravity="right|bottom"
        app:layout_widthPercent="50%"
        app:layout_heightPercent="50%"
        />

</android.support.percent.PercentFrameLayout>

ProgressBar

<ProgressBar
    android:id="@+id/progress_bar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    />

默认是一个圆形旋转进度条。


<ProgressBar
    android:id="@+id/progress_bar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    style="?android:attr/progressBarStyleHorizontal"
    android:max="100"
    />

水平进度条

textAllCaps属性

<Button
    android:id="@+id/button"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:text="Button" />

我们在布局文件里面设置的文字是“Button”,但最终的显示结果却是“BUTTON”。这是由于系统会对Button中的所有英文字母自动进行大写转换,如果你不想要的效果,可以使用如下配置来禁用这一默认特性:

<Button
    android:id="@+id/button"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:textAllCaps="false"
    android:text="Button" />