Android学习总结

  1. activity是Android SDK中Activity的一个实例,负责控制各组件与用户的交互

  2. 布局定义了一系列组件,包括Button、TextView、 RecyclerView。布局和组件之间的关系可用下图表示:


    布局和组件的关系

常用组件和布局的继承关系如下图:


常用组件和布局的继承关系
  1. 项目的app/res/values目录下保存了一系列的资源,包括字符串资源,图片资源等,包括布局也是资源的一种。它们都通过资源ID被引用。如果控件也需要被引用,则在xml文件中定义它们时,可以加上android:id属性,为其设置ID。
  2. xml布局是如何转换为视图对象的呢?activity子类被创建后,onCreate(Bundle)方法会被调用,它再调用
public void setContentView(int layoutRestId)

根据传入的布局资源ID,使用LayoutInflater类实例化该布局中定义的每一个View。activity子类都需要声明在AndroidManifest.xml配置文件中,在该文件中会设置一个launcher activity,app启动时会创建该activity。

  1. MVC设计模式。MVC即模型,视图,控制,activity或者fragment(service还未学习)可以作为控制器,它们将视图即View实例对象显示在屏幕,将模型(实例类)中的数据显示更新在屏幕上。GeoQuiz项目的MVC示意图如下:


    MVC模式示意图
  1. activity的生命周期。
    Activity生命周期

在activity的各个阶段,Android系统会自动调用相应的方法。重载这些方法,添加log,切换手机横竖屏,可以通过logcat看到activity的生命周期中相应方法的调用。

  1. 保存数据以应对设备旋转。
protected void onSaveInstanceState(Bundle outstate)

该方法通常在onStop()方法前被调用,除非用户按了后退键。该方法的默认实现把所有activity视图状态保存在Bundle中,Bundle是一种存储键-值对的一种结构。可以通过覆盖onSaveInstanceState()方法,将一些信息保存在Bundle中:

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    super.onSaveInstanceState(savedInstanceState);
    Log.i(TAG, "onSaveInstanceState");
    savedInstanceState.putInt(KEY_INDEX, mCurrentIndex);
    savedInstanceState.putBoolean(KEY_IS_CHEATER, mIsCheater);
}

在onCreate()方法中获取信息:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.d(TAG, "onCreate(Bundle) called");
    setContentView(R.layout.activity_quiz);

    if (savedInstanceState != null) {
        mCurrentIndex = savedInstanceState.getInt(KEY_INDEX);
        mIsCheater = savedInstanceState.getBoolean(KEY_IS_CHEATER);
    }
    ...
}

还可以存BooleanArray。操作系统会将Bundle对象放入activity记录中,可以理解为暂存。覆盖onSaveInstanceState()保存当前activity的小的或状态的数据;覆盖onStop()方法,保存永久性数据,如用户编辑的文字等。(?如何理解?)

  1. tools命名空间。使用该命名空间,可以覆盖某个组件的任意属性,改属性仅在预览中生效。
  2. 启动activity并互相传递数据。一个activity启动另一个activity最简单的方式是用startActivity方法:
public void startActivity(Intent intent)

activity调用该方法后,调用请求发给了操作系统的ActivityManager。ActivityManager启动哪个activity呢?答案在传入的参数intent。intent有很多构造方法,其中一种为:

public Intent(Context packageContext, Class cls)

传入的Class类型参数告诉ActivityManager要启动的activity,Context类型参数告诉ActivityManager在哪里可以找到它(?不是在manifest配置文件中声明了吗?)。可以在startActivity(Intent intent)的intent上附加上extra信息,传递给启动的activity。如同在Bundle中保存信息一样,extra信息也是一种键值对,在子activity中提供一个借口,父activity把信息通过借口传过来即可,无需关心“键”是什么样的:

//子activity
public static Intent newIntent(Context packageContext, boolean answerIsTrue) {
     Intent intent = new Intent(packageContext, CheatActivity.class);
     intent.putExtra(EXTRA_ANSWER_IS_TRUE, answerIsTrue);
     return intent;
}
//父activity
mCheatButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        //start cheatactivity
        boolean answerIsTrue = mQuestionBank[mCurrentIndex].isAnswerTrue();
        Intent intent = CheatActivity.newIntent(QuizActivity.this, answerIsTrue);
        startActivity(intent);
    }
});

子activity在onCreate方法中就可以获取该extra信息:

mAnswerIsTrue = getIntent().getBooleanExtra(EXTRA_ANSWER_IS_TRUE, false);

可以用intent传递更多的信息,使用多参数版本的intent构造函数即可(还未实践)。

以上是父activity传递信息给子activity,要获取子activity的信息,可以修改startActivity方法:

startActivityForResult(intent, REQUEST_CODE_CHEAT);

子activity可以调用:

public final void setResult(int resultCode)
public final void setResult(int resultCode, Intent intent)

如果子activity没有调用setResult,而父activity又是调用startActivityForResult启动子activity的,父activity会收到Activity.RESULT_CANCELLED。在项目实践中,子activity的Button监听回调被调用后,它设置了返回结果:

private void setAnswerShownResult(boolean isAnswerShown) {
    Intent intent = new Intent();
    intent.putExtra(EXTRA_ANSWER_SHOWN, isAnswerShown);
    setResult(RESULT_OK, intent);
}

public static boolean wasAnswerShown(Intent result) {
    return result.getBooleanExtra(EXTRA_ANSWER_SHOWN, false);
}

ActivityManager在子activity被销毁后调用父类的onActivityResult()方法,故父activity要重载该方法:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (resultCode != Activity.RESULT_OK) {
        return;
    }

    if (requestCode == REQUEST_CODE_CHEAT) {
        if (data == null) {
            return;
        }
        mIsCheater = CheatActivity.wasAnswerShown(data);
    }
}

这里,解析信息放在了子activity定义的一个接口中,因为“键”存在子activity中,最终的还是通过intent来解析到的。

  1. Fragment。fragment与activity很类似,它也是负责创建并管理用户界面,与模型对象进行交互。fragment的生命周期与activity类似,但是fragment的生命周期方法由它的托管activity而不是操作系统调用。fragment并没有像activity一样在onCreate方法中完成布局和组件view的实例化,而是在onCreateView方法中完成的,如下:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.fragment_crime, container, false);

    mTitleField = v.findViewById(R.id.crime_title);
    ...

    mDateButton = v.findViewById(R.id.crime_date);
    ...

    mSolvedCheckBox = v.findViewById(R.id.crime_solved);
    ...

    return v;
}

第二个参数是视图的父视图。以上fragment类中完成了主要的工作,但它的视图显然还无法显示出来,它需要托管给activity。Activity类中添加了FragmentManager类,它负责管理fragment并将它们的视图添加到activity的视图层级结构中。具体做法:

public class CrimeActivity extends SingleFragmentActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment);
    
        FragmentManager fm = getSupportFragmentManager();
        Fragment fragment = fm.findFragmentById(R.id.fragment_container);
    
        if (fragment == null) {
            fragment = new createFragment();
            fm.beginTransaction()
                    .add(R.id.fragment_container, fragment)
                    .commit();
        }
    }
}

fragment事务的add接口,第一个参数为容器视图资源ID,第二个参数是要加入的Fragment。二者视图关系可用下图表示,fragment视图要放置在activity的视图层级结构中:


CrimeActivity托管着CrimeFragment
  1. RecyclerView。RecyclerView是ViewGroup的子类,每一个列表项是作为View子对象来显示的。它们的显示依赖于两个类:ViewHolder和Adapter。在控制器中重载这两个类。实践项目中RecyclerView作为fragment布局的一个组件,在onCreateView方法中要先调用:
mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

接下来,控制器把要显示的模型对象(一般是ArrayList对象?)作为参数构造了一个Adapter,然后调用:

mCrimeRecyclerView.setAdapter(mAdapter);

接着,Adapter调用getItemCount()方法获取要显示的列表项的个数,根据该个数,循环调用onCreateViewHolder( ViewGroup viewGroup, int i)和onBindViewHolder(CrimeHolder crimeHolder, int i)。ViewHolder的任务是什么呢?它首先会把列表项布局实例化,然后再通过各组件的ID,找到它们的引用。在onBindViewHolder,Adapter会把要显示的模型参数传递给ViewHolder,它通过组件显示在屏幕上。项目实践中相关代码示例:

private class CrimeHolder extends RecyclerView.ViewHolder
            implements View.OnClickListener {
    ......
    public CrimeHolder(LayoutInflater inflater, ViewGroup parent) {
        super(inflater.inflate(R.layout.list_item_crime, parent, false));
    
        itemView.setOnClickListener(this);
        mTitleTextView = itemView.findViewById(R.id.crime_title);
        mDateTextView = itemView.findViewById(R.id.crime_date);
        mSolvedImageView = itemView.findViewById(R.id.crime_solved);
    }
    public void bind(Crime crime) {
        mCrime = crime;
        mTitleTextView.setText(mCrime.getTitle());
        mDateTextView.setText(mCrime.getDate().toString());
        mSolvedImageView.setVisibility(mCrime.isSolved() ? View.VISIBLE : View.GONE);
    }
    ......
}


@Override
private class CrimeAdapter extends RecyclerView.Adapter {
    ......
    public CrimeHolder onCreateViewHolder( ViewGroup viewGroup, int i) {
        LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
    
        return new CrimeHolder(layoutInflater, viewGroup);
    }
    
    @Override
    public void onBindViewHolder(CrimeHolder crimeHolder, int i) {
        Crime crime = mCrimes.get(i);
        crimeHolder.bind(crime);
    }
    ......
}

你可能感兴趣的:(Android学习总结)