Android仿QQ空间动态页及标题栏渐变
先看看我们今天要实现功能的效果图:

来看下我们今天要介绍的主角:
PullToZoomInListView
Github地址如下:
https://github.com/matrixxun/PullToZoomInListView
一个下拉放大的空间,这种效果在ios应用中很常见,Android中不少应用也有它的身影,玩过QQ的人都知道,QQ空间动态界面有着这几个功能(下拉刷新,图片放大,上拉加载更多,标题栏渐变,图标变化)
本着不重复造轮子,在前人的基础上而外添加了些功能:
1、添加了一个footer
2、添加了一个监听器,用于监听下拉刷新和上拉加载更多
3、添加了一个判断是否有更多数据的方法
修改后的PullToZoomInListView代码如下:
public class PullToZoomListView extends ListView implements AbsListView.OnScrollListener {private View footerView;private TextView tv_footer_text;private View fl_progress_bar;private boolean isNoMore = false;private boolean isLoad = false;private static final Interpolator sInterpolator = new Interpolator() {public float getInterpolation(float paramAnonymousFloat) {float f = paramAnonymousFloat - 1.0F;return 1.0F + f * (f * (f * (f * f)));}};int mActivePointerId = -1;private FrameLayout mHeaderContainer;private int mHeaderHeight;public int getmHeaderHeight() {return mHeaderHeight;}/*** 设置头部的高度** @param mHeaderHeight*/public void setmHeaderHeight(int mHeaderHeight) {this.mHeaderHeight = mHeaderHeight;AbsListView.LayoutParams lp = new AbsListView.LayoutParams(DensityUtil.dp2px(mContext,AbsListView.LayoutParams.MATCH_PARENT), mHeaderHeight);getHeaderContainer().setLayoutParams(lp);}private ImageView mHeaderImage;float mLastMotionY = -1.0F;float mLastScale = -1.0F;float mMaxScale = -1.0F;private AbsListView.OnScrollListener mOnScrollListener;private ScalingRunnalable mScalingRunnalable;private int mScreenHeight;private ImageView mShadow;private Context mContext;public PullToZoomListView(Context paramContext) {super(paramContext);init(paramContext);mContext = paramContext;}public PullToZoomListView(Context paramContext, AttributeSet paramAttributeSet) {super(paramContext, paramAttributeSet);init(paramContext);mContext = paramContext;}public PullToZoomListView(Context paramContext, AttributeSet paramAttributeSet, int paramInt) {super(paramContext, paramAttributeSet, paramInt);init(paramContext);mContext = paramContext;}private void endScraling() {if (this.mHeaderContainer.getBottom() >= this.mHeaderHeight)Log.d("mmm", "endScraling");this.mScalingRunnalable.startAnimation(100L);}private void init(Context paramContext) {DisplayMetrics localDisplayMetrics = new DisplayMetrics();((Activity) paramContext).getWindowManager().getDefaultDisplay().getMetrics(localDisplayMetrics);this.mScreenHeight = localDisplayMetrics.heightPixels;this.mHeaderContainer = new FrameLayout(paramContext);this.mHeaderImage = new ImageView(paramContext);mHeaderImage.setScaleType(ImageView.ScaleType.CENTER_CROP);int i = localDisplayMetrics.widthPixels;setHeaderViewSize(i, (int) (9.0F * (i / 16.0F)));this.mShadow = new ImageView(paramContext);FrameLayout.LayoutParams localLayoutParams = new FrameLayout.LayoutParams(-1, -2);localLayoutParams.gravity = Gravity.CENTER;this.mShadow.setLayoutParams(localLayoutParams);this.mHeaderContainer.addView(this.mHeaderImage);this.mHeaderContainer.addView(this.mShadow);// addHeaderView(this.mHeaderContainer);footerView = LayoutInflater.from(paramContext).inflate(R.layout.zoomlistview_footer, null);tv_footer_text = (TextView) footerView.findViewById(R.id.tv_footer_text);fl_progress_bar = footerView.findViewById(R.id.fl_progress_bar);//addFooterViewaddFooterView(footerView);this.mScalingRunnalable = new ScalingRunnalable();super.setOnScrollListener(this);}private void onSecondaryPointerUp(MotionEvent paramMotionEvent) {int i = (paramMotionEvent.getAction()) >> 8;if (paramMotionEvent.getPointerId(i) == this.mActivePointerId)if (i != 0) {this.mLastMotionY = paramMotionEvent.getY(0);this.mActivePointerId = paramMotionEvent.getPointerId(0);return;}}private void reset() {this.mActivePointerId = -1;this.mLastMotionY = -1.0F;this.mMaxScale = -1.0F;this.mLastScale = -1.0F;}public ImageView getHeaderView() {return this.mHeaderImage;}public FrameLayout getHeaderContainer() {return mHeaderContainer;}public void setHeaderView() {addHeaderView(this.mHeaderContainer);}/** public boolean onInterceptTouchEvent(MotionEvent ev) {** final int action = ev.getAction() & MotionEvent.ACTION_MASK; float* mInitialMotionX= 0; float mLastMotionX= 0;** float mInitialMotionY= 0; float mLastMotionY = 0;** boolean isIntercept=false; switch (action) { case* MotionEvent.ACTION_DOWN:** mLastMotionY=ev.getY(); break;** case MotionEvent.ACTION_MOVE: mInitialMotionY = ev.getY();** if(Math.abs(mInitialMotionY-mLastMotionY)>50) { isIntercept=true; }* break;** }** return isIntercept; }*/protected void onLayout(boolean paramBoolean, int paramInt1, int paramInt2, int paramInt3, int paramInt4) {super.onLayout(paramBoolean, paramInt1, paramInt2, paramInt3, paramInt4);if (this.mHeaderHeight == 0)this.mHeaderHeight = this.mHeaderContainer.getHeight();}@Overridepublic void onScroll(AbsListView paramAbsListView, int paramInt1, int paramInt2, int paramInt3) {float f = this.mHeaderHeight - this.mHeaderContainer.getBottom();if ((f > 0.0F) && (f < this.mHeaderHeight)) {Log.d("mmm", "1");int i = (int) (0.65D * f);this.mHeaderImage.scrollTo(0, -i);} else if (this.mHeaderImage.getScrollY() != 0) {Log.d("mmm", "2");this.mHeaderImage.scrollTo(0, 0);}if (this.mOnScrollListener != null) {this.mOnScrollListener.onScroll(paramAbsListView, paramInt1, paramInt2, paramInt3);}}public void onScrollStateChanged(AbsListView paramAbsListView, int paramInt) {if (this.mOnScrollListener != null) {this.mOnScrollListener.onScrollStateChanged(paramAbsListView, paramInt);}if (paramAbsListView.getLastVisiblePosition() == (getCount() - 1)) {if (isNoMore) {footerView.setVisibility(View.GONE);footerView.setPadding(0, -200, 0, 0);} else {footerView.setVisibility(View.VISIBLE);footerView.setPadding(0, 0, 0, 0);tv_footer_text.setText(R.string.load_more_loading);fl_progress_bar.setVisibility(View.VISIBLE);startLoadMore();}}}/*** 是否还有更多数据* @param hasNoMore*/public void setLoadFinish(boolean hasNoMore) {fl_progress_bar.setVisibility(View.GONE);isLoad = false;isNoMore = hasNoMore;if (isNoMore) {tv_footer_text.setText(R.string.load_more_loaded_no_more);} else {tv_footer_text.setText(R.string.load_more_loading);}}@Overridepublic boolean dispatchTouchEvent(MotionEvent paramMotionEvent) {Log.d("mmm=dispatchTouchEvent=", "" + (0xFF & paramMotionEvent.getAction()));switch (0xFF & paramMotionEvent.getAction()) {case MotionEvent.ACTION_OUTSIDE:case MotionEvent.ACTION_DOWN:if (!this.mScalingRunnalable.mIsFinished) {this.mScalingRunnalable.abortAnimation();}this.mLastMotionY = paramMotionEvent.getY();this.mActivePointerId = paramMotionEvent.getPointerId(0);this.mMaxScale = (this.mScreenHeight / this.mHeaderHeight);this.mLastScale = (this.mHeaderContainer.getBottom() / this.mHeaderHeight);break;}return super.dispatchTouchEvent(paramMotionEvent);}public boolean onTouchEvent(MotionEvent paramMotionEvent) {Log.d("mmm=========******", "" + (0xFF & paramMotionEvent.getAction()));switch (0xFF & paramMotionEvent.getAction()) {case MotionEvent.ACTION_OUTSIDE:case MotionEvent.ACTION_DOWN:if (!this.mScalingRunnalable.mIsFinished) {this.mScalingRunnalable.abortAnimation();}this.mLastMotionY = paramMotionEvent.getY();this.mActivePointerId = paramMotionEvent.getPointerId(0);this.mMaxScale = (this.mScreenHeight / this.mHeaderHeight);this.mLastScale = (this.mHeaderContainer.getBottom() / this.mHeaderHeight);break;case MotionEvent.ACTION_MOVE:Log.d("mmm", "mActivePointerId" + mActivePointerId);int j = paramMotionEvent.findPointerIndex(this.mActivePointerId);if (j == -1) {Log.e("PullToZoomListView", "Invalid pointerId=" + this.mActivePointerId + " in onTouchEvent");} else {if (this.mLastMotionY == -1.0F)this.mLastMotionY = paramMotionEvent.getY(j);if (this.mHeaderContainer.getBottom() >= this.mHeaderHeight) {ViewGroup.LayoutParams localLayoutParams = this.mHeaderContainer.getLayoutParams();float f = ((paramMotionEvent.getY(j) - this.mLastMotionY + this.mHeaderContainer.getBottom())/ this.mHeaderHeight - this.mLastScale)/ 2.0F + this.mLastScale;if ((this.mLastScale <= 1.0D) && (f < this.mLastScale)) {localLayoutParams.height = this.mHeaderHeight;this.mHeaderContainer.setLayoutParams(localLayoutParams);return super.onTouchEvent(paramMotionEvent);}this.mLastScale = Math.min(Math.max(f, 1.0F), this.mMaxScale);localLayoutParams.height = ((int) (this.mHeaderHeight * this.mLastScale));if (localLayoutParams.height < this.mScreenHeight)this.mHeaderContainer.setLayoutParams(localLayoutParams);this.mLastMotionY = paramMotionEvent.getY(j);return true;}this.mLastMotionY = paramMotionEvent.getY(j);}break;case MotionEvent.ACTION_UP:reset();endScraling();break;case MotionEvent.ACTION_CANCEL:int i = paramMotionEvent.getActionIndex();this.mLastMotionY = paramMotionEvent.getY(i);this.mActivePointerId = paramMotionEvent.getPointerId(i);break;case MotionEvent.ACTION_POINTER_DOWN:onSecondaryPointerUp(paramMotionEvent);try {this.mLastMotionY = paramMotionEvent.getY(paramMotionEvent.findPointerIndex(this.mActivePointerId));} catch (IllegalArgumentException e) {// TODO: handle exception}break;case MotionEvent.ACTION_POINTER_UP:}return super.onTouchEvent(paramMotionEvent);}public void setHeaderViewSize(int paramInt1, int paramInt2) {Object localObject = this.mHeaderContainer.getLayoutParams();if (localObject == null)localObject = new AbsListView.LayoutParams(paramInt1, paramInt2);((ViewGroup.LayoutParams) localObject).width = paramInt1;((ViewGroup.LayoutParams) localObject).height = paramInt2;this.mHeaderContainer.setLayoutParams((ViewGroup.LayoutParams) localObject);this.mHeaderHeight = paramInt2;}public void setOnScrollListener(AbsListView.OnScrollListener paramOnScrollListener) {this.mOnScrollListener = paramOnScrollListener;}public void setShadow(int paramInt) {this.mShadow.setBackgroundResource(paramInt);}class ScalingRunnalable implements Runnable {long mDuration;boolean mIsFinished = true;float mScale;long mStartTime;ScalingRunnalable() {}public void abortAnimation() {this.mIsFinished = true;}public boolean isFinished() {return this.mIsFinished;}public void run() {float f2;ViewGroup.LayoutParams localLayoutParams;if ((!this.mIsFinished) && (this.mScale > 1.0D)) {float f1 = ((float) SystemClock.currentThreadTimeMillis() - (float) this.mStartTime)/ (float) this.mDuration;f2 = this.mScale - (this.mScale - 1.0F) * PullToZoomListView.sInterpolator.getInterpolation(f1);localLayoutParams = PullToZoomListView.this.mHeaderContainer.getLayoutParams();if (f2 > 1.0F) {Log.d("mmm", "f2>1.0");localLayoutParams.height = PullToZoomListView.this.mHeaderHeight;localLayoutParams.height = ((int) (f2 * PullToZoomListView.this.mHeaderHeight));PullToZoomListView.this.mHeaderContainer.setLayoutParams(localLayoutParams);PullToZoomListView.this.post(this);startReload();//FIXMEreturn;}this.mIsFinished = true;}}public void startAnimation(long paramLong) {this.mStartTime = SystemClock.currentThreadTimeMillis();this.mDuration = paramLong;this.mScale = ((float) (PullToZoomListView.this.mHeaderContainer.getBottom()) / PullToZoomListView.this.mHeaderHeight);this.mIsFinished = false;PullToZoomListView.this.post(this);}}/*** 加载更多*/private void startLoadMore() {if (mListViewListener != null && !isLoad) {isLoad = true;mListViewListener.onLoadMore();}}/*** 下拉刷新*/private void startReload() {if (mListViewListener != null && !isLoad) {isLoad = true;mListViewListener.onReload();}}public void setPullToZoomListViewListener(PullToZoomListViewListener l) {mListViewListener = l;}public interface PullToZoomListViewListener {void onReload();void onLoadMore();}private PullToZoomListViewListener mListViewListener;}
接下来看下MainActivity的布局文件:
xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"tools:context="commumu.parallaxheader.MainActivity">android:id="@+id/ptzlv_container"android:layout_width="match_parent"android:layout_height="match_parent"android:dividerHeight="0dp"android:fadingEdge="none"android:listSelector="@android:color/transparent"android:scrollbarStyle="outsideOverlay" />android:id="@+id/rl_top"android:layout_width="match_parent"android:layout_height="@dimen/nav_top_bar_height"android:background="@drawable/bg_nav_panel"android:gravity="center">android:id="@+id/tv_nick"android:layout_width="wrap_content"android:layout_height="match_parent"android:layout_centerHorizontal="true"android:gravity="center_vertical"android:text="生活如此多娇"android:textColor="@android:color/white"android:textSize="@dimen/text_title_big_size"android:textStyle="bold" />android:layout_width="wrap_content"android:layout_toLeftOf="@+id/tv_nick"android:layout_height="match_parent">style="?android:attr/progressBarStyleSmall"android:id="@+id/progressBar"android:layout_width="35dp"android:layout_centerVertical="true"android:layout_height="35dp"android:indeterminateDrawable="@drawable/bg_refresh_loding"android:visibility="gone" />android:id="@+id/rl_userinfo_top_avatar"android:layout_width="40dp"android:layout_height="46dp"android:layout_alignParentBottom="true"android:layout_toLeftOf="@id/tv_nick"android:visibility="gone">android:id="@+id/iv_avatar"android:layout_width="35dp"android:layout_height="35dp"android:layout_centerInParent="true"android:background="@android:color/transparent"android:scaleType="centerCrop"android:src="@drawable/icon_default_avatar" />android:id="@+id/iv_certified"android:layout_width="12dp"android:layout_height="12dp"android:layout_alignParentBottom="true"android:layout_alignParentRight="true"android:layout_marginBottom="4dp"android:layout_marginRight="4dp"android:scaleType="centerCrop"android:src="@drawable/icon_authenticate"android:visibility="visible" />android:id="@+id/ib_back"android:layout_width="45dp"android:layout_height="match_parent"android:layout_alignParentLeft="true"android:background="@drawable/bg_actionbar_selector"android:gravity="center_vertical"android:padding="12dp"android:scaleType="centerCrop"android:src="@drawable/icon_back_white" />android:id="@+id/ib_notification"android:layout_width="45dp"android:layout_height="match_parent"android:layout_alignParentRight="true"android:background="@android:color/transparent"android:gravity="center_vertical"android:padding="10dp"android:scaleType="centerCrop"android:src="@drawable/icon_setting_white"android:visibility="visible" />
我们先来理一理思路,仔细分析下有哪些功能和变化
功能分析:
1、下拉刷新,图片放大
2、上拉标题栏渐变,返回图片变化
3、上拉加载更多
好了,思路是有了,接下来看看如何实现,来看看MainActivity的具体实现
public class MainActivity extends AppCompatActivity implements PullToZoomListView.PullToZoomListViewListener, AbsListView.OnScrollListener {private Context mContext;private PullToZoomListView ptzlv_container;private RelativeLayout mRlTop;private TextView tv_nick;private ArrayAdaptermAdapter; private RoundImageView iv_avatar;private ImageView iv_certified;private ImageButton ib_back;private ImageButton ib_notification;private ImageView mTopCerfitied;private ViewGroup.MarginLayoutParams mRlAvatarViewLayoutParams;private RoundImageView riv_avatar;private RelativeLayout mRlAvatarView;private ProgressBar pb_loading;private View mNavHeaderView;private View mHeaderView;private ListadapterData = new ArrayList<>(); private boolean loadMore = false;private int mTopAlpha;private boolean mTopBgIsDefault = true;protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mContext = this;initView();initData();StatusBarUtils.from(this).setTransparentStatusbar(true)//状态栏是否透明.setTransparentNavigationbar(false)//Navigationbar是否透明.setActionbarView(mRlTop)//view是否透明.setLightStatusBar(false)//状态栏字体是否为亮色.process();}private void initData() {if (loadMore) {adapterData.add("Java");adapterData.add("Android");adapterData.add("IOS");adapterData.add("Php");adapterData.add("C");adapterData.add("C++");adapterData.add("C#");adapterData.add(".NET");adapterData.add("Ruby");adapterData.add("Python");adapterData.add("Go");} else {adapterData.add("Activity");adapterData.add("Service");adapterData.add("Content Provider");adapterData.add("Intent");adapterData.add("BroadcastReceiver");adapterData.add("ADT");adapterData.add("Sqlite3");adapterData.add("HttpClient");adapterData.add("DDMS");adapterData.add("Android Studio");adapterData.add("Fragment");adapterData.add("Loader");}mAdapter.notifyDataSetChanged();}private void initView() {mHeaderView = View.inflate(mContext, R.layout.header_userinfo, null);mNavHeaderView = View.inflate(mContext, R.layout.header_nav_layout, null);riv_avatar = (RoundImageView) mHeaderView.findViewById(R.id.riv_avatar);ptzlv_container = (PullToZoomListView) findViewById(R.id.ptzlv_container);mRlTop = (RelativeLayout) findViewById(R.id.rl_top);tv_nick = (TextView) findViewById(R.id.tv_nick);iv_avatar = (RoundImageView) findViewById(R.id.iv_avatar);iv_certified = (ImageView) findViewById(R.id.iv_certified);ib_back = (ImageButton) findViewById(R.id.ib_back);ib_notification = (ImageButton) findViewById(R.id.ib_notification);mRlAvatarView = (RelativeLayout) findViewById(R.id.rl_userinfo_top_avatar);mTopCerfitied = (ImageView) findViewById(R.id.iv_certified);mRlTop = (RelativeLayout) findViewById(R.id.rl_top);pb_loading = (ProgressBar) findViewById(R.id.progressBar);mRlAvatarViewLayoutParams = (ViewGroup.MarginLayoutParams) mRlAvatarView.getLayoutParams();//设置背景图ptzlv_container.getHeaderView().setImageResource(R.drawable.icon_scroller_header);ptzlv_container.getHeaderView().setScaleType(ImageView.ScaleType.CENTER_CROP);//设置header大小ptzlv_container.setHeaderViewSize(ViewGroup.LayoutParams.MATCH_PARENT, DensityUtil.dp2px(mContext, 280));//设置headerViewptzlv_container.getHeaderContainer().addView(mHeaderView);ptzlv_container.setHeaderView();//添加headerViewptzlv_container.addHeaderView(mNavHeaderView);mAdapter = new ArrayAdapter(MainActivity.this, android.R.layout.simple_list_item_1, adapterData);//设置Adapterptzlv_container.setAdapter(mAdapter);ptzlv_container.setOnScrollListener(this);//添加刷新监听和加载更多监听ptzlv_container.setPullToZoomListViewListener(this);}private Handler mHandler = new Handler() {public void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what) {case 1:updateImageBgUI();break;case 2:updateImageBgUI();mAdapter.notifyDataSetChanged();break;case 3:pb_loading.setVisibility(View.INVISIBLE);break;case 4:loadMore = true;initData();ptzlv_container.setLoadFinish(true);break;}}};/*** 更新图片背景*/private void updateImageBgUI() {int[] location = new int[2];riv_avatar.getLocationOnScreen(location);if (location[1] < 255) {mTopBgIsDefault = false;//mRlAvatarView visible// if (location[1] <= 46 && location[1] >= 0) {// mRlAvatarView.setVisibility(View.VISIBLE);// mRlAvatarViewLayoutParams.topMargin = DensityUtil.dp2px(mContext, location[1]);// mRlAvatarView.setLayoutParams(mRlAvatarViewLayoutParams);// } else {// // mRlAvatarView marginTop > 46 invisible// if (mRlAvatarView.getVisibility() != View.VISIBLE) {// mRlAvatarView.setVisibility(View.VISIBLE);// }// // out of screen// if (location[1] < 0 && ((ViewGroup.MarginLayoutParams) mRlAvatarView.getLayoutParams()).topMargin != 0) {// mRlAvatarViewLayoutParams.topMargin = 0;// mRlAvatarView.setLayoutParams(mRlAvatarViewLayoutParams);// }// // mRlAvatarView marginTop > 46 invisible// if (location[1] > 46 && mRlAvatarView.getVisibility() != View.INVISIBLE) {// mRlAvatarView.setVisibility(View.INVISIBLE);// }// }if (ptzlv_container.getFirstVisiblePosition() >= 1 || location[1] < 0) {if (mTopAlpha != 255) {mRlTop.setBackgroundColor(Color.argb(255, 66, 66, 66));mTopAlpha = 255;ib_back.setImageResource(R.drawable.icon_back_bold_white);}} else {mTopAlpha = 255 - location[1];mRlTop.setBackgroundColor(Color.argb(mTopAlpha, 66, 66, 66));ib_back.setImageResource(R.drawable.icon_back_white);}} else {setDefaultImageBg();}}/*** 设置默认背景*/private void setDefaultImageBg() {if (!mTopBgIsDefault) {mTopBgIsDefault = true;mRlTop.setBackgroundResource(R.drawable.bg_nav_panel);tv_nick.setTextColor(getResources().getColor(android.R.color.white));}}public void onReload() {mHandler.removeMessages(3);pb_loading.setVisibility(View.VISIBLE);mHandler.sendEmptyMessageDelayed(3, 1000);Toast.makeText(mContext, "更新中...", Toast.LENGTH_SHORT).show();}public void onLoadMore() {mHandler.removeMessages(4);mHandler.sendEmptyMessageDelayed(4, 1000);Toast.makeText(mContext, "加载中,请稍后...", Toast.LENGTH_SHORT).show();}public void onScrollStateChanged(AbsListView view, int scrollState) {if (scrollState == SCROLL_STATE_IDLE) {mHandler.removeMessages(1);mHandler.sendEmptyMessage(1);}}public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {mHandler.removeMessages(1);mHandler.sendEmptyMessage(1);}}
源码地址:
https://github.com/diycoder/ParallaxHeader/tree/master
到这里就结束啦。
评论
