zoukankan      html  css  js  c++  java
  • Android应用--新浪微博客户端新特性滚动视图和启动界面实现

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


    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);
    	}
    
    	@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);
    	}
    
    }
    


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

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


  • 相关阅读:
    iaas,paas,saas理解
    July 06th. 2018, Week 27th. Friday
    July 05th. 2018, Week 27th. Thursday
    July 04th. 2018, Week 27th. Wednesday
    July 03rd. 2018, Week 27th. Tuesday
    July 02nd. 2018, Week 27th. Monday
    July 01st. 2018, Week 27th. Sunday
    June 30th. 2018, Week 26th. Saturday
    June 29th. 2018, Week 26th. Friday
    June 28th. 2018, Week 26th. Thursday
  • 原文地址:https://www.cnblogs.com/james1207/p/3271354.html
Copyright © 2011-2022 走看看