近段时间一直在加班,在赶一个项目,如今项目接近尾声,那么须要对过去一段时间工做内容进行复盘,总结下比较好的解决方案,积累一些经验,我认为的学习方式,是「理论—实践—总结—分享」,这一种很好的沉淀方式。html
在以前项目中,有个需求是这样的,要显示书的阅读足迹列表,具体要求是显示最近30天阅读状况,布局是用列表项布局,而后若是有更早的书,则显示更早的阅读状况,布局是用网格布局,如图所示:java
要是放在以前的作法,通常都是ListView,再对特殊item样式进行单独处理,后来Android在5.0的时候出了一个RecyclerView组件,简单介绍下RecyclerView,一句话:只管回收与复用View,其余的你能够本身去设置,有着高度的解耦,充分的扩展性。至于用法,你们能够去官网查看文档便可,网上也不少文章介绍如何使用,这里很少说。想讲的重点是关于装饰者模式如何在RecyclerView中应用,以下:android
定义:Decorator模式(别名Wrapper),动态将职责附加到对象上,若要扩展功能,装饰者提供了比继承更具弹性的代替方案。设计模式
也就是说动态地给一个对象添加一些额外的职责,好比你能够增长功能,相比继承来讲,有些父类的功能我是不须要的,我可能只用到某部分功能,那么我就能够自由组合,这样就显得灵活点,而不是那么冗余。性能优化
有几个要点:app
UML图:框架
咱们既然知道了装饰者和被装饰对象有相同的超类型,在作书的阅读足迹这个页面的时候,整个页面外部是一个RecyclerView,好比这样:ide
<android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swipe_layout" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_behavior="@string/appbar_scrolling_view_behavior"> <com.dracom.android.sfreader.widget.recyclerview.FeedRootRecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent" android:drawSelectorOnTop="true"/> </android.support.v4.widget.SwipeRefreshLayout>
同时每一个Item项里面又嵌套一个RecyclerView,但外部只有2个Item项,一个Item项表明最近30天要显示的书的内容,一个Item项是显示更早书的内容。其中由于涉及到RecyclerView嵌套的问题,因此须要作滑动冲突的相关处理。因此这里用到自定义扩展的RecyclerView,具体解决滑动冲突代码以下:工具
@Override public boolean onInterceptTouchEvent(MotionEvent e) { final int action = MotionEventCompat.getActionMasked(e); final int actionIndex = MotionEventCompat.getActionIndex(e); switch (action) { case MotionEvent.ACTION_DOWN: mScrollPointerId = MotionEventCompat.getPointerId(e, 0); mInitialTouchX = (int) (e.getX() + 0.5f); mInitialTouchY = (int) (e.getY() + 0.5f); return super.onInterceptTouchEvent(e); case MotionEventCompat.ACTION_POINTER_DOWN: mScrollPointerId = MotionEventCompat.getPointerId(e, actionIndex); mInitialTouchX = (int) (MotionEventCompat.getX(e, actionIndex) + 0.5f); mInitialTouchY = (int) (MotionEventCompat.getY(e, actionIndex) + 0.5f); return super.onInterceptTouchEvent(e); case MotionEvent.ACTION_MOVE: { final int index = MotionEventCompat.findPointerIndex(e, mScrollPointerId); if (index < 0) { return false; } final int x = (int) (MotionEventCompat.getX(e, index) + 0.5f); final int y = (int) (MotionEventCompat.getY(e, index) + 0.5f); if (getScrollState() != SCROLL_STATE_DRAGGING) { final int dx = x - mInitialTouchX; final int dy = y - mInitialTouchY; final boolean canScrollHorizontally = getLayoutManager().canScrollHorizontally(); final boolean canScrollVertically = getLayoutManager().canScrollVertically(); boolean startScroll = false; if (canScrollHorizontally && Math.abs(dx) > mTouchSlop && (Math.abs(dx) >= Math.abs(dy) || canScrollVertically)) { startScroll = true; } if (canScrollVertically && Math.abs(dy) > mTouchSlop && (Math.abs(dy) >= Math.abs(dx) || canScrollHorizontally)) { startScroll = true; } return startScroll && super.onInterceptTouchEvent(e); } return super.onInterceptTouchEvent(e); } default: return super.onInterceptTouchEvent(e); } }
按照思路就是内部拦截法,也就是RecyclerView本身处理,默认是不拦截,若是滑动距离超过所规定距离,咱们就拦截本身处理,设置是可滚动的状态。oop
解决完滑动冲突以后,具体看看item项中的布局:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:paddingBottom="4dp" android:paddingLeft="10dp" android:paddingRight="10dp" android:paddingTop="4dp"> <LinearLayout android:id="@+id/head_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <RelativeLayout android:layout_width="match_parent" android:layout_height="24dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" android:textColor="@color/black_alpha" android:textSize="16sp" android:text="@string/zq_account_read_footprint_recent_month"/> </RelativeLayout> <View android:layout_width="match_parent" android:layout_height="0.5dp" android:layout_marginTop="8dp" android:background="@android:drawable/divider_horizontal_bright"/> </LinearLayout> <com.dracom.android.sfreader.widget.recyclerview.BetterRecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="12dp" android:layout_marginTop="8dp" android:orientation="vertical" android:scrollbars="none"/> </LinearLayout>
能够看到,每一个Item项目一个头部,一个RecyclerView。而后是Adapter的适配,这里就是经常使用的RecyclerView Adapter的方式,要继承RecyclerView.Adapter<RecyclerView.ViewHolder>方法,同时要实现onCreateViewHolder、onBindViewHolder、getItemCount、getItemViewType方法,具体代码以下:
public class ReadFootPrintAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private final static int RECENT_MONTH = 1002; //最近三十天的Item private final static int MORE_EARLY = 1003; //更早的Item private Context mContext; private List<ReadBookColumnInfo> mColumns; private RecentReadAdapter mRecentReadAdapter; private MoreEarlyAdapter mMoreEarlyAdapter; public ReadFootPrintAdapter(Context context){ this.mContext = context; mColumns = new ArrayList<ReadBookColumnInfo>(); mRecentReadAdapter = new RecentReadAdapter(context); mMoreEarlyAdapter = new MoreEarlyAdapter(context); } public void setLoadEnable(boolean loadEnable) { mIsLoadEnable = loadEnable; } public void setColumns(List<ReadBookColumnInfo> columns) { this.mColumns = columns; notifyDataSetChanged(); } public void setRecentReadListener(OnOpenBookListener onOpenBookListener){ mRecentReadAdapter.setOnListener(onOpenBookListener); } public void setMoreEarlyListener(OnOpenBookListener onOpenBookListener){ mMoreEarlyAdapter.setOnListener(onOpenBookListener); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if(viewType == RECENT_MONTH){ View view = LayoutInflater.from(mContext).inflate(R.layout.recycler_read_footprint_recent_month,parent,false); return new ColumnViewHolder1(view); } if(viewType == MORE_EARLY){ View view = LayoutInflater.from(mContext).inflate(R.layout.recycler_read_footprint_more_early,parent,false); return new ColumnViewHolder2(view); } else{ return null; } } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if(holder instanceof ColumnViewHolder){ ColumnViewHolder columnViewHolder = (ColumnViewHolder) holder; ReadBookColumnInfo readBookColumnInfo = mColumns.get(position); if(readBookColumnInfo.getReadBookInfos().size() > 0){ columnViewHolder.headLayout.setVisibility(View.VISIBLE); } else{ columnViewHolder.headLayout.setVisibility(View.GONE); } columnViewHolder.loadData(readBookColumnInfo); } } @Override public int getItemCount() { return mColumns.size(); } @Override public int getItemViewType(int position) { if (position == 0 ) return RECENT_MONTH; else return MORE_EARLY; } private abstract class ColumnViewHolder extends RecyclerView.ViewHolder { View headLayout; RecyclerView recyclerView; public ColumnViewHolder(View itemView) { super(itemView); headLayout = itemView.findViewById(R.id.head_layout); recyclerView = (RecyclerView) itemView.findViewById(R.id.recycler_view); } abstract void loadData(ReadBookColumnInfo readBookColumnInfo); } private class ColumnViewHolder1 extends ColumnViewHolder { public ColumnViewHolder1(View itemView) { super(itemView); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(mContext); linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL); recyclerView.setLayoutManager(linearLayoutManager); recyclerView.setAdapter(mRecentReadAdapter); } @Override void loadData(ReadBookColumnInfo readBookColumnInfo) { mRecentReadAdapter.setData(readBookColumnInfo.getReadBookInfos()); } } private class ColumnViewHolder2 extends ColumnViewHolder { public ColumnViewHolder2(View itemView) { super(itemView); GridLayoutManager gridLayoutManager = new GridLayoutManager(mContext,3); gridLayoutManager.setOrientation(GridLayoutManager.VERTICAL); recyclerView.setLayoutManager(gridLayoutManager); recyclerView.setAdapter(mMoreEarlyAdapter); } @Override void loadData(ReadBookColumnInfo readBookColumnInfo) { mMoreEarlyAdapter.setData(readBookColumnInfo.getReadBookInfos()); } } }
原本到这里,基本功能是完成了,可后来产品说要加个上下刷新,加载更多的操做。需求是随时可变的, 咱们能不变的就是修改的心,那应该怎么作合适呢,是再增长itemType类型,加个加载更多的item项,那样修改的点会更多,此时想到了装饰者模式,是否是能够有个装饰类对这个adapter类进行组合呢,这样不须要修改原来的代码,只要扩展出去,何况咱们知道都须要继承RecyclerView.Adapter,那么就能够把ReadFootPrintAdapter当作一个内部成员设置进入。咱们来看下装饰者类:
public class ReadFootPrintAdapterWrapper extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private final static int LOADMORE = 1001; private final static int NORMAL = 1002; private RecyclerView.Adapter internalAdapter; private View mFooterView; public ReadFootPrintAdapterWrapper(RecyclerView.Adapter adapter){ this.internalAdapter = adapter; this.mFooterView = null; } public void addFooterView(View footView) { mFooterView = footView; } public void notifyDataChanged() { internalAdapter.notifyDataSetChanged(); notifyDataSetChanged(); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { if(viewType == LOADMORE){ return new LoadMoreViewHolder(mFooterView); } return internalAdapter.createViewHolder(parent,viewType); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if(holder instanceof LoadMoreViewHolder){ return; }else{ internalAdapter.onBindViewHolder(holder,position); } } @Override public int getItemCount() { int count = internalAdapter.getItemCount(); if (mFooterView != null && count != 0) count++; return count; } @Override public int getItemViewType(int position) { if(mFooterView != null && getItemCount() - 1 == position) return LOADMORE; return NORMAL; } public class LoadMoreViewHolder extends RecyclerView.ViewHolder { public LoadMoreViewHolder(View itemView) { super(itemView); } } }
这个Wrapper类就是装饰类,里面包含了一个RecyclerView.Adapter类型的成员,一个底部View,到时候在外部调用的时候,只须要传递一个RecyclerView.Adapter类型的参数进去便可,这样就造成了组合的关系。具体使用以下:
mFooterView = LayoutInflater.from(mContext).inflate(R.layout.refresh_loadmore_layout, mRecyclerView, false); mReadFootPrintAdapterWrapper = new ReadFootPrintAdapterWrapper(mReadFootPrintAdapter); mReadFootPrintAdapterWrapper.addFooterView(mFooterView); mRecyclerView.setAdapter(mReadFootPrintAdapterWrapper);
这样即达到需求要求,又能对原来已有的代码不进行修改,只进行扩展,何乐而不为。
这虽然是工做中一个应用点,但我想在开发过程当中还有不少应用点,用上设计模式。平常开发中基本都强调设计模式的重要性,或许你对23种设计模式都很熟悉,都了解到它们各自的定义,但是等真正应用了,却发现没有踪影可寻,写代码也是按照之前老的思路去作,那样就变成了知道是知道,却不会用的尴尬局面。如何突破呢,我以为过后复盘和重构颇有必要,就是利用项目尾声阶段,空的时候去review下本身写过的代码,反思是否有更简洁的写法,还有能够参考优秀代码,它们是怎么写,这样给本身找找灵感,再去结合本身已有的知识存储,说不定就能走上理论和实践相结合道路上。
源于对掌握的Android开发基础点进行整理,罗列下已经总结的文章,从中能够看到技术积累的过程。
1,Android系统简介
2,ProGuard代码混淆
3,讲讲Handler+Looper+MessageQueue关系
4,Android图片加载库理解
5,谈谈Android运行时权限理解
6,EventBus初理解
7,Android 常见工具类
8,对于Fragment的一些理解
9,Android 四大组件之 " Activity "
10,Android 四大组件之" Service "
11,Android 四大组件之“ BroadcastReceiver "
12,Android 四大组件之" ContentProvider "
13,讲讲 Android 事件拦截机制
14,Android 动画的理解
15,Android 生命周期和启动模式
16,Android IPC 机制
17,View 的事件体系
18,View 的工做原理
19,理解 Window 和 WindowManager
20,Activity 启动过程分析
21,Service 启动过程分析
22,Android 性能优化
23,Android 消息机制
24,Android Bitmap相关
25,Android 线程和线程池
26,Android 中的 Drawable 和动画
27,RecylerView 中的装饰者模式
28,Android 触摸事件机制
29,Android 事件机制应用
30,Cordova 框架的一些理解
31,有关 Android 插件化思考
32,开发人员必备技能——单元测试