Android手表商场项目总结

项目总结

  • 不熟练的地方
  • 运用到的知识
  • 工具库的封装

待提高的地方

  • 将设计稿100%实现

遇见的问题与解决

需求:实现图片按钮的需求。要求在一个按钮的实现图片加文本居中显示。

实现:自定义控件ImageButton。ImageButton继承了LinearLayout,加载了R.layout.ui_imagebutton_h布局,R.layout.ui_imagebutton_h里定义了一个ImageViewTextView,暴露设置文字,文字大小,文字颜色,图片等设置的接口。问题解决。

当初的思路有其他两种实现方式:

  1. 自定义控件继承与Button或ImageButton,重写onDraw()方法,绘制图片与文字。这种思路可以保留Button的系统定义的功能,不用我们自己再去写。但可能代码复杂度要高点,不如复合控件来得快。有时依项目要求要重写 onMeasure onLayout()方法来控制布局大小。
  2. 利用Button 的 android:drawableLeft 来实现图文混排。实际测试发现,无法实现图文居中显示,图与文字的距离无法控制,系统默认为图片在控件的最左边。可能需要我们自己复写 相应drawableLeft属性对应的方法。

需求:圆形按钮的实现加点击效果的实现。

实现:实现圆形按钮就是为此建立一个drawable文件即可。同理,点击效果使用selector即可。


 
     
     
     
     
     


需求:主页面需要在底部添加4个水平排列且均匀分布的选项按钮,选项按钮样式为图片在上,文字在下,同时单击选项按钮时,主页面的界面改变成相应的页面,按钮的图片颜色与文字都变化成指定的颜色。很常见的导航按钮。

实现:选项按钮的样式实现仍然采用复合型控件的方式,名OptionView,其xml文件包括一个表示指示器的TextView控件,一个显示图片的ImageView,一个显示文字的TextView,无难度。图片颜色的变化利用ColorMatrix颜色矩阵。文字的颜色变化系统可以控制。 指示器的显示与否我选择控制其透明度来实现。在OptionView类中,暴露一个change()方法,使得可以同时改变指示器,图片,文字。图片颜色的变化用一个自定义方法handleImage(Bitmap bm, int value)来处理。OptionView中定义一个私有变量isChange(),并暴露其get/setter方法。由于在此需求该控件只在xml引用,故只重写了public OptionView(Context context, AttributeSet attrs)方法,并自己自定义所需的xml属性,来获得文字,变化的颜色,文字大小,需设置的图片源等。在主页面加载布局文件时,获得4个OptionView的实例,为其添加单击事件监听。维护一个OptionView实例的的全局变量mSelected,表示该控件为当前选择控件,在主页面的onCreate方法中将某一个OptionView实例赋值给它同时调用它的change()方法。而在单击事件中,只要判断当前选择的OptionView控件是否为当前选择的控件,如果是,无作为,如果不是,则调用两者的change()方法并改变mSelected的值,接下来,就是Fragment的切换的问题了。需求实现。

单击事件的思路:最初实现按钮的单击事件时使用了复杂的观察者模式,让OptionView实现一个接口A,接口B的实现类保存所有A接口的实例并暴露添加,移除,通知观察这变化的办法。虽然结果也能实现,但是代码复杂,组织混乱。尤其是其实这里并不适用这个模式,让所有的按钮对单击事件感兴趣,如果某个按钮的单击事件会使得多个组件,多个页面,多个所谓的“观察者”感兴趣的话,才比较适合。一个主题通知所有观察者去变化。而这里,组件的单击事件存在互斥关系,且唯有且仅有此optionView的控件感兴趣。所以还不如用 维护一个变量来得实在。

指示器的变化思路:可以通过改变控件的View.setVisibility(visibility);方法来显示和隐藏控件。


需求:在单击optionView的事件中,要将 主页面 替换成 相应的页面。

实现:Fragment 实现。4个optionView分别对应4个Fragment的子类。在主界面维护显示的Fragment的变量mMain,单击事件中去判断mMain的是否为空,空实例化相应的Fragment出来,不为空直接调用FragmentTransaction.replace(R.id.main, mMain, "main")方法替换掉。需求实现。

值得注意的是:处理好Fragment 与 Activity 之间的生命周期问题。调用replace方法,会调用remove方法,所以要在Fragment的生命周期里恰当的保存好所需的信息。同时要处理好Activity的重建,例如设备转换屏幕时。


需求:第一个optionView所对应的Fragment需实现一个上部图片轮滑的效果来显示广告,同时需要显示4个系列的入口。整体效果应如下: 

实现:上部利用ViewPager 加 自定义的 IndicatorView 来实现圆点效果。中间显示的利用GridView来实现。主要是适配器Mainshow如何适配的问题。实现GridView的OnItemClickListener事件实现跳转需求。同时解决Fragment生命周期的问题,避免重复加载布局。该需求最大的难点为适配器。所以说说适配器是如何实现的。适配器的关键是Item的设置。在该需求中,选择自定义控件ListImage来填充Item。ListImage为左图,标题,分割线,图片,分别对应TextView,TextView,ImageView的复合型控件,实现起来不难。但是要控制ImageView图片的适配问题。同时解决如何不同分割线的绘制。需求实现。

实现分割线效果:我实现的思路将分割线分割成一个小图片,然后平铺。而Android实现平铺效果的代码如下:



需求实现。

实现图片轮滑效果:这个实现了ViewPager.PageAdapter,问题解决,指示器的样式只要传给指示器一个ViewPage的实例即可。这指示器基本采用网上源码来实现。所以没什么好说的。传好参数就行了。

解决布局问题:这里我使用了Lin为了但是我们要让其显示在Item的最下面,但是实际效果为


需求:在通过主界面点击ListImage后进入列表显示商品介绍。每一个Item显示图片,显示手表名称,手表简介,手表获得的赞数,手表获得的评论。单击Item触发单击事件,进入该手表的详情信息页。

在这个需求里,我遇到的问题为:这些信息都是通过网络来的,所以我必须跟服务器进行交互。这里我直接封装了常用的网络访问库 NetHelp,使用基本的HttpURLConnection与服务器进行交互,暴露sendRequestWithHttpConnect(String request,int Method, IDate i)方法,接受请求命令,注意不是完整的url地址,有关于url的地址会在方法内进行拼装,因为我提供了一个合同类Contract定义了与服务端的数据交互的url常量。在NetHelp类中我可以维护一个保存当前请求的参数mparmasHashMap类型,如果服务端的请求url发生变化时,不必更改所有使用的url,同时保证拼接url不会出现拼接不完全的问题。因为一个完整的url我分成了三部分,指向服务端的域名的基本url,向服务端请求的具体方法,携带的参数 key - value 值。尤其是在拼接参数时同时对参数进行中文编码。当然,在NetHelp类中不处理返回的response,由调用者来自行决定如何处理。我提供一个IDate接口,里面只有一个方法HandlerDate(HttpURLConnection c),可以让调用者回调利用返回的HttpURLConnection自行处理返回的结果。

回到这个需求,首次进入我们肯定没有数据,所以我们要向服务端请求数据,服务端返回的一大段json,正常来讲为服务器返回的为 该数据集合的实体类的list集合的json。这里就产生了两个问题:1、要不要使用实体类封装数据。2、数据量庞大时,内存不足怎么解决。我自己思考的答案是:要用实体类,封装简单,在adapter适配器里适配数据时也更容易,数据量庞大时,使用化整体为部分的思想,要么向服务器请求部分数据,类似网页的分页。要么将数据存储在本地进行处理。在这个请求里,往极限了想,如果服务端返回10000条数据需要我们显示,存储在本地没必要,我们显示的也就那么点,所以第一让服务器返回显示的数据总数,我们根据数据总数,将数据分割成单次显示的数量,或者传给服务器两个参数,页数page,每页显示的数量pagesize,让服务器自己返回数据集合,我们再添加一个“加载更多”的控件,让page变化就可以了。解析json,网上一大堆成熟的框架,也是分分钟的事。剩下的就是发起请求的问题。值得注意的是,Android并不允许我们在UI线程中去访问网络,所以使用异步的 AsyncTask就是板上钉钉的事了。传好参数,数据成功通过回调函数获得。

解决了获取数据的问题,接下来就是如何填充数据的问题了,即ListViewListAdapter如何写。首先,在 字符串类型的填充很容易,但麻烦的是填充图片。我们需要解决图片下载,缓存,oom问题,自适应问题,乱序问题,滑动闪烁问题。问题一个一个来。

ListAdapter最重要的方法getView()重写过程中,很容易获得传过来的数据源的实体类对象。值得注意的是,在数据源的json中,图片并没有在实体类中完全解析成相应的Bitmap对象或drawable对象,当然如果项目需求需要,我们也可以实现,但是对于不知道什么时候使用实体类,让实体类去保存维护一堆bitmap对象,这个代码就不是那么优雅了。我的做法是保存的是路径或图片的名称,使用时根据其路径或名称再通过网络或本地文件或数据库解析成Bitmap对象。在加载的Adapter的布局文件中,控件的实例能找到并通过实体类进行封装,但是到ImageView这里就有点特殊了。因为填充网络图片就会遇到上面所说的问题。

首先,解决乱序问题。为ImageView.setTag(url)设置独有的Tag属性。在Adapter里维护一个当前适配的ListView引用,每次填充图片时,调用ListView.findViewWithTag(TagName)找到填充指定的ImageView.问题解决。

缓存问题。缓存问题是为了辅助oom问题引入的。这个需求采用LruCache来管理图片缓存。最大缓存设为最大可用内存的1/8

自适应问题。在这个需求中,要求图片的宽刚好能放满整个手机设备的屏幕宽,高度自定。而我们向服务器请求的图片url可能宽度无法填满或溢出,所以我们要根据屏幕的宽进行缩放。为此定义了HelpUntil.zoom(Bitmap mBitmap, int width, int height)将指定的Bitmap按指定的寛高进行缩放。问题解决。这里其实也可以把这个过程放到服务端处理,给服务端发送所需图片的寛高,让服务端为我们处理好,也可以。

滑动闪烁问题:滑动闪烁的问题是出在了ListView的滑动事件里。当ListView里滑动时,后台图片仍然在下载,导致各种跳跃,闪烁。所以在滑动时我们应该控制其ListView的滑动事件,当滑动时,停止下载,仅去下载当前可见范围的ImageView.即下载图片的调用放置在ListView的滑动事件上。所以在Adpater里暴露了cancelAllTasks()取消所有下载任务的方法与读取可见图片源的方法loadBitmaps(int firstVisibleItem, int visibleItemCount)。问题解决。

图片获取:图片获取的地方有两个:一个为本地缓存,即LruCache,另一个为网络请求了。总是先在缓存查询是否有已经下载好的图,有则显示,没有再去下载。这里的下载是典型的多线程下载。故使用了异步类BitmapWorkerTask继承AsyncTask解决单个图片下载问题。private Set taskCollection维护所有的下载任务。重要的是BitmapWorkerTask里如何下载。很好,这里就运用到了之前封装的NetHelp的方法了。在BitmapWorkerTask内部中调用复写doInBackground,封装好请求,BitmapWorkerTask实现IDate接口,将自身传给NetHelp的sendRequestWithHttpConnect(String request,int Method, IDate i)doInBackground就完成了。无论是对将数据流解析成Bitmap对象,还是将Bitmap放入缓存,缩放等等都在HandlerDate()方法里解决了。当然处理完下载图片的问题,还要在UI线程更新数据。那么就在onPostExecute(Bitmap bitmap)通过Tag找到相应的ImageView控件,将下载好的图片显示出来。在taskCollection移除任务就可以了。到这里,需求就完整实现了。

最后一个问题oom问题:避免加载大对象,不要将高清大图直接加载到内存中去,只保存图片尺寸大小,不保存图片到内存。同时记得调用Bitmap 的recycle()方法通知清理图片资源。

另外一种思路:使用Volley框架的 ImageLoader 和 NetworkImageView也能实现需求。


在项目中偶然得知的知识点

  1. xml属性设置时 背景色设为透明的值为 @null
  2. paint.setAntiAlias(true); // 设置抗锯齿,也即是边缘做平滑处理
  3. cMatrix.setScale (rScale, gScale, bScale, aScale);通过设置颜色矩阵的3原色的比例来实现颜色的变化。>>>>>这里引申出改变图像颜色还有哪些方法?
  4. 代码设置颜色值的有效性问题。
  5. 理解了构建Fragment的参数containerViewId 的具体意思。
  6. 图片平铺
  7. setCompoundDrawablesWithIntrinsicBounds的应用
  8. HttpClient 与 Android HttpURLconnect 的区别。
  9. url 传参时的中文乱码的解决
  10. 使用post传参时不能在获得状态吗之后再对request写入参数
  11. BitmapFactory.Options 的具体应用环境

你可能感兴趣的:(Andriod学习心得)