はじめに
こんなかんじのアニメーションするTabLayoutを実装してみました。
!注意!
- この記事の著者はAndroid, Javaともに初心者です。誤字、脱字、間違いを含みます。予めご了承ください。
- 当人Android関係の勉強が必要だったため、そのまとめ、備忘録的な記事です。技術的なことは他を参照して下さい。
TabLayout + ViewPager
TabLayout + ViewPager でメイン画面のレイアウトファイルを作ります。
TabLayout, ViewPagerの使い方について詳しくは、他の記事、公式を参照推奨です。
ここではシンプルにTabLayout,ViewPagerを配置してMainActivityのほうで制御します。
おまけでタイトルバーを一番上に設置しておきました。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.hoge.hogeapp.MainActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_below="@+id/titlebar"
android:background="@color/color_white1">
<android.support.design.widget.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="70dp"
app:tabIndicatorColor="@color/color_green1"
app:tabMode="scrollable"
android:background="@android:color/white"
android:elevation="4dp"
tools:targetApi="lollipop" />
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
<include
android:id="@+id/titlebar"
layout="@layout/item_titlebar" />
</RelativeLayout>
</LinearLayout>
Adapterを作って紐付けをします。
MainFragmentPagerAdapter adapter = new MainFragmentPagerAdapter(getSupportFragmentManager());
ViewPager viewPager = findViewById(R.id.pager);
viewPager.setAdapter(adapter);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(viewPager);
Adapterの設定
package com.hoge.hogeapp;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
public class MainFragmentPagerAdapter extends FragmentPagerAdapter {
public MainFragmentPagerAdapter(FragmentManager fm) { super(fm); }
@Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new FugaFragment();
case 1:
return new HogeFragment();
default:
return new PiyoFragment();
}
}
@Override
public CharSequence getPageTitle(int position) { return null; }
@Override
public int getCount() { return MainActivity.tabLength; }
}
タブのタイトルはActivityで設定していくので、getPageTitle()はnullを返すようにして
getItem(), getCount()をそれぞれ設定します。
TabLayoutの中身のViewを作る
次にTabLayoutの中身にテキストとアイコンをセットしていきます。
// create TAB1
tabLayout.getTabAt(0).setCustomView(R.layout.item_tab1);
// create TAB2
tabLayout.getTabAt(1).setCustomView(R.layout.item_tab2);
:
:
もしくは
// create TAB1
tabLayout.getTabAt(0).setText(R.string.tab1);
tabLayout.getTabAt(0).setIcon(R.drawable.tab1Icon);
// create TAB2
tabLayout.getTabAt(1).setText(R.string.tab2);
tabLayout.getTabAt(1).setIcon(R.drawable.tab2Icon);
:
:
のようにtabLayoutにセットしていくのが一番楽なんですが
レイアウトファイルをたくさん作らないといけなかったり、
setText(), setIcon()するとレイアウトファイルから扱うことが出来なくなってしまうので...
今回はレイアウトファイルをinflateしてDataBindingで画像とテキストをバインドします。
その後、TabLayout.Tabの配列にバインドされたViewをセットしていきます。
LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
TabLayout.Tab[] tab = new TabLayout.Tab[tabLength];
// create TAB1
tab[0] = tabLayout.getTabAt(0);
View tabView = inflater.inflate(R.layout.item_tab, null);
ItemTabBinding binding = ItemTabBinding.bind(tabView);
Drawable drawable = ResourcesCompat.getDrawable(getResources(), R.drawable.tab1Icon, null);
binding.setItemTabData(new ItemTabData(drawable, R.string.tab1));
tab[0].setCustomView(tabView);
:
:
面倒くさいですがなんかすごい拡張性があるような気がします。
タブごとにレイアウトファイルを用意できたり、
viewGroup = (ViewGroup) tabLayout.getChildAt(0);
View childView = viewGroup.getChildAt(tabPosition);
ImageView tabIconView1 = (ImageView) childView.findViewById(R.id.tab_icon);
TextView tabTitleView1 = (TextView) childView.findViewById(R.id.tab_title);
:
:
各タブ毎の子Viewのインスタンスを取得して
タブの中身のパーツごとにアニメーションを設定したりすることが出来ます。使うかどうかは別として、出来ます。
こんな感じにまとめてみました。
private View[] tabIconView;
private View tabView;
private LayoutInflater inflater;
:
:
// create tab
TabLayout.Tab[] tab = new TabLayout.Tab[tabLength];
inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
tabIconView = new View[tabLength];
for(int i = 0; i < tabLength; i++) {
tab[i] = tabLayout.getTabAt(i);
switch (i) {
case 0:
bindTabData(R.drawable.ic_cached, R.string.tab1);
break;
case 1:
bindTabData(R.drawable.ic_alarm, R.string.tab2);
break;
case 2:
bindTabData(R.drawable.ic_notifications, R.string.tab3);
break;
case 3:
bindTabData(R.drawable.ic_android, R.string.tab4);
break;
default:
break;
}
tab[i].setCustomView(tabView);
}
// create tab dataBinding
private void bindTabData(int drawableRoot, int stringRoot) {
tabView = inflater.inflate(R.layout.item_tab, null);
ItemTabBinding binding = ItemTabBinding.bind(tabView);
Drawable drawable = ResourcesCompat.getDrawable(getResources(), drawableRoot, null);
binding.setItemTabData(new ItemTabData(drawable, getString(stringRoot)));
}
tabLength = 4 として、タブを4つほど作ります。
DataBindingの部分はメソッドにして、引数にバインドしたいリソースファイルを取るようにすると見易いです。
次に、タブのレイアウトファイル、DataBindingのモデルが必要なので追加します。
DataBindingについては他を参照推奨
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.hoge.hogeapp.MainActivity">
<data>
<variable name="itemTabData" type="com.hoge.hogeapp.MainActivity.ItemTabData" />
</data>
<RelativeLayout
android:id="@+id/item_tab"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="80dp"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@drawable/tab_color_selector">
<ImageView
android:id="@+id/tab_icon"
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_marginTop="12dp"
android:src="@{itemTabData.tabIcon}"
android:layout_gravity="center" />
<TextView
android:id="@+id/tab_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="@{itemTabData.tabTitle}"
android:layout_gravity="center"
android:textColor="@color/color_green1"
android:textSize="12sp" />
</LinearLayout>
</RelativeLayout>
</layout>
public class ItemTabData {
private Drawable tabIcon;
private String tabTitle;
private ItemTabData(Drawable tabIcon, String tabTitle) {
this.tabIcon = tabIcon;
this.tabTitle = tabTitle;
}
public Drawable getTabIcon() {
return tabIcon;
}
public void setTabIcon(Drawable tabIcon) {
this.tabIcon = tabIcon;
}
public String getTabTitle() {
return tabTitle;
}
public void setTabTitle(String tabTitle) {
this.tabTitle = tabTitle;
}
}
build.gradleに追記するのを忘れずに
dataBinding {
enabled = true
}
ここまでで、タブの生成が完了しました。
ListenerにAnimationの設定をする
次にアニメーションの設定を行っていきたいと思います。
とその前に、
// get tabs instance
viewGroup = (ViewGroup) tabLayout.getChildAt(0);
for(int tabPosition = 0; tabPosition < tabLength; tabPosition++) {
View childView = viewGroup.getChildAt(tabPosition);
tabIconView[tabPosition] = (View) childView.findViewById(R.id.tab_icon);
}
各タブ毎の子ViewのidからアニメーションさせたいViewを配列で取得しておきます。
tabLayout, viewGroupはメンバ変数として宣言してアクティビティ内からアクセスできるようにしておいて下さい。
では、さっそくリスナーの設定から
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
addOnTabSelectedListenerを使用するのが一番いいみたいです。※version26.1.0以降
これで、TabLayout.OnTabSelectedListenerを拡張することが出来ます。
それでは、各コールバックにアニメーションを追加していきます。
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
@Override
public void onTabSelected(TabLayout.Tab tab) {
int position = tab.getPosition();
switch (position) {
case 0:
Animation tabAnimation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.roll_anim);
tabIconView[0].startAnimation(tabAnimation);
break;
case 1:
Animation tabAnimation = AnimationUtils.loadAnimation(MainActivity.this, R.anim.updown_anim);
tabIconView[1].startAnimation(tabAnimation);
break;
:
:
}
}
@Override
public void onTabUnselected(TabLayout.Tab tab) {
int position = tab.getPosition();
switch (position) {
case 0:
tabIconView[0].setAnimation(null);
break;
case 1:
tabIconView[1].setAnimation(null);
break;
:
:
}
}
@Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
各タブが選択されたときに取得したViewに対してアニメーションを開始し、選択された状態が終了したときに停止させるといい感じなんじゃないかと思います。
まず、レイアウトファイルから扱っていきます。resフォルダの下にanimフォルダを作ってその中に作っていきます。
View Animationを使った簡単なものを実装します。サンプルとしてこんな感じで作ってみました。
アニメーションについては他を参照推奨
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<rotate
android:duration="800"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="200"
android:repeatMode="restart"
android:repeatCount="-1" />
</set>
アクティビティから扱いたい場合は、
// composite animation
AlphaAnimation alphaAnimation = new AlphaAnimation(0.9f, 0.2f);
alphaAnimation.setRepeatCount(Animation.INFINITE);
alphaAnimation.setRepeatMode(Animation.REVERSE);
RotateAnimation rotateAnimation = new RotateAnimation(0, 360, 45, 45);
rotateAnimation.setRepeatCount(Animation.INFINITE);
rotateAnimation.setRepeatMode(Animation.RESTART);
AnimationSet animationSet = new AnimationSet(false);
animationSet.addAnimation(alphaAnimation);
animationSet.addAnimation(rotateAnimation);
animationSet.addAnimation(new ScaleAnimation(0.1f, 1, 0.1f, 1));
animationSet.addAnimation(new TranslateAnimation(50, 0, 150, 0));
animationSet.setDuration(3000);
iconView.startAnimation(animationSet);
のように、記述するとちょっと複雑な感じに複数アニメーションをセット出来ます。(Property Animationのほうが...)
ここまで出来たら大体いい感じなんですが、
最後に、選択されたタブ全体のスケールを大きくするようにしておきます。
onTabSelected, onTabUnselectedにそれぞれ次のように追記すればよいかと思います。
final View view = viewGroup.getChildAt(tabLayout.getSelectedTabPosition());
view.setScaleX(1.25F);
view.setScaleY(1.25F);
final View view = viewGroup.getChildAt(tabLayout.getSelectedTabPosition());
view.setScaleX(1.0F);
view.setScaleY(1.0F);
後はメソッド化してswitch文に並べれば、大体やりたかったこととしては完成です。お疲れ様でした。
最後に
...もうGradleでビルドしたくないしFlutterとか使ってみたいです
ソース https://github.com/udzuv/AnimatingTabLayout
おわり