程序员对产品的思考暨项目总结:一品茶香
申明:此篇博文将从产品架构的五个层面分析我的个人项目,展示作为程序员脑海所闪烁的星点光芒,对于专业大牛请换位思考,虚心接受大家的有益建议
产品架构分析
- 战略层
1)用户需求:更加便捷的买到实惠、正宗的茶产品
2)产品目标: 建立茶产品销售平台大品牌 - 范围层
1)内容需求:茶叶/茶具种类及详细信息
2)功能需求:注册/登陆—搜索—对比—下单—付款—送货/收货 结构层
1)交互设计:
点击(跳转)、长按(菜单)、滑动(加载)、触摸(震动)、摇一摇
2)信息架构:
①首屏–>显示功能区、热销茶产品信息
②二屏–>显示用户搜索茶产品的详细信息③三屏-->茶产品分类显示/搜索/对比 ④四屏-->购物车商品详情显示 ⑤五屏-->用户个人中心信息
框架层
1)界面设计:
①首屏:页面垂直布局—->轮播图模块+链接区域模块+秒杀模块+热销茶产品模块
②二屏:页面上下布局—->搜索框模块+茶产品列表显示模块
③三屏:页面左右布局—->顶部搜索模块+左边分类列表模块+右边茶产品详情模块
④四屏:页面垂直布局—->列表显示购物车商品详情
⑤五屏:页面嵌套布局—->顶部主要信息模块+可操作功能模块注:详细界面元素设计请留言私信,用户用得习惯就是对的
2)导航设计:提供用户初次使用产品导航,了解有哪些东西,‘我’能做什么
3)线框图:某种程度上理解为UI/UE的初始版- 表现层
1)感知设计:视觉、听觉、触觉
小结:以上层次分析,意在表明程序员在开发实际功能时并非只着眼功能的实现,而且要融合产品架构构建高效程序
项目核心功能代码分析
(1)相关功能的实现方式
①引导页
private void transPage() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
//是否第一次进入
if (isFirstIn) {
//显示四个滑屏
startActivity(new Intent(SplashActivity.this, GuideActivity.class));
} else {
//直接跳到登陆界面
startActivity(new Intent(SplashActivity.this, HomeActivity.class));
}
finish();
}
}, DELAY_MILLIS);
}
----------
加载滑屏layout
----------
private void initViewPager() {
mPager = (ViewPager) findViewById(R.id.vPager);
listViews = new ArrayList<View>();
LayoutInflater mInflater = getLayoutInflater();
listViews.add(mInflater.inflate(R.layout.guide_layout1, null));
listViews.add(mInflater.inflate(R.layout.guide_layout2, null));
listViews.add(mInflater.inflate(R.layout.guide_layout3, null));
listViews.add(mInflater.inflate(R.layout.guide_layout4, null));
mPager.setAdapter(new MyPagerAdapter(listViews));
mPager.setCurrentItem(0);
}
public void huan(View view){
if (view.getId()==R.id.hua1) {
mPager.setCurrentItem(0);
}
if (view.getId()==R.id.hua2) {
mPager.setCurrentItem(1);
}
if (view.getId()==R.id.hua3) {
mPager.setCurrentItem(2);
}
if (view.getId()==R.id.hua4) {
mPager.setCurrentItem(3);
}
}
②轮播图实现
开源JazzyViewPager实现ViewPager切换动画
<com.hpsvse.tea.jazzviewpager.JazzyViewPager
android:id="@+id/index_product_images_container"
android:layout_width="match_parent"
android:layout_height="125dp"
oid:background="@color/white" />
申明private JazzyViewPager mViewPager = null;
设置动画方法:mViewPager.setTransitionEffect(TransitionEffect.CubeOut);
mViewPager.setCurrentItem(0);
mViewPager.setAdapter(new MyAdapter());
mViewPager.setOnPageChangeListener(new MyPageChangeListener());
mViewPager.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
if (mImageUrls.size() == 0 || mImageUrls.size() == 1)
return true;
else
return false;
}
});
适配器MyAdapter
`
public class MyAdapter extends PagerAdapter {
@Override
public int getCount() {
return mImageViews.length;
}
@Override
public boolean isViewFromObject(View view, Object obj) {
if (view instanceof OutlineContainer) {
return ((OutlineContainer) view).getChildAt(0) == obj;
} else {
return view == obj;
}
}
@Override
public void destroyItem(View container, int position, Object object) {
((ViewPager) container).removeView(mViewPager
.findViewFromObject(position));
}
@Override
public Object instantiateItem(View container, int position) {
ImageLoader.getInstance().displayImage(mImageUrls.get(position),
mImageViews[position]);
((ViewPager) container).addView(mImageViews[position], 0);
mViewPager.setObjectForPosition(mImageViews[position], position);
return mImageViews[position];
}
}
`
③秒杀时间走动实现
自定义TextView –RiseNumberTextView
`
// 限时秒杀
private void initSettime() {
timer = new Timer();
calendar = calendar.getInstance();
int miao = calendar.get(Calendar.SECOND);
setTime(0, 0, (60 - miao));
task = new TimerTask() {
@Override
public void run() {
Message message = new Message();
message.what = 2;
mHandler.sendMessage(message);
}
};
timer.schedule(task, 1000, 1000);
}
// 设置时间
private void setTime(int hour, int min, int seconds) {
IndexActivity.this.seconds = secon
IndexActivity.tis.hour = hour;
}
`
④Gallery 图片滑动
`
private Gallery mStormGallery = null;
mStormAdapter = new IndexGalleryAdapter(this,
R.layout.activity_index_gallery_item, mStormListData,
new int[] { R.id.index_gallery_item_image,
R.id.index_gallery_item_text,
R.id.index_gallery_item_name,
R.id.index_gallery_item_id });
mStormGallery.setAdapter(mStormAdapter);
//设置相应的监听事件
mStormGallery
.setOnItemClickListener(new OnItemClickListener())
`
⑤常见listview列表 自定义adapter 上拉加载,下拉刷新
⑥自定义listview—>MyListView gridview—>MyGridView 实现左右布局
数据填充adapter即可
`
`
(2)核心技术总结
①listview
实现多种布局的方式:
.自定义适配器MyAdapter继承BaseAdapter
..重写 getViewTypeCount() – 返回你有多少个不同的布局
..重写 getItemViewType(int) – 由position返回view type id
..根据getItemViewType()的不同值,在getView中创建正确的convertView,加载多种布局
listview数据加载优化:
1.合理设计布局、adapter,设置height、width为match_parent
2.复用convertView,listview在滑动过程中,滑出屏幕的item会缓存成一个convertview对象,减少创建view的次数
3.ViewHolder机制, 减少findviewById的次数,适用item种类较多
4.涉及图片使用异步加载同时对图片进行内存优化以及引入三级缓存机制;涉及多数据采用分页加载;异步加载的时候引入线程池和线程队列;使用RecyclerView替代listview
5.只加载可见view
②handler机制
ThreadLocal : 线程局部变量,ThreadLocal不是线程,它的作用是可以在每个线程中存储数据每个线程都有自己的一个ThreadLocal,它是变量的一个‘拷贝’,修改它不影响其他线程
..handler的构造方法会首先得到当前线程保存的Looper实例,继而通过该实例得到Looper实例中的MessageQueue对象
..Looper.prepare()在本线程中保存一个Looper实例,然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个
..Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中
..Looper.loop()会让当前线程进入一个无限循环,不断从MessageQueue的实例中读取消息
..构造Handler实例,重写handleMessage
③asyncTask异步任务
使用方式:实现
onPreExecute():这里是最终用户调用Excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框
doInBackground(Params...):后台执行,比较耗时的操作都可以放在这里。注意这里不能直接操作UI,此方法在后台线程执行。
在执行过程中可以调用publicProgress(Progress…)来更新任务的进度
onProgressUpdate(Progress...):可以使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度
onPostExecute(Result):相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回
每个AsyncTask只能被执行一次,否则抛出异常"Cannot execute task: the task is already running"
④http网络通信
HTTP工作原理:
1.客户端(一般是指浏览器,这里是指自己写的程序)与服务器建立连接
2.建立连接后,客户端向服务器发送请求
3.服务器接收到请求后,向客户端发送响应信息
4.客户端与服务器断开连接
HTTP协议的特点:
1.简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST
2.灵活:HTTP 允许传输任意类型的数据对象。正在传输的类型由Content-Type 加以标记
3.无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大
Get与Post请求区别:
1.Post请求可以向服务器传送数据,而且数据放在HTML HEADER内一起传送到服务端URL地址,数据对用户不可见,post安全性较高
2.Post传送的数据量较大,一般被默认为不受限制
1.get是把参数数据队列加到提交的URL中,值和表单内各个字段一一对应,get安全性非常低
2.get 传送的数据量较小,不能大于2KB
⑤json优势
1.数据格式比较简单,易于读写,格式都是压缩的,占用带宽小
2.易于解析,客户端JavaScript可以简单的通过eval()进行JSON数据的读 取
3.支持多种语言,简化了服务器端和客户端的代码开发量,易于维护
⑥自定义控件
view绘制过程:
View在Draw的时候分成两个阶段:measure和layout,在measure阶段时主要就是为了计算两个参数:height和width。这是个递归的过程,从顶向下,DecorView开始依次调用自己子元素的measure。计算完成这两个参数后就开始layout,最后再是draw的调用
3种实现方式:
1.自绘控件:重写onMeasure() onDraw() onLayout()
2.组合控件:标题栏控件--带左边按钮的/右边按钮,首先定义标题栏布局title.xml,如带按钮(Button,标题栏中的返回按钮)和文字(TextView,标题栏中的显示的文字),在自定义MyView类的方法中通过LauoutInflater.inflate()加载title.xml,然后findViewById()获取到Button实例,在其onclick()中调用finish()实现关闭当前activity,间接实现返回功能
3.继承控件:继承现有控件--listview的每个item上添加删除按钮定义一个含有Button的delete.xml---MyListview 继承Listview---重写onDown() onFling() onTouch()...---在MyListview的构造函数中实例化GestureDetector用于监听手势,当手指按下会触发onDown(),快速滑动时会触发onFling()[类似微信滑动],然后加载delete.xml添加到listview各个item上面,在onFling()中也可给Button设置点击事件,实现删除item功能
⑦View中onTouch,onTouchEvent,onClick的执行顺序
Android事件构成:点按、长按、拖拽、滑动
•按下(ACTION_DOWN )
•移动(ACTION_MOVE)
•抬起(ACTION_UP)
执行顺序:
dispatchTouchEvent---->onTouch---->onTouchEvent----->onClick。在所有ACTION_UP事件之后才触发onClick点击事件