#Day01 #
#项目介绍 ##
关于这个项目不会像手机卫士和新闻客户端(智慧西安)那样详细讲解,个别的模块和代码用现成的代码,只详细讲解不会的地方。
我们现在开发的项目都是需要去调用服务器的,这个项目的服务器不在用tomcat服务器,而是用自己写的服务器,装在手机里边的,这样的好处是,运行时只要在手机里将服务器启动就可以了,不需要连接tomcat.
首页:
上边是一个轮播条的效果,下来是listview展示的应用,最后还有下拉加载
listview每个条目进去后是应用的详情页,里边的收藏和分享按钮的功能暂时没有实现
应用:
应用页面只是简单的实现了下列表,不能点击进去,其实点击进去后和首页是一样的
游戏页面:
游戏页面暂时没有实现,简单的写了下textview
专题:
往下滑,是专题的一个一个的标签页,暂时不能点击进去
推荐:
滑动时,是一种随机的,随机弹出一些关键字
分类:
分为游戏和应用两种 用gridview实现 暂时不能点击进去
排行:
自定义的控件实现
侧边栏:
没有用slidingmenu,而是用google自带的一个控件
标题栏:
不是以前那样自己写的标题栏,而是用google自带的actionbar标题栏
项目部署流程 ##
1.客户端:googlePlay(UTF-8编码)
2.服务端工程:WebServer
3.服务端工程运行到手机上面去,webInfo文件夹必须拷贝到手机内置sd卡路径下
4.工程需要通过actionBar去管理应用的头(Title),关联actionBar所在的库文件,android-support-v7-appcompat
5.关联Xutils的库文件
6.运行项目前, 必须先启动WebServer, 保证可以从服务器获取数据
框架搭建 ##
1.自定义Application
/**
-
自定义Application
* -
@author Kevin
*
*/
public class BaseApplication extends Application {private static Context context; private static int mainThreadId; private static Handler handler; @Override public void onCreate() { super.onCreate(); context = getApplicationContext(); mainThreadId = android.os.Process.myTid();// 获取当前主线程id handler = new Handler(); } public static Context getContext() { return context; } public static int getMainThreadId() { return mainThreadId; } public static Handler getHandler() { return handler; } }
2.UIUtils封装
/**
-
工具类, 专门处理UI相关的逻辑
* -
@author Kevin
*
*/
public class UIUtils {public static Context getContext() { return BaseApplication.getContext(); } public static int getMainThreadId() { return BaseApplication.getMainThreadId(); } public static Handler getHandler() { return BaseApplication.getHandler(); } /**
-
根据id获取字符串
*/
public static String getString(int id) {
return getContext().getResources().getString(id);
}/**
-
根据id获取图片
*/
public static Drawable getDrawable(int id) {
return getContext().getResources().getDrawable(id);
}/**
-
根据id获取颜色值
*/
public static int getColor(int id) {
return getContext().getResources().getColor(id);
}/**
-
根据id获取尺寸
*/
public static int getDimen(int id) {
return getContext().getResources().getDimensionPixelSize(id);
}/**
-
根据id获取字符串数组
*/
public static String[] getStringArray(int id) {
return getContext().getResources().getStringArray(id);
}/**
-
dp转px
*/
public static int dip2px(float dp) {
float density = getContext().getResources().getDisplayMetrics().density;
return (int) (density * dp + 0.5);
}/**
-
px转dp
*/
public static float px2dip(float px) {
float density = getContext().getResources().getDisplayMetrics().density;
return px / density;
}/**
-
加载布局文件
*/
public static View inflate(int layoutId) {
return View.inflate(getContext(), layoutId, null);
}/**
-
判断当前是否运行在主线程
* -
@return
*/
public static boolean isRunOnUiThread() {
return getMainThreadId() == android.os.Process.myTid();
}/**
-
保证当前的操作运行在UI主线程
* -
@param runnable
*/
public static void runOnUiThread(Runnable runnable) {
if (isRunOnUiThread()) {
runnable.run();
} else {
getHandler().post(runnable);
}
}
}
指针自定义控件 ##
导入ViewPagerIndicator开源框架
查看例子程序代码, TabPageIndicator
使用自定义的PagerTab(类似TabPageIndicator)
引入android-support-v7-appcompat
定义BaseActivity,继承ActionbarActivity
解决v4包冲突问题(删除当前项目中的v4包,使用appcompat中的v4包)
在UIUtils中新增部分方法(getColorStateList等)
增加资源文件/drawable/bg_tab_text.xml(描述标签背景),/color/tab_text_color.xml(描述标签文字颜色)
tab_text_color.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:color="@color/tab_text_color_selected"/>
<item android:state_selected="true" android:color="@color/tab_text_color_selected"/>
<item android:color="@color/tab_text_color_normal"/>
</selector>
----------------------------------------------
bg_tab_text.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/bg_tab_pressed" android:state_pressed="true"></item>
<item android:drawable="@android:color/transparent"/>
</selector>
主页面展示 ##
主页面布局文件
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.itcast.googleplayteach.ui.PagerTab
android:id="@+id/pager_tab"
android:layout_width="match_parent"
android:layout_height="42dp"
android:background="@drawable/bg_tab" />
<android.support.v4.view.ViewPager
android:id="@+id/vp_pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
注意: 必须给PagerTab设置背景, 否则PagerTab的onDraw方法不会调用,导致无法绘制Tab分割线和指示器.(原因: PagerTab继承ViewGroup, ViewGroup是一个容器,系统为了节省性能,默认不调用onDraw方法,只有设置背景之后,才会调用)
主页面加载
public class MainActivity extends BaseActivity {
private PagerTab mPagerTab;
private ViewPager mViewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPagerTab = (PagerTab) findViewById(R.id.pager_tab);
mViewPager = (ViewPager) findViewById(R.id.vp_pager);
mViewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager()));
mPagerTab.setViewPager(mViewPager);
}
class MyPagerAdapter extends FragmentPagerAdapter {
private String[] mTabNames;// 页签名称集合
public MyPagerAdapter(FragmentManager fm) {
super(fm);
mTabNames = UIUtils.getStringArray(R.array.tab_names);
}
// 加载每个页签标题
@Override
public CharSequence getPageTitle(int position) {
return mTabNames[position];
}
// 返回Fragment对象,将onCreateView方法的返回view填充给ViewPager
// 此方法类似instantiateItem
@Override
public Fragment getItem(int position) {
// 从工厂类中生产Fragment并返回
System.out.println("getitem-->" + position);
return FragmentFactory.createFragment(position);
}
// 返回item个数
@Override
public int getCount() {
return mTabNames.length;
}
}
}
Fragment抽取
public class BaseFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
TextView view = new TextView(UIUtils.getContext());
view.setText(this.getClass().getSimpleName());
return view;
}
}
分别实现子类HomeFragment(首页),AppFragment(应用),GameFragment(游戏),SubjectFragment(专题),RecommendFragment(推荐),CategoryFragment(分类),HotFragment(排行)
Fragment工厂
/**
-
Fragment工厂类
*/
public class FragmentFactory {// 保存Fragment集合,方便复用 private static HashMap<Integer, BaseFragment> sFragmentMap = new HashMap<Integer, BaseFragment>(); // 根据指针位置,生产相应的Fragment public static BaseFragment createFragment(int position) { BaseFragment fragment = sFragmentMap.get(position); if (fragment == null) { switch (position) { case 0: fragment = new HomeFragment(); break; case 1: fragment = new AppFragment(); break; case 2: fragment = new GameFragment(); break; case 3: fragment = new SubjectFragment(); break; case 4: fragment = new RecommendFragment(); break; case 5: fragment = new CategoryFragment(); break; case 6: fragment = new RankFragment(); break; default: break; } sFragmentMap.put(position, fragment); } return fragment; } }
运行程序
第一次运行出现崩溃, 要求添加主题AppCompat. 这是因为引入了AppCompat包来实现Actionbar效果, 该包要求必须添加AppCompat相关的主题
<activity
android:name="com.itcast.googleplayteach.MainActivity"
android:label="@string/app_name"
android:theme="@style/Theme.AppCompat.Light" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
加载页面(LoadingPage) ##
总结加载页面过程中可能出现的几种状态
正在加载
加载失败
数据为空
访问成功
BaseFragment依靠LoadingPage来加载布局
public class BaseFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
LoadingPage view = new LoadingPage(UIUtils.getContext());
return view;
}
}
正在加载
public class LoadingPage extends FrameLayout {
private View mLoadingView;
public LoadingPage(Context context) {
super(context);
initView();
}
private void initView() {
// 正在加载
if (mLoadingView == null) {
mLoadingView = onCreateLoadingView();
addView(mLoadingView);
}
}
/**
- 初始化正在加载布局
*/
private View onCreateLoadingView() {
return UIUtils.inflate(R.layout.layout_loading);
}
}
自定义Progressbar
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminateDrawable="@drawable/custom_progress" />
----------------------------------------------
custom_progress.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- 正向旋转的圆,速度快 -->
<item>
<rotate
android:drawable="@drawable/spinner_big_inner"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="720" />
</item>
<!-- 逆向旋转的圆,速度慢 -->
<item>
<rotate
android:drawable="@drawable/spinner_big_outer"
android:fromDegrees="360"
android:pivotX="50%"
android:pivotY="50%"
android:toDegrees="0" />
</item>
</layer-list>
加载失败&数据为空
/**
-
初始化布局
*/
private void initView() {
// 加载失败
if (mErrorView == null) {
mErrorView = onCreateErrorView();
addView(mErrorView);
}// 数据为空 if (mEmptyView == null) { mEmptyView = onCreateEmptyView(); addView(mEmptyView); } } ---------------------------------------- /**
-
初始化加载失败布局
*/
private View onCreateErrorView() {
return UIUtils.inflate(R.layout.layout_error);
}/**
-
初始化数据为空布局
*/
private View onCreateEmptyView() {
return UIUtils.inflate(R.layout.layout_empty);
}------------------------------------------ layout_error.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" > <ImageView android:id="@+id/imageView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_error_page" /> <Button android:id="@+id/btn_retry" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/btn_selector" android:paddingLeft="10dp" android:paddingRight="10dp" android:textColor="#000" android:text="加载失败,请重试" /> </LinearLayout>
根据当前状态,展示相应页面
private static final int STATE_UNLOAD = 0;// 未加载
private static final int STATE_LOADING = 1;// 正在加载
private static final int STATE_LOAD_EMPTY = 2;// 数据为空
private static final int STATE_LOAD_ERROR = 3;// 加载失败
private static final int STATE_LOAD_SUCCESS = 4;// 访问成功
private int mCurrentState = STATE_UNLOAD;// 当前状态
/**
-
根据当前状态,展示正确页面
*/
private void showRightPage() {
if (mLoadingView != null) {
mLoadingView
.setVisibility((mCurrentState == STATE_LOADING || mCurrentState == STATE_UNLOAD) ? View.VISIBLE
: View.GONE);
}if (mEmptyView != null) { mEmptyView .setVisibility(mCurrentState == STATE_LOAD_EMPTY ? View.VISIBLE : View.GONE); } if (mErrorView != null) { mErrorView .setVisibility(mCurrentState == STATE_LOAD_ERROR ? View.VISIBLE : View.GONE); } // 访问成功 if (mSuccessView == null && mCurrentState == STATE_LOAD_SUCCESS) { mSuccessView = onCreateSuccessView(); if (mSuccessView != null) {// 防止子类返回null addView(mSuccessView); } } if (mSuccessView != null) { mSuccessView .setVisibility(mCurrentState == STATE_LOAD_SUCCESS ? View.VISIBLE : View.GONE); } }
访问成功
/**
-
初始化访问成功布局, 子类必须实现
*/
public abstract View onCreateSuccessView();----------------------------- public abstract class BaseFragment extends Fragment { private LoadingPage mLoadingPage; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mLoadingPage = new LoadingPage(UIUtils.getContext()) { @Override public View onCreateSuccessView() { return BaseFragment.this.onCreateSuccessView(); } }; return mLoadingPage; } // 由子类实现创建布局的方法 public abstract View onCreateSuccessView(); }
加载网络数据
LoadingPage.java
/**
-
加载数据
*/
public void loadData() {
// 状态归零
if (mCurrentState == STATE_LOAD_EMPTY
|| mCurrentState == STATE_LOAD_ERROR
|| mCurrentState == STATE_LOAD_SUCCESS) {
mCurrentState = STATE_UNLOAD;
}if (mCurrentState == STATE_UNLOAD) { // 异步加载网络数据 new Thread() { @Override public void run() { // 开始加载网络数据 final ResultState state = onLoad(); // 必须在主线程更新界面 UIUtils.runOnUiThread(new Runnable() { @Override public void run() { if (state != null) { // 更新当前状态 mCurrentState = state.getState(); // 更新当前页面 showRightPage(); } } }); } }.start(); } } /**
-
加载网络数据,必须子类实现
* -
@return 返回加载状态
*/
public abstract ResultState onLoad();/**
-
使用枚举表示访问网络的几种状态
*/
public enum ResultState {
STATE_SUCCESS(STATE_LOAD_SUCCESS), // 访问成功
STATE_EMPTY(STATE_LOAD_EMPTY), // 数据为空
STATE_ERROR(STATE_LOAD_ERROR);// 访问失败private int state; private ResultState(int state) { this.state = state; } public int getState() { return state; } } ------------------------------------ BaseFragment.java @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { mLoadingPage = new LoadingPage(UIUtils.getContext()) { @Override public View onCreateSuccessView() { return BaseFragment.this.onCreateSuccessView(); } @Override public ResultState onLoad() { return BaseFragment.this.onLoad(); } }; return mLoadingPage; } // 由子类实现创建布局的方法 public abstract View onCreateSuccessView(); // 由子类实现加载网络数据的逻辑, 该方法运行在子线程 public abstract ResultState onLoad(); // 开始加载网络数据 public void loadData() { if (mLoadingPage != null) { mLoadingPage.loadData(); } } ---------------------------------------------- MainActivity.java mPagerTab.setOnPageChangeListener(new OnPageChangeListener() { @Override public void onPageSelected(int position) { BaseFragment fragment = FragmentFactory .createFragment(position); fragment.loadData(); } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageScrollStateChanged(int arg0) { } });
加载失败,点击重试
// 加载失败
if (mErrorView == null) {
mErrorView = onCreateErrorView();
// 点击重试
mErrorView.findViewById(R.id.btn_retry).setOnClickListener(
new OnClickListener() {
@Override
public void onClick(View v) {
loadData();
}
});
addView(mErrorView);
}
测试不同状态的界面展示效果
/**
-
首页
* -
@author Kevin
*
*/
public class HomeFragment extends BaseFragment {@Override public View onCreateSuccessView() { TextView view = new TextView(UIUtils.getContext()); view.setText(this.getClass().getSimpleName()); return view; } @Override public ResultState onLoad() { return ResultState.STATE_SUCCESS; } } ------------------------------------ /**
-
应用
-
@author Kevin
*
*/
public class AppFragment extends BaseFragment {@Override public View onCreateSuccessView() { return null; } @Override public ResultState onLoad() { return ResultState.STATE_EMPTY; } } -------------------------------------- /**
-
游戏
-
@author Kevin
*
*/
public class GameFragment extends BaseFragment {@Override public View onCreateSuccessView() { // TODO Auto-generated method stub return null; } @Override public ResultState onLoad() { return ResultState.STATE_ERROR; } }