新浪微博客户端新特性滚动视图和启动界面实现
使用过新浪微博客户端的童鞋都清楚,客户端每一次升级之后第一次启动界面就会有新特性的介绍,用户通过左右滑动视图可以查看新的特性,查看完最后一个特性之后就进入了主界面了。如果再一次启动程序的时候,就不会再显示新特性介绍的视图了,就会有一个启动界面,延迟一小会然后直接进入主界面。现在很多的应用也是这样,一开始都会介绍这款新应用的一些特性的,这样感觉用户体验也比较良好。我想网上也有很多大神发表过相应的文章介绍这种功能的实现过程,不过我比较喜欢穿一手鞋,记录下自己开发的点滴,这也是分享技术的好去处。
我就用官方新浪微博客户端的新特性来展示这项功能的实现:
上面就是界面效果,下面来看代码实现。
只贴功能滚动视图的布局文件,其他的可以到我的资源页下载源码参考
下载地址: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);
- }
- @Override
- protected 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;
- }
- }
- }
- }
- @Override
- protected 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);
- }
- }
- }
- @Override
- public void computeScroll() {
- if (mScroller.computeScrollOffset()) {
- scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
- postInvalidate();
- }
- }
- @Override
- public 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 {
- @Override
- protected 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() {
- @Override
- public 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; // 当前位置
- @Override
- protected 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是黑屏过度,所以- 1
- for (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() {
- @Override
- public 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;
- }
- @Override
- public void onClick(View v) {
- int pos = (Integer) (v.getTag());
- setCurPoint(pos);
- mScrollLayout.snapToScreen(pos);
- }
- }
核心代码就是以上的了,实现起来也并不太复杂。童鞋们快快整合到你们的应用上面去吧。
关于新浪微博客户端的开发进度比较慢,因为平时要工作,也并不是时刻都有精力去写博客和编写代码的,程序员也需要生活,代码并不是一切,各位程序员们要注意身体啊。下一篇博客就会介绍主界面的实现了,可能并不能实现官方那样的效果,一些复杂的界面效果由于本人的能力有限也没办法实现,不过作为学习和实战已经够用了。
新浪微博客户端新特性滚动视图和启动界面实现相关推荐
- 我要学ASP.NET MVC 3.0(一): MVC 3.0 的新特性
摘要 MVC经过其1.0和2.0版本的发展,现在已经到了3.0的领军时代,随着技术的不断改进,MVC也越来越成熟.使开发也变得简洁人性化艺术化. 园子里有很多大鸟都对MVC了如指掌,面对问题犹同孙悟空 ...
- 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 ...
- coreldraw x7 分布_CorelDRAW X7新特性汇总
这篇教程是向大家介绍CorelDRAW X7新特性汇总,教程很不错,推荐过来,喜欢的朋友一起来学习吧 CorelDRAW X7新特性 一. 轻松启动和运行 CorelDRAW Graphics Sui ...
- Xamarin实现App展示启动界面
目录 1.效果展示 2.原理 3.demo实现 3.1 定义主题 3.2 主题实现 3.3 活动的实现 3.4 其他说明 4.代码下载 5.参考 在App在启动时,首先会展示启动界面.与此同时,程序可 ...
- 新浪微博客户端(12)-判断当前软件是否是新版本(是否显示新特性)
保存软件版本,通过版本比对来决定是否显示新特性界面. AppDelegate.m - (BOOL)application:(UIApplication *)application didFinishL ...
- 【Redis】 - Redis 6.0 新特性之客户端缓存
Redis 6.0 新特性之客户端缓存 1. 为什么需要客户端缓存 1.1 低延迟和大规模提供数据服务 1.2 其他 cache 层 2. Redis 中的客户端缓存 2.1 什么样的数据集应该被客户 ...
- Redis系列(十四)、Redis6新特性之RESP3与客户端缓存(Client side caching)
Redis6引入新的RESP3协议,并以此为基础加入了客户端缓存的新特性,在此特性下,大大提高了应用程序的响应速度,并降低了数据库的压力,本篇就带大家来看一下Redis6的新特性:客户端缓存. 目录 ...
- android studio viewo,android studio 3.6.0 绑定视图新特性的方法
Android studio 3.6.0 绑定视图使用方法 1.确保你的 build gradle 最低为3.6.0 dependencies { classpath 'com.android.too ...
- 重新想象 Windows 8.1 Store Apps (91) - 后台任务的新特性: 下载和上传的新特性, 程序启动前预下载网络资源, 后台任务的其它新特性...
原文:重新想象 Windows 8.1 Store Apps (91) - 后台任务的新特性: 下载和上传的新特性, 程序启动前预下载网络资源, 后台任务的其它新特性 [源码下载] 重新想象 Wind ...
最新文章
- 数据竞赛Tricks集锦
- OPM攻击事件后:我们从中学到了什么?
- 为什么面试你要35K,而HR只给你25K...
- 这才是真正的 Git——分支合并
- bzoj1095 [ZJOI2007]Hide 捉迷藏
- 一文详解 | 开放搜索兼容Elasticsearch做召回引擎
- dubbo源码解析-spi(二)
- unity 打开项目路径无效_unity3d建立的文件有中文路径,现在新建了项目打不开了...
- CMS收集器中两个致命的问题
- Python如何从社交用户信息中寻找潜在客户?
- Netcore webservice
- manual 离线手册 韩顺平php_PHP - Manual: 手册的格式 (官方文档)
- acwing蓝桥杯刷题
- python制作QQ游戏--大家来找茬游戏辅助(一)
- hr面试性格测试30题_网友应聘华为表现优秀,最终却挂在性格测试上,看真题我哭……...
- WordPress文章/页面浏览量计数器插件Post Views Counter
- 照片删除格式化恢复后损坏的碎片重组修复数据恢复方法
- Wireshark TS | 丢包?不要轻易下结论续
- Win11无法识别以太网怎么办?Win11以太网未识别网络的解决方法
- 图像抓拍录像视频捕获软件微软Amcap怎么使用