前一篇已经将SplashActivity编写好了,这篇主要梳理一下主页面MainActivity。包括:
实现ViewPager上方的头布局Tab的高亮和缩放动画;
实现指示线的随手指移动而移动的效果
基类的编写
一般项目开发中,会涉及到很多的Activity和Fragment的使用,而且我们在这些activity或者fragment中操作的方法大致一样:初始化view,初始化data,初始化listener,以及实现一些控件的onClick方法。
所以为了方便,我们将这些方法向上抽取,放到一个BaseActivity以及一个BaseFragment当中去。当需要使用的时候,直接继承我们已经写好的这些基类,这样,不仅少了不少重复代码,而且可读性极大增强,逻辑更加清晰。
public abstract class BaseActivity extends FragmentActivity implements View.OnClickListener{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initViews(); initData(); initListeners(); } protected abstract void initViews(); protected abstract void initData(); protected abstract void initListeners(); /**可以处理共同点击事件的按钮(比如像一些项目的TitleBar都有一个返回按钮)*/ protected abstract void processClick(View view); @Override public void onClick(View view) { processClick(view); } }
说明:这里基类继承的是FragmentActivity而不是Activity,主要是考虑到主页面的viewpager嵌套了两个fragment,它的的适配器是继承FragmentPagerAdapter,这是v4包下的,所以主页面需要通过继承FragmentActivity来getSupportFragmentManager得到这个fragmentManager。
这里顺道把BaseFragment的编写也贴上,后面要用到。跟BaseActivity差不多。
public abstract class BaseFragment extends Fragment implements View.OnClickListener { @Override public void onAttach(Activity activity) { super.onAttach(activity); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = initView(inflater, container, savedInstanceState); return view; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); initData(); initListener(); } protected abstract View initView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState); protected abstract void initData(); protected abstract void initListener(); protected abstract void processClick(View view); @Override public void onClick(View v) { processClick(v); } protected void enterActivity(Class<?> targetClass) { startActivity(new Intent(getActivity(), targetClass)); } protected void enterActivity(Class<?> targetClass, Bundle bundle) { Intent intent = new Intent(getActivity(), targetClass); intent.putExtras(bundle); startActivity(intent); }
说明:这里多了enterActivity()的两个重载的方法,是根据该项目需求抽取的。因为该项目中,fragment中加入的是一个listview控件,点击其中的一个item会跳转到一个activity中去,有时带数据,有时不带,为了代码整洁,所以都抽取到BaseFragment中了。
主页面布局以及标题栏Tab高亮&缩放效果
前提:既然是自己定义标题栏,就不能忘了把系统自带的标题栏给去掉。
在style.xml文件中AppTheme的样式下加入下面代码:
<item name="android:windowNoTitle">true</item>
1. 主页面布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@mipmap/base_bg" android:orientation="vertical"> <RelativeLayout android:layout_width="match_parent" android:layout_height="55dp" android:background="@mipmap/base_titlebar_bg"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:id="@+id/tv_tab_video" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center" android:text="视频" android:textColor="@color/indicate_line" android:textSize="18sp" /> <TextView android:id="@+id/tv_tab_audio" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="center" android:text="音乐" android:textColor="@color/gray_white" android:textSize="18sp" /> </LinearLayout> <View android:id="@+id/indicate_line" android:layout_width="0dp" android:layout_height="3dp" android:layout_alignParentBottom="true" android:background="@color/indicate_line" /> </RelativeLayout> <android.support.v4.view.ViewPager android:id="@+id/vp" android:layout_width="match_parent" android:layout_height="match_parent"> </android.support.v4.view.ViewPager> </LinearLayout>
2. 标题栏高亮及缩放:
这里缩放的动画效果是用的属性动画,API_12及以上才有,如果想兼容低版本,可以用 nineoldandroids-2.4.0.jar来实现。
/** * 高亮显示并且缩放标签标题 * @param position viewpager的currentItem的position */ private void highligthAndScaleTabTitle(int position) { tvVideo.setTextColor(position == 0 ? getResources().getColor(R.color.indicate_line) : getResources().getColor(R.color.gray_white)); tvAudio.setTextColor(position == 1 ? getResources().getColor(R.color.indicate_line) : getResources().getColor(R.color.gray_white)); tvVideo.animate(). scaleX(position == 0 ? 1.2f : 1.0f). scaleY(position == 0 ? 1.2f : 1.0f). setDuration(200); tvAudio.animate(). scaleX(position == 0 ? 1.0f : 1.2f). scaleY(position == 0 ? 1.0f : 1.2f). setDuration(200); }
标题栏指示线的随手指移动而移动效果
思考:
指示线的宽是多少了呢?该项目中是屏幕宽的一半,但能写死吗?万一后来需求改了,标题栏要求添加一个tab呢?很显然,这个指示线不能写死,得根据标签的数量动态去设置,两个标签,宽就是屏幕的1/2,三个标签,宽就是屏幕的1/3。fragment的数量就是标签的数量,因为每一个标签对应一个fragment。
如何让指示线随手指的滑动而滑动呢?是不是应该这样:viewpager滑动一个页面的距离(即屏幕宽),指示线就应该移动它本身的宽度。在viewpager的onPageScrolled(int position, float positionOffset, int positionOffsetPixels)方法中,参数position是当前fragment的位置,参数positionOffset就是手指滑动距离占屏幕总宽的比率,参数positionOffsetPixels就是滑动的像素值。
有了滑动比率,指示线的宽,就好办了,拿 lineWidth positionOffset不就是指示线应该滑动的距离吗。原理就是这么简单,但要注意,指示线在移动的时候,要把原始的位置加上,不然,它会一直在屏幕左边滑动。lineWidth position + lineWidth * positionOffset。
偏移亮计算出来了,这里还是用属性动画来实现随手指滑动。通过setDuration(0)来达到效果。
贴张图再次加深对上面文字的理解。
计算指示线宽度的代码:
/** * 计算指示器的宽度 */ private void calculateIndicateLineWidth() { Point point = new Point(); this.getWindowManager().getDefaultDisplay().getSize(point); int screenWidth = point.x; lineWidth = screenWidth / fragments.size(); LogUtils.i("lineWidth = " + lineWidth); indicateLine.getLayoutParams().width = lineWidth; indicateLine.requestLayout(); }
指示线随手指滑动的代码:(在监听viewpager的onPageScrolled()的方法里设置)
@Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { // LogUtils.i("position = " + position + ",positionOffset = " + positionOffset // + ",positionOffsetPixels = " + positionOffsetPixels); float delatX = lineWidth * (position + positionOffset); LogUtils.i("delatX = " + delatX); indicateLine.animate().translationX(delatX).setDuration(0); }
主页面完整代码
说明:该项目用了xUtils框架中的注解功能,省去大量的 findViewById()操作。
public class MainActivity extends BaseActivity { @ViewInject(R.id.vp) private ViewPager vp; @ViewInject(R.id.tv_tab_video) private TextView tvVideo; @ViewInject(R.id.tv_tab_audio) private TextView tvAudio; @ViewInject(R.id.indicate_line) private View indicateLine; private List<BaseFragment> fragments; private int lineWidth;//指示线的宽度 protected void initViews() { setContentView(R.layout.activity_main); ViewUtils.inject(this); } protected void initData() { fragments = new ArrayList<>(); fragments.add(new VideoListFragment()); fragments.add(new AudioListFragment()); calculateIndicateLineWidth(); MainUiAdapter adapter = new MainUiAdapter(getSupportFragmentManager(), fragments); vp.setAdapter(adapter); highligthAndScaleTabTitle(0); } protected void initListeners() { tvVideo.setOnClickListener(this); tvAudio.setOnClickListener(this); vp.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { // LogUtils.i("position = " + position + ",positionOffset = " + positionOffset // + ",positionOffsetPixels = " + positionOffsetPixels); float delatX = lineWidth * (position + positionOffset); LogUtils.i("delatX = " + delatX); indicateLine.animate().translationX(delatX).setDuration(0); } @Override public void onPageSelected(int position) { highligthAndScaleTabTitle(position); } @Override public void onPageScrollStateChanged(int state) { } }); } @Override protected void processClick(View view) { switch (view.getId()) { case R.id.tv_tab_video: vp.setCurrentItem(0); break; case R.id.tv_tab_audio: vp.setCurrentItem(1); break; } } /** * 高亮显示并且缩放标签标题 * @param position viewpager的currentItem的position */ private void highligthAndScaleTabTitle(int position) { tvVideo.setTextColor(position == 0 ? getResources().getColor(R.color.indicate_line) : getResources().getColor(R.color.gray_white)); tvAudio.setTextColor(position == 1 ? getResources().getColor(R.color.indicate_line) : getResources().getColor(R.color.gray_white)); tvVideo.animate(). scaleX(position == 0 ? 1.2f : 1.0f). scaleY(position == 0 ? 1.2f : 1.0f). setDuration(200); tvAudio.animate(). scaleX(position == 0 ? 1.0f : 1.2f). scaleY(position == 0 ? 1.0f : 1.2f). setDuration(200); } /** * 计算指示器的宽度 */ private void calculateIndicateLineWidth() { Point point = new Point(); this.getWindowManager().getDefaultDisplay().getSize(point); int screenWidth = point.x; lineWidth = screenWidth / fragments.size(); LogUtils.i("lineWidth = " + lineWidth); indicateLine.getLayoutParams().width = lineWidth; indicateLine.requestLayout(); } private long exitTime = 0; @Override public void onBackPressed() { if(System.currentTimeMillis() - exitTime > 2000){ ToastUtil.showShort(this, "再按一次退出手机影音"); exitTime =System.currentTimeMillis(); }else{ finish(); } } }
好了,先整理到这里。
<p>版权声明:本文为博主原创文章,未经博主允许不得转载。</p>