【0080】【项目实战】-【智慧北京】-【02】【fragment-UI框架搭建】-【主页底栏标签开发】-【创建/填充5个标签页面】-【ViewPager的禁用滑动】-【侧边栏开启和禁用】-【使用Xutils】-【使用Gson解析Json】-【网络缓存】-【设置侧边栏数据】-【点击侧边栏切换菜单详情页】

【说明】

【1】项目的UI框架比较重要;在一开始就将MainActivity一分为二;

【2】主要使用的fragment,本项目中使用的Activity极少;

【3】创建一个基类BasePager,将View先加载好,然后对frameLayout的内容进行添加;

1.使用fragment搭建UI框架

1.1 fragment的认识

【fragment】【编程的思想】高内聚,低耦合;碎片化管理;在Android4.0之后大力推广的;

【fragment的生命周期】成对记忆:Activity存在的生命周期fragment都有,还多了三对,最关键的是记住;onCreateView();

【Activity生命周期6个】成对记忆:onCreate-onDestory;onStrat-onStop;onPause-onResume;

1.2 项目中使用fragment

【说明】根据布局,需要创建两个fragment,LeftMenuFragment和ContentFragment

【思路】创建一个共有的BaseFragment,写共有的方法,然后被继承;

【基类源码】【源码】/zhbj74/src/com/itheima/zhbj74/fragment/BaseFragment.java

 1 package com.itheima.zhbj74.fragment;
 2 
 3 import android.app.Activity;
 4 import android.os.Bundle;
 5 import android.support.v4.app.Fragment;
 6 import android.view.LayoutInflater;
 7 import android.view.View;
 8 import android.view.ViewGroup;
 9 
10 public abstract class BaseFragment extends Fragment {
11 
12     public Activity mActivity;//这个activity就是MainActivity
13 
14     // Fragment创建
15     @Override
16     public void onCreate(Bundle savedInstanceState) {
17         super.onCreate(savedInstanceState);
18         mActivity = getActivity();// 获取当前fragment所依赖的activity
19     }
20 
21     // 初始化fragment的布局
22     @Override
23     public View onCreateView(LayoutInflater inflater, ViewGroup container,
24             Bundle savedInstanceState) {
25         View view = initView();
26         return view;
27     }
28 
29     // fragment所依赖的activity的onCreate方法执行结束
30     @Override
31     public void onActivityCreated(Bundle savedInstanceState) {
32         super.onActivityCreated(savedInstanceState);
33         // 初始化数据
34         initData();
35     }
36 
37     // 初始化布局, 必须由子类实现
38     public abstract View initView();
39 
40     // 初始化数据, 必须由子类实现
41     public abstract void initData();
42 }

【左侧边栏布局】

【主页面布局】

2.【替换】本身已经具有侧边栏的效果,现在使用fragment替换掉

【替换的思路】将原来的内容删除掉,加载现在的fragment内容;

【主页面掏空】

【frameLayout】此布局不会出现自动增加布局的排列的内容;

【侧边栏掏空】

【效果如下】

【关联源码】引用库之后的关联源码的方法,增加一个配置文件;重启eclipse;

【替换源码】/zhbj74/src/com/itheima/zhbj74/MainActivity.java

 1 package com.itheima.zhbj74;
 2 
 3 import android.os.Bundle;
 4 import android.support.v4.app.FragmentManager;
 5 import android.support.v4.app.FragmentTransaction;
 6 import android.view.Window;
 7 
 8 import com.itheima.zhbj74.fragment.ContentFragment;
 9 import com.itheima.zhbj74.fragment.LeftMenuFragment;
10 import com.jeremyfeinstein.slidingmenu.lib.SlidingMenu;
11 import com.jeremyfeinstein.slidingmenu.lib.app.SlidingFragmentActivity;
12 
13 /**
14  * 主页面
15  * 
16  * @author Kevin
17  * @date 2015-10-17
18  */
19 public class MainActivity extends SlidingFragmentActivity {
20 
21     private static final String TAG_LEFT_MENU = "TAG_LEFT_MENU";
22     private static final String TAG_CONTENT = "TAG_CONTENT";
23 
24     @Override
25     public void onCreate(Bundle savedInstanceState) {
26         super.onCreate(savedInstanceState);
27         requestWindowFeature(Window.FEATURE_NO_TITLE);// 去掉标题,
28         // 必须在setContentView之前调用
29         setContentView(R.layout.activity_main);
30 
31         // Utils.doSomthing();
32         // R.drawable.p_10
33         setBehindContentView(R.layout.left_menu);
34         SlidingMenu slidingMenu = getSlidingMenu();
35         slidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);// 全屏触摸
36         slidingMenu.setBehindOffset(200);// 屏幕预留200像素宽度
37 
38         initFragment();
39     }
40 
41     /**
42      * 初始化fragment
43      */
44     private void initFragment() {
45         FragmentManager fm = getSupportFragmentManager();
46         FragmentTransaction transaction = fm.beginTransaction();// 开始事务
47         transaction.replace(R.id.fl_left_menu, new LeftMenuFragment(),
48                 TAG_LEFT_MENU);// 用fragment替换帧布局;参1:帧布局容器的id;参2:是要替换的fragment;参3:标记
49         transaction.replace(R.id.fl_main, new ContentFragment(), TAG_CONTENT);
50         transaction.commit();// 提交事务
51         // Fragment fragment =
52         // fm.findFragmentByTag(TAG_LEFT_MENU);//根据标记找到对应的fragment
53     }

【效果】可以使用fragment加载出主页面和侧边栏;

 

3.主页底栏标签开发

3.1 布局分析及完成

【说明】在上半部分的页面一次性加载五个页面,通过下面的点击进行切换;

3.2 布局完成

【去掉ViewButton的小圆圈】

 【低栏灰色的背景的添加】

【文字上面的图片的添加】

【单个效果的完成】

【样式的抽取】名字和图片没有必要进行抽取;

 

【文字选中图片的改变】图片都会改变颜色,需要创建5个状态选择器,因为图片都不一致;

【文字选中的颜色的改变】修改一个状态选择器,只需要在样式中改变即可;

【布局源码】

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical" >
 6 
 7     <com.itheima.zhbj74.view.NoScrollViewPager
 8         android:id="@+id/vp_content"
 9         android:layout_width="match_parent"
10         android:layout_height="0dp"
11         android:layout_weight="1" />
12 
13     <RadioGroup
14         android:id="@+id/rg_group"
15         android:layout_width="match_parent"
16         android:layout_height="wrap_content"
17         android:background="@drawable/bottom_tab_bg"
18         android:gravity="center"
19         android:orientation="horizontal" >
20 
21         <RadioButton
22             android:id="@+id/rb_home"
23             style="@style/Bottom_Tab_Style"
24             android:drawableTop="@drawable/tab_home_selector"
25             android:checked="true"
26             android:text="首页" />
27 
28         <RadioButton
29             android:id="@+id/rb_news"
30             style="@style/Bottom_Tab_Style"
31             android:drawableTop="@drawable/tab_news_selector"
32             android:text="新闻中心" />
33 
34         <RadioButton
35             android:id="@+id/rb_smart"
36             style="@style/Bottom_Tab_Style"
37             android:drawableTop="@drawable/tab_smart_selector"
38             android:text="智慧服务" />
39 
40         <RadioButton
41             android:id="@+id/rb_gov"
42             style="@style/Bottom_Tab_Style"
43             android:drawableTop="@drawable/tab_gov_selector"
44             android:text="政务" />
45 
46         <RadioButton
47             android:id="@+id/rb_setting"
48             style="@style/Bottom_Tab_Style"
49             android:drawableTop="@drawable/tab_setting_selector"
50             android:text="设置" />
51     </RadioGroup>
52 
53 </LinearLayout>

【效果】存在bug,五个可以同时选中;问题的原因:没有设置id,无法区分单个的button;

【增加id】

【效果】

【添加默认首页被选中的状态】

【效果】默认首页被选中,可以单独的切换5个页面;

4.创建5个标签页面

4.1 标签页面的结构分析

【说明】外面是大的线性布局,里面嵌套相对布局和帧布局;

4.2  【标题头布局的完成】

 

【源码】

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical" >
 6 
 7     <RelativeLayout
 8         android:layout_width="match_parent"
 9         android:layout_height="wrap_content"
10         android:background="@drawable/title_red_bg" >
11 
12         <TextView
13             android:id="@+id/tv_title"
14             android:layout_width="wrap_content"
15             android:layout_height="wrap_content"
16             android:layout_centerHorizontal="true"
17             android:layout_centerVertical="true"
18             android:text="智慧北京"
19             android:textColor="#fff"
20             android:textSize="22sp" />
21 
22         <ImageButton
23             android:id="@+id/btn_menu"
24             android:layout_width="wrap_content"
25             android:layout_height="wrap_content"
26             android:layout_alignParentLeft="true"
27             android:layout_centerVertical="true"
28             android:layout_marginLeft="10dp"
29             android:background="@null"
30             android:src="@drawable/img_menu" />
31     </RelativeLayout>
32 
33 </LinearLayout>

4.3 下半部分帧布局的添加

 

4.4 标题的抽取

 

4.5 标签基类的书写

【说明】因为存在不同的标签页,可以使用一个基类完成,然后继承;

【说明】此类就是一个单纯的java类,不存在与Activity对应的关系;

 1 package com.itheima.zhbj74.base;
 2 
 3 import android.app.Activity;
 4 import android.view.View;
 5 import android.view.View.OnClickListener;
 6 import android.widget.FrameLayout;
 7 import android.widget.ImageButton;
 8 import android.widget.TextView;
 9 
10 import com.itheima.zhbj74.MainActivity;
11 import com.itheima.zhbj74.R;
12 import com.jeremyfeinstein.slidingmenu.lib.SlidingMenu;
13 
14 /**
15  * 五个标签页的基类
16  * 
17  * @author Kevin
18  * @date 2015-10-18
19  */
20 public class BasePager {
21 
22     public Activity mActivity;
23 
24     public TextView tvTitle;
25     public ImageButton btnMenu;
26     public FrameLayout flContent;// 空的帧布局对象, 要动态添加布局
27 
28     public View mRootView;// 当前页面的布局对象
29 
30     public BasePager(Activity activity) {
31         mActivity = activity;
32         mRootView = initView();
33     }
34 
35     // 初始化布局
36     public View initView() {
37         View view = View.inflate(mActivity, R.layout.base_pager, null);
38         tvTitle = (TextView) view.findViewById(R.id.tv_title);
39         btnMenu = (ImageButton) view.findViewById(R.id.btn_menu);
40         flContent = (FrameLayout) view.findViewById(R.id.fl_content);
41         return view;
42     }
43 
44     // 初始化数据
45     public void initData() {
46 
47     }
48 }

4.6 子类的书写

【说明】子类的任务主要是在帧布局中动态的添加内容

 

5.填充5个标签页

5.1 将Viewpager与fragment联系起来

【说明】找到需要添充内容的控件;

【说明】ViewPager的适配器

【说明】初始化Activity,并添加到Viewpager中;

【小技巧】将标题栏和帧布局的View对象添加到容器中;

【效果】可以进行滑动了,存在了5个标签页;但是帧布局的内容没有显示;

【修改】原因是initData方法没有被调用,设置的文字无法显示;

【效果】帧布局中的内容出现了滚动改变;

5.2 标题的改变

【说明】需要依次修改5处;

【效果】标题栏的内容随着滚动不停的改变;

6.ViewPager的禁用滑动

【说明】ViewPage此处需要禁止滑动,只容许点击生效;

【禁用原理】实现自定义的Viewpager,重写onTouchEvent方法,在此方法中什么都不做; 

【效果】无法滑动了,低栏的图标的切换没有关联起来,没有效果;

6.点击标签切换页面

【说明】点击切换ImageButton可以切换Viewpager的内容;

【原理】设置ImageButton的监听事件;

【效果】存在动画

 【去动画】将切换动画的【mViewPager.setCurrentItem(0, false);// 参2:表示是否具有滑动动画】第2个参数改为false;

7.性能优化

【存在的问题】会提前加载下一个Viewpager的页面,或者上一个页面,会浪费流量和机器的性能;

 

【修改1】

【方法1】繁琐,需要书写多次;

【方法2】推荐使用该方法;

【效果】刚开始默认的首页没有调用数据,需要手动加载调用;

【修改】手动加载第一页;

 

【效果】首页自动加载,点击切换功能完善,也不会提前加载下一个页面了;

8.侧边栏开启和禁用

8.1【说明】

【现在情形】但是现在在每个标签都存在侧边栏;

【实际使用的情形】只有在新闻中心、智慧服务、政府中可以滑动选择出来;

8.2 隐藏菜单按钮

【效果】

 

8.3 禁用侧边栏

【说明】判断是第一个或者是最后一个的时候禁用侧边栏;

【说明】禁用侧边栏需要拿到MainActivity的SlideMenu对象;

 

【效果】即使修改了仍然可以滑动出现侧边栏;

【原因及修改】该方法第一次没有被调用;

【效果】只有中间的三个标签可以调用;

9.使用Xutils访问服务器获取json

9.1 新闻中心的样式

【说明】所有的数据全部来源于网络;

9.2 服务器中的资源

【项目需要使用到的资源】

【项目资源在服务器上的位置】

【返回码】与响应码无关;

9.3 XUtils-请求服务器拿数据

【说明】在切换到新闻中心按钮的时候去拿服务器的数据;

【导入库】

【网络请求的使用】

【新建类:维护项目中的常量】优化:域名可能发生改变;域名不能说话那个localhost,因为手机与pc不在统一本地;

【使用模拟器预留的ip地址】10.0.2.2;问:真机该怎么办?

【域名的添加】

【增加权限】

【将请求的数据显示打印出来】

【请求数据的效果】将服务器中的数据顺利读取下来了;

10.使用Gson解析Json

【导入Gson的jar包】

【源码】/zhbj74/src/com/itheima/zhbj74/domain/NewsMenu.java

【说明】要与每个字段都一一对应;

 1 package com.itheima.zhbj74.domain;
 2 
 3 import java.util.ArrayList;
 4 
 5 /**
 6  * 分类信息封装
 7  * 
 8  * 使用Gson解析时,对象书写技巧: 
 9  * 1. 逢{}创建对象, 逢[]创建集合(ArrayList) 
10  * 2. 所有字段名称要和json返回字段高度一致
11  * 
12  * @author Kevin
13  * @date 2015-10-18
14  */
15 public class NewsMenu {
16 
17     public int retcode;
18     public ArrayList<Integer> extend;
19     public ArrayList<NewsMenuData> data;
20 
21     // 侧边栏菜单对象
22     public class NewsMenuData {
23         public int id;
24         public String title;
25         public int type;
26 
27         public ArrayList<NewsTabData> children;
28 
29         @Override
30         public String toString() {
31             return "NewsMenuData [title=" + title + ", children=" + children
32                     + "]";
33         }
34     }
35 
36     // 页签的对象
37     public class NewsTabData {
38         public int id;
39         public String title;
40         public int type;
41         public String url;
42 
43         @Override
44         public String toString() {
45             return "NewsTabData [title=" + title + "]";
46         }
47 
48     }
49 
50     @Override
51     public String toString() {
52         return "NewsMenu [data=" + data + "]";
53     }
54 
55 }

 【点击新闻按钮测试】

 

11.网络缓存

11.1服务器数据的缓存

【说明】数据的缓冲:将url的地址作为Key,将json数据作为value缓存之后封装起来;

【书写一个缓存工具类】/zhbj74/src/com/itheima/zhbj74/utils/CacheUtils.java

【说明】使用SP类;写缓存和读缓存;

 1 package com.itheima.zhbj74.utils;
 2 
 3 import android.content.Context;
 4 
 5 /**
 6  * 网络缓存工具类
 7  * 
 8  * @author Kevin
 9  * @date 2015-10-18
10  */
11 public class CacheUtils {
12 
13     /**
14      * 以url为key, 以json为value,保存在本地
15      * 
16      * @param url
17      * @param json
18      */
19     public static void setCache(String url, String json, Context ctx) {
20         //也可以用文件缓存: 以MD5(url)为文件名, 以json为文件内容
21         PrefUtils.setString(ctx, url, json);
22     }
23 
24     /**
25      * 获取缓存
26      * 
27      * @param url
28      * @param ctx
29      * @return
30      */
31     public static String getCache(String url, Context ctx) {
32         //文件缓存: 查找有没有一个文件叫做MD5(url)的, 有的话,说明有缓存
33         return PrefUtils.getString(ctx, url, null);
34     }
35 }

 【数据请求逻辑】如果缓存当中具有数据则直接解析json数据,否则请求数据在解析;

【写缓存】在服务器的数据请求成功之后写缓存;

【源码】

 1     /**
 2      * 从服务器获取数据 需要权限:<uses-permission android:name="android.permission.INTERNET"
 3      * />
 4      */
 5     private void getDataFromServer() {
 6         HttpUtils utils = new HttpUtils();
 7         utils.send(HttpMethod.GET, GlobalConstants.CATEGORY_URL,
 8                 new RequestCallBack<String>() {
 9 
10                     @Override
11                     public void onSuccess(ResponseInfo<String> responseInfo) {
12                         // 请求成功
13                         String result = responseInfo.result;// 获取服务器返回结果
14                         System.out.println("服务器返回结果:" + result);
15 
16                         // JsonObject, Gson
17                         processData(result);
18 
19                         // 写缓存
20                         CacheUtils.setCache(GlobalConstants.CATEGORY_URL,
21                                 result, mActivity);
22                     }
23 
24                     @Override
25                     public void onFailure(HttpException error, String msg) {
26                         // 请求失败
27                         error.printStackTrace();
28                         Toast.makeText(mActivity, msg, Toast.LENGTH_SHORT)
29                                 .show();
30                     }
31                 });
32     }

 【测试】

 【将应用中的sp存储文件的值查看】

【说明】下次打开的时候会自动出现上次缓存的内容;

 

11.2 优化

【说明】本地缓存的数据有可能与服务器当前更新的数据不一致,因此需要再判断同步更新数据;

【策略】【1】定时更新,定时一段时间,过了这段时间就更新;【2】不管什么情况下都会请求服务器的数据,在读缓存的同时继续调用服务器的数据;

【缓存存在的意义】在应用打开时可以先让用户看到数据,然后再更新数据;起到了一个临时数据的作用,防止用户不好的体验;

 

11.3  数据缓存的形式;

【说明】数据缓存的形式可以sp的形式缓存,也可以以文件的形式(谷歌电子市场项目就是以文字的形式)缓存;

12.设置侧边栏数据

12.1 侧边栏数据的说明

【说明】侧边栏显示的内容就是服务器返回的数据的字段;

12.2 基于主体框架解析数据的取回

【说明】需要将新闻中心的数据显示到侧边栏;

【思路】在新闻中心中通过MainActivity拿到LeftMenuFragment的对象,然后设置侧边栏的数据;

 

【MainActivity的获取】

【LeftMenuFragment的获取】

 

 

【侧边栏设置数据框架】

【侧边栏的布局】

 

【使用ViewUtils】通过注解的方式进行;

 

 【原理】

【框架的完善】

【item的布局】

【适配器中的item加载和文字的设定】此处的View没有重用;

【效果】

 【布局的调整】

12.3 侧边栏文字选中之后的文字的底色的改变

【效果】选中之后变为红色

【说明】因为listView不存在选中的点击的动作,只存在按下的的动作,因此使用的是enabled属性;

【临时测试】使用enabled的选项可以测试出该项是否起作用;

【默认】红色;可用是红色;

【效果】因为此时都可用,所以都是红色;

【定义当前被选中的逻辑】

【 切换逻辑-设置监听事件】

12.4 点击之后侧边框的收起

 

12.5 主页面按钮的点击事件

【说明】通过按钮的点击可以调出侧边栏;再次点击之后隐藏侧边栏;

13点击侧边栏切换菜单详情页

13.1【效果】

【分析】在点击侧边栏的按钮之后,此处修改的内容是新闻标签页中的framLayout标签页;

  切换修改的界面总共是4个;

13.2 详情页的对象的封装

【说明】使用一个基类BaseMenuDetailPager类;

【新建基类】以为每个切换的标签页的布局和内容没有共同点,因此每个子类各自实现initView;

【子类】

13.3 frameLayout的内容的添加

 

【分析】现在是侧边栏需要拿到新闻中心的对象;

【获取MainActivity】

【获取主页面的fragment】

 【ContentFragment获取新闻中心页】

 

【设置frameLayout的内容】

13.4【新闻中心页面的内容的添加】

【初始化新闻详情页】使用ArrayList进行封装;

【测试效果】点击新闻中心页之后,显示的文字会重叠;

 

 【修改】清除之前的旧布局

13.5【bug1】刚开始默认加载的页面应该新闻菜单详情页,现在不是;

【正常的情况】

【效果】

 13.6 标题的切换

【存在的问题】标题一直是新闻的字样,没有进行切换;

【效果】标题进行了切换;

 

 13.7 BUG2

【侧边栏和详情页同步的问题】在点击为新闻中心的专题页面,然后再点击其他的页面之后再切换回来的时候,侧边栏显示的专题页面和详情页面不一致;

 【修改-疑点】个人感觉此处不应该是赋值为初始位置,而应该记录之前的位置的值,而重新加载

14.总结

【布局结构的框架】

【真机访问的线上的地址】

 

 

posted @ 2018-02-26 08:38  OzTaking  阅读(253)  评论(0)    收藏  举报