近一个月的时间,前几天把android版的凤凰微课HD版做完了,之前做iPad版的时候没写什么总结,这次因为是自己的第一个android项目,所以想写一些经验。
自己现在对android兴趣不是很浓,并不像对待iOS那样会去搜很多资料来看,android更像是一个扩展,也算是完成任务,不过做的过程中还是学到了不少东西的。
项目架构
用的是MVP(Model-View-Presenter)模式,这个模式是对MVC的细化。下图是MVP模式下的一个示意图
下图是项目中的分包图
其实通过上面的示意图,大家都应该可以知道MVP模式是怎样一个流程了。
View当然就是我们熟悉的Activity,在activity中的所有用户操作也好,所有的数据加载也好,数据交互也好,全部这次的实际操作都由presenter来做,
activity只负责进行页面的展示。
在presenter中很多时候都是需要进行异步操作,下载数据或者是处理数据,在完成了这一系列工作之后要通知activity更新界面或者是做出反馈,
这个时候就通过interface来通知activity啦。
在Presenter中需要存储数据,除了基本类型的数据集合外,更多的是会存储类类型的集合,这些数据模型就在Model中定义。
使用MVP的模式有什么好处呢,我觉得主要是逻辑操作和界面展示分开,在代码结构上显得比较清晰,修改和扩展都比较容易,debug也轻松一些。
记得之前去听一个沙龙讲座的时候,有一个讲师说:“架构就是分文件夹”,虽然听起来很直白,但就我目前的水平来看,确实是这样...就是通过分包,不同的包里面的类做不同的用途。
看回上面的源码分包图,
第一个包放的都是Activity;
api包放的是api地址常量
helpers包放的是一些工具类,http请求、SQLite操作、图片异步加载类等等
models和presenters就是上面的model和presenter啦,最后的views类是放接口的。
下面是mvp模式实际应用的代码片段
//MainActivity.java
import ....;
public class MainActivity extends Activity implements IMainActivity{
MainActivityPresenter _presenter;
ArrayList<DataModel> datas;
.......
@Override
protected void onCreate(Bundle savedInstanceState) {
........
_presenter = new MainActivityPresenter(this);
........
_presenter.loadMainPageData();
}
........
@Override
protected void manageMainPageData(ArrayList<DataModel> mainPageData) {
//get the mainPage data
datas = mainPageData;
/**
*do other things with data
*eg.refresh the listview
*/
listviewAdapter.notifyDataSetChanged();
}
}
//IMainActivity.java
public interface IMainActivity {
void manageMainPageData(ArrayList<DataModel> mainPageData);
}
//MainActivityPresenter.java
public class MainActivityPresenter {
IMainActivity _view;
ArrayList<DataModel> datas;
.......
public MainActivityPresenter(IMainActivity view) {
this._view = view;
}
public void loadMainPageData() {
new MainPageDataDownloadTask().execute(MicroCourseAPI.mainPageData);
}
class MainPageDataDownloadTask extends AsyncTask<String,Integer,ArrayList<DataModel>>
{
@Override
protected ArrayList<DataModel> doInBackground(String... params) {
//下载、解析、处理数据
.......
return startIndexs;
}
@Override
protected void onPostExecute(ArrayList<DataModel> result) {
//在presenter中保存或对数据进行进一步处理
..........
//使用接口通知activity数据准备完毕
_view.manageMainPageData(result);
}
}
通过上面的片段,大家应该都可以知道mvp模式的基本使用了,我讲的不是很清楚,有兴趣的朋友戳右边继续了解— —〉
http://magenic.com/Blog/AnMVPPatternforAndroid
其他具体的实现、各种控件的使用没什么好讲的,这次用到一个开源的horizontal listView实现横向的listView操作:
DevsmartLib-Android
下载图片使用的是AsyncImageLoader,这个在这篇博客里面已经有介绍:
http://blog.csdn.net/kay_sprint/article/details/8778240
其他的listView的使用、Gallery的使用、其他控件、AsyncTask、Handler、HTTP请求等的使用在这里就不再说了,因为都是比较基础的使用而已,并没有怎么自定义。
SQLITE操作
简单说下sqlite的操作,首先要说的是,在代码中频繁出现sql语句,是很不好的一种代码编写习惯,所以对数据库操作的sql语句都要封装起来,不然从整体上和代码的阅读性上来说,都不好。
直接上代码,因为看了代码之后就清晰多了
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "favourVideo.db";
private static final int DATABASE_VERSION = 1;
public DatabaseHelper(Context context) {
//set CursorFactory to null,use the default value
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
//create the table
........
}
@Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
// TODO Auto-generated method stub
}
}
public class DatabaseManager {
private DatabaseHelper dbHelper;
private SQLiteDatabase db;
public DatabaseManager(Context context) {
dbHelper = new DatabaseHelper(context);
db = dbHelper.getWritableDatabase();
}
public void add(FavourData favourData) {
//写insert语句
}
public void delete(FavourData favourData) {
//写删除
db.delete("favourVideo", "SESSIONID = ?", new String[]{favourData.sessionID});
}
public ArrayList<FavourData> queryAll() {
//写查找
}
public void closeDB() {
db.close();
}
//根据自己需要添加方法
}
在操作数据库的使用只要使用DatabaseManager的实例调用相应的方法就可以了,不用再在代码中出现各种突兀的SQL语句。
最后说下android屏幕适配的问题
屏幕适配
现在不管是android还是Windows 8还有现在的iPhone都存在屏幕适配的问题,iPhone并不排除以后会出现更多的分辨率。
android和windows 8的适配基本是使用百分比的形式,在细节部分再微调。
这次的项目,使用的是百分比的适配。
首先,在开发过程中,我使用的是dp作为尺寸单位,在160ppi的屏幕上,1dp=1px,我这次也比较幸运的撞上了使用1280*800的10寸平板,刚好是160ppi
所以计算百分比比较简单,首先是在10寸上面排布好之后,根据每个Layout或者控件在屏幕上的百分比(控件像素大小/屏幕像素大小)
根据这个百分比,在Acticity显示该控件的时候,使用代码设置该控件的大小。
获取屏幕像素的方法
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
ScreenHelper.screenWidth = displayMetrics.widthPixels;
ScreenHelper.screenHeight = displayMetrics.heightPixels;
Log.e("dis px","width "+ScreenHelper.screenWidth+" height "+ScreenHelper.screenHeight);
下面是一些适配的代码片段
RelativeLayout rl = (RelativeLayout)findViewById(R.id.main_topbar);
rl.getLayoutParams().height = (int)(ScreenHelper.screenHeight * 0.06f);
rl.invalidate();
........
LinearLayout.LayoutParams params3 = new LinearLayout.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
params3.setMargins((int) (ScreenHelper.screenWidth * 0.016f),
(int) (ScreenHelper.screenWidth * 0.036f),
(int) (ScreenHelper.screenWidth * 0.016f), 0);
recCourseName.setLayoutParams(params3);
recCourseName.setTextSize(fontSize + 3);
LinearLayout.LayoutParams params4 = new LinearLayout.LayoutParams(
(int) (ScreenHelper.screenWidth * 0.224f + 0.5f),
LayoutParams.WRAP_CONTENT);
params4.setMargins((int)(ScreenHelper.screenWidth * 0.01f), (int)(ScreenHelper.screenHeight * 0.015f), 0, 0);
ImageView line = (ImageView)findViewById(R.id.courseInfo_line);
line.setLayoutParams(params4);
LayoutParams params2 = (LayoutParams)favourBtn.getLayoutParams();
params2.height = (int)(ScreenHelper.screenHeight * 0.047f + 0.5f);
params2.width = (int)(ScreenHelper.screenWidth * 0.06f + 0.5f);
favourBtn.setLayoutParams(params2);
使用屏幕百分比只是其中一个大步骤,因为android的设备尺寸大小也很多,加上现在很多高ppi设备,仅仅依靠百分比来适配界面是不够的,很多时候还需要根据屏幕的物理尺寸,作为调整的前提。
总之,android的适配问题就是烦人。