一、自定义一个AnimationTabHost组件
----------------------------------------------------------------------------------------------
AnimationTabHost.java代码
/**继承TabHost组件,带有切入切出的滑动动画效果。*/
publicclassAnimationTabHostextendsTabHost {
privateAnimationslideLeftIn;
privateAnimationslideLeftOut;
privateAnimationslideRightIn;
privateAnimationslideRightOut;
/**记录是否打开动画效果*/
privatebooleanisOpenAnimation;
/**记录当前标签页的总数*/
privateintmTabCount;
publicAnimationTabHost(Context
context, AttributeSet attrs) {
super(context, attrs);
slideLeftIn= AnimationUtils.loadAnimation(context,
R.anim.slide_left_in);
slideLeftOut= AnimationUtils.loadAnimation(context,
R.anim.slide_left_out);
slideRightIn= AnimationUtils.loadAnimation(context,
R.anim.slide_right_in);
slideRightOut= AnimationUtils.loadAnimation(context,
R.anim.slide_right_out);
isOpenAnimation=false;
}
/**
*设置是否打开动画效果
*@paramisOpenAnimation
* true:打开
*/
publicvoidsetOpenAnimation(booleanisOpenAnimation) {
this.isOpenAnimation= isOpenAnimation;
}
说明:这里的Animation都是自定义的动画效果,可以在res/anim中找到对应的XML文件,下面用slide_left_in.xml来说明定义的大概用法
slide_left_in.xml
android:toXDelta="0"
android:duration="800"/>
android:toAlpha="1.0"
android:duration="300"/>
说明:
①因为这个动画是由几个动画复合组成的,所以外围就用一个set标签括起来,组成一个AnimationSet。
②Translate标签内主要定义位置的变化情况,fromXDelta="100%p",为动画起始时,X坐标上的伸缩尺寸。是指正下方刚好一个View的高度的距离的地方开始出现,100%p是一个相对值,大于0为下方,小于0为上方。toXDelta="0",为动画结束时,X坐标上的伸缩尺寸。另:0.0表示收缩到没有(即刚好达到布局文件的原始位置停止),1.0表示正常无伸缩,值小于1.0表示收缩,值大于1.0表示放大。
③参数fromXDelta和toXDelta都是指控件相对于parent的偏移距离,100%p就是正好在parent外面。左右是from和to来决定的(就是在根的左和右)。
④duration="800",是指整个动作的时间用时为800毫秒,系统会根据这个时间自动调整速度。
⑤alpha标签内定义的是透明度,0为全透明,1.0为不透明,过程为300毫秒,让View为逐渐出现的过程。
/**
*设置标签滑动动画。
*动画顺序为“左进——>左出——>右进——>右出”
*
*@paramanimationResIDs
*动画的资源文件ID
*@returntrue:四个动画文件;
* false:非四个动画文件(无法匹配,采用默认动画)
*/
publicbooleansetTabAnimation(int[] animationResIDs) {
if(3 == animationResIDs.length) {
slideLeftIn= AnimationUtils.loadAnimation(getContext(),
animationResIDs[0]);
slideLeftOut= AnimationUtils.loadAnimation(getContext(),
animationResIDs[1]);
slideRightIn= AnimationUtils.loadAnimation(getContext(),
animationResIDs[2]);
slideRightOut=AnimationUtils.loadAnimation(getContext(),
animationResIDs[3]);
returntrue;
}else{
returnfalse;
}
}
/**
*@return返回当前标签页的总数
*/
publicintgetTabCount() {
returnmTabCount;
}
@Override
publicvoidaddTab(TabSpec tabSpec) {
mTabCount++;
super.addTab(tabSpec);
}
@Override
publicvoidsetCurrentTab(intindex) {
intmCurrentTabID = getCurrentTab();
if(null!= getCurrentView()) {
//第一次设置Tab时,该值为null。
if(isOpenAnimation) {
if(mCurrentTabID == (mTabCount- 1) && index == 0) {
getCurrentView().startAnimation(slideLeftOut);
}elseif(mCurrentTabID == 0
&& index == (mTabCount- 1)) {
getCurrentView().startAnimation(slideRightOut);
}elseif(index > mCurrentTabID)
{
getCurrentView().startAnimation(slideLeftOut);
}elseif(index < mCurrentTabID)
{
getCurrentView().startAnimation(slideRightOut);
}
}
}
super.setCurrentTab(index);
if(isOpenAnimation) {
if(mCurrentTabID == (mTabCount- 1) && index ==
0) {
getCurrentView().startAnimation(slideLeftIn);
}elseif(mCurrentTabID == 0 && index == (mTabCount- 1)) {
getCurrentView().startAnimation(slideRightIn);
}elseif(index > mCurrentTabID) {
getCurrentView().startAnimation(slideLeftIn);
}elseif(index < mCurrentTabID) {
getCurrentView().startAnimation(slideRightIn);
}
}
}
}
说明:mTabCount为当前标签页的总数。setCurrentTab设置当前标签页。index可以理解为:需要显示的那个标签页。
--------------------------------------------------------------------------------------------
二、TabHost选项卡
TabHostActivity.java代码
publicclassTabHostActivityextendsTabActivityimplementsOnTabChangeListener {
privateGestureDetectorgestureDetector;
privateFrameLayoutframeLayout;
privateAnimationTabHostmTabHost;
privateTabWidgetmTabWidget;
/**记录当前分页ID */
privateintcurrentTabID= 0;
@Override
protectedvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tab_host_test_view);
说明:
①OnTabChangeListener接口,标签切换事件监听
②使用TabHost有两种方法,一种是继承TabActivity;一种是不继承TabActivity;在这里是继承TabActivity的;首先我们得写好tab_host_test_view.xml布局文件,在写这个布局文件时要注意,使用TabHost一定要有TabWidget、FramLayout这两个控件,并且TabWidget必须使用系统ID @android:id/tabs;FrameLayout作为标签内容的基本框架,也必须使用系统ID
@android:id/tabcontent;而TabHost可以自定义ID,这是为了在系统初始化时能够使用,否则会报错!布局文件tab_host_test_view.xml如下:
-------
tab_host_test_view.xml
xmlns:android="http://schemas.android.com/apk/res/android"android:id="@android:id/tabhost"
android:layout_width="fill_parent"
android:layout_height="fill_parent"android:background="@drawable/default_bg">
android:layout_width="fill_parent"android:layout_height="fill_parent">
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1.0"
/>
android:padding="3.0dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="0.0"
/>
----------
mTabHost=(AnimationTabHost)findViewById(android.R.id.tabhost);
mTabWidget= (TabWidget) findViewById(android.R.id.tabs);
mTabHost.setOnTabChangedListener(this);
init();
gestureDetector=newGestureDetector(newTabHostTouch());
newView.OnTouchListener() {
publicbooleanonTouch(View v, MotionEvent event) {
if(gestureDetector.onTouchEvent(event)) {
returntrue;
}
returnfalse;
}
};
frameLayout=mTabHost.getTabContentView();
Log.i("TabHostActivity",
"TabHostActivity====>>>onCreate()===>>>>frameLayout:
"
+frameLayout.getChildCount());
}
privatevoidinit() {
setIndicator(R.drawable.tab_logo_0, 0,newIntent(this,
TabHostTestOne.class));
setIndicator(R.drawable.tab_logo_1, 1,newIntent(this,
TabHostTestTwo.class));
setIndicator(R.drawable.tab_logo_2, 2,newIntent(this,
TabHostTestThree.class));
setIndicator(R.drawable.tab_logo_3, 3,newIntent(this,
TabHostTestFour.class));
mTabHost.setOpenAnimation(true);
}
privatevoidsetIndicator(inticon,inttabId, Intent intent) {
View localView =
LayoutInflater.from(this.mTabHost.getContext())
.inflate(R.layout.tab_widget_view,null);
((ImageView)
localView.findViewById(R.id.main_activity_tab_image))
.setBackgroundResource(icon);
String str = String.valueOf(tabId);
TabHost.TabSpec
localTabSpec=mTabHost.newTabSpec(str).setIndicator(
localView).setContent(intent);
mTabHost.addTab(localTabSpec);
}
说明:
①mTabHost = (AnimationTabHost) findViewById(android.R.id.tabhost);
mTabWidget = (TabWidget)
findViewById(android.R.id.tabs);
系统自带
②init():初始化。设置各选项卡的icon、id、intent。
③GestureDetector手势识别类。通过GestureDetector.OnGestureListener来获取当前被触发的操作手势。另:使用OnTouchListener可以比OnClickListener获得更细的控制粒度,例如涉及down-touch/up-touch/no-drag,多点,触摸的强度,按下,松开,拖动等。。。。,而OnClickListener只是点击,只适用于组件的事件触发。
④使用setIndicator(localView)把localView添加进去。
publicvoidonTabChanged(String tabId) {
inttabID = Integer.valueOf(tabId);
for(inti = 0; i
if(i == tabID) {
mTabWidget.getChildAt(Integer.valueOf(i)).setBackgroundColor(
R.color.bule);
}else{
mTabWidget.getChildAt(Integer.valueOf(i))
.setBackgroundResource(R.drawable.main_meun_bg);
}
}
}
说明:标签切换事件,使用getChildAt(int i)来取得每个标签
publicbooleandispatchTouchEvent(MotionEvent event) {
if(gestureDetector.onTouchEvent(event)) {
event.setAction(MotionEvent.ACTION_CANCEL);
}
returnsuper.dispatchTouchEvent(event);
}
说明:
dispatchOnTouchEvent()时派发事件,从最上层View开始派发,dispatchOnTouchEvent()返回true时,View将事件派发给自己onTouchEvent()方法处理,如果返回false,则交给interceptTouchEvent来决定是否要拦截,如果返回false,则传给子View,由子View的dispatchTouchEvent()方法再来派发。因此,要清楚自己的view的继承关系,不要派发到父类的窗口就可以了。
如果没有此段代码,点击各Tab,各页面之间跳转会有滑动动画效果,但是无法实现拖动页面时的动画效果。
privateclassTabHostTouchextendsSimpleOnGestureListener {
/**滑动翻页所需距离*/
privatestaticfinalintON_TOUCH_DISTANCE= 80;
publicbooleanonFling(MotionEvent e1,
MotionEvent e2,floatvelocityX,
floatvelocityY) {
if(e1.getX() - e2.getX()
<= (-ON_TOUCH_DISTANCE)) {
currentTabID=mTabHost.getCurrentTab() - 1;
if(currentTabID< 0) {
currentTabID=mTabHost.getTabCount() - 1;
}
}elseif(e1.getX() - e2.getX() >=ON_TOUCH_DISTANCE) {
currentTabID=mTabHost.getCurrentTab() + 1;
if(currentTabID>=mTabHost.getTabCount()) {
currentTabID= 0;
}
}
mTabHost.setCurrentTab(currentTabID);
returnfalse;
}
}
}
说明:
boolean onFling(MotionEvent e1, MotionEvent e2, float
velocityX, float velocityY)
Touch了滑动一点距离后,up时触发。即用户按下屏幕,快速移动后松开(就是在屏幕上滑动)
e1:第一个ACTION_DOWN事件(手指按下的那一点)
e2:最后一个ACTION_MOVE事件(手指松开的那一点)
velocityX:手指在x轴移动的速度单位:像素/秒
velocityY:手指在y轴移动的速度单位:像素/秒