从 setContentView 开始说起(一)
2017-07-18 10:03
337 查看
众所周知,Android 应用程序中的界面是通过 res/main/ 文件夹下的布局文件创建的,之后再由 setContentView(xxx_layout.xml) 方法将布局文件添加到 Activity 中,从而被显示出来。
我相信很多人都和之前的我一样,在布局文件中创建好 Activity 对应的布局文件之后,就去处理 Activity 中的代码逻辑了。
最近在分析 Activity 启动流程的时候,发现我根本没懂这个函数,或者说忽视这个函数的强大作用。有时候,事情就是这样,因为太平常,所以往往被忽略。今后要避免再犯同样的错误,切记,切记。
Set the activity content from a layout resource. The resource will be inflated, adding all top-level views to the activity
上面那句话的大概意思是:将 layoutResID 对应的资源文件填充到 Activity 上,从而被显示。
大家都知道,Java 中是有一定的命名规范的,就像类名首字母必须大写,方法名首字母小写,之后的单词首字母均大写等等。最主要的是在命名的时候尽量要见名知意,这些简单的道理,Google 的开发人员必然也知道,所以从过往的经验和上面方法的命名中可以大胆地做个假设:
这个方法和 TextView 的 setText 方法类似
接下来,我们就用实例分析假设是否成立。
其实和一般的项目没什么区别。细心的人一定发现了,我在 layout 文件夹下创建了两个布局文件,而且从命名上就可以看出来二者都属于 MainActivity,只是一个是默认显示的,一个是做了相应的操作之后才显示的。接下来我们就先看两个布局文件吧:
下面对 MainActivity 进行解析:
onCreate
在此方法中,我们并没有直接将布局文件的 ID 设置到 setContentView 方法中,而是先通过 LayoutInflater 将布局文件转换成 View,这样做的目的有两点:
第一,在布局切换的时候,如果直接将布局文件资源 ID 设置到 setContentView 方法中,那切换之后再切换回来,之前对布局做的更改将会消失。但是如果先通过 LayoutInflater 将布局文件转换成 View,再设置到 setContentView 方法中便可以避免此问题。
第二,为了在布局切换的时候,给 View 添加动画,使布局切换更优雅。
在此方法中,
第 4、5 行的主要的目的是将布局文件转换成 View;
第 6 行的主要目的是给转换之后的 View 添加动画;
第 7 行是将转换之后的 View 设置到窗体上,从而显示出来。
showNextView
此方法和 onCreate 方法基本一样,只是此方法为第一个布局文件中的 NEXT 按钮添加的点击事件。
showPreviousView
同上,只是此方法为第二个布局文件中的 PREVIOUS 按钮添加的点击事件。
execViewAnim
为了方法复用和代码美观,所以将动画处理专门提了出来。代码逻辑其实挺简单的,给不同的 View 设置不同的显示、隐藏动画。
onContentChanged
This hook is called whenever the content view of the screen changes (due to a call to Window.setContentView or Window.addContentView).
上面这句话的大概意思是:当通过调用 setContentView 或者 addContentView 导致布局文件变化时,此方法便会被调用。
其实通过上面的解释,我们便可以知道,通常我们的 initView 方法便可以在此处调用,因为它和在 onCreate 中的 setContentView 之后调用是一样的。
此处,我们只是简单的测试,将布局文件中 EditText 的内容进行输出。
下面便是运行的效果图:
到此,我们便可以知道,文章开始处的假设是成立的。
我相信很多人都和之前的我一样,在布局文件中创建好 Activity 对应的布局文件之后,就去处理 Activity 中的代码逻辑了。
最近在分析 Activity 启动流程的时候,发现我根本没懂这个函数,或者说忽视这个函数的强大作用。有时候,事情就是这样,因为太平常,所以往往被忽略。今后要避免再犯同样的错误,切记,切记。
一、概念分析
void setContentView (int layoutResID)Set the activity content from a layout resource. The resource will be inflated, adding all top-level views to the activity
上面那句话的大概意思是:将 layoutResID 对应的资源文件填充到 Activity 上,从而被显示。
大家都知道,Java 中是有一定的命名规范的,就像类名首字母必须大写,方法名首字母小写,之后的单词首字母均大写等等。最主要的是在命名的时候尽量要见名知意,这些简单的道理,Google 的开发人员必然也知道,所以从过往的经验和上面方法的命名中可以大胆地做个假设:
这个方法和 TextView 的 setText 方法类似
接下来,我们就用实例分析假设是否成立。
二、案例分析
项目目录结构:
其实和一般的项目没什么区别。细心的人一定发现了,我在 layout 文件夹下创建了两个布局文件,而且从命名上就可以看出来二者都属于 MainActivity,只是一个是默认显示的,一个是做了相应的操作之后才显示的。接下来我们就先看两个布局文件吧:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.smart.simpleanalyse.MainActivity"> <Button android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/next_view" android:onClick="showNextView" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <EditText android:id="@+id/editText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:layout_marginTop="8dp" android:ems="10" android:inputType="textPersonName" android:text="@string/say_something_first" app:layout_constraintBottom_toTopOf="@+id/textView" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </android.support.constraint.ConstraintLayout>
activity_main_another_view.xml
<?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" android:padding="@dimen/medium_padding"> <EditText android:id="@+id/editText" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="8dp" android:layout_marginTop="8dp" android:ems="10" android:inputType="textPersonName" android:text="@string/say_something_second" /> <Button android:id="@+id/textView" android:onClick="showPreviousView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/previous_view" /> </LinearLayout>
strings.xml
<resources> <string name="app_name">simpleanalyse</string> <string name="say_something_first">天王盖地虎</string> <string name="say_something_second">宝塔镇河妖</string> <string name="next_view">NEXT</string> <string name="previous_view">PREVIOUS</string> </resources>
MainActivity.java
package com.smart.simpleanalyse; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.view.LayoutInflater; import android.view.View; import android.view.animation.AlphaAnimation; import android.widget.EditText; import android.widget.Toast; public class MainActivity extends AppCompatActivity { private LayoutInflater mLayoutInflater; private View mFirstView,mSecondView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mLayoutInflater = getLayoutInflater(); mFirstView = mLayoutInflater.inflate(R.layout.activity_main,null); execViewAnim(null,mFirstView); setContentView(mFirstView); // execViewAnim(null,mFirstView); } public void showNextView(View view){ if(mSecondView == null ){ mSecondView = mLayoutInflater.inflate(R.layout.activity_main_another_view,null); } execViewAnim(mFirstView,mSecondView); setContentView(mSecondView); } public void showPreviousView(View view){ if(mFirstView == null ){ mFirstView = mLayoutInflater.inflate(R.layout.activity_main,null); } execViewAnim(mSecondView,mFirstView); setContentView(mFirstView); } private void execViewAnim(@Nullable View first, View second){ // first if(first != null){ AlphaAnimation fadeOut = new AlphaAnimation(1.0f,0.0f); fadeOut.setDuration(500); fadeOut.setFillAfter(true); first.startAnimation(fadeOut); } // second AlphaAnimation fadeIn = new AlphaAnimation(0.0f,1.0f); fadeIn.setDuration(1500); fadeIn.setFillAfter(true); second.startAnimation(fadeIn); } @Override public void onContentChanged() { super.onContentChanged(); Toast.makeText(this, ((EditText)findViewById(R.id.editText)).getText().toString(), Toast.LENGTH_SHORT).show(); } }
下面对 MainActivity 进行解析:
onCreate
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mLayoutInflater = getLayoutInflater(); mFirstView = mLayoutInflater.inflate(R.layout.activity_main,null); execViewAnim(null,mFirstView); setContentView(mFirstView); // execViewAnim(null,mFirstView); }
在此方法中,我们并没有直接将布局文件的 ID 设置到 setContentView 方法中,而是先通过 LayoutInflater 将布局文件转换成 View,这样做的目的有两点:
第一,在布局切换的时候,如果直接将布局文件资源 ID 设置到 setContentView 方法中,那切换之后再切换回来,之前对布局做的更改将会消失。但是如果先通过 LayoutInflater 将布局文件转换成 View,再设置到 setContentView 方法中便可以避免此问题。
第二,为了在布局切换的时候,给 View 添加动画,使布局切换更优雅。
在此方法中,
第 4、5 行的主要的目的是将布局文件转换成 View;
第 6 行的主要目的是给转换之后的 View 添加动画;
第 7 行是将转换之后的 View 设置到窗体上,从而显示出来。
showNextView
public void showNextView(View view){ if(mSecondView == null ){ mSecondView = mLayoutInflater.inflate(R.layout.activity_main_another_view,null); } execViewAnim(mFirstView,mSecondView); setContentView(mSecondView); }
此方法和 onCreate 方法基本一样,只是此方法为第一个布局文件中的 NEXT 按钮添加的点击事件。
showPreviousView
public void showPreviousView(View view){ if(mFirstView == null ){ mFirstView = mLayoutInflater.inflate(R.layout.activity_main,null); } execViewAnim(mSecondView,mFirstView); setContentView(mFirstView); }
同上,只是此方法为第二个布局文件中的 PREVIOUS 按钮添加的点击事件。
execViewAnim
private void execViewAnim(@Nullable View first, View second){ // first if(first != null){ AlphaAnimation fadeOut = new AlphaAnimation(1.0f,0.0f); fadeOut.setDuration(500); fadeOut.setFillAfter(true); first.startAnimation(fadeOut); } // second AlphaAnimation fadeIn = new AlphaAnimation(0.0f,1.0f); fadeIn.setDuration(1500); fadeIn.setFillAfter(true); second.startAnimation(fadeIn); }
为了方法复用和代码美观,所以将动画处理专门提了出来。代码逻辑其实挺简单的,给不同的 View 设置不同的显示、隐藏动画。
onContentChanged
This hook is called whenever the content view of the screen changes (due to a call to Window.setContentView or Window.addContentView).
上面这句话的大概意思是:当通过调用 setContentView 或者 addContentView 导致布局文件变化时,此方法便会被调用。
@Override public void onContentChanged() { super.onContentChanged(); Toast.makeText(this, ((EditText)findViewById(R.id.editText)).getText().toString(), Toast.LENGTH_SHORT).show(); }
其实通过上面的解释,我们便可以知道,通常我们的 initView 方法便可以在此处调用,因为它和在 onCreate 中的 setContentView 之后调用是一样的。
此处,我们只是简单的测试,将布局文件中 EditText 的内容进行输出。
下面便是运行的效果图:
到此,我们便可以知道,文章开始处的假设是成立的。
三、应用场景
至于这个方法的应用还是交给各位看官自己想吧,我说了,就把各位的思维限制了。相关文章推荐
- 从setContentView开始的源码阅读
- Android加载layout, 从setContentView开始
- setContentView(R.layout.XXXXX) 没设置到 就开始 initViews() 会导致空指针
- android应用程序窗口框架学习(4)-从setcontentview说起
- 从setContentView说起
- 从setContentView开始,了解view的加载过程
- Android---setContentView(R.layout.activity_other);ERROR 解决
- 2014-10-31Android学习------setContentView(View view)--------GIF动画实现
- activity的setContentView的另一种写法
- Android(11)——从源码的角度解析setContentView
- Android实现欢迎页:在onCreate方法中两次调用setContentView
- setContentView和inflate区别
- Android setContentView 实现同一个activity下不同view的切换
- 搜索关键字高亮。content是全部的String keyWord是关键字。TextView是后面要SET改变的一个TextView
- setContentViewsetContentView----R.java----String.xml
- android LayoutInflater、setContentView、findviewbyid 区分解析
- setContentView+LayoutInflater=完美切换页面(两者一定要同时使用,setContentView提高切换页面速度必看)setContentView的秘密----续上
- Android Activity的UI绘制流程之setContentView方法详解
- 【转】Android之Inflate()方法用途+setContentView和inflate区别
- setContentView(R.layout.activity_main) Error解决方法