`

程序的主框架

 
阅读更多

通过对OsChina的Android客户端的代码分析,从里面抠出程序的主框架。下面是效果图:

 

 

 

这个框架和我想的不一样,它还是很不错的。因为它的页面可以通过左右滑动和底部按钮进行切换。这点很值得借鉴。如果通过Android提供的控件。能够支持左右滑动的,有ViewPager。但是ViewPager有一点不好,就是不能让它取消左右滑动。界面切换在3.0以后加入了Fragment,这也是Google推荐的做法,但是这也存在一个问题,Fragment不支持左右滑动切换界面。可以说鱼和熊掌不可得兼。那么今天我整理出的这个框架就能很好的解决这个问题。

 

下面简要分析下框架的布局:

 

main.xml

 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@color/white">
    
	<include layout="@layout/main_header" />   
    
    <net.oschina.app.widget.ScrollLayout   
	  android:id="@+id/main_scrolllayout"    
	  android:layout_width="fill_parent"    
	  android:layout_height="0dip"
   	  android:layout_weight="1">    
  
 	  <include layout="@layout/frame_news" />
      
	  <include layout="@layout/frame_question" />
	  
	  <include layout="@layout/frame_tweet" />
	      
	  <include layout="@layout/frame_active" />
       
	</net.oschina.app.widget.ScrollLayout> 
    
    <include layout="@layout/main_footer" />   

</LinearLayout>

 

 

 

从布局文件和上图就可以看出他的框架结构了。中间自定义了一个ScrollView,可以左右切换,并实时改变底部的按钮显示情况以及头部的显示内容切换。而底部的按钮被点击时也会触发界面切换的事件。

 

下面看代码:

 

public class Main extends Activity 
{
	
	private ScrollLayout mScrollLayout;	
	private RadioButton[] mButtons;
	private String[] mHeadTitles;
	private int mViewCount;
	private int mCurSel;//记录底部当前选中的按钮的索引
	
	/**
	 * Header
	 * */
	private ImageView mHeadLogo;
	private TextView mHeadTitle;
	private ProgressBar mHeadProgress;
	private ImageButton mHead_search;
	private ImageButton mHeadPub_post;
	private ImageButton mHeadPub_tweet;
	
	/**
	 * Footer
	 * */
	private RadioButton fbNews;
	private RadioButton fbQuestion;
	private RadioButton fbTweet;
	private RadioButton fbactive;
	private ImageView fbSetting;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        initHeadView();
        initFootBar();
        initPageScroll();
    }

    /**
     * 初始化头部视图(最顶端)
     */
    private void initHeadView()
    {
    	mHeadLogo = (ImageView)findViewById(R.id.main_head_logo);
    	mHeadTitle = (TextView)findViewById(R.id.main_head_title);
    	mHeadProgress = (ProgressBar)findViewById(R.id.main_head_progress);
    	mHead_search = (ImageButton)findViewById(R.id.main_head_search);
    	mHeadPub_post = (ImageButton)findViewById(R.id.main_head_pub_post);
    	mHeadPub_tweet = (ImageButton)findViewById(R.id.main_head_pub_tweet);
    	
    	mHead_search.setOnClickListener(new View.OnClickListener() {
			public void onClick(View v) {
			}
		});
    	mHeadPub_post.setOnClickListener(new View.OnClickListener() {
			public void onClick(View v) {
			}
		});
    	mHeadPub_tweet.setOnClickListener(new View.OnClickListener() {
			public void onClick(View v) {
			}
		});
    }
    /**
     * 初始化底部栏
     * 
     */
    private void initFootBar()
    {
    	fbNews = (RadioButton)findViewById(R.id.main_footbar_news);
    	fbQuestion = (RadioButton)findViewById(R.id.main_footbar_question);
    	fbTweet = (RadioButton)findViewById(R.id.main_footbar_tweet);
    	fbactive = (RadioButton)findViewById(R.id.main_footbar_active);
    	fbSetting = (ImageView)findViewById(R.id.main_footbar_setting);
    }
    
    
    /**
     * 初始化水平滚动翻页
     * 
     * 并设置底部按钮的点击事件的监听
     * 
     */
    private void initPageScroll()
    {
    	mScrollLayout = (ScrollLayout) findViewById(R.id.main_scrolllayout);
    	
    	LinearLayout linearLayout = (LinearLayout) findViewById(R.id.main_linearlayout_footer);
    	mHeadTitles = getResources().getStringArray(R.array.head_titles);
    	mViewCount = mScrollLayout.getChildCount();
    	mButtons = new RadioButton[mViewCount];
    	
    	for(int i = 0; i < mViewCount; i++)
    	{
    		mButtons[i] = (RadioButton) linearLayout.getChildAt(i*2);
    		mButtons[i].setTag(i);
    		mButtons[i].setChecked(false);
    		mButtons[i].setOnClickListener(new View.OnClickListener() {
				public void onClick(View v) {
					int pos = (Integer)(v.getTag());
					//点击当前项刷新
	    			if(mCurSel == pos) {
		    			switch (pos) {
						case 0://资讯+博客
							break;	
						case 1://问答
							break;
						case 2://动弹
							break;
						case 3://动态+留言
							break;
						}
	    			}
					setCurPoint(pos);
					mScrollLayout.snapToScreen(pos);
				}
			});
    	}
    	
    	//设置第一显示屏
    	mCurSel = 0;
    	mButtons[mCurSel].setChecked(true);
    	
    	mScrollLayout.SetOnViewChangeListener(new ScrollLayout.OnViewChangeListener() {
			public void OnViewChange(int viewIndex) {
				//切换列表视图-如果列表数据为空:加载数据
				switch (viewIndex) {
				case 0://资讯
					break;	
				case 1://问答
					break;
				case 2://动弹
					break;
				case 3://动态
					break;
				}
				setCurPoint(viewIndex);
			}
		});
    }
    
    /**
     * 设置底部栏当前焦点
     * 并且改变头部的内容
     * @param index
     */
    private void setCurPoint(int index)
    {
    	if (index < 0 || index > mViewCount - 1 || mCurSel == index)
    		return;
   	
    	mButtons[mCurSel].setChecked(false);
    	mButtons[index].setChecked(true);    	
    	mHeadTitle.setText(mHeadTitles[index]);    	
    	mCurSel = index;
    	
    	mHead_search.setVisibility(View.GONE);
    	mHeadPub_post.setVisibility(View.GONE);
    	mHeadPub_tweet.setVisibility(View.GONE);
		//头部logo、发帖、发动弹按钮显示
    	if(index == 0){
    		mHeadLogo.setImageResource(R.drawable.frame_logo_news);
    		mHead_search.setVisibility(View.VISIBLE);
    	}
    	else if(index == 1){
    		mHeadLogo.setImageResource(R.drawable.frame_logo_post);
    		mHeadPub_post.setVisibility(View.VISIBLE);
    	}
    	else if(index == 2){
    		mHeadLogo.setImageResource(R.drawable.frame_logo_tweet);
    		mHeadPub_tweet.setVisibility(View.VISIBLE);
    	}
    	//处理通知信息
    	else if(index == 3){
    		mHeadLogo.setImageResource(R.drawable.frame_logo_active);
    		mHeadPub_tweet.setVisibility(View.VISIBLE);
		}
    }
    
}
 

代码很清晰,可以看出,主要就是初始化所有的控件,以及设置监听。

 

中间部分的自定义的ScrollLayout:

 

/**
 * 左右滑动切换屏幕控件
 * @author Yao.GUET date: 2011-05-04
 * @modify liux (http://my.oschina.net/liux)
 */
public class ScrollLayout extends ViewGroup {
	private static final String TAG = "ScrollLayout";
	private Scroller mScroller;
	private VelocityTracker mVelocityTracker;
	private int mCurScreen;
	private int mDefaultScreen = 0;
	private static final int TOUCH_STATE_REST = 0;
	private static final int TOUCH_STATE_SCROLLING = 1;
	private static final int SNAP_VELOCITY = 600;
	private int mTouchState = TOUCH_STATE_REST;
	private int mTouchSlop;
	private float mLastMotionX;
	private float mLastMotionY;
    private OnViewChangeListener mOnViewChangeListener;

    /**
     * 设置是否可左右滑动
     * @author liux
     */
    private boolean isScroll = true;
    public void setIsScroll(boolean b) {
    	this.isScroll = b;
    }
    
	public ScrollLayout(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public ScrollLayout(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		mScroller = new Scroller(context);
		mCurScreen = mDefaultScreen;
		mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		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();
				childView.layout(childLeft, 0, childLeft + childWidth,
						childView.getMeasuredHeight());
				childLeft += childWidth;
			}
		}
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		//Log.e(TAG, "onMeasure");
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		final int width = MeasureSpec.getSize(widthMeasureSpec);
		final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
		if (widthMode != MeasureSpec.EXACTLY) {
			throw new IllegalStateException(
					"ScrollLayout only canmCurScreen run at EXACTLY mode!");
		}
		final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
		if (heightMode != MeasureSpec.EXACTLY) {
			throw new IllegalStateException(
					"ScrollLayout only can run at EXACTLY mode!");
		}

		// The children are given the same width and height as the scrollLayout
		final int count = getChildCount();
		for (int i = 0; i < count; i++) {
			getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
		}
		// Log.e(TAG, "moving to screen "+mCurScreen);
		scrollTo(mCurScreen * width, 0);
	}

	/**
	 * According to the position of current layout scroll to the destination
	 * page.
	 */
	public void snapToDestination() {
		final int screenWidth = getWidth();
		final int destScreen = (getScrollX() + screenWidth / 2) / screenWidth;
		snapToScreen(destScreen);
	}

	public void snapToScreen(int whichScreen) {
		//是否可滑动
		if(!isScroll) {
			this.setToScreen(whichScreen);
			return;
		}
		
		scrollToScreen(whichScreen);
	}

	public void scrollToScreen(int whichScreen) {		
		// get the valid layout page
		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) * 1);//持续滚动时间 以毫秒为单位
			mCurScreen = whichScreen;
			invalidate(); // Redraw the layout
            
			if (mOnViewChangeListener != null)
            {
            	mOnViewChangeListener.OnViewChange(mCurScreen);
            }
		}
	}
	
	public void setToScreen(int whichScreen) {
		whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
		mCurScreen = whichScreen;
		scrollTo(whichScreen * getWidth(), 0);
		
        if (mOnViewChangeListener != null)
        {
        	mOnViewChangeListener.OnViewChange(mCurScreen);
        }
	}

	public int getCurScreen() {
		return mCurScreen;
	}

	@Override
	public void computeScroll() {
		if (mScroller.computeScrollOffset()) {
			scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
			postInvalidate();
		}
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		//是否可滑动
		if(!isScroll) {
			return false;
		}
		
		if (mVelocityTracker == null) {
			mVelocityTracker = VelocityTracker.obtain();
		}
		mVelocityTracker.addMovement(event);
		final int action = event.getAction();
		final float x = event.getX();
		final float y = event.getY();
		switch (action) {
		case MotionEvent.ACTION_DOWN:
			//Log.e(TAG, "event down!");
			if (!mScroller.isFinished()) {
				mScroller.abortAnimation();
			}
			mLastMotionX = x;
			
			//---------------New Code----------------------
			mLastMotionY = y;
			//---------------------------------------------
			
			break;
		case MotionEvent.ACTION_MOVE:
			int deltaX = (int) (mLastMotionX - x);
			
			//---------------New Code----------------------
			int deltaY = (int) (mLastMotionY - y);
			if(Math.abs(deltaX) < 200 && Math.abs(deltaY) > 10)
				break;
			mLastMotionY = y;
			//-------------------------------------
			
			mLastMotionX = x;
			scrollBy(deltaX, 0);
			break;
		case MotionEvent.ACTION_UP:
			//Log.e(TAG, "event : up");
			// if (mTouchState == TOUCH_STATE_SCROLLING) {
			final VelocityTracker velocityTracker = mVelocityTracker;
			velocityTracker.computeCurrentVelocity(1000);
			int velocityX = (int) velocityTracker.getXVelocity();
			//Log.e(TAG, "velocityX:" + velocityX);
			if (velocityX > SNAP_VELOCITY && mCurScreen > 0) {
				// Fling enough to move left
				//Log.e(TAG, "snap left");
				snapToScreen(mCurScreen - 1);
			} else if (velocityX < -SNAP_VELOCITY
					&& mCurScreen < getChildCount() - 1) {
				// Fling enough to move right
				//Log.e(TAG, "snap right");
				snapToScreen(mCurScreen + 1);
			} else {
				snapToDestination();
			}
			if (mVelocityTracker != null) {
				mVelocityTracker.recycle();
				mVelocityTracker = null;
			}
			// }
			mTouchState = TOUCH_STATE_REST;
			break;
		case MotionEvent.ACTION_CANCEL:
			mTouchState = TOUCH_STATE_REST;
			break;
		}
		return true;
	}

	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		//Log.e(TAG, "onInterceptTouchEvent-slop:" + mTouchSlop);
		final int action = ev.getAction();
		if ((action == MotionEvent.ACTION_MOVE)
				&& (mTouchState != TOUCH_STATE_REST)) {
			return true;
		}
		final float x = ev.getX();
		final float y = ev.getY();
		switch (action) {
		case MotionEvent.ACTION_MOVE:
			final int xDiff = (int) Math.abs(mLastMotionX - x);
			if (xDiff > mTouchSlop) {
				mTouchState = TOUCH_STATE_SCROLLING;
			}
			break;
		case MotionEvent.ACTION_DOWN:
			mLastMotionX = x;
			mLastMotionY = y;
			mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
					: TOUCH_STATE_SCROLLING;
			break;
		case MotionEvent.ACTION_CANCEL:
		case MotionEvent.ACTION_UP:
			mTouchState = TOUCH_STATE_REST;
			break;
		}
		return mTouchState != TOUCH_STATE_REST;
	}
	
	/**
	 * 设置屏幕切换监听器
	 * @param listener
	 */
	public void SetOnViewChangeListener(OnViewChangeListener listener)
	{
		mOnViewChangeListener = listener;
	}

	/**
	 * 屏幕切换监听器
	 * @author liux
	 */
	public interface OnViewChangeListener {
		public void OnViewChange(int view);
	}
}
 

上面是主要的代码:

下面我将这个demo放到github上面,欢迎下载研究,并再次感谢OsChina:

 

https://github.com/michaelye/OsChinaMainLayout

  • 大小: 93.2 KB
  • 大小: 10.6 KB
分享到:
评论

相关推荐

    Android 应用程序主框架搭建

    Android 应用程序主框架搭建

    谢家老幺-主框架程序

    谢家老幺-主框架程序 工具的主框架,你懂的

    利用QT实现的主框架和插件系统

    Qt是1991年奇趣科技开发的一个跨平台的C++图形用户界面应用程序框架。它提供给应用程序开发者建立艺术级的图形用户界面所需的所有功能。Qt很容易扩展,并且允许真正地组件编程。...利用QT实现的主框架和插件系统,

    主页面框架

    Android主页常用的框架activity也fragment页面切换、

    微信小程序demo:页面框架(源代码+截图)

    微信小程序demo:页面框架(源代码+截图)微信小程序demo:页面框架(源代码+截图)微信小程序demo:页面框架(源代码+截图)微信小程序demo:页面框架(源代码+截图)微信小程序demo:页面框架(源代码+截图)微信小程序demo...

    盛荣应用程序框架1.62

    一个应用程序可以拥有多个主框架。每一个主框架可以是单文档、单文档分格窗口、多文档、多文档分格窗口等风格。主框架有命令条集合、菜单条、命令条和状态条组成,也可以加入分格窗口或者是多页文档。这些主框架是有...

    通用应用程序框架

    这是一个应用程序框架,现在已经完成部分框架代码与用户管理、权限管理与日志管理模块。如果您有任何的问题或者是建议可以发邮件到我的邮箱,您可以使用本源代码在您的任何应用之中(包括商业应用)。如果您想要和...

    VC++的应用程序框架

    这篇文章,详细介绍了MFC应用程序中视图类、主框架类、文档类等之间的关系与各自的特点,很好的资源!

    知识付费小程序源码tp框架-带后台+前端-300条数据 带流量主

    知识付费小程序源码tp框架-带后台+前端-300条数据 带流量主

    窗口视窗以及框架体系

    详细说明了文档,视窗与程序主框架的关系,值得借鉴

    C# 酒店管理系统 winform程序 三层框架

    C# 酒店管理系统 winform程序 三层框架

    Socket多客户端程序

    该代码为用VS2008写的Socket程序,程序主框架为MFC,其中包括服务器端和客户端,支持多个客户端同时运行,服务器端为多线程的图标程序,服务器端可以进行全客户群聊天;

    自己制作的主框架系统(说明文档和联合测试项目)

    在初步了解windows运行机制后,我开始制作自己的主框架系统了。这个主框架系统能够为我们程序员服务,通过一系列简单的指令,能帮助程序员减轻很多负担,并且效果也挺好。 我写这个主框架系统的思路是这样的:在一...

    N76E003 程序框架 例程.rar

    包含简单程序框架,定时器 串口0 串口1 ADC FLASH操作。 无中断时,单片机跑着主程序,当中断时,单片机进入中断服务程序。所以,中断其实是比主程序优先级更高的线程。那要怎样处理单片机中断,才能避免资源冲突呢...

    ios-自定义tabbar主框架.zip

    本程序的主要功能是自定义tabbar的主框架,里边实现了自定义tabbar,自定义button,自定义badgebutton,各个tabbaritem的切换,有需要的朋友,可直接下载,拿去使用

    Labview 状态机 程序框架设计

    在LabVIEW中进行程序框架设计,似乎只见到过一种框架,那就是状态机,(并行的算不算?)还有“主/从设计模式”、“生产/消费模式”之类的,但好像也是建立在状态机的基础上的。如果真是这样的话,状态机就成了唯一...

    程序缘 Flex AIR之旅-搭框架配套主程序

    程序缘 Flex AIR之旅-搭框架配套主程序,用于学习借鉴的Flex Air的工程源码,导入flash builder4.6即可。已实现一般C/S结构展现界面的实现,如菜单、快捷工具栏、主窗口等。

    笑话与趣图框架微信小程序源码

    这是一款以笑话和趣味图为主的一款微信小程序源码 或者也可以说是一个框架吧 里面的内容是内置在小程序里面的,所以说是一款框架也可以 因为内置的内容,所以内容数量有限! 大家可以用来养账号,或者有能力的二次...

    Qt应用程序主窗体框架代码

    详见博客:...1. Qt资源系统、菜单栏和工具栏 1.1 使用资源 1.2 编写代码方式添加菜单 1.3 菜单栏 1.4 工具栏 2. 中心部件 3. Dock部件 4. 状态栏 5. 例程代码 6. 自定义菜单

    C# winform开发的可多Tab页的主程序框架

    如果你在找一个简单易上手的 C# winform主程序框架,那这个就非常适合你。 它可以在一个主窗体内显示多个窗体(用户控件的方式),并可以切换,每个Tab可以自行关闭。有主页的工作台,可以放一些常用按钮和统计。 它...

Global site tag (gtag.js) - Google Analytics