好久没有写博客了!最近做了Android 设置的列表菜单风格改为Iphone的tab菜单风格的尝试!我知道,有许多朋友有自己的方式已经实现了这个界面风格的开发,今天大家来看看我的做法吧!先看看如下图的效果:
做这个开发前,首先要看看Android默认的设置列表菜单风格的实现!由 AndroidManifest.xml可以知道Settings这个Activity是我们关注的焦点!所以我们来到Settings.java来一探究竟,从中我们可以清除的知道他其实是一个PreferenceActivity,而PreferenceActivity又继承了ListActivity,我们知道ListActivity其实就是专门用于显示ListView的activity.这样一来,我们就清楚了,为什么显示的是列表菜单。实际上在Settings这个Activity里面利用这个函数loadHeadersFromResource来加载不同的xml配置文件,就可以加载对应的菜单了!在这个xml文件里面每一项菜单项几乎都定义了自己的fragment,这样一来,只要你点击对应的菜单就能进入对应的fragment了!
如果,你多关注一下Settings这个activity,你就会发现这个activity比你想象的要复杂的多!比如,在手机里面很多需要对手机设置的地方都可能调用Settings这个activity,想要通过改动这个activity来实现Iphone的tab菜单风格,我觉得要冒一定的风险!所以,在做Iphone的tab菜单风格的时候我提出了两个规则:
(1)尽量不影响Settings这个activity,包括这个activity的定义、已经被何时、何地被调用。
(2)尽量要求效率!
一般来说,做tab菜单风格无非有三种方式:(1)TabHost + ViewPager ;(2)ActionBar + ViewPager;(3)TabWidget + ViewPager;从中可以知道,都要用到ViewPager . 而ViewPager 是通过Adapter加载不同的view来实现不同的tab的菜单显示!这样的话就需要你写一个activity,通过加载包含ViewPager 的layout来实现!这样一来的话,要实现这个效果,你就需要从写一个另外一直方式的Settings的activity,这显然是有风险的!然后来看看ActionBar能否实现Iphone的tab折中底部tab按钮的效果!答案是否定的!(网上有各种办法,我实验的结果是无法实现这种效果的)。
ok,来说说我的实现办法把!
首先,我选择TabActivity来取代原来Settings的Activity地位!定义自己的TabActivity:TabSetting,在AndroidManifest.xml里面改为:
<activity android:name="TabSetting"
android:label="@string/settings_label_launcher"
android:taskAffinity="com.android.settings"
android:theme="@style/Theme.Settings.Light"
android:configChanges="keyboardHidden|screenSize|mcc|mnc"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<action android:name="android.settings.SETTINGS" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.APP_SETTINGS" />
</intent-filter>
</activity>
原来的settings配置如下:
<activity android:name="Settings"
android:label="@string/settings_label_launcher"
android:taskAffinity="com.android.settings"
android:theme="@style/Theme.Settings.Light"
android:configChanges="keyboardHidden|screenSize|mcc|mnc"
android:launchMode="singleTask">
</activity>
然后来看看TabSetting这个Activity的布局文件的定义:
<?xml version="1.0" encoding="utf-8"?>
<TabHost
android:id="@+android:id/tabhost"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<FrameLayout
android:id="@+android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1.0" />
<TabWidget
android:orientation="horizontal"
android:id="@+android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="57.0dip"
android:divider="@null"
style="?android:attr/tabWidgetStyle" />
</Linear
从上面的布局,我们就这里采用的是TabWidget.
接下来的重点就是TabHost的配置了!我们直接看代码:
private void createTabs(int selectedIndex) {
mTabHost = getTabHost();
LinearLayout tab_view;
final LayoutInflater inflater = LayoutInflater.from(this);
if (mTabHost != null) {
Intent start_activity_intent = TabSettings_content.getIntent_for_tab(this);
start_activity_intent.putExtra(INIT_SELECTED_TAB, selectedIndex);
tab_view =(LinearLayout) inflater.inflate(tab_view_resource_id, mTabHost.getTabWidget(), false);
((TextView)tab_view.findViewById(R.id.tab_hint)).setText(R.string.meng_tab_network);
((ImageView)tab_view.findViewById(R.id.tab_icon)).setImageResource(R.drawable.xunhu_tab_network);
start_activity_intent.putExtra(SELECT_TAB_INTENT_EXTRA, NETWORK_TAB_INDEX);
mTabHost.addTab(mTabHost.newTabSpec("network")
.setIndicator(tab_view)
.set_SameContent(start_activity_intent));
tab_view =(LinearLayout) inflater.inflate(tab_view_resource_id, mTabHost.getTabWidget(), false);
((TextView)tab_view.findViewById(R.id.tab_hint)).setText(R.string.meng_tab_device);
((ImageView)tab_view.findViewById(R.id.tab_icon)).setImageResource(R.drawable.xunhu_tab_device);
start_activity_intent =(Intent) start_activity_intent.clone();
start_activity_intent.putExtra(SELECT_TAB_INTENT_EXTRA, DEVICE_TAB_INDEX);
mTabHost.addTab(mTabHost.newTabSpec("device")
.setIndicator(tab_view)
.set_SameContent(start_activity_intent));
tab_view =(LinearLayout) inflater.inflate(tab_view_resource_id, mTabHost.getTabWidget(), false);
((TextView)tab_view.findViewById(R.id.tab_hint)).setText(R.string.meng_tab_personal);
((ImageView)tab_view.findViewById(R.id.tab_icon)).setImageResource(R.drawable.xunhu_tab_personal);
start_activity_intent =(Intent) start_activity_intent.clone();
start_activity_intent.putExtra(SELECT_TAB_INTENT_EXTRA, PERSONAL_TAB_INDEX);
mTabHost.addTab(mTabHost.newTabSpec("personal")
.setIndicator(tab_view)
.set_SameContent(start_activity_intent));
tab_view =(LinearLayout) inflater.inflate(tab_view_resource_id, mTabHost.getTabWidget(), false);
((TextView)tab_view.findViewById(R.id.tab_hint)).setText(R.string.meng_tab_system);
((ImageView)tab_view.findViewById(R.id.tab_icon)).setImageResource(R.drawable.xunhu_tab_system);//
start_activity_intent =(Intent) start_activity_intent.clone();
start_activity_intent.putExtra(SELECT_TAB_INTENT_EXTRA, SYSTEM_TAB_INDEX);
mTabHost.addTab(mTabHost.newTabSpec("system")
.setIndicator(tab_view)
.set_SameContent(start_activity_intent));
mTabHost.setOnTabChangedListener(new OnTabChangeListener() {
public void onTabChanged(String tabId) {
if (tabId.equals("network"))
{
mSelectedTab = NETWORK_TAB_INDEX;
}
else if(tabId.equals("device"))
{
mSelectedTab = DEVICE_TAB_INDEX;
}
else if(tabId.equals("personal"))
{
mSelectedTab = PERSONAL_TAB_INDEX;
}
else if(tabId.equals("system"))
{
mSelectedTab = SYSTEM_TAB_INDEX;
}
//mTabHost.setCurrentTab(mSelectedTab);
TabSetting.this.updateTabStyle(TabSetting.this.mTabHost);
}
});
}
}
start_activity_intent就是用于启动我们shettings的avtivity的!所以,我的目的其实就是不同tab菜单的切换其实都是用一个activity,而且这个activity就是系统原本有的shettings的activity。只是在不同的tab,loadHeadersFromResource加载不同的xml来实现显示不同的菜单!这里我们来看看这个我们启动的activity(TabSettings_content)的定义:
public class TabSettings_content extends Settings{
private static final boolean DEBUG = true;
private static int mCurr_SelectedTab = TabSetting.NETWORK_TAB_INDEX;
private static int mNext_SelectedTab = TabSetting.NETWORK_TAB_INDEX;
private int SettingMenuStyle_index = -1;
....
....
....
}
从上面的代码可以知道 TabSettings_content其实就是一个Settings的封装。
那在TabSettings_content里面是怎么知道在不同的tab界面加载不同的xlm文件呢!这个时候activity的onNewIntent这个方法就有用处了!如下:
@Override
public void onNewIntent(Intent newIntent) {
super.onNewIntent(newIntent);
// update our intent so that we can consult it to determine whether or
// not the most recent launch was via the event
setIntent(newIntent);
//Get the saved selected tap_index,
int tab = newIntent.getIntExtra(TabSetting.SELECT_TAB_INTENT_EXTRA, mCurr_SelectedTab);
if (!getResources().getBoolean(R.bool.TabSettingPage_Change_Animal)){
if(tab != mCurr_SelectedTab){
mCurr_SelectedTab = tab;
mNext_SelectedTab = mCurr_SelectedTab;
invalidateHeaders();
}
}else if (tab != -1 && tab != mCurr_SelectedTab) {
<span style="BACKGROUND-COLOR: #ff0000">Switch_tab_anima(tab);
</span> }
}
从上面的代码,你可能有点晕,因为你不太知道Switch_tab_anima(tab);这个语句在做什么。通过字面的意思,可以知道这应该就是tab切换并且还播放动画了! 其实这里播放的动画其实就是左右两个tab往左边移出、右边tab从右边移入的这样的一个动画效果!下面代码来看看这个方法Switch_tab_anima的实现吧:
private void Switch_tab_anima(int the_tap_index){
if(false == Init_Animation){
Init_Animation_Play();
}
if (the_tap_index != mCurr_SelectedTab && the_tap_index != -1) {
if((the_tap_index > mCurr_SelectedTab && !(the_tap_index == 3 && mCurr_SelectedTab == 0)) || (the_tap_index == 0 && mCurr_SelectedTab == 3)){
TabSettings_ListView.startAnimation(slide_left_out);
}
else{
TabSettings_ListView.startAnimation(slide_right_out);
}
mNext_SelectedTab = the_tap_index;
}
}
一样的,上面有两行我标记红色的代码,这个地方MainHandler就是一个我定义的Handler,主要目的就是延时,其目的是:原来的tab移除以后,新的tab才开始移入。这里的延时就是等原来的tab移除。
最后,你会发现,我实现的这个tab菜单风格,当用手指左右滑动菜单时候,并不会切换到下一个tab(或者上一个tab),这怎么办呢!下面通过2步来实现:
(1):首先要能获取手指触摸屏的事件:这里你不得不选择dispatchTouchEvent:
@Override
public boolean dispatchTouchEvent(MotionEvent m) {
// Elog.d(TAG, "dispatchTouchEvent()");
Log.d("TabSetting", "TabSetting dispatchTouchEvent()");
if (this.detector != null) {
this.detector.onTouchEvent(m);
}
boolean flag = super.dispatchTouchEvent(m);
Log.d("TabSetting", "TabSetting dispatchTouchEvent() flag="+flag);
return flag;
}
(2):我们刚刚定义的TabActivity:TabSetting 需要实现OnGestureListener这个手势接口!该接口的onFling和onScroll来实现识别手指滑动的操作,操作成功则切换不同tab:下面代码是方法onFling的实现(和方法onScroll实现一样的):
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
int tab_index;
if(TouchedDown_Done == true){
return false;
}
if (e1.getX() - e2.getX() > <span style="BACKGROUND-COLOR: #ff0000">move_switch_len</span>) {
tab_index = mSelectedTab == 3 ? 0:mSelectedTab+1;
mTabHost.setCurrentTab(tab_index);
mSelectedTab = tab_index;
TouchedDown_Done = true;
return true;
}
else if(e2.getX() - e1.getX() > move_switch_len){
tab_index = mSelectedTab == 0 ? 3:mSelectedTab-1;
mTabHost.setCurrentTab(tab_index);
mSelectedTab = tab_index;
TouchedDown_Done = true;
return true;
}
else{
return false;
}
}
关注一下move_switch_len这个量吧。这个值不能太大也不能太小!你知道原因吗?
我想该说的已经说完了!我这里实现tab风格其实还是调用原来的Settings这个activity,只是外面用一个TabActivity包装了一下!这就是所有的!你应该了解了吧。