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

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

 

 

 

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

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

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

/2013.08.20_Function_Scroller_Demo/res/layout/function_scroller.xml

[html] view plain
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent" >
  5. <com.wwj.scroller.MyScrollLayout
  6. android:id="@+id/ScrollLayout"
  7. android:layout_width="match_parent"
  8. android:layout_height="match_parent" >
  9. <FrameLayout
  10. android:layout_width="match_parent"
  11. android:layout_height="match_parent"
  12. android:background="@drawable/guide_1" >
  13. </FrameLayout>
  14. <FrameLayout
  15. android:layout_width="match_parent"
  16. android:layout_height="match_parent"
  17. android:background="@drawable/guide_2" >
  18. </FrameLayout><FrameLayout
  19. android:layout_width="match_parent"
  20. android:layout_height="match_parent"
  21. android:background="@drawable/guide_3" >
  22. </FrameLayout>
  23. <FrameLayout
  24. android:layout_width="match_parent"
  25. android:layout_height="match_parent"
  26. android:background="@drawable/guide_4" >
  27. </FrameLayout>
  28. <FrameLayout
  29. android:layout_width="match_parent"
  30. android:layout_height="match_parent"
  31. android:background="#00000000" >
  32. </FrameLayout>
  33. </com.wwj.scroller.MyScrollLayout>
  34. <LinearLayout
  35. android:id="@+id/llayout"
  36. android:layout_width="wrap_content"
  37. android:layout_height="wrap_content"
  38. android:layout_alignParentBottom="true"
  39. android:layout_centerHorizontal="true"
  40. android:layout_marginBottom="16dp"
  41. android:orientation="horizontal" >
  42. <ImageView
  43. android:layout_width="wrap_content"
  44. android:layout_height="wrap_content"
  45. android:layout_margin="8dp"
  46. android:clickable="true"
  47. android:src="@drawable/guide_round" />
  48. <ImageView
  49. android:layout_width="wrap_content"
  50. android:layout_height="wrap_content"
  51. android:layout_margin="8dp"
  52. android:clickable="true"
  53. android:src="@drawable/guide_round" />
  54. <ImageView
  55. android:layout_width="wrap_content"
  56. android:layout_height="wrap_content"
  57. android:layout_margin="8dp"
  58. android:clickable="true"
  59. android:src="@drawable/guide_round" />
  60. <ImageView
  61. android:layout_width="wrap_content"
  62. android:layout_height="wrap_content"
  63. android:layout_margin="8dp"
  64. android:clickable="true"
  65. android:src="@drawable/guide_round" />
  66. </LinearLayout>
  67. </RelativeLayout>

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

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

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

[java] view plain
  1. package com.wwj.scroller;
  2. import android.content.Context;
  3. import android.util.AttributeSet;
  4. import android.view.MotionEvent;
  5. import android.view.VelocityTracker;
  6. import android.view.View;
  7. import android.view.ViewGroup;
  8. import android.widget.Scroller;
  9. /**
  10. * 自定义滑动视图
  11. * @author Administrator
  12. *
  13. */
  14. public class MyScrollLayout extends ViewGroup {
  15. private VelocityTracker mVelocityTracker;       // 用于判断甩动手势
  16. private static final int SNAP_VELOCITY = 600;   // 滑动距离
  17. private Scroller mScroller;                     // 滑动控制器
  18. private int mCurScreen;                         // 当前屏幕
  19. private int mDefaultScreen = 0;                 // 默认屏幕
  20. private float mLastMotionX;
  21. private OnViewChangeListener mOnViewChangeListener;
  22. public MyScrollLayout(Context context) {
  23. super(context);
  24. init(context);
  25. }
  26. public MyScrollLayout(Context context, AttributeSet attrs, int defStyle) {
  27. super(context, attrs, defStyle);
  28. init(context);
  29. }
  30. public MyScrollLayout(Context context, AttributeSet attrs) {
  31. super(context, attrs);
  32. init(context);
  33. }
  34. // 初始化变量
  35. private void init(Context context) {
  36. mCurScreen = mDefaultScreen;
  37. mScroller = new Scroller(context);
  38. }
  39. @Override
  40. protected void onLayout(boolean changed, int l, int t, int r, int b) {
  41. if (changed) {
  42. int childLeft = 0;
  43. final int childCount = getChildCount();
  44. for (int i = 0; i < childCount; i++) {
  45. final View childView = getChildAt(i);   // 得到孩子
  46. if (childView.getVisibility() != View.GONE) {
  47. final int childWidth = childView.getMeasuredWidth();    // 获取view测量的宽度
  48. childView.layout(childLeft, 0, childLeft + childWidth, childView.getMeasuredHeight());
  49. childLeft += childWidth;
  50. }
  51. }
  52. }
  53. }
  54. @Override
  55. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  56. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  57. final int width = MeasureSpec.getSize(widthMeasureSpec);
  58. final int count = getChildCount();
  59. for (int i = 0; i < count; i++) {
  60. getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
  61. }
  62. scrollTo(mCurScreen * width, 0);        // 设置滚动视图的位置
  63. }
  64. // 滑动到目标位置
  65. public void snapToDestination() {
  66. final int screenWidth = getWidth();
  67. final int destScreen = (getScrollX() + screenWidth / 2) / screenWidth;
  68. snapToScreen(destScreen);
  69. }
  70. // 滑动到屏幕
  71. public void snapToScreen(int whichScreen) {
  72. // 获得有效的页面
  73. whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
  74. if (getScrollX() != (whichScreen * getWidth())) {
  75. final int delta = whichScreen * getWidth() - getScrollX();
  76. mScroller.startScroll(getScrollX(), 0, delta, 0, Math.abs(delta) * 2);
  77. mCurScreen = whichScreen;
  78. invalidate();       // 重绘布局
  79. if (mOnViewChangeListener != null) {
  80. mOnViewChangeListener.OnViewChange(mCurScreen);
  81. }
  82. }
  83. }
  84. @Override
  85. public void computeScroll() {
  86. if (mScroller.computeScrollOffset()) {
  87. scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
  88. postInvalidate();
  89. }
  90. }
  91. @Override
  92. public boolean onTouchEvent(MotionEvent event) {
  93. final int action = event.getAction();
  94. final float x = event.getX();
  95. switch (action) {
  96. case MotionEvent.ACTION_DOWN:   // 手指按下动作
  97. if (mVelocityTracker == null) {
  98. mVelocityTracker = VelocityTracker.obtain();    // 得到一个新的甩动手势
  99. mVelocityTracker.addMovement(event);
  100. }
  101. if (!mScroller.isFinished()) {
  102. mScroller.abortAnimation();
  103. }
  104. mLastMotionX = x;
  105. break;
  106. case MotionEvent.ACTION_MOVE:
  107. int deltaX = (int) (mLastMotionX - x);
  108. if(IsCanMove(deltaX)) {
  109. if(mVelocityTracker != null) {
  110. mVelocityTracker.addMovement(event);
  111. }
  112. mLastMotionX = x;
  113. scrollBy(deltaX, 0);
  114. }
  115. break;
  116. case MotionEvent.ACTION_UP: // 手指抬起
  117. int velocityX = 0;
  118. if (mVelocityTracker != null) {
  119. mVelocityTracker.addMovement(event);
  120. mVelocityTracker.computeCurrentVelocity(1000);
  121. velocityX = (int) mVelocityTracker.getXVelocity();
  122. }
  123. if (velocityX > SNAP_VELOCITY && mCurScreen > 0) {
  124. // 往左移动
  125. snapToScreen(mCurScreen - 1);
  126. } else if (velocityX < - SNAP_VELOCITY && mCurScreen < getChildCount() - 1) {
  127. // 往右移动
  128. snapToScreen(mCurScreen + 1);
  129. } else {
  130. snapToDestination();
  131. }
  132. if (mVelocityTracker != null) {
  133. mVelocityTracker.recycle();
  134. mVelocityTracker = null;
  135. }
  136. break;
  137. }
  138. return true;
  139. }
  140. /**
  141. * 判断是否可以移动
  142. * @param deltaX
  143. * @return
  144. */
  145. private boolean IsCanMove(int deltaX) {
  146. if (getScrollX() <= 0 && deltaX < 0) {
  147. return false;
  148. }
  149. if (getScrollX() >= (getChildCount() - 1) * getWidth() && deltaX > 0) {
  150. return false;
  151. }
  152. return true;
  153. }
  154. public void SetOnViewChangeListener(OnViewChangeListener listener) {
  155. mOnViewChangeListener = listener;
  156. }
  157. // 接口
  158. public interface OnViewChangeListener {
  159. public void OnViewChange(int View);
  160. }
  161. }

接着是启动界面的实现了

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

[java] view plain
  1. package com.wwj.scroller;
  2. import android.os.Bundle;
  3. import android.os.Handler;
  4. import android.app.Activity;
  5. import android.content.Intent;
  6. /**
  7. * 程序功能:实现滚动显示新功能介绍 第一次启动程序的时候用户左右滑动查看新特性,查看完之后进入主界面 再次启动的时候直接进入主界面
  8. *
  9. * @author wwj
  10. *
  11. */
  12. public class SplashActivity extends Activity {
  13. @Override
  14. protected void onCreate(Bundle savedInstanceState) {
  15. super.onCreate(savedInstanceState);
  16. setContentView(R.layout.splash);
  17. // 判断功能介绍界面是否显示过
  18. boolean isPlayed = SettingUtil.get(this,
  19. SettingUtil.FUNCTION_SCROLLER_PLAYED, false);
  20. if (!isPlayed) { // 进入功能介绍界面
  21. startActivity(new Intent(this, FunctionScroller.class));
  22. finish();
  23. return;
  24. }
  25. // 延迟进入
  26. new Handler().postDelayed(new Runnable() {
  27. @Override
  28. public void run() {
  29. startActivity(new Intent(SplashActivity.this, WeiboMain.class));
  30. finish();
  31. }
  32. }, 2500);
  33. }
  34. }

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

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

[java] view plain
  1. package com.wwj.scroller;
  2. import android.app.Activity;
  3. import android.content.Intent;
  4. import android.os.Bundle;
  5. import android.view.View;
  6. import android.view.View.OnClickListener;
  7. import android.widget.ImageView;
  8. import android.widget.LinearLayout;
  9. import com.wwj.scroller.MyScrollLayout.OnViewChangeListener;
  10. public class FunctionScroller extends Activity implements OnClickListener{
  11. private MyScrollLayout mScrollLayout;       // 滑动视图
  12. private ImageView[] mImageViews;            // 点图片
  13. private int mViewCount;                     // 视图个数
  14. private int currentPosition = 0;            // 当前位置
  15. @Override
  16. protected void onCreate(Bundle savedInstanceState) {
  17. super.onCreate(savedInstanceState);
  18. setContentView(R.layout.function_scroller);
  19. findViews();
  20. init();
  21. }
  22. private void findViews() {
  23. mScrollLayout = (MyScrollLayout) findViewById(R.id.ScrollLayout);
  24. LinearLayout linearLayout = (LinearLayout) findViewById(R.id.llayout);
  25. mViewCount = mScrollLayout.getChildCount();
  26. mImageViews = new ImageView[mViewCount - 1]; // 最后一个view是黑屏过度,所以- 1
  27. for (int i = 0; i < (mViewCount - 1); i++) {
  28. mImageViews[i] = (ImageView) linearLayout.getChildAt(i);
  29. mImageViews[i].setEnabled(true);
  30. mImageViews[i].setTag(i);
  31. mImageViews[i].setOnClickListener(this);
  32. }
  33. }
  34. private void init() {
  35. mImageViews[currentPosition].setEnabled(false);
  36. mScrollLayout.SetOnViewChangeListener(new OnViewChangeListener() {
  37. @Override
  38. public void OnViewChange(int index) {
  39. if (index == mViewCount - 1) {
  40. // 记录滚屏已经播放过,以后不再播放
  41. SettingUtil.set(FunctionScroller.this, SettingUtil.FUNCTION_SCROLLER_PLAYED, true);
  42. startActivity(new Intent(FunctionScroller.this, WeiboMain.class));
  43. finish();
  44. }
  45. setCurPoint(index);
  46. }
  47. });
  48. }
  49. /**
  50. * 设置位置显示
  51. * @param index
  52. */
  53. private void setCurPoint(int index) {
  54. if (index < 0 || index > mViewCount - 2 || currentPosition == index) {
  55. return;
  56. }
  57. mImageViews[currentPosition].setEnabled(true);
  58. mImageViews[index].setEnabled(false);
  59. currentPosition = index;
  60. }
  61. @Override
  62. public void onClick(View v) {
  63. int pos = (Integer) (v.getTag());
  64. setCurPoint(pos);
  65. mScrollLayout.snapToScreen(pos);
  66. }
  67. }

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

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

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

  1. 我要学ASP.NET MVC 3.0(一): MVC 3.0 的新特性

    摘要 MVC经过其1.0和2.0版本的发展,现在已经到了3.0的领军时代,随着技术的不断改进,MVC也越来越成熟.使开发也变得简洁人性化艺术化. 园子里有很多大鸟都对MVC了如指掌,面对问题犹同孙悟空 ...

  2. ASP.NET MVC 3.0(一): MVC 3.0 的新特性 摘要

    ASP.NET MVC 3.0(一): MVC 3.0 的新特性 摘要 ASP.NET MVC 3.0(二): MVC的概念及MVC 3.0开发环境 ASP.NET MVC 3.0(三): 初识MVC ...

  3. coreldraw x7 分布_CorelDRAW X7新特性汇总

    这篇教程是向大家介绍CorelDRAW X7新特性汇总,教程很不错,推荐过来,喜欢的朋友一起来学习吧 CorelDRAW X7新特性 一. 轻松启动和运行 CorelDRAW Graphics Sui ...

  4. Xamarin实现App展示启动界面

    目录 1.效果展示 2.原理 3.demo实现 3.1 定义主题 3.2 主题实现 3.3 活动的实现 3.4 其他说明 4.代码下载 5.参考 在App在启动时,首先会展示启动界面.与此同时,程序可 ...

  5. 新浪微博客户端(12)-判断当前软件是否是新版本(是否显示新特性)

    保存软件版本,通过版本比对来决定是否显示新特性界面. AppDelegate.m - (BOOL)application:(UIApplication *)application didFinishL ...

  6. 【Redis】 - Redis 6.0 新特性之客户端缓存

    Redis 6.0 新特性之客户端缓存 1. 为什么需要客户端缓存 1.1 低延迟和大规模提供数据服务 1.2 其他 cache 层 2. Redis 中的客户端缓存 2.1 什么样的数据集应该被客户 ...

  7. Redis系列(十四)、Redis6新特性之RESP3与客户端缓存(Client side caching)

    Redis6引入新的RESP3协议,并以此为基础加入了客户端缓存的新特性,在此特性下,大大提高了应用程序的响应速度,并降低了数据库的压力,本篇就带大家来看一下Redis6的新特性:客户端缓存. 目录 ...

  8. android studio viewo,android studio 3.6.0 绑定视图新特性的方法

    Android studio 3.6.0 绑定视图使用方法 1.确保你的 build gradle 最低为3.6.0 dependencies { classpath 'com.android.too ...

  9. 重新想象 Windows 8.1 Store Apps (91) - 后台任务的新特性: 下载和上传的新特性, 程序启动前预下载网络资源, 后台任务的其它新特性...

    原文:重新想象 Windows 8.1 Store Apps (91) - 后台任务的新特性: 下载和上传的新特性, 程序启动前预下载网络资源, 后台任务的其它新特性 [源码下载] 重新想象 Wind ...

最新文章

  1. 数据竞赛Tricks集锦
  2. OPM攻击事件后:我们从中学到了什么?
  3. 为什么面试你要35K,而HR只给你25K...
  4. 这才是真正的 Git——分支合并
  5. bzoj1095 [ZJOI2007]Hide 捉迷藏
  6. 一文详解 | 开放搜索兼容Elasticsearch做召回引擎
  7. dubbo源码解析-spi(二)
  8. unity 打开项目路径无效_unity3d建立的文件有中文路径,现在新建了项目打不开了...
  9. CMS收集器中两个致命的问题
  10. Python如何从社交用户信息中寻找潜在客户?
  11. Netcore webservice
  12. manual 离线手册 韩顺平php_PHP - Manual: 手册的格式 (官方文档)
  13. acwing蓝桥杯刷题
  14. python制作QQ游戏--大家来找茬游戏辅助(一)
  15. hr面试性格测试30题_网友应聘华为表现优秀,最终却挂在性格测试上,看真题我哭……...
  16. WordPress文章/页面浏览量计数器插件Post Views Counter
  17. 照片删除格式化恢复后损坏的碎片重组修复数据恢复方法
  18. Wireshark TS | 丢包?不要轻易下结论续
  19. Win11无法识别以太网怎么办?Win11以太网未识别网络的解决方法
  20. 图像抓拍录像视频捕获软件微软Amcap怎么使用

热门文章

  1. 100%成功率的顶象面积验证码识别方案
  2. 机器学习sklearn实战-----随机森林调参乳腺癌分类预测
  3. python moviepy视频拼接
  4. 最长递增子序列(LIS)
  5. 风控外部数据源文章汇总
  6. 区块链资料高清PDF合集
  7. 利用百度地图的路书功能实现汽车实时定位
  8. (记录)Centos7下安装Mysql8.0的过程以及遇到的问题
  9. 如何设置office2003为默认打开方式
  10. mac Security CRT 破解