SmartBeijing Day03

Day03 01.前几天内容总结##

Day03 02.页签详情页实现 ##

实现菜单详情页-新闻NewsMenuDetailPager模块

		上边是自定义控件 中间是内容部分,内容部分最大的是一个一个标签,能够来回切换

		下边这个可以左右滑动,就是一个ViewPager



实现左右滑动的ViewPager功能               

	这个页面以前写的TextView不要了,在layout中创建布局pager_news_menu_detail.xml

	先不管上边那个,下边这个肯定是一个ViewPager

	SlidingMenuLibrary库中的android-support-v4.jar下的view中找到ViewPager,抄下全路径复制到pager_news_menu_detail.xml控件名称上,给它加id为vp_news_detail



		pager_news_menu_detail.xml布局此处略



	NewsMenuDetailPager的initView加载布局,并把view返回回去

	用ViewUtils注解方式初始化ViewPager

		activity页面,需要将activity对象传过去,用的是inject(Activity activity)

		不是activity页面,需要将当前view传过去,用的是inject(Object handler,View view)

		

		handler: 当前的对象,用this

		view: 上边inflate布局时返回的view



			@ViewInject(R.id.vp_news_detail)

			private ViewPager mViewPager;



			@Override

			public View initView() {

				View view = View.inflate(mActivity, R.layout.news_menu_detail, null);

				ViewUtils.inject(this, view); // 把当前的View对象注入到xUtils框架中

				return view;

			}



	给ViewPager写适配器NewsMenuAdapter,entends的pagerAdapter

	实现方法:getPageTitle,getCount,isViewFromObject,instantiateItem,destroyItem



		isViewFromObject中通常都是 return view == object;

		destroyItem中通常都是 container.removeView((View) object);



	给adapter的每个页面进行填充

		思路和最开始主页面的标签页ViewPager思路类似

		当时写一个RadioButton,用一个BasePager把整个页面描述起来



	在com.itcast.zhxa02.base.impl包下创建一个页签详情页TabDetailPager继承BaseMenuDetailPager专门描述内容部分ViewPager页



	BaseMenuDetailPager是侧边栏四个页面的基类

	TabDetailPager单独给它写一个父类,父类中和BaseMenuDetailPager的完全一样,也是initView,initData方法

	所以可以让TabDetailPager和菜单详情页4个页面共用一个父类



	页签详情页TabDetailPager的initView中实现它的view

		中间布局viewPager,先用一个TextView表现页面中的内容,在initView中写一个TextView,从SettingPager中抄一个TextView过来即可



		页签详情页TabDetailPager写好后,在菜单详情页-新闻NewsMenuDetailPager中去初始化这一堆页签

		应该初始化多少个页签是由接口返回决定

		接口data中的新闻模块的childern有多少个元素就应该写多少个页签



	网络数据是在新闻中心NewsCenterPager拿到的,要将网络数据传递给菜单详情页-新闻NewsMenuDetailPager

	这个很好给,菜单详情页4个页面本就属于新闻中心,新闻中心很容易拿到菜单详情页-新闻对象

		在新闻中心NewsCenterPager解析json数据的processData中,初始化了菜单详情页这4个页面数据

		里边就有菜单详情页-新闻NewsMenuDetailPager,网络数据mNewsMenuBean也刚好在这个方法中

			

	通过构造方法将数据从新闻中心传递给菜单详情页-新闻



		// 初始化详情页数据

		mMenuDetailPagers = new ArrayList<BaseMenuDetailPager>();

		mMenuDetailPagers.add(new NewsMenuDetailPager(mActivity, mNewsMenuBean.data.get(0).children));

		mMenuDetailPagers.add(new TopicMenuDetailPager(mActivity));

		mMenuDetailPagers.add(new PhotosMenuDetailPager(mActivity));

		mMenuDetailPagers.add(new InteractMenuDetailPager(mActivity));



	这时报错,把构造方法修改一下就可以,选中NewsMenuDetailPager然后ctrl+1,选择change constructor 'NewsMenuDetailPager(Activity)':Add parameter 'ArrayList<NewsTabData>'

	自动修改菜单详情页-新闻的构造方法并来到菜单详情页-新闻中,接收并在全局变量处声明出来,叫做mTabList

		

		private ArrayList<NewsTabData> mTabList;// 页签网络数据

		public NewsMenuDetailPager(Activity activity,ArrayList<NewsTabData> children) {

				super(activity);

				mTabList = children;

		}



	把数据设置给NewsMenuAdapter

		getCount方法中:return mTabList.size();



	初始化布局的方法instantiateItem中,初始化布局不能直接从网络数据mTabList去取,应该从TabDetailPager去做



		在NewsMenuDetailPager中实现初始化布局的方法initdata

		每一个页签都是一个TabDetailPager对象,应该维护一个集合

		到NewsMenuDetailPager的initData初始化数据,children下有12个页签,应该初始化12个页签页面对象

		写for循环,int i = 0,i小于网络数据的大小mTabList.size(),然后i++,for循环中new一个TabDetailPager

		

		成员变量处写一个集合,把它维护在集合中,所以泛型为TabDetailPager,叫做mPagers,这是页签页面对象的集合

		在初始化数据的initData方法中去new这个泛型为TabDetailPager的集合

		初始化一个页面后就add给mPagers



		最后NewsMenuDetailPager中代码如下:

			private ArrayList<TabDetailPager> mPagers;// 页签页面对象集合				

				@Override

				public void initData() {

					mPagers = new ArrayList<TabDetailPager>();

					// 初始化12个页签页面对象

					for (int i = 0; i < mTabList.size(); i++) {

						TabDetailPager pager = new TabDetailPager(mActivity,mTabList.get(i));

						mPagers.add(pager);

					}



		这样就将十二个页签对象全部放在mPagers集合中了,以后要从ViewPager展现布局或对象时,从mPagers去取

		

	现在要在NewsMenuDetailPager的instantiateItem中初始化item

	

		先拿到对象的pager,即mPagers.get(position)拿到对应pager对象

		Pager对象中的mRootView就是它的根布局

		把根布局addView塞给container

		把view最后return回去

		然后pager.initData()初始化数据

		

		最后NewsMenuDetailPager中的instantiateItem方法如下:

			@Override

			public Object instantiateItem(ViewGroup container, int position) {

				TabDetailPager pager = mPagers.get(position);

				View view = pager.mRootView;

	

				pager.initData();// 初始化数据

	

				container.addView(view);

				return view;

			}



	这个逻辑和首页逻辑类似

		ContentFragment中底部5个页签页签页面每个都是ViewPager页面

		当时是在ContentAdapter的instantiateItem中,用mPagers.get(position),

		然后container.addView(view)把它添加进来,即:

			@Override

			public Object instantiateItem(ViewGroup container, int position) {

				BasePager pager = mPagers.get(position);

				View view = pager.mRootView;// 获取当前页面的根布局

				// pager.initData();// 初始化数据, 不要再此调用, 此处会默认上一页,当前页和下一页,浪费流量和性能

				container.addView(view);

				return view;

			}

	

	只不过当时为了提高性能,在页面对象选中后才去initData

	即在ContentFragment的setOnPageChangeListener的onPageSelected中

			@Override

			public void onPageSelected(int position) {

				mPagers.get(position).initData();// 只有当前页面被选中后才初始化数据

			}

	

	放在instantiateItem中也没问题,只不过稍微浪费点流量,性能,NewsMenuDetailPager中就不做这个优化了



	getCount中返回的mTabList.size()和mPagers.size()一样,mTabList有多少个,mPagers就有多少个,return一个mPagers.size()也行,这两个都行,因为网络上(即mTabList)有多少个这样的数据,就有多少个这样的页面(即mPagers),所以拿的数量一样,在这就写成mPagers.size()



	adapter写好后,就可以给ViewPager去设置adapter了,即在NewsMenuDetailPager的initData中,mViewPager.set一个Adapter,new一个NewsMenuAdapter,即:

			@Override

			public void initData() {

				mPagers = new ArrayList<TabDetailPager>();

				// 初始化12个页签页面对象

				for (int i = 0; i < mTabList.size(); i++) {

					TabDetailPager pager = new TabDetailPager(mActivity,mTabList.get(i));

					mPagers.add(pager);

				}

		

				mViewPager.setAdapter(new NewsMenuAdapter());

			}

	 

	这是给ViewPager设置数据的逻辑



	运行程序,点新闻中心,新闻中心展示页签详情页了

		因为NewsMenuDetailPager中直接在initView中加载了布局

		布局中有mViewPager对象,当initData初始化页面数据时,马上给ViewPager初始化Adapter

		Adapter塞的是每个TabDetailPager,TabDetailPager的initView实现布局实现的就是页签详情页的TextView

		所以运行程序后,在新闻中心展现的是页签详情页

	

新闻中心内容部分ViewPager事件被父控件NoScrollViewPager拦截的处理



		新闻中心内容部分ViewPager,新闻中心上边整体“标题栏+内容部分”也是ViewPager,NoScrollViewPager

		现在返回的是super,super中做了复杂判断,有时false,有时true

		希望不拦截事件就直接改成返回false

		即重写NoScrollViewPager的onInterceptTouchEvent直接返回false



		在NoScrollViewPager中重写onInterceptTouchEvent方法,返回false,表示不拦截子控件的事件,保证嵌套的子viewpager可以滑动,即:

				//拦截事件

				@Override

				public boolean onInterceptTouchEvent(MotionEvent ev) {

					return false;//不拦截子控件的事件,保证嵌套的子viewpager可以滑动

				} 

	

		运行程序,切到新闻中心滑动,可以流畅滑过来了

Day03 03.ViewPagerIndicator使用&样式修改 ##

在页签详情页TabDetailPager每个页面显示页面相关信息“北京,中国,国际,体育,生活等”



通过构造方法将数据从菜单详情页-新闻传递给页签详情页



	网络数据是在菜单详情页-新闻NewsMenuDetailPager的构造方法中接收的mTabList

	在initData中从mTabList集合取出当前位置信息以构造方法的方式传递给页签详情页TabDetailPager即可



		在NewsMenuDetailPager的initData中把TabDetailPager修改为:

			TabDetailPager pager = new TabDetailPager(mActivity,mTabList.get(i));



		这时会报错误,鼠标放在TabDetailPager处,选择Change constructor'TabDetailPage(Activity)':Add parameter,跳到TabDetailPager中并自动给构造方法加了NewsTabData newsTabData参数,在成员变量处写一个页签网络数据对象mTabData来接收数据

		

				private NewsTabData mTabData;// 页签网络数据

				public TabDetailPager(Activity activity, NewsTabData newsTabData) {

					super(activity);

					mTabData = newsTabData;				

				}



		初始化数据时初始化相关信息

		在TabDetailPager中实现initData方法去更新initView方法中TextView的值



		将TextView改成全局,在initData中去setText,即:

			view.setText(mTabData.title);

		这时可以将initView方法中的页签详情页删掉:

			view.setText("页签详情页");

	

	运行程序,点击新闻中心,内容部分就是北京,向右滑动,出现中国,国际,体育,生活等等





	接下来去实现上边的指示器,可以指示页面的是北京,中国,国际,体育还是其他



用开源框架ViewPagerIndicator实现指示器效果

	是ViewPager的指针项目,指针意思就是指某个页面,可以从GitHub下载

	把GutHub玩熟悉以后别的资料都不需要,只需要一个GitHub就行,它上边涵盖了很多控件



	ViewPagerIndicator使用流程:

引入ViewPagerIndicator包
在布局文件中声明这个自定义控件
仿照sample代码,初始化自定义控件,和viewpager绑定
重写PagerAdapter的getPageTitle方法,返回页面的标题
给MainActivity加主题样式
基于样式进行修改(背景样式+文字样式)
事件处理: 重写TabPagerIndicator的dispatchTouchEvent方法, 请求父控件及祖宗控件不拦截当前控件的事件

	具体实现:

	1.选中zhxa02,右键Properties,选择Android,Add,将ViewPagerIndicatorLibrary去Add进来

	  Add进来后报错内容'Fond 2 Versions of android-support-v4.jar in the dependency list'

	

		解决ViewPagerIndicator-Library和SlidingMenuLibrary的V4冲突

			在SlidingMenu-master的V4包,长度是385585,SHA-1类似MD5,文件的数据摘要是48c94ae...

				MD5可以把字符串或文件算成一个数据摘要,SHA-1也一样

				SHA-1是根据SHA-1算法对V4包文件,MD5是对密码,也可以对文件进行加密

			在ViewPagerIndicator-master的V4包,SHA-1是53307dc

				这两个V4包文件的SHA-1,长度都不一样,它两就冲突了

				都是V4包,文件长度不一样,是因为V4包有好几个版本

			

				上次v4冲突是当前项目zhxa02,和SlidingMenuLibrary冲突,把当前项目V4包删掉就可以了

				

				现在是ViewPagerIndicator-Library的V4,不能删

				因为ViewPagerIndicator-Library和其他库没有关系,不可能找SlidingMenuLibrary库

			

				把SlidingMenuLibrary中的V4抄到ViewPagerIndicator-Library中的libs下,把它原来的V4包覆盖掉

				这两个库中的V4包一样了就不会冲突了

	

				覆盖掉后ViewPagerIndicator-Library中的v4包与sample中的V4冲突

		解决ViewPagerIndicator-Library和sample的V4冲突

				sample关联着ViewPagerIndicator,所以sample中的libs下的V4可以删掉,删掉后就不报错了

			

				有两种方式,一种是删,不能删就把两个V4文件搞成一样的

		

	2.在布局文件中声明这个自定义控件

		在pager_news_menu_detail.xml的ViewPager正上方再搞一个自定义控件,这个自定义控件从例子程序ListSamples的simple_tabs.xml中可以抄过来,这就是我们的indicator

	3.仿照sample代码,初始化自定义控件,和viewpager绑定

		例子程序SampleTabsDefault中findViewById拿到indicator,给indicator去setViewPager,把ViewPager和indicator关联在一起,把这两行代码抄过来



		在NewsMenuDetailPager的initView中初始化indicator,用Xutils注解方式在成员变量处用注解声明

				@ViewInject(R.id.indicator)

				private TabPageIndicator mIndicator;

		在initData方法中给mIndicator去setViewPager

				mIndicator.setViewPager(mViewPager);//将ViewPager和指针绑定在一起

		只有把它两绑定在一起,才能在滑动ViewPager时让ViewPagerIndicator也滑动,反之亦然



		绑定之前,必须保证ViewPager已经设置完数据,ViewPager去setAdapter设置数据

		所以应该将mIndicator.setViewPager(mViewPager)放在initData的mViewPager.setAdapter(new NewsMenuAdapter)之后,不然会出现异常



	4.重写PagerAdapter的getPageTitle方法,返回页面的标题 

		这个例子程序的SampleTabsDefault的GoogleMusicAdapter中有个方法getPageTitle



		这里继承的是FragmentPagerAdapter,下一个项目可能会介绍到它

		FragmentPagerAdapter意思是ViewPager中塞的是Fragment



		getPageTitle是从成员变量处的CONTENT数组取东西,用position对长度取余,即:

			return CONTENT[position % CONTENT.length].toUpperCase();

			其实没有必要,因为position不会越界的,它取了一个余然后从CONTENT数组中去取的,然后转化成大写了

			例子程序中Tabs除了ViewPager和它关联,有很明显特点是每个页面都有标题,这个标题是在getPageTitle去初始化,toUpperCase把它转成大写,原来是小写(Recent)

			运行例子程序发现标签已都转成大写了(即RECENT),所以要重写PagerAdapter的getPageTitle方法,返回指针标题

			返回的是北京,中国,国际,体育,这些标题,这个数据在mTabList中有

		即在NewsMenuDetailPager的NewsMenuAdapter的getPageTitle方法中写:

				return mTabList.get(position).title;



		运行程序,点新闻中心,这时北京,中国,国际,体育,生活等标题都显示出来了,而且可以往右滑动

		切换对应页面看不见效果,但是可以点击对应标题切换到对应页面

		例子程序是给SampleTabsDefault的Activity设置了主题样式,打开例子程序清单文件,找下activity申明的地方,给SampleTabsDefault设置了theme主题,即:

					android:theme="@style/Theme.PageIndicatorDefaults"

		这是它自定义的叫PageIndicatorDefaults的Theme,照着它把主题也抄到zhxa02项目清单文件中

		但NewsMenuDetailPager不是一个Activity,清单文件中怎么给设置主题

					NewsMenuDetailPager属于MainActivity,直接给MainActivity设置主题

		到zhxa02的AndroidManifest.xml中,在MainActivity处添加:

					android:theme="@style/Theme.PageIndicatorDefaults" 

		这个主题能用是因为已把源码关联在一起了,可以随意用它的资源文件



		这个主题点进去其实是ViewPagerIndicatorLibrary库(从eclipse的左上角看)的vpi_styles.xml

		

		运行程序,直接点到新闻中心,北京,中国,国际,体育,生活,这些标签名出来了,变黑是因为那个主题样式本来就是黑的

	

		到zhxa02的MainActivity,打开activity_main.xml布局文件,将布局文件背景色换成白色,即:

				android:background="#fff"

	

		运行程序,背景变白了,北京,中国,国际,体育,生活,这些标签名字本身是白色,所以看不见了

给MainActivity加主题样式

		要改成黑色字,选中后是红色,下边标签是红色小短杠的样式



		到zhxa02的清单文件查看样式写法

			点击android:theme="@style/Theme.PageIndicatorDefaults"后到vpi_styles.xml中了

			

				里边的PageIndicatorDefaults就是要的样式

				 <style name="Theme.PageIndicatorDefaults" parent="android:Theme">

			        <item name="vpiIconPageIndicatorStyle">@style/Widget.IconPageIndicator</item>

			        <item name="vpiTabPageIndicatorStyle">@style/Widget.TabPageIndicator</item>

			    </style>

				里边有两种样式

					vpiIconPageIndicatorStyle是图标的PageIndicator

					vpiTabPageIndicator,要用的就是它,ctrl进去看下样式

						它的样式还是在vpi_styles.xml中,为下:

							<style name="Widget.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">12dp</item>

						        <item name="android:paddingBottom">12dp</item>

						        <item name="android:textAppearance">@style/TextAppearance.TabPageIndicator</item>

						        <item name="android:textSize">16sp</item>

						        <item name="android:maxLines">1</item>

						    </style>



						要改它的背景图片就着重先看background怎么写的,vpi__tab_indicator点进去到了vpi_tab_indicator.xml中

							发现它是状态很多的状态选择器,平时就两个状态,这里区分的细,焦点,获取不获取都有



		要的效果很简单,只需要默认什么都没有,选中后下边是红色小短杠		

		把图片换成全透明,直接@一个android:color/transparent,默认如果没有被选中,是不是全透明的颜色

		被选中之后是红色小短杠图片news_tab_item_bg_select.9.png,将它拷贝到库文件ViewPagerIndicatorLibrary的drawable-hdpi中

		将ViewPagerIndicatorLibrary的vpi__tab_indicator.xml中图片vpi__tab_selected_holo换成news_tab_item_bg_select

			unselected默认的全都换成全透明图片,即: @android:color/transparent

			selected选中的全都换成小红短杠,即:@drawable/news_tab_item_bg_selector

	

		运行程序,新闻中心的北京,中国,体育,国际等的背景都是透明,选中后就变成红色小短杠了



		上边修改了背景图片的样式,接下来再改文字样式



			文字大小搞成14sp吧,文字颜色之类的在textAppearance专门设置文字具体样式

			点击TextAppearance.TabPageIndicator进来看下,textStyle样式为bold加粗的样式,textColor是颜色

			默认是的话,它的颜色就说选中和不选中,好像都是这个白色吧,但是它把颜色用@color/vpi_dark_theme状态选择器,点进去又是状态选择器,只不过全都是颜色,换成自己的颜色,自己的颜色默认是黑色,选中后是红色,在vpi_dark_theme.xml中,enabled=false,这个表示不可用,不可用时应该是黑色,直接写黑色就可以了,即:

					<item android:state_enabled="false" android:color="#000"/>

		

			state_window_focused="false"表示没有获取焦点,也应该是黑色,即:

					<item android:state_window_focused="false" android:color="#000"/>

			state_pressed="true"表示被按下,应该是红色,即:

					<item android:state_pressed="true" android:color="#f00"/>

			state_selected="true"表示被选中,也是红色,即:

					<item android:state_selected="true" android:color="#f00"/>

			下边是默认颜色,应该是黑色,即:

			 		<item android:color="#000"/> <!-- not selected -->

		

			运行程序,点新闻中心可以了,这就是修改内容部分标签的背景样式和文字样式(状态选择器的样式修改)

Day03 04.页签滑动事件处理 ##

android有两个难点,自定义控件,事件处理



解决ViewPagerIndicator滑动时侧边栏出来的问题

	ViewPagerIndicator的事件被SlidingMenu拦截了

	又涉及父控件将子控件的事件拦掉的情况



	上边处理‘标题栏加内容部分’父控件将内容部分ViewPager的滑动事件拦截问题时,是让它爹重写拦截事件的onIntercepter方法,直接false

	这种方式弊端是如果父控件不只一个,只要有一个父控件拦截了事件,就得找出所有父控件分别重写onIntercepter方法去处理



让所有父控件都不要拦截事件的处理:

	重写事件分发方法dispatchTouchEvent,然后调用getParent().requestDisallowInterceptTouchEvent(true);	

		getParent找所有父控件,传true,所有父控件就都不会拦截此事件了



	ViewPagerIndicatorLibrary的TabPageIndicator中,加如下代码: 



		// 事件分发

		// dispatchTouchEvent->onInterceptTouchEvent->OnTouchEvent

		@Override

		public boolean dispatchTouchEvent(MotionEvent ev) {

			getParent().requestDisallowInterceptTouchEvent(true);// 子控件希望父控件及祖宗控件不拦截当前控件的事件

			return super.dispatchTouchEvent(ev);

		}

	

	运行程序,点击新闻中心,滑动北京,中国,国际,体育,侧边栏都不出来了



再说另外一个事件处理,往后滑到体育,再往回滑(在内容部分的ViewPager中滑)侧边栏又出来了



刚只是处理的内容部分的ViewPager上边的标签(ViewPagerIndicator),现在滑的是ViewPager



解决ViewPager滑动时侧边栏出来的问题



	当切到北京时让侧边栏出来,当切到中国,国际,体育时让侧边栏不出来

	可以通过控制侧边栏的可用不可用达到侧边栏出来不出来效果

	

	照着刚才逻辑,让ViewPager重写dispatchTouchEvent方法解决也可以,但是要额外添加很多逻辑判断

	

	之前在首页的设置时让侧边栏不出来,新闻中心,智慧服务,政务时出来,也用的侧边栏可用不可用



	但现在在新闻中心的中国,国际时侧边栏也要禁用掉,所以要监听ViewPager的页面切换事件

	

	NewsMenuDetailPager的initData中,当ViewPager和indicator同时使用时,点击事件必须设置给indicator

 			mIndicator.setOnPageChangeListener(this);

	让当前类实现onPageChangeListener,实现方法onPageScrolled,onPageSelected,onPageScrollStateChanged

	设置页面的滑动监听,一旦页面切换到onPageSelected方法,在这打印position,即:

				@Override

				public void onPageSelected(int position) {

					System.out.println("当前位置:" + position);

				}

	运行程序,页签和ViewPager就同步切换了



	接下来在onPageSelected方法中,判断如果当前位置等于0就打开SldingMenu,else禁用SldingMenu

		打开和禁用SlidingMenu的方法在ContentFragment已写过一次,叫做setSlidingMenuEnable

		拷贝到NewsMenuDetailPager中,在这个判断中调用并分别设置true和false,即:

				@Override

				public void onPageSelected(int position) {

					System.out.println("当前位置:" + position);

					if (position == 0) {

						// 打开SlidingMenu

						setSlidingMenuEnable(true);

					} else {

						// 禁用SlidingMenu

						setSlidingMenuEnable(false);

					}

				}



	运行程序,点击新闻中心,第一次点进去后肯定不会走onPageSelected方法,这个方法只有页面切了后才走

		在第0个位置设置了true,其实不用去设置,它本来默认就可用,所以SlidingMenu能拉出来

		所以第一次不需要初始化,因为SlidngMenu本身默认就是true

		再到中国,国际,SlidingMenu出不来了,既然SlidingMenu用不了,就被内容部分的ViewPager给拦截了

Day03 05.点击按钮切换页签页面##

智慧北京完整项目条目(内容部分的页签)右侧有小箭头,点箭头也可以切页面



实现点击小箭头切换下一页功能



到pager_news_menu_detail.xml布局文件中,用id为btn_next_page的ImageButton实现小箭头

	用ImageView也行,去掉背景,即:android:background="@null"

	有时@null会出问题,一旦出现问题就把它改成全透明颜色即可,因为有些控件不支持@null



	NewsMenuDetailPager的initView中初始化ImageButton并用XUtils注解方式设置点击事件



	写一个方法nextPage,传View参数,给方法加注解OnClick,注解中将id传进去

	这个还比原来复杂一些,原来可以在布局文件Pager_news_menu_detail的ImageButton控件上写个onClick属性

	写完之后在NewsMenuDetailPager中写方法

	这个xUtils不需要加属性,通过注解方式写了nextPage方法,知道这样用就行了,后边就不用它了

	

	实现点击后跳到下一个页面,很简单,让ViewPager去setCurrentItem

	设置第几个,用mViewPager去getCurrentItem后++,即:

			 mViewPager.setCurrentItem(++mViewPager.getCurrentItem());

	报错Invalid argument to operation ++/--,不认识++,括起来还是不认识,好像还不能这样去写成一行,那就分开写

		不支持拿api去++,只支持整数进行++,不智能



			这就是跳到下一个页面的逻辑,即:

			@OnClick(R.id.btn_next_page)

			public void nextPage(View view) {

				//切换到下一个页面

				int currentItem = mViewPager.getCurrentItem();

				mViewPager.setCurrentItem(++currentItem);

			}



		++currentItem到最后一个页面再++不会角标越界挂掉,ViewPager原生已做了处理



	运行程序,到最后汽车时继续点也不会超出边界,这就是跳转到下一页的逻辑

Day03 06.页签网络数据获取及解析 ##

使用HttpUtils实现页签网络数据获取并用Gson解析数据



	内容部分ViewPager的北京页签页面,分为两部分,上边部分是ViewPager,下边是listView

	TabDetailPager的initView的TextView去掉,在layout文件夹下写一个布局文件pager_tab_detail.xml,此处略

	

		在上边先是一个ViewPager,在pager_news_menu_detail.xml中有ViewPager布局抄下

		ViewPager的id为vp_top_news,是头条新闻叫做topNews

		listView的id为lv_news

		在TabDetailPager的initView中初始化布局和控件



		ViewPager中要填充4张图片,还有listView的数据,都要从网络获取

			initData调用getDataFromServer();



		然后写getDataFromServer()方法

		new一个HttpUtils,叫做utils,用utils去send请求,参数分别为HttpMethod.GET,mUrl,第三个参数是new一个RequestCallBack,泛型是String,重写onSuccess,onFailure

		// 请求网络数据

		private void getDataFromNet() {

				HttpUtils utils = new HttpUtils();

				utils.send(HttpMethod.GET, mUrl, new RequestCallBack<String>() {

					@Override

					public void onSuccess(ResponseInfo<String> responseInfo) {

						CacheUtils.setCache(mActivity, mUrl, responseInfo.result);

						processData(responseInfo.result, false);

						lvList.onRefreshComplete(true);// 隐藏下拉刷新控件

					}

					@Override

					public void onFailure(HttpException error, String msg) {

						Log.e(TAG, "请求失败:" + msg);

						lvList.onRefreshComplete(false);// 隐藏下拉刷新控件

						Toast.makeText(mActivity, "请求失败", Toast.LENGTH_SHORT).show();

					}

				});

		}



	看接口中的json串,北京,中国,国际在url中都带有相关链接,这些链接都是服务器返回的,服务器返回什么链接就什么链接

	北京是url:"/10007/list_1.json"链接,将这个链接在浏览器地址栏中输入localhost:8080/zhbj/10007/list_1.json,出来一大串json串

		北京的json数据分为两部分:

		topnews头条新闻,topnews中有蜗居生活,中秋赏月,天空翱翔,感官设计,是ViewPager数据

		news新闻数据,网上大讲堂第多少期等,是listview的数据



	构造方法TabDetailPager中声明下,并在成员变量处写个mUrl接收

			private String ;// 页签网路数据的url

			NewsMenuTab mNewsTab;// 页签数据

	

			public TabDetailPager(Activity activity, NewsMenuTab newsTab) {

			super(activity);

					mNewsTab = newsTab;

					mUrl = GlobalContants.SERVER_URL + mNewsTab.url;

			}



	然后在参数那写成mUrl,这是当前页签的网络接口



	分别处理下onSuccess,onFailure

	onFailure处理方式和新闻中心NewsCenterPager完全一样,把新闻中心的onFailure抄过来

	onSuccess逻辑也一样,将新闻中心的onSuccess抄过来后,processData解析数据

	

	解析数据得写方法processData,方法中new一个Gson,还得在domain包下写一个NewsBean对象去承载新闻详情的数据

	NewsBean.java此处略



	照着浏览器中localhost:8080/zhbj/10007/list_1.json接口中的json数据去写



	topimage是内容部分上半部分ViewPager头条新闻中的图片

	图片是从网络去拿的,先去请求接口拿到每一张图片的url链接topimage,因为图片不能直接塞在json中返回

	json中只放图片url下载链接,有了url下载链接后再去请求下载地址http://10.0.2.2:8080/zhbj/10007/1452327318UU91.jpg



	把10.0.2.2改成localhost在浏览器地址栏输入,即:http://localhost:8080/zhbj/10007/1452327318UU91.jpg

	回车就是这张图片,请求下载后才去填充图片,这是头条新闻



	还有新闻news,新闻news又是一个对象,所以再写一个普通新闻对象News.java

	普通新闻和头条新闻类似,把TopNews中的抄过来,只不过普通新闻叫的是listimage不叫topimage,此处省略News.java

	

	将数据在News中打印下,写个toString,打印下title就行

	在NewsData中也打印下title,topnews,news

	头条新闻也要打印下,在TopNews中写个toString,打印下title就行



	bean对象写完后,在TabDetailPager的processData中用gson解析,返回的肯定是NewsBean对象叫它newsBean,即:

				gson.fromjson(result,NewsBean.class);

	把newsBean打印一下,即:

				// System.out.println("页签页解析结果:" + newsBean);

	

	运行程序,切到新闻中心,数据已打印出来,北京,中国打印了两次是因为ViewPager默认会把下一页加载过来,所以打北京时中国也会加载

Day03 07.头条新闻页面&滑动事件处理 ##

12个页签页面是最底层页面了,大概是6层布局,都是在MainActivity实现

	TabDetailPager的initData调用了请求网络的数据getDataFromServer(),getDataFromServer方法中拿数据解析



用CacheUtils给页签页面TabDetailPager实现缓存功能



	1)在initData中用CacheUtils去读缓存

	把mUrl传进去,ctx传个mActivity,返回String对象叫做cache,判断缓存是否为空,不为空直接解析数据

			@Override

			public void initData() {

				// view.setText(mTabData.title);

				String cache = CacheUtils.getCache(mUrl, mActivity);

				if (!TextUtils.isEmpty(cache)) {

					processData(cache, false);

				}

				getDataFromServer();

			}

	2)在getDataFromServer的onSuccess拿到数据后去写缓存

		mUrl传过来,json是result,ctx写个mActivity,这是写缓存

			@Override

			public void onSuccess(ResponseInfo<String> responseInfo) {

				CacheUtils.setCache(mActivity, mUrl, responseInfo.result);

				processData(responseInfo.result, false);

			}

	昨天专门在CacheUtils中介绍了缓存的读写,缓存现在是放在sp中,有时也可放在本地文件中

	这时需要以url为文件名,以json为文件内容写进去,有同学试了这种思路,发现问题是要以url为文件名,但是url中有斜杠问号百分号字符,这种情况无法生成文件名,只需要对url进行MD5运算,以url的MD5值为文件名,MD5都是一些数字和字母

	读缓存找有没有MD5这样的文件存在,MD5能避免创建不了文件的问题





	接下来填充内容,新闻中心北京页签的ViewPager页面就是头条新闻



	在TabDetailPager的getDataFromServer中调用processData解析完数据后,就可以拿到topnews

	processData中拿一下,newsBean可以直接找data,下边的topnews集合就是头条新闻的数据,把它叫做mTopNewsList,即:

			ArrayList<TopNews>  mTopNewsList = newsBean.data.topnews;



	把头条新闻的数据集合mTopNewsList搞成全局变量

	有了数据后给ViewPager设置Adapter

	在TabDetailPager中搞一个类TopNewsAdapter,继承的是PagerAdapter(listView继承的是BasePager,ViewPager继承的是PagerAdapter),并实现它的几个方法

	isViewFromObject都是一样写法,直接return view == object;再实现instantiateItem方法

	destroyItem方法  直接写:container.removeView((View) object);

	getCount方法拿的是它的网络数据,(mTopNewsList)有多少个就是多少个  return mTopNewsList.size();



	然后在instantiateItem方法去初始化布局,怎么去初始化数据?

	头条新闻中就是一个一个ImageView对象,之前已写过了,在instantiateItem中new一个ImageView

			即: ImageView view = new ImageView(mActivity);

	给ImageView设置图片,之前本地图片都放在了资源目录下,直接通过资源文件id引入,现在这些图片全部是从网络上下载

	

使用BitMapUtils从网络下载图片



	根据图片url去下载图片

			第一步 下载图片

			第二步 将图片设置给IamgeView

			至少要经过这么两步运算

	xutils提供了bitMapUtils专门去下载网络图片

		在TopNewsAdapter构造方法中初始化BitMapUtils

		通过它在instantiateItem中调用display(T container,String uri)

					T类型的container,在这传的就是ImageView对象

					String类型的url,指图片的下载链接从mTopNewsList集合可以拿到

		第一步下载第二步设置,它一句话就搞定了,即在instantiateItem方法中:

			mBitmapUtils.display(view, mTopNewsList.get(position).topimage);// 参1:要设置图片的ImageView对象;参2:图片下载链接

		BitMapUtils的功能不局限于此,它还进行了缓存

	图片一般都会做缓存,即第3步:图片缓存(以图片url为文件名,以图片为文件内容缓存起来)

	

	因为第一次把图片下载完后,下次就没必要从网络上继续加载了,这时Xutils的BitMapUtils就已经对图片进行了缓存

	它只需要加载一次,下次就无需加载直接从本地去读



仿开源框架BitmapUtils实现图片缓存功能

	只需以图片url的md5为文件名(key),图片为文件内容(value)以文件形式保存起来,这就是做了一个缓存



	图片只能以文件形式进行缓存

	之前缓存浏览器中打开的json数据,就是字符串,可以放在数据库,文件,sp中

	但图片是一个文件,必须以文件形式缓存起来

	

	这是bitmapUtils的第三个功能,对图片进行了缓存



	第4个功能,解决了内存溢出问题,从网络加载图片给ImageView设置时,图片非常多的情况下,快速滑动很容易造成内存溢出

	BitmapUtils解决了内存溢出,mBitmapUtils.display(view, mTopNewsList.get(position).topimage);一句话全都搞定了



		它底层怎么去做的,后边会仿照bitmaputils写个demo解释图片怎么去下载怎么去缓存

		把它display后,container还得加一个addView,把imageView添加进来,即在instantiateItem中写如下代码:

				container.addView(view);

		同时也要把这个view返回回去,即: return view;



		在processData中拿到数据后设置TopNewsAdapter,为了安全起见判断mTopNewsList是否为null

		不等于空就给ViewPager设置TopNewsAdapter,参数去new一个TopNewsAdapter设置网页数据,即:

			if (mTopNewsList != null) {

				mViewPager.setAdapter(new TopNewsAdapter());

			}

	运行程序,点击新闻中心,老头蜗居图片加载过来了,而且页面可以滑,每个页面图片基本都一样,都是一个老头蜗居图

	滑动老头图片,想看到另外3张图片,但一滑滑到中国页面了,它自己里边不能滑,只能去切页面了



解决头条新闻原生ViewPager事件被“内容部分的ViewPager+listView”拦截的问题



	1)老头蜗居图片所在的地方是ViewPager

	2)内容部分不包括页签ViewPagerIndicator的地方,即“内容部分的ViewPager+listview”的地方是一个ViewPager

	3)最外边的“主页面标题栏+内容部分”,即主页面除了底部标签栏之外的地方,也是ViewPager

	这是三层ViewPager嵌套,很少见,史无前例的复杂布局



	因为父控件内容部分ViewPager+listview”的ViewPager把子控件头条新闻原生ViewPager的事件拦截掉了

	照着今天上午思路,让这个孩子去请求所有父控件不要去拦截这个事件

		当时需要在TabPageIndicator中重写dispatchTouchEvent方法去拦截



		现在头条新闻ViewPager对象用的是系统原生ViewPager,原生的没办法重写它的方法

		只能自定义ViewPager为HorizontalScrollViewPager继承ViewPager,重写它的构造方法

			然后重写dispatchTouchEvent方法,去getParent().requestDisallowInterceptTouchEvent(true),传个true请求所有父控件不要拦截事件

				@Override

				public boolean dispatchTouchEvent(MotionEvent ev) {

						// 请求所有父控件不要拦截触摸事件

						getParent().requestDisallowInterceptTouchEvent(true);

						return super.dispatchTouchEvent(ev);

				}



		到pager_tab_detail.xml中用全类名com.itcast.zhxa02.view.HorizontalScrollViewPager覆盖android.support.V4.view.ViewPager



		TabDetailPager的ViewPager也要改成HorizontalScrollViewPager

		运行程序,到新闻中心滑动北京页面的老头蜗居图片,可以把其他3张图片切换出来



解决因BitMapUitls底层给ImageView设置图片时,设置的是图片ImageViewResource而不是背景BackGroundResource导致的白边问题



	发现头条新闻ViewPager上边没有贴着ViewPagerIndicator的边,上边留了一个白边

	实际ViewPager是贴着ViewPagerIndicator的,留出白边是因为这个图片比较窄,ViewPager比较宽

	有同学说直接设置背景就行,这种情况没法给它设置背景,因为mBitmapUtils.display(view, mTopNewsList.get(position).topimage)一句话让BitmapUtils去设置图片了,所以没法设置背景



	这个原因是BitMapUitls底层给ImageView设置图片时,设置的是图片而不是背景,设置的是ImageViewResource

	而不是BackGroundResource,除非改它的源码,没有必要改它的源码



	之前已介绍过,给imageview设置ScaleType,FIT_XY表示宽高填充父窗体即可解决

	即在TabDetailPager的instantiateItem方法中

			view.setScaleType(ScaleType.FIT_XY);// 宽高填充父窗体

	设置一个ScaleType就搞定了,运行程序,就没白边了



	

根据手势判断父控件在不同情况下是否需要拦截事件的实现

	把情况归下类:

	1)希望北京的头条新闻滑到最后一张图片(第4张图片)时,再滑能切到中国页面

	2)中国的头条新闻滑到的第一个图片,再往左滑能滑到北京页面

	3)头条新闻ViewPager下方还要添加ListView,上下滑动时应该让父控件处理

	4)滑到北京(第一个页面)的头条新闻的第一个图片时,再滑把侧边栏拉出来

	

	HorizontalScrollViewPager的dispatchTouchEvent中不能getParent().requestDisallowInterceptTouchEvent(true)这么写(注释掉即可),不能让父控件永远不拦截,需要做3个判断

	要根据当时的手势来判断要拦截还是不拦截



	那这个手势怎么去拿到?

		dispatchTouchEvent的参数有个MotionEvent对象,从MotionEvent对象中可以拿到当前点击动作

		switch判断ev.getAction()

			第一个case,MotionEvent.ACTION_DOWN

			第二个Case,MotionEvent.ACTION_MOVE



			ACTION_DOWN按下时,获取当时X坐标和Y坐标,startX,startY,抽取成全局成员变量

					int startX = (int) ev.getX();

					int startY = (int) ev.getY();

			ACTION_MOVE去移动时,获取当时移动后的坐标,endX,endY,这个没必要设成全局

					int endX = (int) ev.getX();

					int endY = (int) ev.getY();

	

			在ACTION_MOVE中分别计算X和Y偏移量

					int dx = endX - startX;

					int dy = endY - startY;

		计算偏移量是想判断是左右滑还是上下滑,哪个方向的偏移量大就向哪个方向滑

		判断Math.abs(dx) > Math.abs(dy)说明是左右滑,else是上下滑

		上下滑动需要父控件拦截,getParent().requestDisallowInterceptTouchEvent(false);传个false		

	

		左右滑动还得判断

			判断dx > 0是向右滑动

				向右滑中继续判断如果getCurrentItem() == 0当前是第一个页面,需要父控件拦截,getParent().requestDisallowInterceptTouchEvent(false); 传个false

			else向左滑动

				向左滑动中继续判断如果“getCurrentItem() == getAdapter().getCount() - 1”,当前是最后一个页面,需要父控件拦截,getParent().requestDisallowInterceptTouchEvent(false); 传个false

		

		这3中情况需要拦截,剩余情况不需要拦截

			一上来先让它去不要拦截,即在ACTION_DOWN刚点进来让它不要拦截,即:

				getParent().requestDisallowInterceptTouchEvent(true);// 不要拦截

		

		ACTION_DOWN中一上来让它不要拦截,getParent().requestDisallowInterceptTouchEvent(true);传个true

				不调这句话,一上来就会拦截,后边ACTION_MOVE根本就不会走进去

		所以在这个地方先不要让他爹去拦截,让孩子在MOVE时再判断是否拦截

		

		运行程序,点新闻中心

			第一个页面上下滑动时没作用

			向右滑时,当前是第一个头条新闻页面需要父控件拦,这时侧边栏就可以出来

			向左滑到最后一个页面时,再向左滑就滑动到中国页面的第一个页面了

		

		市面上的客户端处理不会这么复杂,只能在里边去滑,滑到头就滑不动了,切页面要点页签去切页面

		或者从下边去滑才能切页面

		这个逻辑确实复杂,根据手势判断哪些条件下需要拦截不需要拦截来决定平滑切换,这就是手势滑动

Day03 08.头条新闻标题更新##

给头条新闻ViewPager添加动态切换的标题



	下边给新闻加一个标题,头条新闻ViewPager左下方有中秋赏月,还有四个小圆点

		用帧布局或相对布局把ViewPager和这个布局压在一起,上次已做过

		

		TabDetailPager布局文件pager_tab_detail.xml中

			在HorizontalScrollViewPager正上方增加FrameLayout,宽填充屏幕,高包裹内容

			下边用相对布局

				左边是id为tv_title的蜗居生活textView(标题栏),右边4个小圆点图片,宽是填充屏幕,高是包裹内容,设置背景background是半透明颜色#9000

				TextView设置TextColor文字颜色白色#fff,textsize为14sp

				给RelativeLayout设置layout_gravity为bottom,在HorizontalScrollViewPager下方

				

				标题栏写好后,在TabDetailPager中给标题内容进行修改,先把TextView对象声明一下,即:

							private TextView tvTitle; //头条新闻的标题

				用ViewUtils去findViewById,即:

							@ViewInject(R.id.tv_title)

							private TextView tvTitle;// 头条新闻标题

		

			页面切换时要去更新标题

				给ViewPager设置页面切换事件监听,ViewPager在TabDetailPager的processData的setAdapter下边,给mViewPager设置页面切换监听,mViewPager.setOnPageChangeListener(this);	

		

				让TabDetailPager类实现OnPageChangeListener,上边去设置Listener时,传个this即可

		

				页面被选中的onPageSelected方法中,拿到头条新闻位置对象mTopNewsList去get(position)拿到当前位置的TopNews对象,把它叫做topNews,再给tvTitle去setText, 文字是topNews.title,这是更新头条新闻的标题

						@Override

						public void onPageSelected(int position) {

							// 更新头条新闻标题

							TopNews topNews = mTopNewsList.get(position);

							tvTitle.setText(topNews.title);

						}

			

				只有在页面滑动时才会做初始化,需要手动初始化第一次页面

				在TabDetailPager的processData的if中手动初始化第一个页面,即:	

						tvTitle.setText(mTopNewsList.get(0).title);//初始化第一页标题

			

				运行程序,第一页标题蜗居生活出来了,滑动出现中秋赏月,天空翱翔

Day03 09.头条新闻页面指示器(小圆点)##

给头条新闻ViewPager添加动态切换的圆点指示器



	小圆点功能已写两遍,自定义控件写了一遍,zhbj项目新手引导页写了稍微高级的,再写就没意思了



	用另外一种方式实现

		ViewPagerIndicator本身具有此功能,Cicles下边,Default是点点点样子

		此处需要的效果是,默认灰色,选中后红色,一跳一跳的



		ViewPagerIndicator例子的Snap下照着去写,在ListSamples项目的SampleCirclesSnap.java,就几行代码

		它的布局文件simple_circles.xml,ViewPager下边是CirclePageIndicator

		在ListSamples例子程序中findViewById拿到它,给indicator设置ViewPager,和TabIndicator很像

		只是加一句话setSnap(true)表示是快照方式为true,这样才会一跳一跳



		把CirclePageIndicator抄到pager_tab_detail.xml布局文件中,在相对布局右侧

		

	在TabDetailPager中,先去声明此控件,即:

			@ViewInject(R.id.indicator)

			private CirclePageIndicator mIndicator;

	

	给mIndicator设置ViewPager,当ViewPager数据初始化完后设置,在processData的if判断中setAdapter下边,增加如下代码:

			mIndicator.setViewPager(mViewPager);

		

	这时去设置页面监听时,它两已绑定在一起,监听应该设置给mIndicator,这时应该注释掉重写写成									mIndicator.setOnPageChangeListener(this);



	运行程序,效果出来了,不会自动滑动是因为没有给它设置快照setSnap(true)

	在mIndicator.setViewPager(mViewPager)下边写:

			mIndicator.setSnap(true);// 快照方式展示

	

	样式不太一样,默认是灰色,选中后是红色,viewpagerIndicator开源框架支持修改样式

	Cicles下边有个Styled(via layout),Styled它这有三个,via layout是通过布局修改样式

	还有一个via methods,在代码中通过方法去修改布局,还有一个via theme,通过主题修改

	布局方式修改简单一些,写个布局文件就可以,别的比较烦,看Styled(via layout)是怎么去写



	ListSamples项目的SampleCirclesStyledLayout.java,这时原来的代码中完全一样,只需改它的布局文件

	看下themed_circles.xml怎么写的,做了好多处理,app:自己定义属性,radius半径,fillColor,pageColor,strokeColor,strokeWidth

	这些color都是什么color?

	首先它有个背景,android:background="#FFCCCCCC",这是个灰色背景,实际上不需要背景

	然后它的半径是10dp,fillColor是#FF888888(怎么判断它是什么颜色,最后6位每两位按顺序分别代表红绿蓝)

	它是灰色被选中后的小圆点颜色,发现fillColor是被选中后的颜色,PageColor是#88FF0000是红色

	strokeColor为#FF000000表示线条颜色,每个小圆点有个边框为黑色,strokeWidth为2dp表示线条宽度是2dp



	把这些属性抄过来,给自己的布局文件pager_tab_detail.xml的CirclePageIndicator加属性

	加过来后挨个改下,fillColor是选中后的颜色是红色,所以改为#f00,默认颜色pageColor是灰色#cccccc

	radius半径改为3dp,strokeColor不需要有它的边框,可以删掉,stokeWidth最好别删,一删会有默认线条宽度,强制写成0dp,表示没有边框宽度



	报错error:Error parsing XML:unbound prefix,是因为域名空间问题,没有绑定某个前缀,它不认识app:是啥

	自定义属性必须在布局文件上边声明域名空间,从例子程序的themed_circles.xml中把域名空间抄过来,放在pager_tab_detail.xml上边LinearLayout处,即:

		xmlns:app="http://schemas.android.com/apk/res-auto"

	



	 运行程序,点新闻中心,看到颜色修改过来了,而且还有个小功能,什么功能呢,可以点小圆点右侧左侧去切页面

	它支持上下页翻页,其实没用,没人知道小圆点还能点



解决再次切入ViewPager后页面和小圆点不同步的问题



	北京页面滑动到第3张图片(天空翱翔),突然切到体育,北京页面就被ViewPager自己销毁掉了

	再切换到北京时,ViewPager要重新初始化北京页面,点北京页面切换看下,图片是第一张(蜗居生活)

	但小红点还是位于第3个位置(天空翱翔),两个没有同步过来

	

	ViewPagerIndicator自作聪明,正常情况下页面销毁后,所有变量乱七八糟的都销毁掉就行

	但ViewPagerIndicator自作聪明销毁后把上次那个位置记录下来,下次再初始化时,就拿上次那个位置去搞

	但每次进来后定义了第一个页面,这个点却是上次保存下来的点位置,所以会导致不同步



	解决:

		每次初始化数据后单独去调mIndicator

		TabDetailPager的processData的if (mTopNewsList != null)判断中增加下边代码: 

				mIndicator.onPageSelected(0);//让圆点设置到第一个页面的位置

		让第0个页面被选中必须调用这句话,因为indicator在页面销毁后默认保存上次位置状态

		但我们不需要这个状态,每次都要从第一个圆点开始展示



	运行程序,bug解决了

Day03 10.普通新闻列表展现##

实现了头条新闻功能后,接下来实现它下边普通新闻listview的数据



普通新闻ListView列表的实现



	在TabDetailPager的processData方法中解析了数据后,之前拿的是头条新闻数据

	现在要换数据拿了,newsBean中有个data,它下边有个news是普通新闻的集合,把它叫做mNewsList并搞成全局

	在TabDetailPager的processData方法中加如下代码:

				mNewsList = newsBean.data.news;

	这是普通新闻的数据,在这判断mNewsList不等于null时给它去设置adapter,就写在TabDetailPager中,叫做NewsAdapter,并实现它的几个方法

		getCount 应该return mNewsList.size();

		getItem  应该return mNewsList.get(position); 返回值是一个News对象

		getItemId 应该return position;

		getView 写一个item布局list_item_news.xml

			此处省略list_item_news.xml

		相对布局中左边是id为iv_icon的ImageView,右上是id为tv_title的标题TextView,右下是id为tv_time的日期TextView

		文字标题最多展示两行,设置android:maxLines="2",两行之后加省略号,设置android:ellipsize为end



		图片宽高包裹内容,图片有多大就显示多大

			如果服务器返回大小不一会导致布局凌乱,通常采用的是把宽高写死,保证不会过大或过小,宽度就写成120dp,高度80dp

		但有时会有别的问题,比如现在把宽高设成120dp,80dp,就非得把高设成150dp,imageView本身就这么长,图片这么窄

		不太好,希望imageView多大就多大,设置android:scaleType=“fitXY”,这个在代码中用过,这时就把它填充起来了

		这样填充美女的脸就变形了,还有别的属性值centerCrop,Crop裁剪

		写成android:scaleType=“centerCrop”,把宽高都设置成80dp,它会把左右两个美女都裁剪掉留下中间美女

		这样就不会变形了,用fitXY三个美女都显示但不好看,centerCrop既能填充父窗体还能进行裁剪

		这个属性确实非常常用,在不影响图片比例前提下能够填充屏幕,它采用裁剪方式实现

		通常裁剪图片来适配宽高,这样图片才不会变形

		在这介绍它,但是不用它,还是用fitXY



	给图片ImageView加边框

		完整项目每个图片都有小灰边框,要给ImageView加边框,android:background="#9000"灰色

		同时设置背景和图片,即:android:src="@android/image_demo"

		图片就把背景给盖住了,看不出任何效果

		然后给ImageView设置内边距,即android:padding="1dp",这时边框就出来了

		这是给图片加边框的方法,很常用

		

	list_item_news.xml布局文件写完后,listView就开始加载了,TabDetailPager的普通新闻数据适配器NewsAdapter的getView方法中,判断convertView是否为空,为空去加载布局,即:

			convertView = View.inflate(mActivity, R.layout.list_item_news,null);

	需要一个ViewHolder,即:

				static class ViewHolder {

				public ImageView ivIcon;

				public TextView tvTitle;

				public TextView tvTime;

				}



	在getView中声明ViewHolder,即: ViewHolder holder = null;

	在getView的if判断中new一个ViewHolder,即: holder = new ViewHolder();

	然后

			holder.ivIcon = (ImageView) convertView.findViewById(R.id.iv_icon);

			holder.tvTitle = (TextView) convertView.findViewById(R.id.tv_title);

			holder.tvTime = (TextView) convertView.findViewById(R.id.tv_time);

	最后将holder设置给convertView,即: convertView.setTag(holder);

	else,convertView不为空,从convertView中取出holder中的布局

		holder = (ViewHolder)convertView.getTag();

	这时可以给控件设置图片之类的东西,通过getItem拿到当前新闻对象,即:

			News news = getItem(position);

	给图片设置,需要bitmapUtils,构造方法中new一个bitmapUtils,并抽取成成员变量,即:

			public NewsAdapter() {

				mBitmapUtils = new BitmapUtils(mActivity);

				mBitmapUtils.configDefaultLoadingImage(R.drawable.news_pic_default);// 设置默认加载中的图片

				//mBitmapUtils = new MyBitmapUtils();

			}

	在getView中可以用它了,即: mBitmapUtils.display(holder.ivIcon, news.listimage);

	再给他设置标题和日期,如下:		

			holder.tvTitle.setText(news.title);

			holder.tvTime.setText(news.pubdate);

	最后把convertView返回回去



	processData的if (mNewsList != null) { 中new这个adapter,并抽取成全局变量,即:

				mNewsAdapter = new NewsAdapter();

	同样在这个地方给lisView设置Adapter,即: lvList.setAdapter(mNewsAdapter);

	

	运行程序,往下去滑,出现问题:



	1.解决android系统设置scaleType值为centerCrop时图片边框下边线消失的问题

		图片的边框下边这条线没了  

		是android系统的一个bug,android系统在去设置scaleType等于centerCrop去裁剪时,边框会出现小问题

		改用fitXY可以解决这个问题,这是android系统的问题不用去管它,以后如果不设置边框尽量用centerCrop



	3.解决listView滑动过程中出现黑色背景的问题

	  滑动时listView居然变成黑色的了,到根布局pager_tab_detail.xml中

	  上边是ViewPager,下边是listView,黑色是因为listView在底层绘制时,有一个缓冲颜色,默认是黑色,只需要把颜色换一下就行,在这个布局文件的listView添加属性: android:cacheColorHint="#fff"

		默认的这样一个缓冲颜色改成白色#fff就好

	

		android:cacheColorHint属性是为了防止listView滑动过程中出现黑色背景,需要设置此属性为白色或全透明

	

		运行程序,滑动时不会出现黑色,而是正常显示了



		有些同学可能会有疑惑,老师我之前用listView时没遇到黑色问题,为什么在这个地方会有问题?

		这个要解释还稍微有点费劲,有可能是因为上边加了一个ViewPager,而这个listView现在只在下边这个小小区域内活动

		有可能出现这样一个情况,以后不管他在什么情况下出现,只要发现listView一滑动是黑色,只需要给它在布局文件中加android:cacheColorHint="#fff"就解决了



		不用纠结什么时候设置这个属性,出现bug时设置就行



	4.将ViewPager作为ListView头布局实现整体上下滑动效果

		希望在滑listView时ViewPager能作为listView的一份子参与到页面滑动过程中

		pager_tab_detail.xml布局写控件时上边是ViewPager,下边是listView,两者没有任何关联

		滑动listView时,ViewPager不会往上走,导致listView只能下边小范围滑动

Day03 11.将头条新闻以头布局形式添加给listview ##

	完整项目效果ViewPager是listView的一个item条目,只不过样式非常特别,和普通样式完全不一样

	通过给listView添加头布局,把ViewPager作为listView头布局展现

	

	TabDetailPager的initView中通过lvList.addHeaderView()添加头布局



	以头布局的形式将头条新闻ViewPager添加给listView,所以这个布局文件不能在pager_tab_detail.xml中去写

	应该将头条新闻布局单独抽取出来,再写一个布局list_header.xml

		头布局,把刚才的帧布局抄过来

		此处省略list_header.xml	



	pager_tab_detail.xml界面中相当于只有一个listView,有了listView后先把头布局加载出来

	在TabDetailPager的initView中用View去加载布局文件,即:

			// 加载头条新闻作为头布局

			View headerView = View.inflate(mActivity, R.layout.list_header, null);

	这是头条新闻的头布局,塞给listView,即:

			lvList.addHeaderView(headerView);// 将头条新闻布局以头布局的方式添加给listview,作为listview的一份子,当上下滑动时,头条新闻(ViewPager)也能跟随滑动

		

	前边用注解去声明的ViewPager,即:

			@ViewInject(R.id.vp_top_news)

			private HorizontalScrollViewPager mViewPager;

	

			View view = View.inflate(mActivity, R.layout.pager_tab_detail, null);

			ViewUtils.inject(this, view);



	但现在这个pager_tab_detail布局中没有ViewPager了,肯定会空指针异常

	应该把当前头布局也注入一次,把ViewUtils.inject(this,view)方法抄一份放在如下位置:

			// 加载头条新闻的头布局

			View headerView = View.inflate(mActivity, R.layout.list_header, null);

			ViewUtils.inject(this, headerView);// 必须将头布局也注入到xutils中,才能够初始化viewPager对象 



	原来只是把整体的listView注入,现在要把整个头布局(ViewPager)也要注入

	注入后,头条新闻标题tvTitle,指示器mIndicator,mViewPager才都可以通过headerView去findViewById拿到



	运行程序,切换到新闻中心,这时候整体上下滑动就可以实现了

Day03 12.BitmapUtils设置默认图片&listview去掉分割线##

去掉侧边栏ListView中间的分割线



	fragment_left_menu.xml中的listView,属性divider 表示分割线

	android:divider="" 可以指定分割线

		要加线条图片,就@drawable/把线条图片加载进去

		不要分割线 直接@null即可

		即给ListView添加android:divider="@null"



		除了这个属性,还有dividerHeight,可以设置分割线高度

		运行程序,侧边栏中listView的分割线去掉了



	不管是下边ListView图片还是上边ViewPager图片,会有加载中的默认图片,下载完后先给一张默认图片

	玩应用时先给一张默认图,然后再去下载,下载完后默认图片才会变成真实图



	作为android开发人员,要有一台android手机不断体验各种不同的软件,用它们时考虑分别怎么实现的

	作为一个专业的android开发人员,都要成为软件的玩家,不断体验各种各样的软件,至少知道有这么个效果

	不一定必须写出来,至少有思路



使用BitmapUtils设置默认图片功能



	TabDetailPager的NewsAdapter的构造方法中,给mBitmapUtils设置configDefaultLoadingImage(int resId),即:

		mBitmapUtils.configDefaultLoadingImage(R.drawable.news_pic_default);// 设置默认加载中的图片

	

	运行程序,有默认的北京图片



	头条新闻(ViewPager)也设置默认图片

	到TabDetailPager的TopNewsAdapter的构造方法中,即:

		mBitmapUtils.configDefaultLoadingImage(R.drawable.topnews_item_default);



	运行程序,切换到新闻中心的一瞬间就会出现默认图片

	

	图片缓存在DDMS/data/data/com.itcast.zhxa02/cache,清理缓存就是清理这个cache,里边的xBitmapCache就是xUtils的图片缓存,里边全是图片缓存文件,这个图片是用图片下载链接的MD5拼起来的,后边加了个后缀叫做.0

	可以把它导出来加.png,打开就是老头蜗居图片

Day03 13.下拉刷新-隐藏头布局 ##

通过给ListView加头部局的形式实现下拉刷新效果



下拉刷新效果

	下拉刷新,往下拉显示下拉刷新,再往下拉显示松开刷新,松开手是正在刷新

	专门写个下拉刷新的listView,在view包new一个RefreshListView

	RefreshListView是基于原生listView进行的功能扩展,继承的还是listView,重写下它的构造方法

		此处省略RefreshListView.java



	下拉刷新的listView,要给它加头布局,把布局文件pull_to_refresh_header.xml写出来

		此处省略pull_to_refresh_header.xml



	RefreshListView中写个方法initView加载布局

		View mHeaderView = View.inflate(getContext,R.layout.pull_to_refresh_header,null);

	声明为全局变量,以头布局形式塞给ListView,也就是调listview的addHeaderView方法,刚才是在外边调的ListView的这个方法,现在在ListView里边调,即:

			this.addHeaderView(mHeaderView);//给listView添加头布局

	

	这个initView方法在每个构造方法(有3个不同参数的构造方法)都调一下



	pager_tab_detail.xml中将ListView控件名改为拷贝的RefreshListView的全路径com.itcast.zhxa02.view.RefreshListView

	布局中的这些属性都能用,因为RefreshListView本身继承的是listView

	

	回到TabDetailPager中,将:

				@ViewInject(R.id.lv_news)

				private ListView lvList;

				改为下拉刷新的listView,即:

				@ViewInject(R.id.lv_news)

				private RefreshListView lvlist;



	给listView加了下拉刷新的头布局了,RefreshListView只要在三个构造方法中初始化,马上就在RefreshListView中调用initView的this.addHeaderView(mHeaderView)去加载下拉刷新的头布局



	运行程序,点新闻中心,下拉刷新效果就出来了



	先加的下拉刷新头布局,再加的ViewPager头布局



	是在TabDetailPager的initView中添加的头布局,这时lvList(listView)已经初始化好了,要是没有初始化好,lvList就应该是空指针异常,要初始化一定要走它的构造方法才能初始化,即在RefreshListView的三个构造方法中初始化

	要初始化一个对象,底层肯定要走构造方法,不管走的是哪个构造方法,都马上在RefreshListView的initView中addView,这个View就是下拉刷新的头布局,所以下拉刷新的头布局是最先加上去的头布局

	这个加完初始化完后,才给listView又回到TabDetailPager的initView中添加了头条新闻的头布局

	

	顺序是谁先加(下拉刷新),谁在上边,谁后加(头条新闻)谁在下边

	

	下拉刷新头布局加载出来后,一直都在这展示着,没有隐藏掉,要的效果是'下拉刷新的头布局'是隐藏掉的

	只有向下拉,才会出来,一上来就应该把'下拉刷新的头布局'给隐藏掉,即在RefreshView的initView中隐藏'下拉刷新的头布局'

	

	 隐藏'下拉刷新的头布局'方法:

		写一个控件时,有时会给控件设置paddingTop值,paddingTop如果是0,可能和父控件的最顶点相贴

		paddingTop越大,这个控件越往下

		可以给paddingTop设置负值,平时设置正值,正直越来越往下越来越往下,现在要给它设置负值往上走



	以前写的都是正值,android可以写负值,写负值向相反方向走考虑,只不过不常用



	设置负的上边距布局就会向上走,负的上边距要设置成负的多少才能完全隐藏掉,这个负paddingTop应该是刚好是‘下拉刷新的头布局’高度



	在RefreshListView的initView中给mHeaderView(下拉刷新的头布局)去setpadding(int left,int top,int right,int bottom),上下左右的内边距都可以设置,left,right,bottom都不用管,直接设置成0,首先要拿到这个布局的高度

	

	mHeaderView.getHeight()可以拿到控件高度,即: int mHeaderViewHeight = mHeaderView.getHeight();

	这时把它设置成负的即可,即: mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);



	这时布局就会向上走了,可以在这打印它的高度,即:	System.out.println("height:" + mHeaderViewHeight);

	

	运行程序,点新闻中心,它还没隐藏,height打印的全是0



	因为在初始化布局时,onCreate,从MainActivity来讲,整个页面还没有创建完成

	创建流程是先去测量,再去layout,再去drawable,还没有测量完成,那你怎么知道宽高是多少,所以只能是0



	非要知道它的宽高,可以手动去测量,先要mHeaderView直接去,既然不调measure,就非得在RefreshListView的initView方法帮你调下measure,即:mHeaderView.measure(widthMeasureSpec,heightMeasureSpec);

		widthMeasureSpec,heightMeasureSpec这两个值不传,直接传个0表示手动测量,即:

			mHeaderView.measure(0,0);//手动测量布局宽高



	这样具体宽高就会由系统底层决定,我们不做任何决定,反正就调一下它告诉系统我就要测量

	

	然后高度mHeaderViewHeight就应该等于mHeaderView去getMeasuredHeight,表示获取

	刚才测量了,然后获取测量后的高度,把它传过来,即: mHeaderViewHeight = mHeaderView.getMeasuredHeight();

	把刚才写的int mHeaderViewHeight = mHeaderView.getHeight();注释掉即可



	运行程序,发现‘下拉刷新的头布局’就没了,它打印的高度height是58,已经获取出来了

	但是initView方法可能走了很多次,所以打印了很多条,因为有12个页签页面,每个页签都要把每个页签都初始化好



	现在‘下拉刷新的头布局’既然往上走隐藏掉了,往下拉应该能拉出来吧,看看它能不能拉出来,出不来对吧

Day03 14.总结 ##

----------------------------------------------------------------
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值