Android应用经典主界面框架之二:仿网易新闻客户端、CSDN 客户端 (Fragment ViewPager)
第二种主界面风格则是以网易新闻、凤凰新闻以及新推出的新浪博客(阅读版)为代表,使用ViewPager+Fragment,即ViewPager里适配器里放的不是一般的View,而是Fragment。所以适配器不能继承PagerAdapter,而要继承FragmentPagerAdapter,这是在android.support.v4.app.FragmentPagerAdapter包里的。有点奇葩的是,FragmentPagerAdapter只在这个包里有,在android.app.*这个包下面么有。到后面会发现,只能用android.support.v4.app.*包下面的东西。两个包里的FragmentManager是不通用的,而且两个包里提供的Fragment也不大一样。如果继承android.app.*下的Fragment,则不能重新写构造函数,只能用默认的。v4的包里么有这个限制。
下图是网易新闻、凤凰新闻、新浪博客的截图:
关于仿网易新闻客户端代码已经很多了,本人主要根据开源的这个CSDN客户端的制作,准备一步步搞下。这本是一个大牛之作发在oschina上,参考链接里分5步去实现。我看了它的代码,是染在一起的。比如要完这个导航不需要额外的三个包,而他的资源里是弄一起的。所以准备自己玩玩,顺便记录开发中的问题。
第一步:最上面的导航栏
即有“网易新闻”四个大字这一栏。布局文件head_title_panel.xml:
- <span style="font-family:Comic Sans MS;font-size:18px;"><?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@color/light_blue"
- android:orientation="horizontal" >
- <ImageView
- android:id="@+id/headIcon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginLeft="8dp"
- android:layout_marginRight="4dp"
- android:src="@drawable/biz_navigation_tab_news_pressed" />
- <ImageView
- android:id="@+id/headDivider"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginLeft="4dp"
- android:layout_marginRight="4dp"
- android:src="@drawable/base_action_bar_back_divider" />
- <TextView
- android:id="@+id/headTV"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center_vertical"
- android:layout_marginLeft="4dp"
- android:layout_weight="1"
- android:text="CSDN资讯"
- android:textColor="@color/white"
- android:textSize="21sp"
- android:textStyle="bold">
- </TextView>
- </LinearLayout></span>
为了日后操作上的方便,我将它映射成一个HeadTitlePanel.java文件,可以看到这种写法跟上篇 上下panel的定义是有点区别的。
- <span style="font-family:Comic Sans MS;font-size:18px;">package org.yanzi.ui;
- import org.yanzi.csdnproject.R;
- import android.content.Context;
- import android.util.AttributeSet;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.widget.ImageView;
- import android.widget.RelativeLayout;
- import android.widget.TextView;
- public class HeadTitlePanel extends RelativeLayout {
- private Context mContext;
- private TextView mHeadTV;
- private ImageView mHeadIcon;
- private ImageView mHeadDivider;
- public HeadTitlePanel(Context context, AttributeSet attrs) {
- super(context, attrs);
- // TODO Auto-generated constructor stub
- mContext = context;
- View parent = LayoutInflater.from(mContext).inflate(R.layout.head_title_panel, this, true);
- mHeadTV = (TextView) parent.findViewById(R.id.headTV);
- }
- }
- </span>
第二步:ViewPager的导航栏
这个本来我是准备自己封装个的,网上也有用Radiobutton封装的,考虑到它这个导航栏还是不固定长度可以滑动的,时间原因暂时不封装了,使用开源Android-ViewPagerIndicator-master.zip 这个包,这个人的github链接:https://github.com/JakeWharton 将其中的library文件夹改名ViewPagerIndicator_library导进来。这个里面有好几种Indicator,我们主要用TabPageIndicator这个。
第三步:MainActivity的布局:
activity_main.xml
- <span style="font-family:Comic Sans MS;font-size:18px;"><RelativeLayout 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="#eee"
- tools:context=".MainActivity" >
- <org.yanzi.ui.HeadTitlePanel
- android:id="@+id/head_title_panel"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true" />
- <com.viewpagerindicator.TabPageIndicator
- android:id="@+id/page_indicator"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/head_title_panel"
- android:background="@color/transparentblue" />
- <android.support.v4.view.ViewPager
- android:id="@+id/view_pager"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_below="@id/page_indicator"
- />
- </RelativeLayout></span>
MainActivity.java
- <span style="font-family:Comic Sans MS;font-size:18px;">package org.yanzi.csdnproject;
- import org.yanzi.viewpager.adapter.TabAdapter;
- import com.viewpagerindicator.TabPageIndicator;
- import android.os.Bundle;
- import android.app.Activity;
- import android.app.FragmentManager;
- import android.view.Menu;
- import android.support.v4.app.FragmentActivity;
- import android.support.v4.app.FragmentPagerAdapter;
- import android.support.v4.view.ViewPager;
- public class MainActivity extends FragmentActivity {
- private TabPageIndicator mPageIndicator;
- private ViewPager mViewPager;
- private FragmentPagerAdapter fragPagerAdapter;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- initUI();
- fragPagerAdapter = new TabAdapter(getSupportFragmentManager());
- mViewPager.setAdapter(fragPagerAdapter);
- mPageIndicator.setViewPager(mViewPager, 0);
- }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(R.menu.main, menu);
- return true;
- }
- private void initUI(){
- mPageIndicator = (TabPageIndicator)findViewById(R.id.page_indicator);
- mViewPager = (ViewPager)findViewById(R.id.view_pager);
- }
- }
- </span>
出奇的简单,比单纯用Fragment还简单,原因是只需把Fragment塞到适配器里就ok了,适配器为我们做了Fragment的切换等工作,我们能做的也就是在适配器里new Fragment的时候判断是否已存在。以下几点需要注意:
1、在styles.xml里它定义了样式:
- <span style="font-family:Comic Sans MS;font-size:18px;"> <style name="MyTheme" parent="AppBaseTheme">
- <item name="vpiTabPageIndicatorStyle">@style/MyWidget.TabPageIndicator</item>
- <item name="android:windowBackground">@drawable/init_pic</item>
- <item name="android:windowNoTitle">true</item>
- <item name="android:animationDuration">5000</item>
- <item name="android:windowContentOverlay">@null</item>
- </style>
- <style name="MyWidget.TabPageIndicator" parent="Widget">
- <item name="android:gravity">center</item>
- <item name="android:background">@drawable/vpi__tab_indicator</item>
- <item name="android:paddingLeft">22dip</item>
- <item name="android:paddingRight">22dip</item>
- <item name="android:paddingTop">8dp</item>
- <item name="android:paddingBottom">8dp</item>
- <item name="android:textAppearance">@style/MyTextAppearance.TabPageIndicator</item>
- <item name="android:textSize">16sp</item>
- <item name="android:maxLines">1</item>
- </style>
- <style name="MyTextAppearance.TabPageIndicator" parent="Widget">
- <item name="android:textStyle">bold</item>
- <item name="android:textColor">@color/black</item>
- </style></span>
这个是依赖于导进去的包的。
2、它这里用了android:windowBackground的属性,所以app开启瞬间会有图片弹出,之后设置MainActivity的布局背景为android:background="#eee",又把图片替换了。如果不设android:background="#eee" 会一直看到这个图片不消失。
3、因为开篇讲的原因,MainActivity只能继承自FragmentActivity。
第四步:MainFragment.java,此类继承Fragment,且是android.support.v4.app.Fragment下的。
- <span style="font-family:Comic Sans MS;font-size:18px;">package org.yanzi.fragment;
- import org.yanzi.csdnproject.R;
- import android.os.Bundle;
- import android.support.v4.app.Fragment;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.TextView;
- public class MainFragment extends Fragment {
- private int mNewsType = 0;
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- View v = inflater.inflate(R.layout.tab_item_fragment_main, null);
- TextView tip = (TextView) v.findViewById(R.id.id_tip);
- Bundle b = getArguments();
- String title = b.getString("TITLES");
- tip.setText(title);
- return v;
- }
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- super.onActivityCreated(savedInstanceState);
- }
- }
- </span>
就是弄了一个布局,然后从中取得参数并显示。
第五步:ViewPager的适配器TabAdapter.java
- <span style="font-family:Comic Sans MS;font-size:18px;">package org.yanzi.viewpager.adapter;
- import org.yanzi.constant.Constant;
- import org.yanzi.fragment.MainFragment;
- import android.os.Bundle;
- import android.support.v4.app.Fragment;
- import android.support.v4.app.FragmentManager;
- import android.support.v4.app.FragmentPagerAdapter;
- public class TabAdapter extends FragmentPagerAdapter {
- public TabAdapter(FragmentManager fm) {
- super(fm);
- // TODO Auto-generated constructor stub
- }
- @Override
- public Fragment getItem(int position) {
- // TODO Auto-generated method stub
- MainFragment fragment = new MainFragment();
- Bundle b = new Bundle();
- String title = Constant.TITLES[position];
- b.putString("TITLES", title);
- fragment.setArguments(b);
- return fragment;
- }
- @Override
- public CharSequence getPageTitle(int position) {
- // TODO Auto-generated method stub
- return Constant.TITLES[position];
- }
- @Override
- public int getCount() {
- // TODO Auto-generated method stub
- return Constant.TITLES.length;
- }
- }
- </span>
主要是重写getItem,在这里实例化Fragment,同时设传递的参数。其实这里可以通过
- <span style="font-family:Comic Sans MS;font-size:18px;">FragmentManager</span>
按照Tag查找对应的Fragment是否存在,再进行实例化。
另外,getPageTitle这个接口必须重写,其实看TabPageIndicator.java这个类的实现可以发现,它需要传进去一个ViewPager,然后获得适配器,从适配器里得到Title。因为用了这个开源包,不能再用ViewPager的setOnPageChangeListener接口,只能用mPageIndicator.setOnPageChangeListener(listener)进行监听。
完毕,源码链接:链接:http://pan.baidu.com/s/1bn4EFbp 密码:dx4s
效果如下:
http://blog.csdn.net/yanzi1225627/article/details/31462007#comments