MainActivity详解
一、注解库——ButterKnife的使用
用 @bind 给字段注释并且ButterKnife会根据给定的View ID去查找并自动转换为与你的Layout中相匹配的View。
class ExampleActivity extends Activity {
@Bind(R.id.title) TextView title;
@Bind(R.id.subtitle) TextView subtitle;
@Bind(R.id.footer) TextView footer;
@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_activity);
ButterKnife.bind(this);
// TODO Use fields...
}
}
上面的例子中的通用代码生成的代码大概是下面的样子:
public void bind(ExampleActivity activity) {
activity.subtitle = (android.widget.TextView) activity.findViewById(2130968578);
activity.footer = (android.widget.TextView) activity.findViewById(2130968579);
activity.title = (android.widget.TextView) activity.findViewById(2130968577);
}
ButterKnife的应用场景:
你可以在任何地方调用ButterKnife.bind而不是调用findViewById.
绑定自定义对象,使用一个Activity作为view根节点;
使用ButterKnife.bind(this)绑定一个view的子节点字段。
二、DrawerLayout实现侧滑菜单(抽屉式导航栏)
2、1创建抽屉式导航栏布局
抽屉式导航栏是隐藏在屏幕左边缘的一部分界面,它包含了APP的主要导航选项。大多数情况下是隐藏的,但是如果用户从屏幕左边缘滑动手指,同时在应用顶层触摸操作栏中的应用图标(当用户点击了在Action Bar中的app icon),它将会显示出来。
为了添加导航抽屉,你需要在你的布局界面中声明一个DrawerLayout对象作为布局的根节点。同时在DrawerLayout内部添加两个view:
1、添加一个包含屏幕主内容(当抽屉式导航栏处于隐藏状态时为主要布局)的视图
2、添加一个包含抽屉式导航栏内容的视图
例如,以下布局使用包含两个子视图的 DrawerLayout:包含主内容的 FrameLayout(在运行时由 Fragment 填充)和抽屉式导航栏的 ListView
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- The main content view -->
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- The navigation drawer -->
<ListView android:id="@+id/left_drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:choiceMode="singleChoice"
android:divider="@android:color/transparent"
android:dividerHeight="0dp"
android:background="#111"/>
</android.support.v4.widget.DrawerLayout>
此布局演示了一些重要的布局特性:
1)在 DrawerLayout 中,主内容视图(上面的 FrameLayout)必须是第一个子视图,因为 XML 顺序意味着按 z 序(层叠顺序)排序,并且抽屉式导航栏必须位于内容顶部。
2)主内容视图设置为匹配父视图的宽度和高度, 因为在抽屉式导航栏处于隐藏状态时, 它代表整个 UI。
3)抽屉式导航栏视图 (ListView) 必须使用 android:layout_gravity 属性指定其水平重力。要支持“从右到左”(RTL) 语言,请使用 “start”(而非 “left”)指定该值(这样当布局为 RTL 时,抽屉式导航栏会显示在右侧)。
4)抽屉式导航栏视图以 dp 为单位指定其宽度, 且高度与父视图相匹配。抽屉式导航栏的宽度不应超过 320dp,从而用户始终可以看到部分主内容。
2、2初始化抽屉式导航栏列表
在Activity中,首要任务之一是初始化抽屉式导航栏的项目列表。如何执行该操作取决于应用的内容,但抽屉式导航栏通常包含 ListView,因此该列表应由 Adapter 填充(例如,ArrayAdapter 或 SimpleCursorAdapter)
可以使用字符串数组初始化导航列表
public class MainActivity extends Activity {
private String[] mPlanetTitles;
private DrawerLayout mDrawerLayout;
private ListView mDrawerList;
...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPlanetTitles = getResources().getStringArray(R.array.planets_array);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (ListView) findViewById(R.id.left_drawer);
// Set the adapter for the list view
mDrawerList.setAdapter(new ArrayAdapter<String>(this,
R.layout.drawer_list_item, mPlanetTitles));
// Set the list's click listener
mDrawerList.setOnItemClickListener(new DrawerItemClickListener());
...
}
}
此代码还调用 setOnItemClickListener() 来接收抽屉式导航栏列表中的点击事件。下一部分介绍如何在用户选择某一项时实现此接口并更改内容视图。
2、3 处理导航点击事件
当用户在抽屉式导航栏的列表中选择某一项时,系统会在提供给 setOnItemClickListener() 的 OnItemClickListener 上调用 onItemClick()。
在 onItemClick() 方法中执行的操作取决于应用结构的实现方法。在以下示例中,在该列表中选择每个项目会将不同的 Fragment 插入主内容视图(由 R.id.content_frame ID 标识的 FrameLayout 元素):
private class DrawerItemClickListener implements ListView.OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
selectItem(position);
}
}
/** Swaps fragments in the main content view */
private void selectItem(int position) {
// Create a new fragment and specify the planet to show based on position
Fragment fragment = new PlanetFragment();
Bundle args = new Bundle();
args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position);
fragment.setArguments(args);
// Insert the fragment by replacing any existing fragment
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.content_frame, fragment)
.commit();
// Highlight the selected item, update the title, and close the drawer
mDrawerList.setItemChecked(position, true);
setTitle(mPlanetTitles[position]);
mDrawerLayout.closeDrawer(mDrawerList);
}
@Override
public void setTitle(CharSequence title) {
mTitle = title;
getActionBar().setTitle(mTitle);
}
2、4 侦听打开和关闭事件
要侦听抽屉式导航栏的打开和关闭事件,请在 DrawerLayout 上调用 setDrawerListener() 并向其传递 DrawerLayout.DrawerListener 的实现。此接口为抽屉式导航栏事件(例如,onDrawerOpened() 和 onDrawerClosed())提供了回调。
但是,如果您的 Activity 包括操作栏,则可扩展 ActionBarDrawerToggle 类,而非实现 DrawerLayout.DrawerListener。 ActionBarDrawerToggle 实现了 DrawerLayout.DrawerListener,因此您仍然可以替代这些回调,但这还有助于在操作栏图标与抽屉式导航栏之间正确交互(下一部分中将进一步阐述),
正如抽屉式导航栏设计指南中所述,您应在抽屉式导航栏可见时修改操作栏的内容,例如,更改标题和移除与主内容有关的操作项目。 以下代码显示了如何通过使用 ActionBarDrawerToggle 类的实例替代 DrawerLayout.DrawerListener 回调方法来实现这一点:
public class MainActivity extends Activity {
private DrawerLayout mDrawerLayout;
private ActionBarDrawerToggle mDrawerToggle;
private CharSequence mDrawerTitle;
private CharSequence mTitle;
...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
mTitle = mDrawerTitle = getTitle();
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close) {
/** Called when a drawer has settled in a completely closed state. */
public void onDrawerClosed(View view) {
super.onDrawerClosed(view);
getActionBar().setTitle(mTitle);
invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
}
/** Called when a drawer has settled in a completely open state. */
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
getActionBar().setTitle(mDrawerTitle);
invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
}
};
// Set the drawer toggle as the DrawerListener
mDrawerLayout.setDrawerListener(mDrawerToggle);
}
/* Called whenever we call invalidateOptionsMenu() */
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// If the nav drawer is open, hide action items related to the content view
boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList);
menu.findItem(R.id.action_websearch).setVisible(!drawerOpen);
return super.onPrepareOptionsMenu(menu);
}
}
2、5 通过应用图标打开和关闭
用户可以通过远离或朝向屏幕的左边缘滑动手势打开和关闭抽屉式导航栏,但是如果您正在使用操作栏,还应允许用户通过触摸应用图标来打开和关闭它。 而且,应用图标还应通过特殊图标说明抽屉式导航栏的存在。 您可以使用上一部分中显示的 ActionBarDrawerToggle 来实现所有这些行为。
要让 ActionBarDrawerToggle 正常运行,请使用构造函数创建其实例,这需要以下参数:
1、托管抽屉式导航栏的 Activity。
2、DrawerLayout。
3、用作抽屉式导航栏指示器的可绘制对象资源。
4、下载操作栏图标包中提供了标准抽屉式导航栏图标。
5、用于描述“打开抽屉式导航栏”操作的字符串资源(用于无障碍功能)。
6、用于描述“关闭抽屉式导航栏”操作的字符串资源(用于无障碍功能)。
然后,无论您是否已创建 ActionBarDrawerToggle 的子类作为抽屉式导航栏侦听器,在整个 Activity 生命周期中,您都需要在几个位置调用 ActionBarDrawerToggle:
public class MainActivity extends Activity {
private DrawerLayout mDrawerLayout;
private ActionBarDrawerToggle mDrawerToggle;
...
public void onCreate(Bundle savedInstanceState) {
...
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerToggle = new ActionBarDrawerToggle(
this, /* host Activity */
mDrawerLayout, /* DrawerLayout object */
R.drawable.ic_drawer, /* nav drawer icon to replace 'Up' caret */
R.string.drawer_open, /* "open drawer" description */
R.string.drawer_close /* "close drawer" description */
) {
/** Called when a drawer has settled in a completely closed state. */
public void onDrawerClosed(View view) {
super.onDrawerClosed(view);
getActionBar().setTitle(mTitle);
}
/** Called when a drawer has settled in a completely open state. */
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
getActionBar().setTitle(mDrawerTitle);
}
};
// Set the drawer toggle as the DrawerListener
mDrawerLayout.setDrawerListener(mDrawerToggle);
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setHomeButtonEnabled(true);
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
mDrawerToggle.syncState();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mDrawerToggle.onConfigurationChanged(newConfig);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Pass the event to ActionBarDrawerToggle, if it returns
// true, then it has handled the app icon touch event
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
// Handle your other action bar items...
return super.onOptionsItemSelected(item);
}
...
}
三、TabHost的初始化
总体来说就是通过枚举类来列出5个Tab对应的内容页。在源码中初始化五个Tab页的代码如下所示:
//初始化底部的五个Tab页
private void initTabs() {
//调用枚举类MainTab的values方法,获得MainTab枚举类数组
MainTab[] tabs = MainTab.values();
final int size = tabs.length;
for (int i = 0; i < size; i++) {
//根据index获得具体对应的MainTab
MainTab mainTab = tabs[i];
//初始化每个Tab对应的图标、文字
TabSpec tab = mTabHost.newTabSpec(getString(mainTab.getResName()));
View indicator = LayoutInflater.from(getApplicationContext())
.inflate(R.layout.tab_indicator, null);
TextView title = (TextView) indicator.findViewById(R.id.tab_title);
Drawable drawable = this.getResources().getDrawable(
mainTab.getResIcon());
title.setCompoundDrawablesWithIntrinsicBounds(null, drawable, null,
null);
if (i == 2) {
indicator.setVisibility(View.INVISIBLE);
mTabHost.setNoTabChangedTag(getString(mainTab.getResName()));
}
title.setText(getString(mainTab.getResName()));
tab.setIndicator(indicator);
tab.setContent(new TabContentFactory() {
@Override
public View createTabContent(String tag) {
return new View(MainActivity.this);
}
});
//为TabHost添加对应的页面内容
mTabHost.addTab(tab, mainTab.getClz(), null);
//如果对应的是我的界面则添加一个消息提醒的右上角图标。
if (mainTab.equals(MainTab.ME)) {
View cn = indicator.findViewById(R.id.tab_mes);
mBvNotice = new BadgeView(MainActivity.this, cn);
mBvNotice.setBadgePosition(BadgeView.POSITION_TOP_RIGHT);
mBvNotice.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
mBvNotice.setBackgroundResource(R.drawable.notification_bg);
mBvNotice.setGravity(Gravity.CENTER);
}
mTabHost.getTabWidget().getChildAt(i).setOnTouchListener(this);
}
}
其中Tab页都在MainTab这个枚举类中初始化生成。
接下来就是将5个Tab页的绑定到主内容视图中,使用注解如下:
@InjectView(android.R.id.tabhost)
public MyFragmentTabHost mTabHost;
@InjectView(R.id.quick_option_iv)
View mAddBt;
ButterKnife.inject(this);
初始化主内容视图,调用initView()方法:
public void initView() {
mDoubleClickExit = new DoubleClickExitHelper(this);
mNavigationDrawerFragment = (NavigationDrawerFragment) getSupportFragmentManager()
.findFragmentById(R.id.navigation_drawer);
mTitle = getTitle();
// Set up the drawer.
mNavigationDrawerFragment.setUp(R.id.navigation_drawer,
(DrawerLayout) findViewById(R.id.drawer_layout));
mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);
if (android.os.Build.VERSION.SDK_INT > 10) {
mTabHost.getTabWidget().setShowDividers(0);
}
initTabs();
// 中间按键图片触发
mAddBt.setOnClickListener(this);
mTabHost.setCurrentTab(0);
mTabHost.setOnTabChangedListener(this);
IntentFilter filter = new IntentFilter(Constants.INTENT_ACTION_NOTICE);
filter.addAction(Constants.INTENT_ACTION_LOGOUT);
registerReceiver(mReceiver, filter);
NoticeUtils.bindToService(this);
if (AppContext.isFristStart()) {
mNavigationDrawerFragment.openDrawerMenu();
DataCleanManager.cleanInternalCache(AppContext.getInstance());
AppContext.setFristStart(false);
}
checkUpdate();
}
在InitView方法中就调用了上面讨论过的initTabs()方法。
参考资料:
http://blog.csdn.net/watermusicyes/article/details/48276679