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<CrimeHolder> {
    ......
    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);
    }
    ......
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,295评论 6 512
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,928评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,682评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,209评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,237评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,965评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,586评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,487评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,016评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,136评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,271评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,948评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,619评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,139评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,252评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,598评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,267评论 2 358

推荐阅读更多精彩内容