`

程序的主框架

 
阅读更多

通过对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应用程序主框架搭建知识点详解 #### 一、引言 在Android应用程序开发过程中,合理的架构设计对于提高软件质量和维护性至关重要。本文旨在探讨一种基于Android平台的应用程序框架设计方案,通过对UI层和...

    谢家老幺-主框架程序

    "谢家老幺-主框架程序"是一个专为软件开发设计的主框架工具,它构成了一个应用程序的基础架构,为开发者提供了构建复杂应用的便捷平台。主框架程序在软件工程中扮演着至关重要的角色,它定义了应用的核心结构、模块...

    主页面框架

    在Android应用开发中,"主页面框架"通常指的是构建应用程序主要交互界面的结构。这个框架涉及到的主要组件是Activity和Fragment,它们是Android系统提供的重要组件,用于管理用户界面和实现页面间的切换。以下是对这...

    MFC在主框架中拆分窗口

    5. **添加到主框架**:最后,将拆分窗口对象添加到主框架类(通常是CFrameWnd或CMDIFrameWnd的派生类)中,通常是通过在主框架的OnCreateClient()函数中创建和初始化拆分窗口。 现在,让我们转向“基于listview的...

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

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

    VC++的应用程序框架

    - **定义**:主框架窗口类是应用程序的主要窗口,对于 SDI(Single Document Interface 单文档接口)和 MDI(Multiple Document Interface 多文档接口)应用程序,主框架窗口类的作用有所不同。 - **生成与访问**: ...

    盛荣应用程序框架1.62

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

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

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

    FANUC机器人常用程序框架及程序设置指导手册.docx

    对于FANUC机器人的使用者来说,理解和掌握其常用的程序框架与程序设置至关重要。本篇指导手册将深入探讨这些核心概念,帮助用户更好地利用FANUC机器人进行各种工作。 一、FANUC机器人程序框架 1. 程序结构:FANUC...

    小程序wepy框架

    - `app.wpy`:这是小程序的主入口文件,用于定义全局配置和组件。 - `components`:存放自定义组件,每个组件都有自己的目录,包含组件的`.wpy`文件和其他资源文件。 - `pages`:存放页面文件,每个页面也是一个...

    VC MDI主框架添加背景图像

    MDI主框架是这种设计的核心部分,它充当着容器,管理着多个子窗口(子文档)。在MDI应用程序中添加背景图像可以提升用户的视觉体验,使界面更具吸引力。下面我们将详细探讨如何在VC6.0中实现这个功能。 首先,我们...

    Windows应用程序框架MFC

    CFrameWnd提供了创建和管理应用程序主窗口的功能,同时作为一个容器,可以包含其他子窗口对象。CWnd类则包含了Windows窗口的基本属性和方法,如消息处理。而CObject类是所有MFC类的基类,提供了诸如调试支持、运行时...

    通用应用程序框架

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

    51单片机汇编程序框架

    【51单片机汇编程序框架】是一个适合初学者学习的主题,主要讲解如何构建一个基于51单片机的汇编语言程序。51单片机是微控制器的一种,广泛应用在各种电子设备中,其汇编语言编程是理解硬件控制和底层软件开发的基础...

    基于Java语言的SairFramework主框架程序设计源码

    该项目是一款采用Java语言编写的SairFramework主框架程序设计源码,总计包含158个文件,涵盖60个类文件、46个Java源文件、24个JAR文件、7个JSON文件、6个XML文件、2个Git忽略文件、2个偏好设置文件、2个PNG图片文件...

    MSP430框架程序

    压缩包中的“MY_A”可能是项目的一个主要源文件或者目录,可能包含了项目的主程序(main.c或main.cpp)、配置头文件(如config.h)、特定模块的驱动程序源文件(如adc.c、i2c.c等)以及可能的库文件。通过这些文件,...

    插件应用程序框架设计-Engine框架案例

    这些框架为开发者提供了一套完整的API和机制,用于创建、管理、加载和卸载插件,确保它们与主应用程序的无缝集成。 3. **插件生命周期管理**:Engine框架通常包含对插件生命周期的管理,包括插件的安装、启动、停止...

    delphi 应用程序框架和设计

    - **项目文件(.dpr)**是项目的核心,通常包含主窗体的实例化以及程序启动时的初始化代码。它们使用Pascal语法编写,可以通过添加自定义代码来扩展功能,如启动画面或特定的初始化过程。 - **单元文件(.pas)**...

    单片机程序框架 MS3.21

    单片机程序框架MS3.21是一种专为单片机开发设计的高效代码组织结构,旨在简化裸机程序的开发过程,提高程序的可读性、可维护性和可扩展性。这个框架引入了状态机的设计模式,使得复杂的控制逻辑能够以更有序的方式...

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

    知识付费小程序源码是开发在线教育、知识分享平台的一种常见技术解决方案,主要基于微信小程序的开发框架,并结合ThinkPHP(简称TP)框架构建后端管理系统。这个特定的资源包括了前后端的完整代码,以及预填充的300...

Global site tag (gtag.js) - Google Analytics