新浪微博客户端新特性滚动视图和启动界面实现

来源:互联网 发布:淘宝手机图片尺寸大小 编辑:程序博客网 时间:2024/05/19 17:59

新浪微博客户端新特性滚动视图和启动界面实现


2013年8月20日新浪微博客户端开发之启动界面实现

前言:

使用过新浪微博客户端的童鞋都清楚,客户端每一次升级之后第一次启动界面就会有新特性的介绍,用户通过左右滑动视图可以查看新的特性,查看完最后一个特性之后就进入了主界面了。如果再一次启动程序的时候,就不会再显示新特性介绍的视图了,就会有一个启动界面,延迟一小会然后直接进入主界面。现在很多的应用也是这样,一开始都会介绍这款新应用的一些特性的,这样感觉用户体验也比较良好。我想网上也有很多大神发表过相应的文章介绍这种功能的实现过程,不过我比较喜欢穿一手鞋,记录下自己开发的点滴,这也是分享技术的好去处。



我就用官方新浪微博客户端的新特性来展示这项功能的实现:

 


 
 

上面就是界面效果,下面来看代码实现。

只贴功能滚动视图的布局文件,其他的可以到我的资源页下载源码参考

下载地址:http://download.csdn.net/detail/wwj_748/5981415


/2013.08.20_Function_Scroller_Demo/res/layout/function_scroller.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent" >    <com.wwj.scroller.MyScrollLayout        android:id="@+id/ScrollLayout"        android:layout_width="match_parent"        android:layout_height="match_parent" >        <FrameLayout            android:layout_width="match_parent"            android:layout_height="match_parent"            android:background="@drawable/guide_1" >        </FrameLayout>        <FrameLayout            android:layout_width="match_parent"            android:layout_height="match_parent"            android:background="@drawable/guide_2" >        </FrameLayout><FrameLayout            android:layout_width="match_parent"            android:layout_height="match_parent"            android:background="@drawable/guide_3" >        </FrameLayout>        <FrameLayout            android:layout_width="match_parent"            android:layout_height="match_parent"            android:background="@drawable/guide_4" >        </FrameLayout>        <FrameLayout            android:layout_width="match_parent"            android:layout_height="match_parent"            android:background="#00000000" >        </FrameLayout></com.wwj.scroller.MyScrollLayout>    <LinearLayout        android:id="@+id/llayout"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentBottom="true"        android:layout_centerHorizontal="true"        android:layout_marginBottom="16dp"        android:orientation="horizontal" >        <ImageView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_margin="8dp"            android:clickable="true"            android:src="@drawable/guide_round" />        <ImageView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_margin="8dp"            android:clickable="true"            android:src="@drawable/guide_round" />        <ImageView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_margin="8dp"            android:clickable="true"            android:src="@drawable/guide_round" />        <ImageView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_margin="8dp"            android:clickable="true"            android:src="@drawable/guide_round" />    </LinearLayout></RelativeLayout>

注意上面的滚动视图是自定义的,所以要注意标签的编写格式,包名+文件名,写全了。



正式介绍自定义滚动视图的代码实现:

/2013.08.20_Function_Scroller_Demo/src/com/wwj/scroller/MyScrollLayout.java

package com.wwj.scroller;import android.content.Context;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.VelocityTracker;import android.view.View;import android.view.ViewGroup;import android.widget.Scroller;/** * 自定义滑动视图 * @author Administrator * */public class MyScrollLayout extends ViewGroup {private VelocityTracker mVelocityTracker;// 用于判断甩动手势private static final int SNAP_VELOCITY = 600;// 滑动距离private Scroller mScroller;// 滑动控制器private int mCurScreen;// 当前屏幕private int mDefaultScreen = 0;// 默认屏幕private float mLastMotionX;private OnViewChangeListener mOnViewChangeListener;public MyScrollLayout(Context context) {super(context);init(context);}public MyScrollLayout(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init(context);}public MyScrollLayout(Context context, AttributeSet attrs) {super(context, attrs);init(context);}// 初始化变量private void init(Context context) {mCurScreen = mDefaultScreen;mScroller = new Scroller(context);}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {if (changed) {int childLeft = 0;final int childCount = getChildCount();for (int i = 0; i < childCount; i++) {final View childView = getChildAt(i);// 得到孩子if (childView.getVisibility() != View.GONE) {final int childWidth = childView.getMeasuredWidth();// 获取view测量的宽度childView.layout(childLeft, 0, childLeft + childWidth, childView.getMeasuredHeight());childLeft += childWidth;}}}}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);final int width = MeasureSpec.getSize(widthMeasureSpec);final int count = getChildCount();for (int i = 0; i < count; i++) {getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);}scrollTo(mCurScreen * width, 0);// 设置滚动视图的位置}// 滑动到目标位置public void snapToDestination() {final int screenWidth = getWidth();final int destScreen = (getScrollX() + screenWidth / 2) / screenWidth;snapToScreen(destScreen);}// 滑动到屏幕public void snapToScreen(int whichScreen) {// 获得有效的页面whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));if (getScrollX() != (whichScreen * getWidth())) {final int delta = whichScreen * getWidth() - getScrollX();mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) * 2);mCurScreen = whichScreen;invalidate();// 重绘布局if (mOnViewChangeListener != null) {mOnViewChangeListener.OnViewChange(mCurScreen);}}}@Overridepublic void computeScroll() {if (mScroller.computeScrollOffset()) {scrollTo(mScroller.getCurrX(), mScroller.getCurrY());postInvalidate();}}@Overridepublic boolean onTouchEvent(MotionEvent event) {final int action = event.getAction();final float x = event.getX();switch (action) {case MotionEvent.ACTION_DOWN:// 手指按下动作if (mVelocityTracker == null) {mVelocityTracker = VelocityTracker.obtain();// 得到一个新的甩动手势mVelocityTracker.addMovement(event);}if (!mScroller.isFinished()) {mScroller.abortAnimation();}mLastMotionX = x;break;case MotionEvent.ACTION_MOVE:int deltaX = (int) (mLastMotionX - x);if(IsCanMove(deltaX)) {if(mVelocityTracker != null) {mVelocityTracker.addMovement(event);}mLastMotionX = x;scrollBy(deltaX, 0);}break;case MotionEvent.ACTION_UP:// 手指抬起int velocityX = 0;if (mVelocityTracker != null) {mVelocityTracker.addMovement(event);mVelocityTracker.computeCurrentVelocity(1000);velocityX = (int) mVelocityTracker.getXVelocity();}if (velocityX > SNAP_VELOCITY && mCurScreen > 0) {// 往左移动snapToScreen(mCurScreen - 1);} else if (velocityX < - SNAP_VELOCITY && mCurScreen < getChildCount() - 1) {// 往右移动snapToScreen(mCurScreen + 1);} else {snapToDestination();}if (mVelocityTracker != null) {mVelocityTracker.recycle();mVelocityTracker = null;}break;}return true;}/** * 判断是否可以移动 * @param deltaX * @return */private boolean IsCanMove(int deltaX) {if (getScrollX() <= 0 && deltaX < 0) {return false;}if (getScrollX() >= (getChildCount() - 1) * getWidth() && deltaX > 0) {return false;}return true;}public void SetOnViewChangeListener(OnViewChangeListener listener) {mOnViewChangeListener = listener;}// 接口public interface OnViewChangeListener {public void OnViewChange(int View);}}


接着是启动界面的实现了

/2013.08.20_Function_Scroller_Demo/src/com/wwj/scroller/SplashActivity.java

package com.wwj.scroller;import android.os.Bundle;import android.os.Handler;import android.app.Activity;import android.content.Intent;/** * 程序功能:实现滚动显示新功能介绍 第一次启动程序的时候用户左右滑动查看新特性,查看完之后进入主界面 再次启动的时候直接进入主界面 *  * @author wwj *  */public class SplashActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.splash);// 判断功能介绍界面是否显示过boolean isPlayed = SettingUtil.get(this,SettingUtil.FUNCTION_SCROLLER_PLAYED, false);if (!isPlayed) { // 进入功能介绍界面startActivity(new Intent(this, FunctionScroller.class));finish();return;}// 延迟进入new Handler().postDelayed(new Runnable() {@Overridepublic void run() {startActivity(new Intent(SplashActivity.this, WeiboMain.class));finish();}}, 2500);}}

新特性介绍的Activity,每次滚动一个视图都要表示视图所在位置,就是下面的点指示器的改变实现。

/2013.08.20_Function_Scroller_Demo/src/com/wwj/scroller/FunctionScroller.java

package com.wwj.scroller;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.ImageView;import android.widget.LinearLayout;import com.wwj.scroller.MyScrollLayout.OnViewChangeListener;public class FunctionScroller extends Activity implements OnClickListener{private MyScrollLayout mScrollLayout;// 滑动视图private ImageView[] mImageViews;// 点图片private int mViewCount;// 视图个数private int currentPosition = 0;// 当前位置@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.function_scroller);findViews();init();}private void findViews() {mScrollLayout = (MyScrollLayout) findViewById(R.id.ScrollLayout);LinearLayout linearLayout = (LinearLayout) findViewById(R.id.llayout);mViewCount = mScrollLayout.getChildCount();mImageViews = new ImageView[mViewCount - 1]; // 最后一个view是黑屏过度,所以- 1for (int i = 0; i < (mViewCount - 1); i++) {mImageViews[i] = (ImageView) linearLayout.getChildAt(i);mImageViews[i].setEnabled(true);mImageViews[i].setTag(i);mImageViews[i].setOnClickListener(this);}}private void init() {mImageViews[currentPosition].setEnabled(false);mScrollLayout.SetOnViewChangeListener(new OnViewChangeListener() {@Overridepublic void OnViewChange(int index) {if (index == mViewCount - 1) {// 记录滚屏已经播放过,以后不再播放SettingUtil.set(FunctionScroller.this, SettingUtil.FUNCTION_SCROLLER_PLAYED, true);startActivity(new Intent(FunctionScroller.this, WeiboMain.class));finish();}setCurPoint(index);}});}/** * 设置位置显示 * @param index */private void setCurPoint(int index) {if (index < 0 || index > mViewCount - 2 || currentPosition == index) {return;}mImageViews[currentPosition].setEnabled(true);mImageViews[index].setEnabled(false);currentPosition = index;}@Overridepublic void onClick(View v) {int pos = (Integer) (v.getTag());setCurPoint(pos);mScrollLayout.snapToScreen(pos);}}

核心代码就是以上的了,实现起来也并不太复杂。童鞋们快快整合到你们的应用上面去吧。

关于新浪微博客户端的开发进度比较慢,因为平时要工作,也并不是时刻都有精力去写博客和编写代码的,程序员也需要生活,代码并不是一切,各位程序员们要注意身体啊。下一篇博客就会介绍主界面的实现了,可能并不能实现官方那样的效果,一些复杂的界面效果由于本人的能力有限也没办法实现,不过作为学习和实战已经够用了。