尊重劳动成果,转载请注明出处:http://blog.csdn.net/growth58/article/details/48382913
关注新浪微博:@于卫国
邮箱:yuweiguocn@gmail.com
Google在2015 I/O大会上公布了Material Design Support Library,使用它可以创建materail应用在API 19以下突然变得很容易。在这个系列中,我们将使用RSS阅读器应用,我们用于Material系列的基础应用,重写让它完全使用新的Design Support Library。
在前面我们做了基本的navigation drawer,在这篇文章中我们将学习怎样实现一个tab bar。
ViewPager已经存在有一段时间并且是一个很好使用和理解的组件,因此我很乐意在这里对它做一个完整的说明。让我们看看在ViewPager中使用Fragment的实现。RSS订阅中每个item的内容是基本的HTML,因此我们将使用WebView来控制渲染(请不要讨厌我对这个渲染是多么可怕——它是基本的HTML没有任何CSS呈现进行渲染的。本文的重点是关于内容周围发生的东西而不是怎样渲染内容本身)。
让我们看看fragment布局:
res/layout/fragment_item.xml
<WebView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
那是相当直接的,也让我们再看一下Fragment 的实现:
ItemFragment.java
public class ItemFragment extends Fragment {
private static final String KEY_ITEM = "ARG_ITEM";
public static final String NEWLINE = "\\n";
public static final String BR = "<br />";
public static final String HTML_MIME_TYPE = "text/html";
public static Fragment newInstance(Context context, Item item) {
Bundle args = new Bundle();
args.putSerializable(KEY_ITEM, item);
return Fragment.instantiate(context, ItemFragment.class.getName(), args);
}
@Override
@SuppressLint("SetJavaScriptEnabled")
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Item item = (Item) getArguments().getSerializable(KEY_ITEM);
View view = inflater.inflate(R.layout.fragment_item, container, false);
if (item != null) {
WebView webView = (WebView) view.findViewById(R.id.web_view);
String html = item.getContent();
html = html.replaceAll(NEWLINE, BR);
webView.getSettings().setUseWideViewPort(true);
webView.getSettings().setLoadWithOverviewMode(true);
webView.getSettings().setJavaScriptEnabled(true);
webView.loadData(html, HTML_MIME_TYPE, null);
}
return view;
}
}
还是很直接——进入一个Item,内容被渲染在WebView中。唯一值得注意的是我们替换了换行为了提高渲染。我们也调用了setUseViewPortMode(true) 和setLoadWithOverviewMote(true)方法在启动时缩小(所以整个内容的宽可以被显示)。这是为移除所有的水平滚动,它将会影响ViewPager容器中的操作。(再次说明,不要讨厌我——ViewPager 是本文的焦点因此我们将修复WebView 渲染以免干扰)。
现在我们已经定义了ViewPager 中的pages ,让我们看看关联到ViewPager上的Adapter:
ArticleViewPagerAdapter.java
public final class ArticleViewPagerAdapter extends FragmentPagerAdapter {
private final Article article;
private final Context context;
private final Resources resources;
public static ArticleViewPagerAdapter newInstance(FragmentManager fragmentManager, Context context, Article article) {
Resources resources = context.getResources();
return new ArticleViewPagerAdapter(fragmentManager, context, resources, article);
}
private ArticleViewPagerAdapter(FragmentManager fragmentManager, Context context, Resources resources, Article article) {
super(fragmentManager);
this.context = context;
this.resources = resources;
this.article = article;
}
@Override
public Fragment getItem(int position) {
Item item = article.getPartAtPosition(position);
return ItemFragment.newInstance(context, item);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
FragmentManager manager = ((Fragment) object).getFragmentManager();
FragmentTransaction trans = manager.beginTransaction();
trans.remove((Fragment) object);
trans.commit();
super.destroyItem(container, position, object);
}
@Override
public int getCount() {
return article.getPartsCount();
}
@Override
public CharSequence getPageTitle(int position) {
return resources.getString(R.string.part_title, article.getPartNumber(position));
}
}
这是一个很普通的Adapter 实现。唯一值得注意的是包含了一个getPageTitle()方法的实现(我们用 “Part X”形式生成了一个标题)。使用过v4 support library中PagerTitleStrip 的人一定熟悉这个,并且新的TabLayout 的实现使用完全相同的机制得到跳转的每个 tab item的文本。
下面我们需要看一下主布局。之前我们定义了一个FrameLayout 包含Toolbar,现在我们需要添加TabLayout 和 ViewPager:
res/layout/include_main.xml
<?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"
android:id="@+id/main_layout"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:clipToPadding="false"
android:paddingLeft="@dimen/home_offset"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:tabMode="scrollable" />
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
在这我们已经添加了TabLayout ——这个控件将给我们做好所有工作!我们已经选择了一个操作模式——fixed:tabs 是动态的大小,因此TabLayout 是完全适配可用的空间;还有scrollable:tabs 是一个固定的大小,如果它们超过可用空间用户可以滚动tabs获取所有。
一般情况下tabs的个数相对少的时候使用fixed模式可以很好的展现,但当tabs的个数太多时使用scrollable模式效果最佳。在这个应用的每篇文章中items 的数量是不定的并且有时可能很大(在示例视频中Dirty Phrasebook包含6部分),因此我选择了scrollable模式。
因为我选择了scrollable 模式,我实际上想让第一个tab文本左边和Toolbar的标题文本对齐,因此我设置了android:paddingLeft来实现这个。当我们滚动tabs时通过设置android:clipToPadding=”false” tabs 将会滚动到左边空白区域。
在布局文件中就还只剩下我们的ViewPager组件。
剩下的工作就是在Activity文件进行关联:
MainActivity.java
public class MainActivity extends AppCompatActivity implements ArticlesConsumer {
private static final String DATA_FRAGMENT_TAG = DataFragment.class.getCanonicalName();
private static final int MENU_GROUP = 0;
private DrawerLayout drawerLayout;
private NavigationView navigationView;
private ViewPager viewPager;
private TabLayout tabLayout;
private Articles articles;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
navigationView = (NavigationView) findViewById(R.id.nav_view);
viewPager = (ViewPager) findViewById(R.id.viewpager);
tabLayout = (TabLayout) findViewById(R.id.tab_layout);
setupToolbar();
setupNavigationView();
setupDataFragment();
}
.
.
.
private void setCurrentArticle(Article article) {
setTitle(article.getTitle());
ArticleViewPagerAdapter adapter = ArticleViewPagerAdapter.newInstance(getSupportFragmentManager(), this, article);
viewPager.setAdapter(adapter);
if (article.getPartsCount() <= 1) {
tabLayout.setVisibility(View.GONE);
} else {
tabLayout.setVisibility(View.VISIBLE);
}
tabLayout.setupWithViewPager(viewPager);
}
}
当选择一个新的Article时所有的工作在setCurrentArticle()方法中完成。首先我们设置一个新ArticleViewPagerAdapter 实例基于新的Article,然后我们应用到了ViewPager。紧接着做了一个快速检查如果article中只有一部分隐藏TabLayout ,否则显示TabLayout 。最后我们将TabLayout关联到初始了tabs的ViewPager 上,并且关联了所有的 paging / tab的选择逻辑。这是一次强有力的方法调用!
运行我们可以看到我们已经完全实现了tab bar:
然而这还有一个很微妙的问题。tab bar支持在已选择的tab的最下面有一个指示条。这个消失的原因是我们在主题中定义了colorAccent值:
res/values/styles.xml
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Base.AppTheme" />
<style name="Base.AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/sa_green</item>
<item name="colorPrimaryDark">@color/sa_green_dark</item>
<item name="colorAccent">@color/sa_green</item>
<item name="colorControlHighlight">@color/sa_green_transparent</item>
</style>
</resources>
我们声明了同样的颜色值作为colorPrimary。colorPrimary 将会被用作AppBarLayout背景颜色,colorAccent 将被用作指示条的颜色。因为它们的颜色是相同的,所以我们的指示条没有显示。现在我们将colorAccent 设置为白色(但这将会导致一个问题和FloatingActionButton一块使用时,我们遇到时再做处理 ):
res/values/styles.xml
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Base.AppTheme" />
<style name="Base.AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/sa_green</item>
<item name="colorPrimaryDark">@color/sa_green_dark</item>
<item name="colorAccent">@android:color/white</item>
<item name="colorControlHighlight">@color/sa_green_transparent</item>
</style>
</resources>
现在我们可以看到指示条已经起作用:
看起来相当不错,但对于如果我们可以做一个非常material化的东西它还不够好,比如向下滚动Toolbar 消失,还有提供一个快速返回作为用户向上滚动返回。在下一篇文章我们将实现这些。
源代码可以从这下载。
请我喝杯咖啡,请使用支付宝扫描下方二维码: