技术拆分
Fragment
Fragment相信大家都不陌生。此处我们用它来存放页面主体所要显示的内容。
实现方式:
在自定义的Fragment类中,重写OnCreateView()方法。在该方法中通过LayoutInflater来Inflate该Fragment的布局文件,赋值给View对象并返回该对象。
Fragment的难点:生命周期。
为了照顾Newcomer,转载一段关于Fragment的介绍:
Android运行在各种各样的设备中,有小屏幕的手机,超大屏的平板甚至电视。针对屏幕尺寸的差距,很多情况下,都是先针对手机开发一套App,然后拷贝一份,修改布局以适应平板神马超级大屏的。难道无法做到一个App可以同时适应手机和平板么,当然了,必须有啊。Fragment的出现就是为了解决这样的问题。你可以把Fragment当成Activity的一个界面的一个组成部分,甚至Activity的界面可以完全有不同的Fragment组成,更帅气的是Fragment拥有自己的生命周期和接收、处理用户的事件,这样就不必在Activity写一堆控件的事件处理的代码了。更为重要的是,你可以动态的添加、替换和移除某个Fragment。
FragmentTabhost
实现标签卡的切换。通过简单的封装替代早期使用的TabHost(Android3.0以后被废弃)。
实现方式:
1. 通过findViewById()方法实例化FragmentTabhost对象
2. 通过setup(Context context, FragmentManager manager, int containerId)方法初始化对象
3. 通过给每个Tab按钮设置标签、图标和文字,并添加至Tab选项卡中,同时绑定Fragment
4. 通过setOnTabChangedListener(OnTabChangedListener listener)监听Tab按钮的改变,控制显示相应的Fragment
ViewPager
作为Fragment的实际容器,用于左右切换当前的View(即实现Fragment的滑动切换效果)。
实现方式:
1. 通过findViewById()方法实例化ViewPager对象
2. 设置并绑定FragmentPagerAdapter适配器。
3. 通过setOnPageChangedListener(OnPageChangedListener listener)来监听ViewPager显示内容的变化,控制FragmentTabHost标签随之切换
代码示例
布局文件
activity_main.xml(MainActivity布局文件)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.wolfgy.test.fragmenttabhostviewpagerfragment.MainActivity">
<android.support.v4.app.FragmentTabHost
android:id="@+id/tabhost"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
/>
</LinearLayout>
content_tab.xml(菜单栏布局文件)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tab_text"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
fragment_first.xml(Fragment布局文件)
(其他Fragment布局文件不重复列出)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="FirstFrom三汪"/>
</LinearLayout>
Java代码
FragmentFirst.java(自定义每个Fragment)
(不重复列出其他Fragment代码文件)
package com.wolfgy.test.fragmenttabhostviewpagerfragment;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class FragmentFirst extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_first,container,false);
return view;
}
}
MyFragmentPagerAdapter.java(ViewPager适配器)
package com.wolfgy.test.fragmenttabhostviewpagerfragment;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.util.Log;
import android.view.ViewGroup;
import java.util.List;
public class MyFragmentPagerAdapter extends FragmentPagerAdapter {
List<Fragment> list;
public MyFragmentPagerAdapter(FragmentManager fm,List list){
super(fm);
this.list = list;
}
@Override
public Fragment getItem(int position) {
return list.get(position);
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment)super.instantiateItem(container, position);
return fragment;
}
}
MainActivity.java(MainActivity代码)
package com.wolfgy.test.fragmenttabhostviewpagerfragment;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTabHost;
import android.support.v4.view.ViewPager;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.TabHost;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends FragmentActivity {
FragmentTabHost fragmentTabHost;
ViewPager viewPager;
String[] titles = {"first","second","third"};
Class[] classes = {FragmentFirst.class,FragmentSecond.class,FragmentThird.class};
List<Fragment> list = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化Fragment并放入集合
initFragment();
//获取屏幕宽高
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
int height = dm.heightPixels;
int width = dm.widthPixels;
//初始化ViewPager
viewPager = (ViewPager)findViewById(R.id.viewpager);
viewPager.setAdapter(new MyFragmentPagerAdapter (getSupportFragmentManager(),list));
//初始化FragmentTabHost
fragmentTabHost = (FragmentTabHost) findViewById(R.id.tabhost);
fragmentTabHost.setup(this,getSupportFragmentManager (),R.id.viewpager);
int arrayCount = titles.length;
for (int i=0;i<arrayCount;i++){
//给每个Tab设置标签、内容
TabHost.TabSpec tabSpec = fragmentTabHost.newTabSpec(titles [i]).setIndicator(getTabItemView(i));
//将Tab绑定进Tab选项卡中,并绑定Fragment
fragmentTabHost.addTab(tabSpec,classes[i],null);
fragmentTabHost.setTag(i);
}
//当前标签改变监听
fragmentTabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
@Override
public void onTabChanged(String s) {
int position = fragmentTabHost.getCurrentTab();
viewPager.setCurrentItem(position);
}
});
//设置viewPager改变监听
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener () {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
fragmentTabHost.setCurrentTab(position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
});
}
private void initFragment(){
Fragment fragment1 = new FragmentFirst();
Fragment fragment2 = new FragmentSecond();
Fragment fragment3 = new FragmentThird();
list.add(fragment1);
list.add(fragment2);
list.add(fragment3);
}
/**
* 初始化并返回Tab子布局
*/
private View getTabItemView(int position){
View view = LayoutInflater.from(MainActivity.this).inflate (R.layout.content_tab,null);
TextView textView = (TextView) view.findViewById(R.id.tab_text);
textView.setText(titles[position]);
return view;
}
}
细节说明
ViewPager和FragmentTabHost的初始化顺序
刚开始接触到这个的时候,出现了一个我百思不得其解的Bug——ClassCastException,浪费了我不少时间。
系统提示是这样的:
android.widget.FrameLayout cannot be cast to android.support.v4.view.ViewPager
看了很多论坛和网站的相关问题。基本都是实例化布局的时候,引用了错误的布局ID。但显然我犯的并不是这种低级错误 。
最后在自己调试的时候,我发现把ViewPager和FragmentTabHost的初始化顺序改变一下,就不报错了。
情况应该是FragmentTabHost在初始化的时候自动给Fragment定义了FrameLayout容器,在后面要把Fragment放入ViewPager的时候便无法通过。而如果先把ViewPager进行初始化以及绑定FragmentPagerAdapter,此时初始化FragmentTabHost,则自然而然地选择了ViewPager作为Fragment的容器。
因此,在使用的时候一定要注意二者的初始化顺序。
Fragment的显示顺序
不知道为什么。标签页默认从第二页开始显示,试过在MainActivity里设置完适配器以后用
viewPager.setCurrentItem(0);
如我所料,没有任何效果。这是我碰到的第一个问题。
如果仁兄知道原因和解决方式,欢迎评论分享。多谢。
Fragment的重叠
如果你是自己手打的代码,不是copy我的代码,你可能会遇到三个Fragment重叠在一起显示的问题。对于这种情况,我的解决方式是给每个Fragment布局添加背景色。这样就不会重叠显示了。
Fragment的大小问题
当你运行demo的时候你可能会发现Fragment的大小不能充满屏幕(如果你如上文所说添加了背景色并且Fragment布局文件中的内容不能够充满屏幕时),就算。
在Fragment的java代码中,我一开始是这样子重写onCreateView()方法的
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_first,null);
return view;
}
熟悉LayoutInflater的朋友都知道,这种写法会导致该布局中的layout__height等一系列大小参数失效,变成wrap_content。
因此我把它改成了
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_first,container,false);
return view;
}
但是也没有任何效果。
我想可能是因为我们装Fragment的容器是ViewPager,相对来说比较特殊。
我的下一个尝试是在MainActivity中获取屏幕宽高,然后通过setArguments方法传入屏幕宽高,然后给Fragment的view设置setMinimumHeight和setMinimumWidth。(你还能在我上传的代码中看到没有删除的获取屏幕宽高的代码)
但是也没有效果。若是有仁兄知道解决方法,也欢迎分享。拜谢再三。
相应的代码片段如下:
这是在MainActivity的onCreate()方法中的代码
//获取屏幕宽高
DisplayMetrics dm = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
height = dm.heightPixels;
width = dm.widthPixels;
//初始化Fragment并放入集合
initFragment();
这是初始化Fragment的方法
private void initFragment(){
Bundle bundle = new Bundle();
bundle.putInt("height",height);
bundle.putInt("width",width);
Fragment fragment1 = new FragmentFirst();
Fragment fragment2 = new FragmentSecond();
Fragment fragment3 = new FragmentThird();
fragment1.setArguments(bundle);
fragment2.setArguments(bundle);
fragment3.setArguments(bundle);
list.add(fragment1);
list.add(fragment2);
list.add(fragment3);
}
Fragment类
package com.wolfgy.test.fragmenttabhostviewpagerfragment;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by Administrator on 2017/1/2.
*/
public class FragmentFirst extends Fragment {
int height,width;
Bundle bundle;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflat(R.layout.fragment_first,container,false);
bundle = getArguments();
height = bundle.getInt("height");
width = bundle.getInt("width");
view.setMinimumHeight(height);
view.setMinimumWidth(width);
return view;
}
}