SmartBeijing Day04

Day04 01.昨天内容总结 ##

Day04 02.下拉刷新实现&刷新状态更新 ##

昨天实现了隐藏下拉刷新头布局,现在实现下拉时显示下拉刷新布局



	要响应onTouchEvent事件,RefreshListView中重写onTouchEvent方法,里边进行switch判断ev.getAction()



			@Override

		public boolean onTouchEvent(MotionEvent ev) {

			switch (ev.getAction()) {

			case MotionEvent.ACTION_DOWN:

				startY = (int) ev.getY();//获取开始的Y坐标

				break;

			case MotionEvent.ACTION_MOVE:

				if (startY == -1) {

					startY = (int) ev.getY();

				}

	

				// 如果当前正在刷新, 不做任何处理

				if (mCurrentState == STATE_REFRESHING) {

					break;

				}

	

				int endY = (int) ev.getY();//获取结束的Y坐标

				int dY = endY - startY;// 计算Y方向移动偏移量

	

				int firstVisiblePosition = getFirstVisiblePosition();// 查看第一个显示的item属于第几个

				// LogUtils.d("firstVisiblePosition=" + firstVisiblePosition);

	

				if (dY > 0 && firstVisiblePosition == 0) {//下拉动作&当前在listview的最顶部时,才让下拉

					int paddingTop = dY - mHeaderHeight;

	

					if (paddingTop < 0 && mCurrentState != STATE_PULL_TO_REFRESH) {//当前不是下拉刷新时,更新为下拉刷新状态

						mCurrentState = STATE_PULL_TO_REFRESH;

						refreshHeaderViewState();



					} else if (paddingTop >= 0 && mCurrentState != STATE_RELEASE_TO_REFRESH) {//进入松开刷新状态

						mCurrentState = STATE_RELEASE_TO_REFRESH;

						refreshHeaderViewState();

					}

	

					mHeaderView.setPadding(0, paddingTop, 0, 0);// 设置头布局padding

					return true;

				}

	

				break;

			case MotionEvent.ACTION_UP:

				startY = -1;// 重新初始化起始坐标位置

	

				if (mCurrentState == STATE_RELEASE_TO_REFRESH) {//松开手时当前状态为松开刷新时,更新为正在刷新

					// 将当前状态更新为正在刷新

					mCurrentState = STATE_REFRESHING;

					mHeaderView.setPadding(0, 0, 0, 0);

					refreshHeaderViewState();

				} else if (mCurrentState == STATE_PULL_TO_REFRESH) {

					mHeaderView.setPadding(0, -mHeaderHeight, 0, 0);// 隐藏头布局

				}

				break;

			default:

				break;

			}

			return super.onTouchEvent(ev);

		}

	

	1.在按下的case MotionEvent.ACTION_DOWN中获取开始的Y坐标,并抽取成全局变量,初始值为-1,表示是初始的值

		只需让"头条新闻ViewPager+普通新闻ListView"上下滑动时,下拉刷新头布局出来,只需要记录Y坐标

	2.在移动的case MotionEvent.ACTION_MOVE中

		1)获取结束的Y坐标,并计算Y方向偏移量

		2)下拉刷新下拉时被头条新闻ViewPager拦截事件的解决

			按住头条新闻ViewPager下拉时,ACTION_DOWN会被头条新闻消费,所以需要判断startY是否等于-1

			是,说明ACTION_DOWN没走进去,需要重新获取startY,否,反之

		

			以前不需要这个判断,这里需要

			因为普通新闻listView中的头条新闻ViewPager也可以滑动

			按住普通新闻listview下拉时下拉刷新头布局可以出来但

			按住头条新闻ViewPager下拉时也希望下拉刷新头布局能出来

			但头条新闻ViewPager是父控件“头条新闻ViewPager+listView”的孩子,默认让孩子消费事件消

			所以按住头条新闻ViewPager下拉时事件会被头条新闻ViewPager的ACTIONDOWN消费掉

			就走不到RefreshListView的ACTIONDOWN中获取startY了

			所以需要在RefreshListView的ACTION_MOVE中重新获取startY



			上边是起始点,下边Y坐标值比上边Y坐标值大,所以从上往下拉dy肯定大于0

		3)判断dy大于0 且 只有在最顶端下拉时才让下拉刷新布局出来

			手机卫士分页中判断过页面有没有到最底部,当时是getLastVisiblePosition拿到当前ACTION最后一个item的显示位置

			上拉一会listView后,使listview处于中间位置,再下拉也是在下拉,但getFirstVisiblePosition获取的可见item位置都不知道是第几个item了,,这时下拉刷新控件不需要出来

			只有firstVisiblePosition等于0才是最顶端位置

					

			所以不仅要判断dy大于0,并且firstVisiblePosition等于0时才让下拉刷新布局出来



		4)下拉刷新布局出来且下拉完后获取下拉移动的paddingTop值

			下拉移动的paddingTop应该是 当前下拉刷新的高度 减去 Y方向已移动距离(dy),但隐藏的距离是负值

			所以 下拉刷新paddingTop = dy-高度

				int paddingTop = dy - mHeaderViewHeight;// 计算当前控件的padding值

	

			将paddingTop设置给mHeaderView去更新位置,这时mHeaderView高度就变成paddingTop了

			在ACTION_MOVE的最后return一个true,表示把这个事件消费了

	

			打印下paddingTop的值

			运行程序,下拉就能出来了,paddingTop是负值,越来越大,下拉刷新完全展示时paddingTop变成了0

			下拉刷新完全展示后再往下拉,PaddingTop绝对值越来越大,多出来的是被拉出来的白色部分



	3.在抬起的case MotionEvent.ACTION_UP中

		要将startY值重新置为负一来重新初始化起始坐标位置,以防下次再下拉时,startY值会误判

		运行程序,下拉出来放开后没缩回去

			

	4.在成员变量处分别用整数1,2,3表示3种状态,即:

			private static final int STATE_PULL_TO_REFRESH = 1;// 下拉刷新状态

			private static final int STATE_RELEASE_TO_REFRESH = 2;// 松开刷新状态

			private static final int STATE_REFRESHING = 3;// 正在刷新状态

		用一个成员变量来表示当前是什么状态,默认为下拉刷新状态,即:

				private int mCurrentState = STATE_PULL_TO_REFRESH;// 当前状态,默认是下拉刷新

		

	5.在onTouchEvent中根据paddingTop & mCurrentState 判断是“下拉刷新”还是“松开刷新”,“正在刷新”状态

		

		1)case MotionEvent.ACTION_MOVE中,paddingTop < 0 && mCurrentState != STATE_PULL_TO_REFRESH时将mCurrentState置为“下拉刷新”STATE_PULL_TO_REFRESH

			判断paddingTop小于0 且 当前状态不是下拉刷新状态,才更新为下拉刷新状态,如果是就不用再更新为下拉刷新状态

		2)case MotionEvent.ACTION_MOVE中,paddingTop >= 0 && mCurrentState != STATE_RELEASE_TO_REFRESH时将mCurrentState置为“松开刷新"STATE_RELEASE_TO_REFRESH

		3)case MotionEvent.ACTION_UP中,mCurrentState == STATE_RELEASE_TO_REFRESH时,将mCurrentState置为"正在刷新"STATE_REFRESHING

		

		padding放在判断前后都无所谓,到最后统一都要更新这个padding值,即:

			mHeaderView.setPadding(0, paddingTop, 0, 0);// 设置控件padding,更新位置

	

	6.pull_to_refresh_header.xml中给每个控件加id,在RefreshListView的initView中初始化控件,声明成全局,即:

				tvTitle = (TextView) mHeaderView.findViewById(R.id.tv_title); //标题

				tvTime = (TextView) mHeaderView.findViewById(R.id.tv_time); //时间

				ivArrow = (ImageView) mHeaderView.findViewById(R.id.iv_arrow);//箭头

				pbLoading = (ProgressBar) mHeaderView.findViewById(R.id.pb_loading);//圆形进度条

				然后到refreshState方法的三个case中分别给控件设置值



	7.箭头从下拉刷新变为松开刷新时,从逆时针旋转180度,松开刷新放开时,又顺时针旋转180度变为下拉刷新

		是箭头的旋转动画RotateAnimation动画效果,旋转动画在这里是两种,一种从上向下,一种从下向上

		1)在RefreshListView中写initAnim方法,初始化箭头动画,即:

				//初始化箭头动画

				private void initAnim() {

					// 箭头向上

					animUp = new RotateAnimation(0, -180, Animation.RELATIVE_TO_SELF, 0.5f,Animation.RELATIVE_TO_SELF, 0.5f);

					animUp.setDuration(200);//动画运行时间

					animUp.setFillAfter(true);// 保持动画结束后的状态

					// 箭头向下

					animDown = new RotateAnimation(-180, 0, Animation.RELATIVE_TO_SELF,0.5f, Animation.RELATIVE_TO_SELF, 0.5f);

					animDown.setDuration(200);

					animDown.setFillAfter(true);// 保持动画结束后的状态

				}



			new一个RotateAnimation叫做animUp,代表箭头向上动画,从0度(fromDegrees)旋转负的180度(逆时针)(toDegrees)

			类型pivotXType一般都是Animation.RELATIVE_TO_SELF,值pivotXValue,pivotYValue一般都是0.5居于正中心旋转

			动画运行时间setDuration为200毫秒,箭头本来是向下,往上旋转后,一直在上边,所以要setFillAfter(true)保持住动画状态

			animDown动画相反,此处略

	

		2)initAnim方法在初始化界面的initView方法中调用

		3)到refreshState方法中根据3种状态用箭头ivArrow分别startAnimation()传参为animUp或animDown或setVisibility(View.INVISIBLE)	

	8.专门写个“根据当前状态,刷新控件”的refreshState方法,根据mCurrentState状态更新箭头方向,文字和进度条是否显示:



				//根据当前状态,刷新控件

				private void refreshState() {

					switch (mCurrentState) {

					case STATE_PULL_TO_REFRESH:

						tvTitle.setText("下拉刷新");

						pbLoading.setVisibility(View.INVISIBLE);

						ivArrow.startAnimation(animDown);

						break;

					case STATE_RELEASE_TO_REFRESH:

						tvTitle.setText("松开刷新");

						pbLoading.setVisibility(View.INVISIBLE);

						ivArrow.startAnimation(animUp);

						break;

					case STATE_REFRESHING:

						tvTitle.setText("正在刷新...");

						pbLoading.setVisibility(View.VISIBLE);

						ivArrow.clearAnimation();// 必须先清除动画,才能够隐藏控件

						ivArrow.setVisibility(View.INVISIBLE);

						break;

					default:

						break;

					}

				}

		

			switch判断mCurrentState

				1)如果是下拉刷新STATE_PULL_TO_REFRESH,文字tvTitle应该setText("下拉刷新"),进度条pbLoading应该setVisibility(View.INVISIBLE)不可见,箭头ivArrow应该startAnimation(animDown)向下

				2)如果是松开刷新STATE_RELEASE_TO_REFRESH,文字tvTitle应该setText("松开刷新"),进度条pbLoading应该setVisibility(View.INVISIBLE)不可见,箭头startAnimation(animUp)向上

				3)如果是正在刷新STATE_REFRESHING,文字tvTitle应该setText("正在刷新"),进度条pbLoading应该应该setVisibility(View.VISIBLE)可见,箭头ivArrow先clearAnimation清除动画,再setVisibility(View.INVISIBLE)隐藏箭头动画

					

				箭头本身有动画效果,而且保持了动画状态,有动画没法隐藏,必须先清除动画才能隐藏

				任何控件只要有动画,让它显示和隐藏就必须先把动画清除掉



	9.refreshState分别在onTouchEvent中切换状态的地方调用(每个状态的判断中)来刷新界面

				在case MotionEvent.ACTION_MOVE中

						mCurrentState = STATE_PULL_TO_REFRESH下边调用

						mCurrentState = STATE_RELEASE_TO_REFRESH下边调用

						mCurrentState = STATE_REFRESHING下边调用

	

	小问题解决:



	1)解决箭头和进度条同时显示的问题

	运行程序,往下拉时,箭头和进度条一起显示出来了

	

		进度条默认要隐藏掉,刚进来第一次往下拉时还没有切状态,不会走refreshState方法

		到pull_to_refresh_header.xml中,给ProgressBar添加属性: android:visibility="invisible"



	2)如果当前是下拉刷新,松开手时不用显示正在刷新,但在松开刷新时松开手,要显示正在刷新

		这个逻辑是在手势抬起后才进行,需要在case MotionEvent.ACTION_UP判断, 如果当前是松开刷新状态,即如果mCurrentState等于STATE_RELEASE_TO_REFRESH时

		要切换为正在刷新,即mCurrentState等于STATE_REFRESHING,否则(else),如果(if)抬起手了

		当前状态mCurrentState还是等于下拉刷新的状态STATE_PULL_TO_REFRESH,那就将下拉刷新布局隐藏掉

	

	3)设置mHeaderViewHeight隐藏布局和设置0完整展示布局

		怎么去完整展示状态?

				隐藏,设置了负padding用于隐藏掉了

				正常展示 将padding全改为0就刚好正常展示

					mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);//隐藏布局

					mHeaderView.setPadding(0, 0, 0, 0);//完整展示布局

		这就是鼠标抬起后(ACTION_UP)的操作			

	

	4)正在刷新时不许上下滑动的实现

	正在刷新时,有可能还会上下滑动,它会又变成下拉刷新,松开刷新,正在刷新时应该静静等着

		在ACTION_MOVE中移动时,判断当前状态mCurrentState是正在刷新状态STATE_REFRESHING

		就什么都不做,直接break跳出循环后,它后边就自己return super.onTouchEvent()了



		运行程序,只有ListView,往上滑没反应,上边没有任何反应,这就是正在刷新的过滤



	5)RefreshListView的onTouchEvent返回值不应该return true的原因

		RefreshListView的父控件是listView,listView要处理上下滑动事件

		此处光为了展示下拉刷新控件就return true消费事件,listView就无法响应上下滑事件了,需要让listview本身的滑动效果也能起作用

		此处应 return super,super可能是false

		

	6)不能在正在刷新的break后边直接return true

		不能在RefreshListView的onTouchEvent如下地方break后边直接return true

		// 如果当前是正在刷新状态, 什么都不做

			if (mCurrentState == STATE_REFRESHING) {

				break;// 跳出循环

			}



		在这ruturn true和在onTouchEvent下边ruturn true没啥区别

		在这return true同样会导致listView没有触摸事件,无法上下滑动

		在这break跳出循环后在onTouchEvent的下边就return了,我们不参与了



	7)需要在下拉动作和ListView最顶部判断中return ture

		RefreshListView的onTouchEvent的下拉动作&当前在listview的最顶部判断中

					if (dy > 0 && firstVisiblePosition == 0) 

		在这要return true,这种地方不希望listView参与进来,要全权去处理控件下拉刷新状态,这种情况应该让我自己处理

Day04 03.自定义进度条#

已实现基本的下拉刷新效果,进度条太丑,自定义一下



自定义下拉刷新功能中的进度条为渐变颜色



	drawable文件中写shape_custom_progressbar.xml,完整项目进度条是圆环

		选shape属性的ring

		圆环有渐变颜色gradient	

				起始颜色startColor 白色#fff

				中间颜色centerColor #9f00

				终点颜色endColor 红色#f00

		渐变方式type有 线性渐变linear,雷达辐射式渐变radial,横扫式渐变sweep

	

				<?xml version="1.0" encoding="utf-8"?>

				<rotate xmlns:android="http://schemas.android.com/apk/res/android"

				    android:fromDegrees="0"

				    android:pivotX="50%"

				    android:pivotY="50%"

				    android:toDegrees="360" >

				    <shape

				        android:innerRadius="15dp"

				        android:shape="ring"

				        android:thickness="5dp"

				        android:useLevel="false" >

				        <gradient

				            android:centerColor="#9f00"

				            android:endColor="#f00"

				            android:startColor="#fff"

				            android:type="sweep" />

				    </shape>

				</rotate>



	在pull_to_refresh_header.xml中的progressbar添加如下如下属性

		 android:indeterminateDrawable="@drawable/shape_custom_prgressbar"

		indeterminateDrawable可以设置进度条图片



	运行程序,发现正在刷新的圆环还有问题,之所以这样转是progressbar自带的效果

	在shape_custom_progressbar.xml中添加android:useLevel="false"去掉这个自带效果

	这个属性表示要不要层级结构,设置false相当于不用它原来的那个动画了

	

	用rotate旋转标签包裹shape,给rotate标签添加如下属性

			android:fromDegrees="0"

		    android:toDegrees="360"

			从0度到360度无死角旋转

			android:pivotX="50%"

    		android:pivotY="50%"

			旋转的相对位置为中心点

	

		运行程序,往下拉,正在刷新的圆形进度条就可以了



	完整项目中圆环更细

	shape_custom_progress.xml的rotate中按alt+/提示

		shape中有

			innerRadius 内径(内环半径)写个15dp

			thickness 厚度写个5dp

Day04 04.下拉刷新回调接口#

到TabDetailPager中去刷新服务器获取数据,在展示正在刷新时去刷新数据

下拉刷新RefreshListView知道什么时候变成正在刷新,下拉刷新,但TabDetailPager不知道

为了让TabDetailPager知道又要用到回调



调用下拉刷新回调接口实现刷新数据功能

	1)RefreshListView中写一个下拉刷新回调接口OnRefreshListener,里边定义onRefresh方法,这就是下拉刷新的回调

	写setOnRefreshListener方法中以参数形式传入OnRefreshListener,用mListener等于listener接收,并声明OnRefreshListener为mListener

				//下拉刷新回调接口

				public interface OnRefreshListener {

					// 下拉刷新回调

					public void onRefresh();

					// 加载更多回调

					public void loadMore();

				}

				private OnRefreshListener mListener;

				public void setOnRefreshListener(OnRefreshListener listener) {

					mListener = listener;

				}

	2)在RefreshListView的鼠标抬起后(ACTION_UP),切为正在刷新(mCurrentState = STATE_REFRESHING)时,

	用mListener.onRefresh()回调这个下拉刷新回调接口

		先判mListener不为null时,才去回调,这就是回调下拉刷新的动作

				// 回调下拉刷新动作

				if (mListener != null) {

					mListener.onRefresh();

					}

				} 

	3)TabDetailPager的initView中,给lvList设置setOnRefreshListener刷新事件,并重写onRefresh()方法去调用getDataFromServer()从服务器获取数据

				lvList.setOnRefreshListener(new OnRefreshListener() {

						@Override

						public void onRefresh() {

							getDataFromServer();

						}

					});



	运行程序,下拉刷新,数据过来了(在logcat中打印了‘页签页解析结果’)

	但是正在刷新布局没有收回去,TabDetailPager知道在刷新完后要收回去,但是RefreshListView控件不知道



调用下拉刷新回调接口实现收起正在刷新布局的功能



	TabDetailPager的getDataFromServer的onSuccess中刷新完要通知RefreshListView收回下拉刷新控件

		1)RefreshListView中再写一个onRefreshComplete方法,专门收起正在刷新布局

			刷新结束就是把padding重新隐藏掉,同时把很多成员变量初始化下

	

					//当刷新完成后,隐藏下拉刷新控件, 初始化各项数据

					public void onRefreshComplete(boolean success) {

						System.out.println("isLoadingMore:" + isLoadingMore);

						if (!isLoadingMore) {

							// 隐藏控件

							mHeaderView.setPadding(0, -mHeaderViewHeight, 0, 0);

							// 设置为默认的下拉刷新状态

							mCurrentState = STATE_PULL_TO_REFRESH;

							tvTitle.setText("下拉刷新");

							pbLoading.setVisibility(View.INVISIBLE);

							ivArrow.startAnimation(animDown);

				

							if (success) {

								setRefreshTime();// 网络访问成功,才设置刷新时间

							}

						} else {

							// 加载更多

							isLoadingMore = false;

							mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);// 隐藏更多

						}

					}

		2)TabDetailPager的getDataFromServer的onSuccess中调用

					// 收起加载更多控件

					lvList.onRefreshComplete(true);

			

		运行程序,往下滑,手指放开就收起来了,这是我们收起的一个操作

		3)还要考虑服务器异时走到onFailure方法

		   数据获取失败,Toast提示用户,控件也要收起,这样用户能继续刷新去重试

		   在onFailture方法中写

				// 收起加载更多控件

				lvList.onRefreshComplete(false);

				Toast.makeText(mActivity, "加载更多失败", Toast.LENGTH_SHORT).show();	

Day04 05.更新刷新时间 ##

实现下拉刷新下边的时间功能,这个时间指的是上次的刷新时



给下拉刷新添加上次刷新成功的时间功能



	1)在下拉刷新RefreshListView中再写一个setRefreshTime方法设置时间,就是给tvTime(TextView)去setText



		每次设置的是当前刷新时间,格式为2015-09-01 17:12,new一个SimpleDateFormat(pattern)对时间格式化

			参数partern要的是年月日格式

				年是yyyy,月是大写的MM,日是dd,时是HH,分是mm,秒是ss

				为什么有时是大写有时是小写?

					老外搞年月日时,认为1月份是从0开始,0是1月份,1是2月份

					如果是小写m返回的一月份就是0, 用大写就表示从1开始不要从0开始

					大写H表示是24小时制,小写h表示是12小时制

					mm和ss之所以两位,是因为个位要补位



			对时间格式化时new一个Date,默认就是当前时间,倒一下包java.util.Date

			返回String类型的time,设置给tvTime



				private void setRefreshTime() {

					SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// H大写表示24小时制;h表示12小时制

					String time = format.format(new Date());

					tvTime.setText(time);

				}



		在RefreshListView的initView中一上来就调用setRefreshTime先去设置下当前时间

		在onRefreshComplete中每次刷新之前,也调用下setRefreshTime去设置下当前时间

	

		ctrl+o可以在.java中查看它的所有方法



		运行程序,往下滑,时间变成9月2号2点43(模拟器中的时间),再刷新时时间就变成45了



	2)这个相当于每次下拉刷新刷新完后(RefreshListView的onRefreshComplete方法),就把时间设置了一下

		但万一刷新失败了,也会走onRefreshComplete方法也把时间给设置了一下

		失败了不设置时间,这个指的是上次刷新成功的时间



		给onRefreshComplete加个参数,表示成功还是失败,成功才设置时间

				public void onRefreshComplete(boolean success){

					if (success) {

						setRefreshTime();// 网络访问成功,才设置刷新时间

					}

				} 

		在TabDetailPager的getDataFromServer的onSuccess的lvList.onRefreshComplete(true)传一个true

		在onFailure方法的lvList.onRefreshComplete(false)传一个false

Day04 06.下拉加载更多-脚布局实现 ##

实现下拉刷新功能时不支持用相对布局作为根布局



	写下拉刷新控件pull_to_refresh_header.xml时不能用相对布局作为根布局

		否则运行就会挂掉,报xml文件有问题

		给一个listView添加headerView时,不支持用相对布局作为根布局

		相对布局本身有一个bug或问题,所以在写listview头布局时尽量用线性布局



通过给ListView加脚布局的形式实现下拉加载更多效果

	

	下拉刷新RefreshListView中initView表示初始化头布局,为了和脚布局分开,重命名为initHeaderView

	刚才这个动作就叫重构,写代码不可能一次性写的非常好,根据需求给新加功能对原来方法名或结构修改就叫重构

	再写一个初始化脚布局的initFooterView,也应该在每次上来就初始化,即在RefreshListView的三个构造方法中调用



	在layout文件夹中写pull_to_refresh_footer.xml,它是progressbar + textView

	可以从pull_to_refresh_header.xml中抄过来修改下



			<?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="match_parent"

			    android:gravity="center"

			    android:orientation="horizontal" >

			    <ProgressBar

			        android:layout_width="wrap_content"

			        android:layout_height="wrap_content"

			        android:layout_gravity="center"

			        android:indeterminateDrawable="@drawable/shape_custom_prgressbar" />

			    <TextView

			        android:layout_width="wrap_content"

			        android:layout_height="wrap_content"

			        android:layout_marginLeft="5dp"

			        android:text="加载中..."

			        android:textColor="#f00"

			        android:textSize="18sp" />

			</LinearLayout>

	

	RefreshListView的initFooterView中,View.inflate布局,并抽取成全局变量

	调用addFooterView(mFooterView)将脚布局添加给RefreshListView



			//初始化脚布局

			private void initFooterView() {

				mFooterView = View.inflate(getContext(),

						R.layout.pull_to_refresh_footer, null);

				this.addFooterView(mFooterView);



				// 隐藏脚布局

				mFooterView.measure(0, 0);

				mFooterViewHeight = mFooterView.getMeasuredHeight();

				mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);



				this.setOnScrollListener(this);

			}



	运行程序,看到把脚布局添加加进来了 



	添加进来后,接下来实现隐藏脚布局,脚布局隐藏方式和和头布局方式完全一样



隐藏上拉加载脚布局的实现

		mFooterView先measure测量一下

		再去getMeasuredHeight()拿到它的高度

		高度拿到后就可以给mFooterView去setpadding隐藏了



	代码如上

Day04 07.下拉加载更多实现 ##

实现了隐藏脚布局的功能后,当用户滑到最底部时,应该显示脚布局



上拉时显示上拉加载布局的实现



	监听页面滑动到底部的事件,手机卫士分页功能就有监听滑动到底部



	RefreshListView类名处,implements OnScrollListener,导包,实现它的方法去设置滑动事件

	initFooterView中setOnScrollListener给RefreshListView设置回调监听

	传this就可以了,因为当前类已经implements实现了它

				this.setOnScrollListener(this);

		

		实现它后一旦页面滑动到底部就会走如下两个方法

		onScrollStateChanged 表示滑动状态发生变化

		onScroll 滑动过程中会回调onScroll方法,不需要这个方法





		这里需要的是onScrollStateChanged,它会回传一个scrollState

		ctrl+鼠标左击OnScrollListener,进入AbsListView$OnScrollListener.class

		看到有3种状态:

			SCROLL_STATE_IDLE = 0;  空闲状态

			SCROLL_STATE_TOUCH_SCROLL = 1;	刚开始触摸状态

			SCROLL_STATE_FLING = 2; 抛飞状态,listView刷一下一滑,它就跑上去了,这就叫飞的动作

	

		看列表静下来结束后是否到最底部,所以看IDLE事件就可以

		RefreshListView的onScrollStateChanged中判断scrollState等于SCROLL_STATE_IDLE表示listview是空闲状态,这时看下有没有到最底部



			getLastVisiblePosition()拿到当前界面显示的最后item位置	

			如果这个位置刚好是listview的最后一个元素,就认为它到最后一个了

				那就再判断如果lastVisiblePosition等于getCount()减1,就是最后一个,并打印日志“到底了”

		

				if (scrollState == SCROLL_STATE_IDLE) {// listview空闲

					int lastVisiblePosition = getLastVisiblePosition();// 当前显示的最后一个item的位置

					if (lastVisiblePosition == getCount() - 1 && !isLoadingMore) {

						// 最底部了

						System.out.println("到底了...");

						}

					}

		

			这就是监听页面到底部了的事件,运行程序

		

		有一个真机的在线链接,把这些tomcat服务器代码全放在了在线服务器上,是哪个地址?

			//服务器线上前缀地址

			public static final String SERVER_URL ="http://zhihuibj.sinaapp.com/zhbj";

			这个是我专门申请了一个域名,叫做zhihuibj.sinaapp.com,是新浪云平台上的域名



		你只需要把

			// 服务器根地址, 10.0.2.2是预留的ip地址,专供模拟器访问PC的服务器使用

			public static String SERVER_URL = "http://10.0.2.2:8080/zhbj";

		这个注释掉

			//服务器线上前缀地址

			把public static final String SERVER_URL ="http://zhihuibj.sinaapp.com/zhbj";

		这个打开

		这样就无缝切换到线上了,这时拿真机跑就没问题了

		放在在线服务器上要花钱,按流量计费,你们没有几个人用,我就不花钱



		还是把项目在android原生模拟器上运行,当新闻中心的listView滑到底部时,就打印'到底了'

	

		到底后需要把脚布局显示出来

		RefreshListView的onScrollStateChanged方法中,mFooterView去setPadding,完全显示,左右宽高都是0

				mFooterView.setPadding(0, 0, 0, 0);// 显示脚布局

		

		运行程序,listview到底了,也在logcat中打了到底了,但是脚布局没有出来,继续往下滑才出来

		这个效果比较恶心,滑到底了再往下滑才出来



	1.解决滑到底部后再往下滑脚布局才出来的问题(调用setSelection设置当前选中最后一个位置)



		用户希望一到底马上出来,之所以需要到底了再次滑才出来,是因为到底后才把脚布局显示出来,那它自然就在下边了

		

		如何不让它在下边,listView有一个设置当前选中哪个位置的方法setSelection(position)

		把当前选中的位置设置为最后一个,getCount() - 1表示是最后一个,即:

			this.setSelection(getCount() - 1);// 让listview显示在最后一个位置,直接显示脚布局

			

		listview默认是从0开始

		如果希望listview从某个位置开始显示,那就调用setSelection(position),把位置传给它即可

		这样listview就会自动定位到这个位置

	

		运行程序,往下滑到底部时,不用再往下滑,脚布局就显示出来了,因为手动强制让listview跑到这个位置了



	2.保证到底后只加载一次网络数据的实现(添加并判断boolean值isLoadingMore)	



		到底后应该去加载这个网络数据,但有时网速比较慢,用户比较着急还往下滑呀滑呀,这时会频繁调到底了

		频繁的去访问网络数据,会出现很多重复数据,所以应该加一个标记判断是否正在加载



		在RefreshListView成员变量处写个isLoadingMore表示是否正在加载更多,默认是false不加载

			private boolean isLoadingMore = false;// 是否正在加载更多

		onScrollStateChanged方法到底后,isLoadingMore就变为true表示开始加载更多

		但只有false的情况下才会走进来,所以在上边判断时,增加‘并且isLoadingMore是false的情况下’,才走进来

		然后设置isLoadingMore为true去开始加载更多

		它是false才设置为true开始加载,已经开始加载就不用加载了,加这个标记去保证只加载一次

	

		RefreshListView的onScrollStateChanged方法如下:

	

		@Override

		public void onScrollStateChanged(AbsListView view, int scrollState) {

			// 滑动状态发生变化

			if (scrollState == SCROLL_STATE_IDLE) {// listview空闲

				int lastVisiblePosition = getLastVisiblePosition();// 当前显示的最后一个item的位置

				if (lastVisiblePosition == getCount() - 1 && !isLoadingMore) {

					isLoadingMore = true;// 开始加载更多

	

					// 最底部了

					System.out.println("到底了...");

					mFooterView.setPadding(0, 0, 0, 0);// 显示脚布局

					this.setSelection(getCount() - 1);// 让listview显示在最后一个位置,直接显示脚布局

	

					// 访问网络数据

					if (mListener != null) {

						mListener.loadMore();// 加载更多

					}

				}

			}

		}

	

		运行程序,往下滑打印到底了,这时用户再往下滑,就不打印到底了

		因为一到底把isLoadingMore设置为true了,false时才走进来,是true就不走进来了,保证到底了只执行一次

	

		访问网络数据是TabDetailPager的逻辑,在RefreshListView肯定没有办法,只能通知TabDetailPager该加载下一页数据了

	

	3.共用下拉刷新回调接口实现加载下一页数据的回调

		

		在RefreshListView的OnRefreshListener回调接口中,再写一个加载更多的回调loadMore方法

		在RefreshListView的onScrollStateChanged中访问网络数据时调,判断mListener不等于空才调mListener的loadMore方法去加载更多

	

		这样写后主页面TabDetailPager会报错,因为它少实现了一个加载更多的方法loadMore

		到TabDetailPager报错的地方,点击Add unimplemented methods去实现

			

				lvList.setOnRefreshListener(new OnRefreshListener() {

					@Override

					public void onRefresh() {

						getDataFromServer();

					}

					@Override

					public void loadMore() {

						if (mMoreUrl != null) {

							getMoreDataFromServer();

						} else {

							Toast.makeText(mActivity, "没有更多数据了...", Toast.LENGTH_SHORT).show();

							// 收起加载更多控件

							lvList.onRefreshComplete(true);

						}

					}

				});

	

	4.实现加载下一页数据的功能

		onRefresh中getDataFromServer()加载的是第一页数据

		1)loadMore中加载的是下一页数据,写一个getMoreDataFromServer()表示加载下一页数据,并生成方法

				//加载下一页数据

				protected void getMoreDataFromServer() {

					HttpUtils utils = new HttpUtils();

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

						@Override

						public void onSuccess(ResponseInfo<String> responseInfo) {

							// 访问成功

							String result = responseInfo.result;// 服务器返回的数据

							processData(result, true);

							// 收起加载更多控件

							lvList.onRefreshComplete(true);

						}

						@Override

						public void onFailure(HttpException error, String msg) {

							// 请求失败

							error.printStackTrace();

							Toast.makeText(mActivity, msg, Toast.LENGTH_SHORT).show();

							// 收起加载更多控件

							lvList.onRefreshComplete(false);

						}

					});

				}

		下一页数据怎么加载

			浏览器地址栏中输入localhost:8080/zhbj/10007/list_1.json回车查看数据格式,这是新闻的列表数据,网上大讲堂之类的

			每个数据前边有个字段more,就是下一页数据,localhost:8080/zhbj/10007/list_1.json是当前页数据

			把浏览器地址栏的/10007/list_1.json改为下一页数据链接/10007/list_2.json,回车就是下一页数据

			还是网上大讲堂,但是后边加了个2表示是下一页数据,这个就是第二页数据

			第二页数据发现没有more,说明没有下一页数据了more是空字符串

			所以加载下一页,就看有没有more字段

		

		2)TabDetailPager解析数据方法processData中有一个newsBean字段,可以从newsBean拿到解析后的data,data有个more就是下一页数据,返回一个String类型的moreUrl		

			用TextUtils..isEmpty(str)判断它是否为空,不为空就给它拼一个链接

			moreUrl只有/10007/list_1.json,没有前缀

			写个String叫做mMoreUrl等于GlobalConstants点SERVER_URL加上moreUrl

			这是下一页地址,把它声明成成员变量

					private String mMoreUrl; //下一页地址

				

					String moreUrl = newsBean.data.more; 

					if (!TextUtils.isEmpty(moreUrl)) {

							mMoreUrl = GlobalConstants.SERVER_URL + moreUrl;

						} else {

							mMoreUrl = null;// 没有下一页数据了

						}

		

			来个else,moreUrl为空时,应该将mMoreUrl置空,表示没有下一页数据了

			这就是这样一个processData,以上的就是初始化下一页链接的代码

			

			TabDetailPager的getMoreDataFromServer中,只需要加载MoreUrl就可以了

			逻辑和getDataFromServer方法类似

			把getDataFromServer方法中的逻辑拷贝到getMoreDataFromServer方法中,把mUrl改为mMoreUrl

			它也会设置数据result,缓存不加了,下拉刷新控件也删掉

		

		3)loadMore方法中判断mMoreUrl不为空时调用getMoreDataFromServer方法加载下一页数据

		  为空说明是最后一页了,Toast提示用户"没有更多数据了..."

				

			

		TabDetailPager的getMoreDataFromServer中拿到的数据result是下一页的json数据

		调用processData对newsBean解析,最后把它放进mNewsList集合,给lvList(listView)去设置Adapter

		这样会导致下一页数据把上一页数据覆盖,希望一页之后追加一页,数据越来越多



	5.解决下一页数据将上一页数据覆盖的问题(加标记isMore判断是否第一页刚进来)

		

		processData方法参数中追加参数boolean isMore,如果isMore等于false才走这些(头条新闻)逻辑,头条新闻只加载一次就行了,剩下的是新闻列表去分页

		这是第一次进来,所以在这判断如果isMore等于false,才走这些逻辑,这是第一页数据初始化

		第一页肯定要给它去设置adapter之类的

		else表示下一页数据,可以从newsBean拿到新闻数据,这时这个数据是更多的数据moreNews

		addAll把moreNews追加给整个集合mNewsList,最后刷新下adapter

				else {// 加载更多

					ArrayList<News> moreNews = newsBean.data.more;

					if (mNewsList != null) {

						mNewsList.addAll(moreNews);// 将更多数据追加给现有的集合

						mNewsAdapter.notifyDataSetChanged();

					}

				}

			这就是下一页数据的逻辑,加了isMore标记后

			getMoreDataFromServer中调用的processData(result)报错了,应该传的是true

							processData(result,true);

			getDataFromServer中调用的processData(result)报错,这个是第一次进来,肯定传个false

							processData(result,false);

			initData中调用processData(cache)报错,这个是缓存,缓存是第一页进来肯定要加载个缓存,这时它也是第一次进来,还是false,即: processData(cache,false);

		



		运行程序,往下滑,第二页过来了,再往下滑没了,没了后要把加载中消失掉

		现在'加载中...'还在这,往下滑没有弹Toast提示

		

	6.解决没有下一页数据时没有提示“没有更多数据了”



		没有弹Toast提示是因为没有走到loadMore中,因为RefreshListView的onScrollStateChanged判断的isLoadingMore是true

		

			现在需要数据返回完后收起加载更多控件

		1)TabDetailPager的getMoreDataFromServer的onSucess中添加如下代码:

				// 收起加载更多控件

				lvList.onRefreshComplete(true);

		

			在onFailure方法中也要收起来,也添加如上代码,只不过把true改为false即可

		调这个onRefreshComplete中是收起来下拉刷新头布局,又不能收脚布局



		2)在自定义控件RefreshListView的onRefreshComplete中再加一个收起脚布局的功能

			判断isLoadingMore是不是在加载更多,不是就隐藏头布局,说明就是下拉刷新

			else表示下拉加载更多

				把isLoadingMore置为false,已经结束了,方便下次下拉刷新再进来

				mFooterView去setPadding设置-mFooterViewHeight隐藏脚布局

					else {

						// 加载更多

						isLoadingMore = false;

						mFooterView.setPadding(0, -mFooterViewHeight, 0, 0);// 隐藏更多

					}

		

		3)TabDetailPager的getMoreDataFromServer中去收起下拉加载更多控件时

			getMoreDataFromServer会走到RefreshListView的onRefreshComplete的else中把加载更多控件隐藏掉

		

		运行程序,点新闻中心往下拉是第二页,再往下拉没了,它就会吐司提示没有更多数据了

		但是它没有走进来,即没有把‘加载中...’布局给隐藏掉

		下拉刷新下刷新新数据时,下拉刷新的布局都不隐藏了



7.解决没有下一页数据时没有隐藏脚布局‘加载中...’

			

		按理加载更多后应该调一下TabDetailPager的onSuccess的lvList.onRefreshComplete(true)把控件收起来

		收起来后,按理说应该走到onRefreshComplete的else把‘加载更多’控件隐藏掉



		onRefreshComplete打印下isLoadingMore值对不对,true或false,确定有没有走到onRefreshComplete的else中

				System.out.println("isLoadingMore:" + isLoadingMore);

		

		运行程序,打印出来isLoadingMore是true,true就走到RefreshListView的onRefreshComplete的else中把‘加载更多’隐藏掉了,并把isLoadingMore重新置为false,说明第一页加载完后,把‘加载更多’控件收起来了

		

		再往下滑加载下一页数据,打印‘到底了...’,但是没有打印isLoadingMore值

		说明到底后没走到RefreshListView的onRefreshComplete中来

			

			onRefreshComplete在TabDetailPager的getMoreDataFromServer中访问网络时调的

			现在是最后一页数据,直接到TabDetailPager的loadMore中吐司提示‘没有更多数据了’

			根本没走getMoreDataFromServer

				因为在TabDetailPager的loadMore忘了调lvList.onRefreshComplete(true)

				弹出‘没有更多数据了...’,但没有把‘加载更多...’收起来就是这个原因

		

				传true或者false,没有太多意义,只针对下拉刷新管用,对‘加载更多...’没有任何作用

				因为这个只有在RefreshListView的onRefreshComplete的if(!isLoadingMore)这个地方用,所以传true或false,没有任何关系



			运行程序,往下滑,吐司提示‘没有更多数据了...’,然后把‘加载更多...’收起来了

		

			切换到中国,中国永远有下一页数据,因为写了一个死循环,第一页的下一页数据是第二页,第二页的下一页数据是第一页,就是为了能够方便看分页效果,这个是无限往下分页了



8.上拉加载更多的逻辑总结

		1)在RefreshListView中用initFooterView去初始化脚布局放在最底部

		2)在RefreshListView的initFooterView中设置了滑动监听this.setOnScrollListener(this);

		   会走到RefreshListView的onScrollStateChanged中判断listView空闲后获得最后一个位置

			再判断到最底部了就把‘加载更多...’脚布局显示出来

			主动去setSelection让它显示在最后一个位置,这样脚布局就直接显示出来了

		3)再进行一次回调loadMore,在主页面TabDetailPager中实现loadMore加载下一页数据

				TabDetailPager的processData中从页面数据中拿到下一页的链接mMoreUrl

				没有moreUrl说明没有下一页,所以在else中把mMoreUrl置为null

			有了下一页链接后,就可以在TabDetailPager的getMoreDataFromServer请求下一页数据,直接调mMoreUrl加载后,需要把这次的数据result追加给原来的集合,传个标记isMore,要加载下一页数据就置为true,即: 				processData(result,true);

			传到TabDetailPager的processData中,等于true就在else中追加下一页数据,等于false表示第一页数据,进来之后原来怎么写就还是怎么样,这是下一页数据的逻辑

		 4)怎么判断的没有下一页数据了?

			TabDetailPager的loadMore的mMoreUrl为空就到底了没有下一页数据了,来到了else中,吐司提示‘没有更多数据了...’并收起加载更多控件的布局

				TabDetailPager的getMoreDataFromServer中加载下一页时,也调用lvList.onRefreshComplete(true)收起'加载更多...'控件

			这是下拉加载更多的逻辑,和手机卫士通讯卫士模块逻辑基本一样

			唯一区别是一个是从数据库取数据,一个是从网络调接口取数据

Day04 08.下拉刷新&上拉加载总结 ##

市面上有很多下拉刷新上拉加载的第三方开源框架,逻辑是相似的,面试时可以讲讲,这样有技术含量

只需要把jar包或库文件导进来,就可以迅速实现效果



	下拉刷新就是给ListView加头布局,设置负padding隐藏布局,通过监听触摸滑动事件,动态计算当前padding,实时设置最新的padding,设置完后就会慢慢跟随手指下滑出来,这是下拉刷新最核心的

	又通过padding值是0,大于0,小于0决定是松开刷新还是下拉刷新,padding到了临界点时马上去更新状态

	松开手时就马上切换为正在刷新,刷新控件就做完了



	面试官问这就做完了

		怎么通知主界面去刷新,主界面怎么知道,不就是个回调,再说回调接口怎么去写

			在TabDetailpager中的回调接口setOnRefreshListener中写个onRefresh()的回调方法

			再暴露一个接口,让RefreshListView去setOnRefreshListener把接口实现的东西传过来

			在RefreshListView控件内部一旦调用mListener.loadMore去下拉刷新

			就在TabDetailPager调一下这个回调接口响应onRefresh()方法

			然后去通知这个界面,这就是下拉刷新

	

		上拉加载核心代码也是加脚布局,逻辑比下拉刷新简单很多,因为不需要响应触摸事件一点一点出来

		一到底部马上就出来,出来后开始加载下一页,加载完后又把它隐藏掉,也是通过回调去通知界面更新



		下拉加载复杂的地方是在TabDetailPager把下一页数据追加在集合中,RefreshlistView中不是很复杂

		下拉刷新是在TabDetailPager的setOnRefreshListener的onRefresh比较简单,就调getDataFromServer()默认加载第一页数据

	

		但是‘下拉刷新’在RefreshListView的下拉刷新触摸监听这块又有点复杂,所以这两各有千秋

		面试中把这个大体思路了解清楚,能说出个六七成就可以

Day04 09.标记已读未读 ##

设置每个页面的点击事件,希望点击后能跳页面或切逻辑做相关处理



	在TabDetailPager的initView中给lvList设置点击事件setOnItemClickListener

	在onItemClick中打印被点击的位置position



			lvList.setOnItemClickListener(new OnItemClickListener() {

				@Override

				public void onItemClick(AdapterView<?> parent, View view,

						int position, long id) {

					System.out.println("点击位置:" + position);

				}

			});



	运行程序,新闻中心点listview每个item,点第一个位置的网上大讲堂,打印的点击位置是2

		因为这有‘下拉刷新’和‘头条新闻ViewPager’这两个头布局作为listview的一部分

		下拉刷新是0,‘头条新闻ViewPager’是1,网上大讲堂是2



解决因为加了头布局导致ListView点击事件中position不是0的问题(代理模式)



	这时调用mNewsList.get(position)从集合mNewsList中取到的是第三个item	

	1)因为mNewsList集合是从网上大讲堂开始算的,是第0个

	   所以在取时要写成:	mNewsList.get(position - 2)才能取到正确的值

		  这样写也可以,但以后将‘头条新闻ViewPager’头布局去掉后,这里还要重新改成-1

		  重新加了头布局又要重新-3,这种方式太麻烦

		

	2)setOnItemClickListener一点不动的前提下,希望position返回的是已经减掉头布局个数的位置

		ctrl+点击setOnItemClickListener到源码中,发现找的是父控件AdapterView.class的setOnItemClickListener,lvList是下拉刷新的RefreshListView,即TabDetailPager的:

					@ViewInject(R.id.lv_news)

					private RefreshListView lvList;



		可以在下拉刷新的RefreshListView中重写setOnItemClickListener方法,进行修改

		在TabDetailPager中去setOnItemClickListener时,找的是RefreshListView中重写的setOnItemClickListener



		相当于把TabDetailPager的setOnItemClickListener接口对象OnItemClickListener

		传递给了RefreshListView的setOnItemClickListener的listener

		对RefreshListView的setOnItemClickListener不做任何改动,它直接调的是super.setOnItemClickListener(Listener),把Listener传给它爹AdapterView.class了,其实AdapterView.class回传回来的position数据有问题



		不希望直接这样去做,那这时要怎么做?

		先把RefreshListView的setOnItemClickListener的super.setOnItemClickListener(Listener);注释掉

		就不在回传这个listener了

		让RefreshListVie实现android.widget.AdapterView.OnItemClickListener

		并实现它的方法onItemClick,setOnItemClickListener

		在RefreshListView的setOnItemClickListener中传个this,不再像TabDetailPager传listener了

					super.setOnItemClickListener(this);

		

		目的是让RefreshListView去处理点击事件了,不再由TabDetailPager的

				lvList.setOnItemClickListener(new OnItemClickListener() {

				}的这个去处理点击事件了

		在RefreshListView中传个this对象,给下拉刷新控件设置点击事件时,当item被点击后会走到RefreshListView的onItemClick中,我给自己设置点击事件,把自己传了进去,我自己能响应到onItemClick方法



	使用回调方式响应item被点击的事件	



		这个方法中需要通知TabDetailPager,item被点击的事件



		listener对象就是RefreshListView中的setOnItemClickListener中的listener

		在TabDetailPager中setOnItemClickListener时,把OnItemClickListener对象传给了RefreshListView的setOnItemClickListener的listener,这个listener就是那个对象



		这个listener一设置进来,TabDetailPager的listener就响应,现在没有在RefreshListView中设置listener,而是直接将当前的这个对象(this)设置给了它,这其实还是一个回调,把它去维护起来叫做mClickListener

				private OnItemClickListener mClickListener;

		在RefreshListView的setOnItemClickListener方法中写

					mClickListener = listener;

		这样就把listener放在mClickListener了

		一旦item被点击后走到onItemClick中,在这个方法中再去回调

		判断mClickListener不为空时去通知下,调onItemClick接口

		onItemClick传什么参数这就传什么参数,但postion在这应该position减getHeaderViewsCount()

		mClickListener.onItemClick()回调后,TabDetailPager的setOnItemClickListener就能拿到这个position

		RefreshListView这个地方对position偷梁换柱进行了修改

			@Override

			public void onItemClick(AdapterView<?> parent, View view, int position,

					long id) {

				// 通知前端界面,item被点击的事件, 回调

				if (mClickListener != null) {

					// 将position减掉头布局个数之后,回调给前端界面

					mClickListener.onItemClick(parent, view, position- getHeaderViewsCount(), id);

				}

			}



		这句话是将position减掉头布局个数后回调给TabDetailPager



		这时一回调,TabDetailPager就马上调onItemClick,TabDetailPager的onItemClick的position就是已经被减掉头布局的位置



		TabDetailPager的这个方法什么都没动,只是在底层转了一圈



		运行程序,点击网上大讲堂,打印的点击位置是0了,这样值就正常了,这就是偷梁换柱的代理模式操作



	

	用代理模式对点击事件的处理和封装(最本质的还是回调)

		TabDetailPager在setOnItemClickListener时设置的是listView的父类adapterView,它底层设置的是它的setOnItemClickListener

		现在把这条路线给咔嚓掉

			去设置listener时,把路径改到RefreshListView,让RefreshListView重写了setOnItemClickListener

			让RefeshListView自己去设置一个点击事件,一旦点击后,点击事件就由AdapterView回传给RefreshListView,这时回传的position之类的信息都要回传回来

		RefreshListView拿到这个事件之后,在它的OnItemClick中肯定会走到onItemClick中,把这个参数又回传给了TabDetailPager,但是在回传过程中对position进行了偷梁换柱,但是TabDetailPager本身并不知情

		相当于做了一个代理,本来TabDetailPager可以直接给AdapterView去设置,现在是让RefreshListView去响应的点击事件,完了之后再通知TabDetailPager和AdapterView





		不直接在TabDetailPager的setOnItemCLickListner中减2而是采用代理模式解决的原因:

		写api,自定义控件等框架时,要全心全意为开发者服务,调用自定义控件的人考虑的越少,说明自定义控件写的越牛逼

		这就是简单的封装,底层虽然做了很多工作,但是封装出来暴露给开发者的接口很简单,开发人员不用考虑太多东西

		



	完整项目,点击‘网上大讲堂’item后,item变成灰色表示标记已读状态,下次再进来还是已读状态

	item被点击后,在本地保持住已读的状态



标记列表item已读未读功能

	TabDetailPager的setOnItemClickListener的onItemClick中标记已读



	浏览器中的json数据,每条对象是一个已读对象,对象中有个id表示这条新闻的唯一标示

	‘网上大讲堂’id:35311,‘马路改建’id:35312

	可以拿这个id作为新闻标识,一旦‘网上大讲堂’被点击,就把id保存在sp,下次再进来sp中有这个id,就认为是已读状态,



	key就叫做read_ids,表示可以有好几个新闻同时已读,values就是它的id

	如果35312表示已读,加一个逗号,再写35312,一直追加

	sp中只能使用read_ids这一个字段,这个字段中的值越来越多一直在更新,可以把id保存在read_ids这样一个字段中

	先从新闻列表拿到当前的新闻对象

				News news = mNewsList.get(position);

	从新闻对象news中可以拿到id,,在原来的基础上再去追加



	要知道sp目前是什么结果,才能在最新结果后边追加

	用PrefUtils去getString,key叫做read_ids,values默认是空串,返回String叫做readIds

		String readIds = PrefUtils.getString(mActivity, "read_ids", "");

	在readIds基础上进行追加,readIds等于readIds加上最新的news.id,再加逗号,即:

			readIds = readIds + news.id + ",";// 将已读新闻id追加到已读ids中



	用PrefUtils.putString把更新后的readIds重新保存起来,key还是read_ids,value已经是新的value了,已经是readIds了

			PrefUtils.putString(mActivity, "read_ids", readIds);



	分析逻辑,先拿到目前的read_ids,如果第一次进来返回的readIds是空串,让空串等于空串加35311再加一个逗号

	结果就是'35311,',下次再进来时'35311,'等于‘335311,’加上新的'35312',后边再加逗号

		即'335311,35312,',这样每次屁股后边跟个逗号

		所以为什么说在这写个空串"",如果写个null空指针就很难控制了,在这就没法再加了,还得判空很烦

		直接写个空串就搞定了,这就是我们去标记它已读未读的这样一个操作



	运行程序,新闻中心点‘网上大讲堂’,再点‘马路改建’,data/data/com.itcast.zhxa02/config.xml

	把它导出来打开后ctrl+f搜索下read_ids,找到read_ids是35311,35312



	但是有个bug,比如'网上大讲堂',点很多次,config.xml导出来看到35311很多,记录一次标记是已读就够了



	判断有没有,没有才追加,有就不再追加了



	如果readIds中已经contains(cs)包含了新闻id,即news.id,就不追加了,不包含才追加

			News news = mNewsList.get(position);

			String readIds = PrefUtils.getString(mActivity, "read_ids", "");

			if (!readIds.contains(news.id)) {// 已读ids中不包含当前id,才进行追加

					readIds = readIds + news.id + ",";// 将已读新闻id追加到已读ids中

					PrefUtils.putString(mActivity, "read_ids", readIds);

				}

			mNewsAdapter.notifyDataSetChanged();

	

	运行程序,新闻中心多次点'网上大讲堂',config.xml的read_ids只记录了一次,这是已读的标记



	标记完后,应把item颜色换成灰色,在这个地方去刷新下adapter,刷新listview,更新已读状态

	notify时getView方法可以重新走一次,在getView方法中判断已读未读

			

				// 判断已读未读

				String readIds = PrefUtils.getString(mActivity, "read_ids", "");

				if (readIds.contains(news.id)) {

					// 标题置灰

					holder.tvTitle.setTextColor(Color.GRAY);

				} else {

					// 标题置黑

					holder.tvTitle.setTextColor(Color.BLACK);

				}

	

	这是刷新已读未读的界面,点击item时,马上去刷新listView更新已读状态,即上边在onItemClick中写的:

			mNewsAdapter.notifyDataSetChanged();

	这时listView根据最新readIds,重新把每个item过了一遍



	运行程序,点新闻中心,刚进来'网上大讲堂'已经是灰色了,因为listview第一次设置数据时就发现第一个item'网上大讲堂'已经是灰色了,再点'马路改建',也变成灰色了



将标记已读未读的全局刷新改为局部刷新可大幅度提高性能



	在这个基础上进行优化,点的是这一个item,但是这一个item一点后,其他item也要去刷新一下,有点浪费性能

	没有必要进行全部刷新,可以实现一个局部刷新,把上边在onItemClick中写的 

				mNewsAdapter.notifyDataSetChanged();注释掉



	点'北京两年'item,找到这个里边的textView,把textView颜色换一下,这就是局部刷新



	当前被点击的item的View对象就是onItemClick方法参数中的view,当lvList去setOnItemClickListener时

	它会回传onItemClick参数中的parent,view,position,id



	parent: 是AdapterView,listView继承的是AdapterView,所以parent本身就是listview对象,就是lvList

	view: 指的是当前被点击的那个item的布局对象

	position: 位置

	id: 是id



	所以view参数就是被点击的item布局对象,拿到被点击的布局对象后去findViewById,就能拿到TextView(‘北京两年内将迁出1200家工业污染企业’这个textview),所以在setOnItemClickListener方法中找到textview

			// 局部刷新

			TextView tvTitle = (TextView) view.findViewById(R.id.tv_title);

			tvTitle.setTextColor(Color.GRAY);

	

	这样性能就很高了,直接找到view对象设置成灰色就行



局部刷新的3种实现方式

		1.给那个view去setTag打个标记,再去findViewByTag,用tag把那个view拿到手

		2.还有一个方式是getTag方式,这个方式没有必要,很麻烦的,得每个View都维护一个Tag

		3.onItemClick方法的参数view就是当前被点击item的view对象,还干嘛要用findViewByTag

	

		运行程序,效果完全一样,点击‘大雾再锁京城’item,只是把自己刷新了,没有刷新其他item

			通过这个view去findViewById拿到对应的textview设置成灰色就行了

Day04 10.跳转新闻详情页面 ##

完整项目点击item后跳页面,在com.itcast.zhxa02包中创建新闻详情页NewsDetailActivity继承自Activity

并在清单文件注册,在onCreate方法中加载布局文件activity_news_detail.xml



布局文件上边有一个标题栏,标题栏前边已经抽取出来了,直接include引入进来就行



预览到这个标题栏和我们要的不太一样

	有2种手段

	1.自己重新打造一个新的标题栏

	2.重用以前的标题栏,对以前的标题栏进行修改



	这里用第二种方式,在左侧有时候是返回按钮,有时候是menu菜单,把title_bar.xml的ImageButton再抄一份,写两个ImageButton,位置完全一样压在一起,修改第二个ImageButton的id为btn_back,把src引用的图片换成back.png

	返回键默认应该是隐藏,给第二个ImageButton中加属性

		android:visibility="gone"

	再预览就只显示menu菜单了



	右侧有两个按钮,一个是调整文字大小,一个分享链接,用线性布局把两个ImageButton放里边,它两同时出现,同时隐藏

	字体的图片icon_textsize.png,给它设置id为btn_text_size

	分享的图片icon_share.png,给它设置id为btn_share

	这两个按钮的条目默认也是隐藏的,给线性布局LinearLayout设置属性:

		 android:visibility="gone"

	给线性布局LinearLayout加id为ll_control



	NewsDetailActivity的onCreate中动态把刚才隐藏的东西都显示出来

		private ImageButton btnBack;//返回按钮

		private ImageButton btnMenu;//menu菜单

		private LinearLayout llControl;

		private ImageButton btnTextSize;//文字大小

		private ImageButton btnShare;//分享

	

		setContentView(R.layout.activity_news_detail);



		btnBack = (ImageButton) findViewById(R.id.btn_back);

		btnMenu = (ImageButton) findViewById(R.id.btn_menu);

	  	llControl = (LinearLayout) findViewById(R.id.ll_control);

		btnTextSize = (ImageButton) findViewById(R.id.btn_text_size);

		btnShare = (ImageButton) findViewById(R.id.btn_share);



		btnBack.setVisibility(View.VISIBLE);

		llControl.setVisibility(View.VISIBLE);



去掉默认的黑框actionBar标题栏

	在修改的红色标题栏上边有窄窄的一条黑色标题栏,在它里边显示信号,电量,时间等

	onCreate的setContentView上边增加:

		requestWindowFeature(Window.FEATURE_NO_TITLE);



	TabDetailPager的onItemClick中增加跳到新闻详情页代码

		// 跳到新闻详情页

		Intent intent = new Intent();

		intent.putExtra("url", news.url);// 将url参数传递到下个activity

		intent.setClass(mActivity, NewsDetailActivity.class);

		mActivity.startActivity(intent);

 

	有些同学说:

	1.直接new,在Intent参数中传NewsDetailActivity.class,就可以搞定

	2.也可以在startActivity参数中传这些,一句话搞定

	之所以用声明这么多的方式,是为后边做伏笔

	

	运行程序,点新闻item,跳转到NewsDetailActivity页面了

Day04 11.WebView介绍和使用 ##

接下来处理新闻详情页NewsDetailActivity除了标题栏之外的下边部分



WebView的介绍



	中间这个地方看着像是textView,实际它是网页,和浏览器一样是一个容器,容器中加载的是网页

	这个容器叫做WebView

	到activity_news_detail.xml中再写一个WebView控件,id为webview

	它是android自带的比较特殊的View对象,专门用来加载网页的



	NewsDetailActivity的onCreate中findViewById拿到WebView

		private WebView mWebView;

		mWebView = (WebView) findViewById(R.id.webview);



	让WebView去加载网页一句话搞定

		mWebView.loadUrl(url);

	把url链接传过来给它

		mWebView.loadUrl("http://www.itcast.cn");



	运行程序,跳转到新闻详情页NewsDetailActivity就展示出了传智播客网页



	网页太大没有适配手机端,还能左右滑,这种网页右下角有时有放大缩小按钮

	给WebView加属性显示放大缩小按钮

	

	mWebView去getsettings,拿到设置对象,要给value设置全都拿settings去搞

	给settings设置如下属性

		WebSettings settings = mWebView.getSettings();

			settings.setBuiltInZoomControls(true);// 显示放大缩小按钮(仅限于非移动端适配的页面)

			settings.setUseWideViewPort(true);// 支持双击缩放(仅限于非移动端适配的页面)

			settings.setJavaScriptEnabled(true);// 支持js



	setBuiltInZoomControls(true); 也支持两个手指在屏幕上滑动来放大缩小

	setUseWideViewPort(true); 双击变小,再双击变大

	

	百度(www.baidu.com)对移动端做过适配,所以无需给它设置放大缩小,设置了也无意义

	

使用WebView加载新闻详情页	



	要加载的是'网上大讲堂','马路改建'这些

	在TabDetailPager的网络数据新闻news中有这些url,要把url传递给NewsDetialActivity

	

	在TabDetailPager的lvList.setOnItemClickListener的onItemClick中

		intent.putExtra("url", news.url);//将url参数传递到下个activity



	NewsDetailActivity中getIntent()拿到Intent对象去getStringExtra("url"),返回一个String类型的mUrl

			private String mUrl;	

			mUrl = getIntent().getStringExtra("url");

	

	让webView去加载网络链接mUrl

		mWebView.loadUrl(mUrl);

	

	运行程序,点新闻中心,点网上大讲堂,清明节新闻(新闻链接假数据就设置了这重复的一条)就出来了

	它一直能拉到底,要的效果是‘点击展开全文’,点击才能展开全文

	这个一上来就把全文加载上来是因为这个链接的‘点击展开全文’是用js去控制的,webview默认不支持js,需要添加如下代码

		settings.setJavaScriptEnabled(true);// 支持js

	

	运行程序,‘点击展开全文’就出来了



给WebView添加进度条功能

		可以给webView设置网页监听

	1.mWebView.setWebViewClient(new WebViewClient() {

			// 监听网页加载结束的事件

			@Override

			public void onPageFinished(WebView view, String url) {

				mProgress.setVisibility(View.GONE);

			}

		});

		并重写方法

			1)onPageStarted 开始加载网页

			2)shouldOverrideUrlLoading 网页跳转时会回调此方法

			3)onPageFinished 网页加载结束



		开始加载网页时可以加载一个进度条,网页加载结束可以把进度条隐藏掉

		要实现这个功能,事先先埋好进度条,这个进度条是压在webView上边的

		activity_news_detail.xml中给webView套一个FrameLayout,在FrameLayout中加个进度条,id为pb_loading

	

		NewsDetailActivity的onCreate中findViewById拿到进度条

			private ProgressBar pbLoading;

			pbLoading = (ProgressBar) findViewById(R.id.pb_loading);

	

		在开始加载的onPageStarted方法中显示进度条

				plLoading.setVisibility(View.VISIBLE);//显示进度条

	

		在网页加载结束的onPageFinished方法中隐藏进度条

				plLoading.setVisibility(View.GONE);//隐藏进度条

	

		相当于监听了下WebView的状态过程

	

		运行程序,点新闻中心的'网上大讲堂',刚进入详情页时,会出来进度条,当新闻加载出来后,进度条就隐藏掉了



		2)shouldOverrideUrlLoading 网页跳转时会回调此方法

			什么叫网页跳转

			加载网页时,网页中还会有超级链接,点超级链接网页要跳



			我们这个地方没有超级链接,但如果有,你点超级链接,它要跳一个页面,点击超级链接跳转页面时会回调监听shouldOverrideUrlLoading方法

			

			要跳的那个页面url就是这个方法参数中回传回来的url,打印一下:

					System.out.println("url:" + url);



			把mWebView.loadUrl(mUrl)换成mWebView.loadUrl("http://www.baidu.com");

			在百度网页再点击超链接,就会在shouldOverrideUrlLoading方法中打印url

	

	2.setWebChromeClient方法,渲染时用的是chrome内核

			mWebView.setWebChromeClient(new WebChromeClient() {

				});

			它有如下2个方法:

				onReceivedTitle 获取网页标题的回调

				onProgressChanged 获取网页进度的回调

	

			可以在onReceivedTitle中打印title值

				System.out.println("title:" + title);

				在onProgressChanged中打印progress值

				System.out.println("progress:" + newProgress);

	

			把上边没测试的‘打印超链接的url’,还有这里的title,progress统一测试下

			运行程序,百度网页中打印的title为‘百度一下’,还打印了progress进度

			网页加载时是一点一点加载,实时拿到它的进度

	

			也可以跳链接,点百度网页中的小说就开始加载小说网页,title打印的是小说书城,进度又打印了一堆(从1%到100%),点击小说后,小说网页的url也打印为http://dushu.baidu.com

	

			可以在小说网页再跳页面,点女生频道,它又开始加载,title是'女生频道'

			这时没有打印url,难道没有跳转页面?这个我们不确定

			点‘女生频道’的帝锦红颜,开始跳页面了打印url为http://m.baidu.com/tc?version=2book

			打印progerss又开始加载帝锦红颜网页了,title是帝锦红颜

	

		所以可以通过shouldOverrideUrlLoading方法和setWebChromeClient方法,把网页加载过程中的蛛丝马迹都拦截掉

		

		WebView在实际开发中非常有用

			想开发一款浏览器就可以用一个webView搞定

			浏览器就是上边是EditText浏览框,一个开始加载按钮,下边是webView

	

			在浏览框中输入想加载的内容,点击开始加载按钮,就会在webView中加载网页

			有时加载网页时,还会显示进度条展示当前加载网页的进度,这个进度可以通过setWebChromeClient的onProgressChanged设置进度条

	

			QQ浏览器之前就是用WebView去做的,但是大部分UC浏览器,chrome浏览器是自己写了一个WebView

			自己写了一个网页浏览器内核,那个还是比较复杂的,知道就行

	

		有时没必要开发一个浏览器,开发朋友圈中分享网页文章,一点进去就是一篇文章,一点就跳转到网页中了

		现在就可以开发出类似于微信的这个功能

	

实现WebView中点击网页后跳转到activity的功能(网页和原生交互)



		更牛逼的功能,项目经理让我加载一个网页,网页中有一个登陆按钮,点击后跳转到我的某个activity

		一般在网页中点击某个东西后,都是网页加载网页,但这时希望网页的加载按钮能把本地的某段代码给调出来

		去本地加载activity,比如微信朋友圈某个网页的‘改变的力量’按钮一点就跳转到微信的关注账号页面

		等于点网页链接时跳到了本地页面

		

		这里讲的这些api就可以实现这个功能,因为每个网络链接点击时有一个url

			一般写url用一个a标签

				<a href="tel:110">联系我们</a> 

			href中就是网络链接http://www.baidu.com,这个链接不一定必须是http

			网页中有个‘联系我们’,点击后跳到打电话页面了

	

			打电话链接是tel:110,也认为这个‘联系我们’的链接'tel:110'就是一个合法链接

			点击后回传到shouldOverrideUrlLoading方法中,这个url就带过来了

			打印出来的url就是'tel:110'

			可以解析这个url,如果发现以'tel:'开头,就读取出电话号码,跳到打电话页面

			从而实现网页和本地代码的交互,这个地方拿到链接后解析出来,写个Intent,然后startActivity就跳到打电话页面了

			这时直接return一个true,表示把事件处理完了,这时这个网页肯定不加载了,网页默认加载时调的是return super.shouldOverrideUrlLoading(view, url);

			super中其实在加载这个网页,直接return true就不加载这个网页了

	



		所以通过这个链接,链接可以是tel,也可以是sms发短信,格式由你自己定

		和服务器人员商量好后,确定在哪种链接时需要跳本地代码,哪种链接时需要把网页加载出来

		这样就实现了一个本地代码的交互

	

		微信网页中的按钮‘改变的力量’就是一个特殊的url,微信的webView拦截到这个url后从里边解析到‘改变的力量’的公众账号id,然后跳到公众账号的页面进行关注

		

		把webView最核心功能都介绍到了

		工作中WebView用的非常多,微信到处都是网页,因为webView能原生加载网页,网页都是动态的

		服务器想什么时候更新就什么时候更新,想用什么样式随时在服务器更新样子就变了,它非常明显的特点就是实时更新

		

		如果是android客户端,想更新特别费劲,还必须打包成apk去下载安装才能实现更新,但网页瞬时就更新了

	

		铁道部火车站的网站也有客户端12306,可以在手机上买票

		那个客户端全用webView去做的,没有用任何原生控件,全都是H5网页,H5功能要比html强大一些

		你会发现它更新几乎不用去覆盖安装,它自己在那正在更新,一更新就更新了

		还有一个非常好的好处,它只需要开发出一套,android和IOS就都可以用了

		IOS也有类似于android的webview的控件随时加载网页

		这是一个趋势,以后越来越多的客户端会使用webView做承载,一上来就是浏览器

		浏览器中各种各样的标签按钮,页面跳转

	

		目前趋势没有完全起来,客户端完全用网页打造,对网速要求非常高,稍微卡一点体验非常差

		想从这个按钮切到另一个按钮,却卡半天才切换出来,但原生控件想切就切,大不了页面是个白板

		网页体验还是没有原生控件那么好,很多注重用户体验的软件,微信,QQ,微博都是用原生控件打造

		总在变的部分页面才会用网页

	

判断软件中哪些是网页哪些是原生控件的方法

		

		设置页面,打开开发者选项,点击显示布局边界选项,这时页面会把所有控件都用蓝颜色框表示出来

		打开‘智慧北京’,标题栏返回键是一个框框,中间textview没有文字所以是一个非常窄的框框

		右边两个按钮是两个框框,下边空白部分中间的这个框框是progressbar

		'百度一下'这个按钮没有框框,它就是这么一大块,这个就是webView,这时就确定它是webView了

		

		之前淘宝客户端聊天页面和微信聊天一样,它是网页,网页怎么交互就怎么来,客户端不用去管,客户端去load一下url就行了,是服务器端人员去开发的,我们不需要了解那么多细节,有bug也是服务器端的bug,和客户端没关系

Day04 12.网页字体大小修改 ##

webview页面加载出来了,接下来实现标题栏上的三个按钮处理点击事件

	让NewsDetailActivity实现OnClickListener

		public class NewsDetailActivity extends Activity implements OnClickListener {

			}

	onCreate方法中分别给三个按钮设置点击监听

			btnBack.setOnClickListener(this);

			btnTextSize.setOnClickListener(this);

			btnShare.setOnClickListener(this);

	onClick中switch判断id:

				@Override

				public void onClick(View v) {

					switch (v.getId()) {

					case R.id.btn_back:

						finish();

						break;

					case R.id.btn_text_size:

						// 修改网页字体大小

						showChooseDialog();

						break;

					case R.id.btn_share:

						// 分享 ShareSDK

						showShare();

						break;

					default:

						break;

					}

				}

	完整项目,点击修改字体大小按钮后,弹出字体设置选择对话框alertDialog



实现修改网页字体大小的功能(alertDialog的单选框)

	case R.id.btn_text_size中调用showChooseDialog()方法并创建

	在showChooseDialog()方法中,new个AlertDialog.Builder

		AlertDialog.Builder builder = new AlertDialog.Builder(this);

	用builder去setTitle为字体设置

		builder.setTitle("字体设置");

	用builder去设置单选效果

		private int mClickItem;// 被点击选择的字体位置

		builder.setSingleChoiceItems(items, mSelectedItem,

				new DialogInterface.OnClickListener() {

					@Override

					public void onClick(DialogInterface dialog, int which) {

						mClickItem = which;

					}

				}); 

	将字体用一个String数组维护起来

		String[] items = new String[] { "超大号字体", "大号字体", "正常字体", "小号字体","超小号字体" };

	默认是正常字体写个2



	用builder分别去设置确定取消按钮

		builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {

			}

		builder.setNegativeButton("取消", null);

	取消啥都不做,直接传个null就行,最后把builder去show出来

			builder.show();



	单选对话框setSingleChoiceItems中,用户在选时会回传到它的onClick方法中,onClick中可以拿到当前被选中的是第几个,即这个方法参数中的int which,将which保存下来

			private int mClickItem;// 被点击选择的字体位置

			在setSingleChoiceItems的onclick中写:

					mClickItem = which;

	要保留下来是因为点击‘确定’时要用到它,点确定要根据它选的是哪个位置去更新对应的字体



	所以在‘确定’的setPositiveButton的onClick中再进行switch判断,看到底点的是哪个,这里判断的就是上边保存的‘被点击选择的字体位置’mClickItem



		字体按钮在android原生标题栏上,要改的是webView的字体

		点击某个字体,webView字体就变了

		通过mWebView去getSetting拿到设置对象settings

			WebSettings settings = mWebView.getSettings();

		超大号字体就setTextSize,传个TextSize.LARGEST

			settings.setTextSize(TextSize.LARGEST);

		setTextSize方法过时了

		现在用的是setTextZoom(int textZoom)传的是整数int,要具体多大字体就多大字体,比如10sp

		setTextSize只有5种大小,setTextZoom字体大小更多

		但用setTextZoom会报错,因为api不支持,14以上才可以用,所以在这还是用setTextSize方法

		

				switch (mClickItem) {

					case 0:

						settings.setTextSize(TextSize.LARGEST);

						// settings.setTextZoom(10);

						break;

					case 1:

						settings.setTextSize(TextSize.LARGER);

						break;

					case 2:

						settings.setTextSize(TextSize.NORMAL);

						break;

					case 3:

						settings.setTextSize(TextSize.SMALLER);

						break;

					case 4:

						settings.setTextSize(TextSize.SMALLEST);

						break;

					default:

						break;

					}

		这样的就把网页字体大小修改了,点击确定后,不管点的是哪个字体大小就已经确定了

		比如当前选的是正常字体,点确定,那当前第二个位置被选中了



		再用一个变量记录被选择后的字体位置

				private int mSelectedItem;// 被最终选择的字体大小的位置

		一旦点击确定后,就让mSelectedItem等于当前被选中的item

				mSelectedItem = mClickItem;

	

		下次再进来就不能每次都设置第2个被选中了,应该是上次选的是谁就是谁

		所以把setSingleChoiceItems的第二个参数2改为mSelectedItem

		

		运行程序,点击标题栏上的字体大小按钮,就会出来字体设置弹框,但是怎么一上来就是超大号字体

		

		因为一上来mSelectedItem默认是0,它默认应该是2

			private int mSelectedItem = 2;// 被最终选择的字体大小的位置, 默认是正常字体大小

		

		比如选一个超小号字体,再去点Dialog弹框时,它会根据位置去展现

	

		有个小问题,退出把页面返回之后再进来,又重新从原来默认的第2个位置正常字体大小去展示

	

		如果想把原来选择的字体永远持久化,就把状态保存在sp

		这个地方就不保存了,就每次一进来又是正常字体,这就是字体大小的修改

Day04 13.sharesdk使用 ##

使用第三方sharesdk实现分享功能(分享WebView中的文章)



	sharesdk是专门用来分享各种社会化渠道的工具,它是第三方分享的平台,支持很多种渠道,比如支持微信,QQ,朋友圈等

	它是一个开放平台,可以在它上边集成很多操作



	去sharesdk官网下载sdk后

	ShareSDK-Android-2.5.7.tar.gz 是它下载下来的渠道

	可以把它整个下载下来,它可以在官网上选择你要分享到哪些渠道,你确定好后,就可以把这个包下载下来



	把ShareSDK-Android-2.5.7.tar.gz 复制到zhbj项目所在的目录文件夹下,然后解压下

	这是android版的sharesdk



	用firefox浏览器打开"快速集成 Mob文档中心.html"

	跟着文档一点一点把sharesdk集成到项目中



1.获取AppKey

	AppKey指应用要用到sharesdk什么功能,你至少要在平台上去注册一下

	去注册下用户账号,邮箱,密码,注册好后zhxa项目要用到它

	在上边添加应用后,sharesdk会自动分配给你AppKey作为应用的标识



	用这个应用的标识可以做相应处理,sharesdk就知道是要用这个应用去分享

	已经给你们生成好了,这个是黑马48期的,即 zhbj48 appkey 5701c42963ec



2.下载SDK,自定义sdk下载,

	新浪微博,微信,QQ,QQ空间,腾讯微博,FaceBook,Twitter,邮件,短信分享,人人网,豆瓣,开心网,蓝牙,Pocket好多渠道

	勾选要分享到哪些渠道后开始'下载SDK',它就把你选的那些渠道sdk打成包下载下来了

	我已经替大家下载下来了



3.看下快速集成怎么用

	1)使用快速工具进行集成,进入shareSDK解压目录把它解压,QuickIntegrater.jar就是快速集成ShareSDK的工具

	

	windows系统下要确定安装了JDK,还要正确配置了JAVAHOME和PATH系统变量

	此时就可以双击 QuickIntegrater 启动程序



	把它双击启动下,会看到勾选了好多渠道,它有好多包

	正常输入项目名称和包名,勾选需要的继承平台,点'确定'会自动生成目录,复制目录中所有文件到项目中覆盖即可

	项目名称指的是eclipse中项目的名称zhxa02,千万不要写成中文‘智慧西安’,包名是com.itcast.zhxa02

	

	去勾选要集成的平台,就选新浪微博,QQ,微信好友,QQ空间,微信朋友圈,印象笔记,微信收藏,WhatsApp

	然后点确定,这时弹框提示:‘集成share SDK所需的文件已经复制到目录“zhxa01”中,请复制其中的文件到您的项目中覆盖’



	什么意思?

	再回到这个地方的时候,它就会生成zhxa02这样的目录,里边会把sharesdk需要集成的东西放进来

	比如说它的资源文件assets中的ShareSDK.xml

		libs文件夹下用的一些第三方的jar包

		资源文件res

		src目录

	把生成的zhxa02复制后覆盖掉eclipse中原来的项目,就会自动去合并,覆盖之前最好先备份一份防止覆盖错了

	到eclipse中zhxa02项目所在的目录下,按ctrl+v合并,合并后把zhxa02项目刷新下

	它就会把sharesdk的东西添加进来,看到它添加了一个sharesdk的包,即:cn.sharesdk.onekeyshare

	这是sdk的一些源码,它会把jar包也全都添加到Android Dependencies中



	包括assets目录下有个ShareSDK.xml



	2)合并后,要配置清单文件

	(1)添加权限,把文档中的权限复制到zhxa02清单文件中

	(2)添加activity信息,sharesdk自己也有一些activity,activity都要在清单文件中配置

		 复制文档中activity信息,粘贴到zhxa02项目清单文件中

	(3)如果集成了微信或易信,还要添加下边两个Activity,我们只把微信的activity复制到zhxa02项目清单文件中

		易信的我们没有集成不需要复制



		zhxa02项目中的wxapi包中就是微信activity

		里边有个WXEntryActivity,要分享到微信,微信平台必须要有这个activity,所以上边(3)必须要注册



	(4)替换mob后台申请的AppKey与各个平台申请的key

	

	修改成你在sharesdk后台注册的应用的appkey

	我们这个appkey是5701c42963ec,修改下

		 <ShareSDK AppKey = "5701c42963ec"/> <!-- 修改成你在sharesdk后台注册的应用的appkey"-->



	图片上还有一个箭头所指是什么呢?新浪微博对吧,因为我们这些东西,就说现在是这样的,就说sharesdk是这样的,你会发现在ShareSDK.xml中SinaWeibo有sdk,TencentWeibo也有Appkey,QZone也有Appkey

		sharesdk其实是把把各大平台的分享功能整合在了一起,它本身没有分享功能

		如果不用ShareSDK,自己分别去在分享的平台注册添加也可以,渠道太多时非常繁琐,所以才用ShareSDK

		sharesdk已经替我们在ShareSDK.xml中把每个平台的Appkey都注册好了,就不用分别去那些平台注册了

		只需要把ShareSDK的AppKey替换一下就行了,剩下的都是ShareSDK替我去注册的appkey



		它只是调用别人的,要分享到微信,就要把微信的jar包,sdk乱七八糟的集成进来

		你会发现libs目录下有个

				微信的jar包,即:

					ShareSDK-Wechat-2.5.7.jar

					ShareSDK-Wechat-Core-2.5.7.jar

					ShareSDK-Wechat-Favorite-2.5.7.jar

					ShareSDK-Wechat-Moments-2.5.7.jar

				分享QQ空间就要把QQ空间的jar包搞进来,即:ShareSDK-QZone-2.5.7.jar



	3)添加分享的代码,在zhxa02项目的代码中调用文档中的这个方法,即可打开一键分享功能进行分享

		把代码拷贝添加到NewsDetailActivity中,把showShare方法直接抄过来就可以了

		点分享按钮时调一下这个方法就可以了,即在case R.id.btn_share:中添加:

						showShare();



		showShare方法的代码是什么

				Notification的图标和文字,它是设置图标和文字

				即:

					// 分享时Notification的图标和文字

					oks.setNotification(R.drawable.ic_launcher,getString(R.string.app_name));

				title标题可以自己设置,即:

					// title标题,印象笔记、邮箱、信息、微信、人人网和QQ空间使用

					oks.setTitle(getString(R.string.share));

				有时标题点击后要跳网页,这时要设置titleurl,仅在人人网和QQ空间使用

				它现在默认写的是sharesdk的官网,即:

					// titleUrl是标题的网络链接,仅在人人网和QQ空间使用

					oks.setTitleUrl("http://sharesdk.cn");

				text是分享文本,可以自己写想分享什么文字,即:

					// text是分享文本,所有平台都需要这个字段

					oks.setText("我是分享文本");

				下边还有一个图片,在分享时可以把图片也加进去,目前这些图片是在sdcard的test.jpg

				要确保SDcard下面存在此张图片才能够分享,我目前手机中没有这张图片

				找张图片把它重命名成test.jpg就可以了

				不需要图片可以把这个注释掉,即:

					// imagePath是图片的本地路径,Linked-In以外的平台都支持此参数

					// oks.setImagePath("/sdcard/test.jpg");//确保SDcard下面存在此张图片

				url是网页,仅在微信朋友圈和好友中使用,即:

					// url仅在微信(包括好友和朋友圈)中使用

					oks.setUrl("http://sharesdk.cn");

				下边的评论,是人人网和QQ空间中使用,即:

					// comment是我对这条分享的评论,仅在人人网和QQ空间使用

					oks.setComment("我是测试评论文本");

	

				每个渠道它要的不一样,sharesdk干脆把他们都写进来,最后展示下,即:

						// 启动分享GUI

						oks.show(this);



	sharesdk分享和手机卫士软件管理短信分享的区别

		手机卫士只是把那段文字打开短信分享出去,只是在呼叫本地的app应用去分享

		这里本地是可以不用装这个app的,可以跳网页

	

	运行程序,点分享按钮出来个PopWindow,上边有勾选的想要的分享渠道

		微信逼格比较高,不能用浏览器访问,必须本地装微信才会跳到微信页面去分享

		别的都可以在本地没有app时分享

		我手机装了微信的前提下,去点‘微信朋友圈’分享,来到微信朋友圈没有发出去

		我这个系统现在是最新版本5.1.1,不知道这个sdk版本兼容是否有问题



		点了分享新浪微博后,会跳转到一个页面,让我去输入想分享的文本

		现在默认写的是我是分享文本,点分享会跳到新浪微博登陆页面,登陆后分享出去

		到新浪微博App登陆新浪微博,看到已经分享成功了,标识‘刚刚  来自ShareSDK’

		sharesdk自动在ShareSDK.xml中给新浪微博注册了AppKey,肯定在注册时写的来源是shareSDK

		所以新浪微博一看是新浪微博的Appkey,就会显示来自于sharesdk



		也可以修改ShareSDK风格,现在是默认风格,可以在NewsDetailActivity中复制过来的showShare中加	oks.setTheme(OnekeyShareTheme.SKYBLUE);//修改风格

			有两种风格,CLASSIC,SKYBLUE,修改成SKYBLUE蓝天风格

	

		运行程序,点击分享时弹出蓝色网格状风格,再点新浪微博,点击确定按钮对勾,跳转到需要你输入分享文字(感想)的页面,下边还有分享到新浪微博朋友的按钮,点确定按钮对勾就分享出去了



分享到新浪微博后将来自ShareSDK改显成来自zhxa02



	刚才分享出去后显示的是来自ShareSDK,也可以显示来自zhxa02,要自己在新浪微博上注册zhxa02应用生成Appkey

	到ShareSDK.xml中把SinaWeibo中的AppKey替换就行

Day04 14.总结 ##

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值