vlayout学习笔记
vlayout(VirtualLayout)是阿里开源的一个针对 RecyclerView 的 LayoutManager 扩展,主要提供一整套布局方案和布局间的组件复用。下面主要简述一下个人读官方文档和实践demo的心得,demo的GitHub地址放在文末,欢迎各位大佬指教~1.初始化RecycleView,基于上下文,初始化一个VirtualLayoutManager;recyclerView.setLayoutManager(layoutManager);将这个VirtualLayoutManager设置进去recyclerView;2.利用第一步的VirtualLayoutManager创建一个DelegateAdapter ;DelegateAdapter delegateAdapter = new DelegateAdapter(layoutManager, hasConsistItemType);3.1.准备一个Adapter列表:List<Adapter> adapterList = new ArrayList<>();添加(add)各种Adapter,add的参数即自定义adapter,这里的自定义adapter是SubAdapter;(代码来自官网demo) adapterList.add(new SubAdapter(new LinearLayoutHelper(20), 20));
adapterList.add(new SubAdapter(new StickyLayoutHelper(true), 1));
adapterList.add(new SubAdapter(new LinearLayoutHelper(20), 20));
adapterList.add(new SubAdapter(new GridLayoutHelper(4), 80));
// adapterList.add(new SubAdapter(new FixLayoutHelper(0, 0), 1));
adapterList.add(new SubAdapter(new FixLayoutHelper(TOP_RIGHT, 0, 0), 1));3.2.自定义adapter的设计:**注意,自定义adapter继承自DelegateAdapter.Adapeter,泛型可以是自定义的ViewHolder;构造函数一般接收两个参数,即LayoutHelper以及一个数据参数(用于adapter的数据支持);** private static class SubAdapter extends DelegateAdapter.Adapter<SubViewHolder> {
private LayoutHelper mLayoutHelper;
private int mItemCount;
private SubAdapter(LayoutHelper layoutHelper, int itemCount) {
mLayoutHelper = layoutHelper;
mItemCount = itemCount;
@Override
public LayoutHelper onCreateLayoutHelper() {
return mLayoutHelper;
@Override
public SubViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
return new SubViewHolder(inflater.inflate(R.layout.item, parent, false));
@Override
public void onBindViewHolder(SubViewHolder holder, int position) {
// do nothing
@Override
protected void onBindViewHolderWithOffset(SubViewHolder holder, int position, int offsetTotal) {
super.onBindViewHolderWithOffset(holder, position, offsetTotal);
holder.setText(String.valueOf(offsetTotal));
holder.itemView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
@Override
public int getItemCount() {
return mItemCount;
}对应的ViewHolder设计: private static class SubViewHolder extends RecyclerView.ViewHolder {
public static volatile int existing = 0;
public static int createdTimes = 0;
public SubViewHolder(View itemView) {
super(itemView);
createdTimes++;
existing++;
public void setText(String title) {
((TextView) itemView.findViewById(R.id.title)).setText(title);
@Override
protected void finalize() throws Throwable {
existing--;
super.finalize();
}**以上,通过给,继承自DelegateAdapter.Adapter的自定义Adapter,设置不同的LayoutHelper,实现了各种各样的布局adapter,然后我们把这些各种各样的布局adapter,加入到一个List<Adapter> adapterList中去,接下来我们把这个adapterList,设置到delegateAdapter中去:**delegateAdapter.addAdapters(adapterList);// 不加会 android.content.res.Resources$NotFoundException: Resource ID!!!! delegateAdapter.addAdapters(adapterList);**这里便是VLayout的美妙之处,我们可以通过继承DelegateAdapter.Adapter,创建一个自定义的Adapter,顺带配备对应的ViewHolder即业务容器;给这个自定义Adapter的onCreateLayoutHelper()传入各种各样的不同类型的不同构造形参的LayoutHelper,配合传给自定义Adapter的数据支持参数位,又可以实现各种各样的布局效果;而这些个各种各样的不同的布局(adapterList),又可以加到同一个delegateAdapter中,借助delegateAdapter、VirtualLayoutManager,将各种各样的这些个布局,整到同一个RecycleView中!**4.最后画龙点睛:recycler.setAdapter(delegateAdapter);**adapter的设计跟RecycleView的adapter设计差不多,都是onCreateViewHolder/ViewHolder/onBindViewHolder/getItemCount四部曲罢了:DelegateAdapter.Adapter无非就是多了个决定布局类型的onCreateLayoutHelper() 方法罢了;****注意getItemCount可以用来控制显示的数量,可以是数据List的Size,也可以通过设定Adapter构造方法,在被调用时通过new过程获取;其他的就在自定义的Adapter中、在自定义ViewHolder中添加业务逻辑/业务方法函数即可;比如可以在onBindViewHolder() 的return之前加套if逻辑组,通过给不同的LayoutHelper设定不同的item颜色等等;****LayoutHelper实例可以通过Java代码在LayoutHelper层进行UI设计约束;item层可以通过在adapter.java中用java代码或者在xml中进行UI设计约束;其中xml记得巧用drawable文件设计:**实战demo的GitHub地址
天猫团队开源跨平台模块化 UI 界面开发框架 Tangram
Tangram,七巧板,是天猫团队刚刚开源的跨平台模块化 UI 界面方案。据悉,之所以命名为 Tangram ,是希望它能像七巧板一样可以通过几块积木就搭出丰富多彩的界面。
什么是 Tangram
Tangram 不仅仅是一个 Native(iOS & Android)的界面开发框架,而是从日常工作中沉淀出的一套界面解决方案,涵盖了 Native SDK、GUI操作台、后端逻辑容器、组件库机制的一整套方案。
Tangram 从手机天猫 - 首页方案抽象而来,是面向组件的界面方案,是开发团队不断权衡性能、稳定性、开发效率、灵活性和动态性多方面表现的结果。除了手机天猫首页外,还支撑了天猫 App 中的天猫直播、我的天猫、猜你喜欢等多个业务,并且在阿里星球等多个阿里系 App 中有所应用。
Tangram 关注的重点
Tangram 关注三个重点:面向业务、多端一致性和高性能。
1、面向业务
Tangram 来源于多次试错和方向的调整,最终站在业务角度出发,权衡多项技术指标的结果。所以面向业务是出发点,是整个 Tangram 体系的最基本原则。
基于这个原则,在端上 Tangram 始终坚持粗粒度组件。粗粒度意味着通用性和灵活性的下降,某种程度上还会对动态性造成影响,但在第2型业务中通用性、灵活性和动态性的需求是有节制的,在粗粒度上完全可以满足业务需求。而且,粗粒度还具有使用成本低,性能更好等优势。在端上重点精力则投入到提升组件库复用度,布局容器和组件的丰富性,从而推动业务发展。
除了端上的工作,另一部分重点工作在控制台和服务网关的建设上。作为一个面向业务的方案,控制台是业务方和产品的接口,控制台的主要目标是让业务方可以直接控制基于 Tangram 建设的产品——调整页面布局,切换页面数据,等。服务网关的建设目标是最大程度的降低业务创建 Tangram 页面的压力和成本。
2、多端一致性
在多年的业务开发经历中,屡次被多端表现不一致的问题困扰。为了实现业务诉求,不得不通过复杂的网关逻辑来兼容多端逻辑不一致情况,以实现表现一致。因此团队制定了两个 Tangram 端开发原则:
任意新功能的提出都是不区分平台,在功能设计中必须同时考虑多端功能,具体的实现方案和逻辑必须多端统一 Review 以保证多端表现一致。
任意一端的变更都必须在改动前把方案同步给其他端,而且变更必须多端同步发布。
3、高性能
在面向业务的原则之下,已经给高性能打下了一个良好的基础。而在高性能的思考上重点基于页面渲染效率和组件回收复用两方面。
页面渲染——为了提升渲染效率,Tangram 将在视图渲染之前把大量的计算工作在 VM 中完成,并缓存在 VM 组成的树形结构里。
回收和复用——Tangram 在 Android 和 iOS 平台上分别开发了 VLayout 和 LazyScroll 两个基础组件,通过一个双索引可见区域组件发现算法,实现了跨父节点组件的高效回收和复用。
此外,开发团队还表示,目前已经完成了新版 Tangram 2.0 的讨论,开始执行2.0版本的重构工作。在 Tangram 2.0 中出于适应业务形态的变化,将对开源的 Tangram 1.0 中基于布局和组件的二维结构进行进一步的抽象,用于支撑更复杂的流式布局。并且对于控制台和服务网关也将进一步升级,大幅提升新业务开发效率。在性能层面,对组件开发模型和渲染模式进行一次较大的升级,在渲染和滚动效率上将得到巨大提升。
本文来自开源中国社区 [http://www.oschina.net]
QVBoxLayout *buttonLayout = new QVBoxLayout;
buttonLayout->addStretch(1);
buttonLayout->addWidget(Button1);
buttonLayout->addStretch(1);
buttonLayout->addWidget(Button2);
buttonLayout->addStretch(1);
buttonLayout->addWidget(Button3);
buttonLayout->addStretch(6);
以上是buttonLayout的布局,addStretch将空白没有widget的地方均分成9分,然后按照参数的大小分配弹簧。
所以,带的参数是说明均分的比例。
vlayout->addStretch(2); //添加弹簧,均分布局
vlayout->addWidget(label); //布局左侧风格栏
vlayout->addWidget(styleComboBox);
vlayout->addStretch(1);
这样的效果是:
如果,vlayout->addStretch(2); 的参数改为1,效果是这样的:
这样就一目了然了,一句话:按比例分配空余空间~~
同类型的函数还有 setStretchFactor()
layout->setStretchFactor(vlayout,1);
layout->setStretchFactor(edit,2);
layout->setStretchFactor(dragWidget,6);
这样就会按照 QBoxLayout 的类型(5种类型),按照比例参数,分配长宽比
a.首先看.notifyDataSetChanged()源码
b.接着查看.notifyChanged()源码
c.接着查看setAdapter()源码中的setAdapterInternal(adapter, false, true)方法
d.notify……方法被调用,刷新数据
a.首先看addItemDecoration源码
b.接着看下markItemDecorInsetsDirty这个方法
c.接着看下mRecycler.markItemDecorInsetsDirty();这个方法
d.回过头在看看addItemDecoration中requestLayout方法
e.在 RecyclerView 中搜索 mItemDecorations 集合
8.1 如何判断RecyclerView控件滑动到顶部和底部
8.2 RecyclerView嵌套RecyclerView 条目自动上滚的Bug
8.3 ScrollView嵌套RecyclerView滑动冲突
8.4 ViewPager嵌套水平RecyclerView横向滑动到底后不滑动ViewPager
9.RecyclerView复杂布局封装库案例
9.1 能够实现业务的需求和功能
9.2 具备的优势分析
10.针对阿里VLayout代码分析
11.版本更新说明
v1.0.0 2016年5月5日
v1.1.0 更新于2017年2月1日
v1.1.1 更新于2017年6月9日
v2.0.0 更新于2018年8月21日
v2.1.0 更新于2018年9月29日
所有的学习笔记,开源项目,还有博客均已经在GitHub开源,大多数都是markdown格式的。链接地址:https://github.com/yangchong211/YCBlogs
关于案例已经开源,开源地址:https://github.com/yangchong211
1.RecycleView的结构
关于RecyclerView,大家都已经很熟悉了,用途十分广泛,大概结构如下所示
RecyclerView.Adapter - 处理数据集合并负责绑定视图
ViewHolder - 持有所有的用于绑定数据或者需要操作的View
LayoutManager - 负责摆放视图等相关操作
ItemDecoration - 负责绘制Item附近的分割线
ItemAnimator - 为Item的一般操作添加动画效果,如,增删条目等
如图所示,直观展示结构
针对上面几个属性,最简单用法如下所示
recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
//设置layoutManager
recyclerView.setLayoutManager(layoutManager);
final RecycleViewItemLine line = new RecycleViewItemLine(this, LinearLayout.HORIZONTAL,1,this.getResources().getColor(R.color.colorAccent));
//设置添加分割线
recyclerView.addItemDecoration(line);
adapter = new MultipleItemAdapter(this);
//设置adapter
recyclerView.setAdapter(adapter);
//添加数据并且刷新adapter
adapter.addAll(list);
adapter.notifyDataSetChanged();
//adapter
//onCreateViewHolder(ViewGroup parent, int viewType)这里的第二个参数就是View的类型,可以根据这个类型判断去创建不同item的ViewHolder
public class MultipleItemAdapter extends RecyclerView.Adapter<recyclerview.viewholder> {
public static enum ITEM_TYPE {
ITEM_TYPE_IMAGE,
ITEM_TYPE_TEXT
private final LayoutInflater mLayoutInflater;
private final Context mContext;
private ArrayList<String> mTitles;
public MultipleItemAdapter(Context context) {
mContext = context;
mLayoutInflater = LayoutInflater.from(context);
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == ITEM_TYPE.ITEM_TYPE_IMAGE.ordinal()) {
return new ImageViewHolder(mLayoutInflater.inflate(R.layout.item_image, parent, false));
} else {
return new TextViewHolder(mLayoutInflater.inflate(R.layout.item_text, parent, false));
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (holder instanceof TextViewHolder) {
((TextViewHolder) holder).mTextView.setText(mTitles[position]);
} else if (holder instanceof ImageViewHolder) {
((ImageViewHolder) holder).mTextView.setText(mTitles[position]);
@Override
public int getItemViewType(int position) {
return position % 2 == 0 ? ITEM_TYPE.ITEM_TYPE_IMAGE.ordinal() : ITEM_TYPE.ITEM_TYPE_TEXT.ordinal();
@Override
public int getItemCount() {
return mTitles == null ? 0 : mTitles.length;
public void addAll(ArrayList<String> list){
if(mTitles!=null){
mTitles.clear();
}else {
mTitles = new ArrayList<>();
mTitles.addAll(list);
public static class TextViewHolder extends RecyclerView.ViewHolder {
@InjectView(R.id.text_view)
TextView mTextView;
TextViewHolder(View view) {
super(view);
ButterKnife.inject(this, view);
public static class ImageViewHolder extends RecyclerView.ViewHolder {
@InjectView(R.id.text_view)
TextView mTextView;
@InjectView(R.id.image_view)
ImageView mImageView;
ImageViewHolder(View view) {
super(view);
ButterKnife.inject(this, view);
2.Adapter
2.1 RecyclerView.Adapter扮演的角色
一是,根据不同ViewType创建与之相应的的Item-Layout
二是,访问数据集合并将数据绑定到正确的View上
2.2 重写的方法
一般常用的重写方法有以下这么几个:
public VH onCreateViewHolder(ViewGroup parent, int viewType)
创建Item视图,并返回相应的ViewHolder
public void onBindViewHolder(VH holder, int position)
绑定数据到正确的Item视图上。
public int getItemCount()
返回该Adapter所持有的Itme数量
public int getItemViewType(int position)
用来获取当前项Item(position参数)是哪种类型的布局
2.3 notifyDataSetChanged()刷新数据
当时据集合发生改变时,我们通过调用.notifyDataSetChanged(),来刷新列表,因为这样做会触发列表的重绘,所以并不会出现任何动画效果,因此需要调用一些以notifyItem*()作为前缀的特殊方法,比如:
public final void notifyItemInserted(int position) 向指定位置插入Item
public final void notifyItemRemoved(int position) 移除指定位置Item
public final void notifyItemChanged(int position) 更新指定位置Item
2.4 数据变更通知之观察者模式
a.首先看.notifyDataSetChanged()源码
/** @see #notifyItemChanged(int)
* @see #notifyItemInserted(int)
* @see #notifyItemRemoved(int)
* @see #notifyItemRangeChanged(int, int)
* @see #notifyItemRangeInserted(int, int)
* @see #notifyItemRangeRemoved(int, int)
public final void notifyDataSetChanged() {
mObservable.notifyChanged();
b.接着查看.notifyChanged();源码
被观察者AdapterDataObservable,内部持有观察者AdapterDataObserver集合
static class AdapterDataObservable extends Observable<AdapterDataObserver> {
public boolean hasObservers() {
return !mObservers.isEmpty();
public void notifyChanged() {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onChanged();
public void notifyItemRangeChanged(int positionStart, int itemCount) {
notifyItemRangeChanged(positionStart, itemCount, null);
public void notifyItemRangeChanged(int positionStart, int itemCount, Object payload) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onItemRangeChanged(positionStart, itemCount, payload);
public void notifyItemRangeInserted(int positionStart, int itemCount) {
for (int i = mObservers.size() - 1; i >= 0; i--) {
mObservers.get(i).onItemRangeInserted(positionStart, itemCount);
观察者AdapterDataObserver,具体实现为RecyclerViewDataObserver,当数据源发生变更时,及时响应界面变化
public static abstract class AdapterDataObserver {
public void onChanged() {
// Do nothing
public void onItemRangeChanged(int positionStart, int itemCount) {
// do nothing
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
onItemRangeChanged(positionStart, itemCount);
c.接着查看setAdapter()源码中的setAdapterInternal(adapter, false, true)方法
setAdapter源码
public void setAdapter(Adapter adapter) {
// bail out if layout is frozen
setLayoutFrozen(false);
setAdapterInternal(adapter, false, true);
requestLayout();
setAdapterInternal(adapter, false, true)源码
private void setAdapterInternal(Adapter adapter, boolean compatibleWithPrevious,
boolean removeAndRecycleViews) {
if (mAdapter != null) {
mAdapter.unregisterAdapterDataObserver(mObserver);
mAdapter.onDetachedFromRecyclerView(this);
if (!compatibleWithPrevious || removeAndRecycleViews) {
removeAndRecycleViews();
mAdapterHelper.reset();
final Adapter oldAdapter = mAdapter;
mAdapter = adapter;
if (adapter != null) {
//注册一个观察者RecyclerViewDataObserver
adapter.registerAdapterDataObserver(mObserver);
adapter.onAttachedToRecyclerView(this);
if (mLayout != null) {
mLayout.onAdapterChanged(oldAdapter, mAdapter);
mRecycler.onAdapterChanged(oldAdapter, mAdapter, compatibleWithPrevious);
mState.mStructureChanged = true;
markKnownViewsInvalid();
d.notify……方法被调用,刷新数据
当数据变更时,调用notify**方法时,Adapter内部的被观察者会遍历通知已经注册的观察者的对应方法,这时界面就会响应变更。
3.ViewHolder
3.1 ViewHolder的作用
ViewHolder作用大概有这些:
adapter应当拥有ViewHolder的子类,并且ViewHolder内部应当存储一些子view,避免时间代价很大的findViewById操作
其RecyclerView内部定义的ViewHolder类包含很多复杂的属性,内部使用场景也有很多,而我们经常使用的也就是onCreateViewHolder()方法和onBindViewHolder()方法,onCreateViewHolder()方法在RecyclerView需要一个新类型。item的ViewHolder时调用来创建一个ViewHolder,而onBindViewHolder()方法则当RecyclerView需要在特定位置的item展示数据时调用。
3.2 ViewHolder与复用
在复写RecyclerView.Adapter的时候,需要我们复写两个方法:
onCreateViewHolder
onBindViewHolder
这两个方法从字面上看就是创建ViewHolder和绑定ViewHolder的意思
复用机制是怎样的?
模拟场景:只有一种ViewType,上下滑动的时候需要的ViewHolder种类是只有一种,但是需要的ViewHolder对象数量并不止一个。所以在后面创建了5个ViewHolder之后,需要的数量够了,无论怎么滑动,都只需要复用以前创建的对象就行了。那么逗比程序员们思考一下,为什么会出现这种情况呢
看到了下面log之后,第一反应是在这个ViewHolder对象的数量“够用”之后就停止调用onCreateViewHolder方法,但是onBindViewHolder方法每次都会调用的
查看一下createViewHolder源代码
发现这里并没有限制
public final VH createViewHolder(ViewGroup parent, int viewType) {
TraceCompat.beginSection(TRACE_CREATE_VIEW_TAG);
final VH holder = onCreateViewHolder(parent, viewType);
holder.mItemViewType = viewType;
TraceCompat.endSection();
return holder;
对于ViewHolder对象的数量“够用”之后就停止调用onCreateViewHolder方法,可以查看
获取为给定位置初始化的视图。
此方法应由{@link LayoutManager}实现使用,以获取视图来表示来自{@LinkAdapter}的数据。
如果共享池可用于正确的视图类型,则回收程序可以重用共享池中的废视图或分离视图。如果适配器没有指示给定位置上的数据已更改,则回收程序将尝试发回一个以前为该数据初始化的报废视图,而不进行重新绑定。
public View getViewForPosition(int position) {
return getViewForPosition(position, false);
View getViewForPosition(int position, boolean dryRun) {
return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView;
@Nullable
ViewHolder tryGetViewHolderForPositionByDeadline(int position,boolean dryRun, long deadlineNs) {
//代码省略了,有需要的小伙伴可以自己看看,这里面逻辑实在太复杂呢
3.3 ViewHolder简单封装
关于ViewHolder简单的封装代码如下所示:
public abstract class BaseMViewHolder<M> extends RecyclerView.ViewHolder {
// SparseArray 比 HashMap 更省内存,在某些条件下性能更好,只能存储 key 为 int 类型的数据,
// 用来存放 View 以减少 findViewById 的次数
private SparseArray<View> viewSparseArray;
BaseMViewHolder(View itemView) {
super(itemView);
if(viewSparseArray==null){
viewSparseArray = new SparseArray<>();
public BaseMViewHolder(ViewGroup parent, @LayoutRes int res) {
super(LayoutInflater.from(parent.getContext()).inflate(res, parent, false));
if(viewSparseArray==null){
viewSparseArray = new SparseArray<>();
* 子类设置数据方法
* @param data
public void setData(M data) {}
* 第二种findViewById方式
* 根据 ID 来获取 View
* @param viewId viewID
* @param <T> 泛型
* @return 将结果强转为 View 或 View 的子类型
@SuppressWarnings("unchecked")
protected <T extends View> T getView(int viewId) {
// 先从缓存中找,找打的话则直接返回
// 如果找不到则 findViewById ,再把结果存入缓存中
View view = viewSparseArray.get(viewId);
if (view == null) {
view = itemView.findViewById(viewId);
viewSparseArray.put(viewId, view);
return (T) view;
* 获取上下文context
* @return context
protected Context getContext(){
return itemView.getContext();
* 获取数据索引的位置
* @return position
protected int getDataPosition(){
RecyclerView.Adapter adapter = getOwnerAdapter();
if (adapter!=null && adapter instanceof RecyclerArrayAdapter){
return getAdapterPosition() - ((RecyclerArrayAdapter) adapter).getHeaderCount();
return getAdapterPosition();
* 获取adapter对象
* @param <T>
* @return adapter
@Nullable
private <T extends RecyclerView.Adapter> T getOwnerAdapter(){
RecyclerView recyclerView = getOwnerRecyclerView();
//noinspection unchecked
return recyclerView != null ? (T) recyclerView.getAdapter() : null;
@Nullable
private RecyclerView getOwnerRecyclerView(){
try {
Field field = RecyclerView.ViewHolder.class.getDeclaredField("mOwnerRecyclerView");
field.setAccessible(true);
return (RecyclerView) field.get(this);
} catch (NoSuchFieldException ignored) {
ignored.printStackTrace();
} catch (IllegalAccessException ignored) {
ignored.printStackTrace();
return null;
* 添加子控件的点击事件
* @param viewId 控件id
protected void addOnClickListener(@IdRes final int viewId) {
final View view = getView(viewId);
if (view != null) {
if (!view.isClickable()) {
view.setClickable(true);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(getOwnerAdapter()!=null){
if (((RecyclerArrayAdapter)getOwnerAdapter()).getOnItemChildClickListener() != null) {
((RecyclerArrayAdapter)getOwnerAdapter()).getOnItemChildClickListener()
.onItemChildClick(v, getDataPosition());
//省略部分代码
//关于adapter封装可以查看我的开源adpater封装库:https://github.com/yangchong211/YCBaseAdapter
//关于recyclerView封装库,可以查看我的开源库:https://github.com/yangchong211/YCRefreshView
4.LayoutManager
4.1 作用
LayoutManager的职责是摆放Item的位置,并且负责决定何时回收和重用Item。
RecyclerView 允许自定义规则去放置子 view,这个规则的控制者就是 LayoutManager。一个 RecyclerView 如果想展示内容,就必须设置一个 LayoutManager
4.2 LayoutManager样式
LinearLayoutManager 水平或者垂直的Item视图。
GridLayoutManager 网格Item视图。
StaggeredGridLayoutManager 交错的网格Item视图。
4.3 LayoutManager当前有且仅有一个抽象函数
具体如下:
public LayoutParams generateDefaultLayoutParams()
4.4 setLayoutManager(LayoutManager layout)源码
a.setLayoutManager入口源码
分析:当之前设置过 LayoutManager 时,移除之前的视图,并缓存视图在 Recycler 中,将新的 mLayout 对象与 RecyclerView 绑定,更新缓存 View 的数量。最后去调用 requestLayout ,重新请求 measure、layout、draw。
public void setLayoutManager(LayoutManager layout) {
if (layout == mLayout) {
return;
// 停止滑动
stopScroll();
if (mLayout != null) {
// 如果有动画,则停止所有的动画
if (mItemAnimator != null) {
mItemAnimator.endAnimations();
// 移除并回收视图
mLayout.removeAndRecycleAllViews(mRecycler);
// 回收废弃视图
mLayout.removeAndRecycleScrapInt(mRecycler);
//清除mRecycler
mRecycler.clear();
if (mIsAttached) {
mLayout.dispatchDetachedFromWindow(this, mRecycler);
mLayout.setRecyclerView(null);
mLayout = null;
} else {
mRecycler.clear();
mChildHelper.removeAllViewsUnfiltered();
mLayout = layout;
if (layout != null) {
if (layout.mRecyclerView != null) {
throw new IllegalArgumentException("LayoutManager " + layout +
" is already attached to a RecyclerView: " + layout.mRecyclerView);
mLayout.setRecyclerView(this);
if (mIsAttached) {
mLayout.dispatchAttachedToWindow(this);
//更新新的缓存数据
mRecycler.updateViewCacheSize();
//重新请求 View 的测量、布局、绘制
requestLayout();
5.ItemDecoration
5.1 作用
通过设置recyclerView.addItemDecoration(new DividerDecoration(this));来改变Item之间的偏移量或者对Item进行装饰。
当然,你也可以对RecyclerView设置多个ItemDecoration,列表展示的时候会遍历所有的ItemDecoration并调用里面的绘制方法,对Item进行装饰。
5.2 RecyclerView.ItemDecoration是一个抽象类
该抽象类常见的方法如下所示:
public void onDraw(Canvas c, RecyclerView parent)
装饰的绘制在Item条目绘制之前调用,所以这有可能被Item的内容所遮挡
public void onDrawOver(Canvas c, RecyclerView parent)
装饰的绘制在Item条目绘制之后调用,因此装饰将浮于Item之上
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent)
与padding或margin类似,LayoutManager在测量阶段会调用该方法,计算出每一个Item的正确尺寸并设置偏移量。
5.3 .addItemDecoration()源码分析
a.通过下面代码可知,mItemDecorations是一个ArrayList,我们将ItemDecoration也就是分割线对象,添加到其中。
可以看到,当通过这个方法添加分割线后,会指定添加分割线在集合中的索引,然后再重新请求 View 的测量、布局、(绘制)。注意: requestLayout会调用onMeasure和onLayout,不一定调用onDraw!
关于View自定义控件源码分析,可以参考我的其他博客:https://github.com/yangchong211/YCBlogs
public void addItemDecoration(ItemDecoration decor) {
addItemDecoration(decor, -1);
//主要看这个方法,我的GitHub:https://github.com/yangchong211/YCBlogs
public void addItemDecoration(ItemDecoration decor, int index) {
if (mLayout != null) {
mLayout.assertNotInLayoutOrScroll("Cannot add item decoration during a scroll or"
+ " layout");
if (mItemDecorations.isEmpty()) {
setWillNotDraw(false);
if (index < 0) {
mItemDecorations.add(decor);
} else {
// 指定添加分割线在集合中的索引
mItemDecorations.add(index, decor);
markItemDecorInsetsDirty();
// 重新请求 View 的测量、布局、绘制
requestLayout();
b.接着看下markItemDecorInsetsDirty这个方法做了些什么
这个方法先获取所有子View的数量,然后遍历了 RecyclerView 和 LayoutManager 的所有子 View,再将其子 View 的 LayoutParams 中的 mInsetsDirty 属性置为 true,最后调用了 mRecycler.markItemDecorInsetsDirty()方法处理复用逻辑。
void markItemDecorInsetsDirty() {
final int childCount = mChildHelper.getUnfilteredChildCount();
//先遍历了 RecyclerView 和 LayoutManager 的所有子 View
for (int i = 0; i < childCount; i++) {
final View child = mChildHelper.getUnfilteredChildAt(i);
//将其子 View 的 LayoutParams 中的 mInsetsDirty 属性置为 true
((LayoutParams) child.getLayoutParams()).mInsetsDirty = true;
//调用了 mRecycler.markItemDecorInsetsDirty(),
//Recycler 是 RecyclerView 的一个内部类,就是它管理着 RecyclerView 的复用逻辑
mRecycler.markItemDecorInsetsDirty();
c.接着看下markItemDecorInsetsDirty()这个方法
该方法就是获取RecyclerView 缓存的集合,然后遍历集合得到RecyclerView 的缓存单位是 ViewHolder,获取缓存对象,在获取到layoutParams,并且将其 mInsetsDirty 字段一样置为 true
void markItemDecorInsetsDirty() {
//就是 RecyclerView 缓存的集合
final int cachedCount = mCachedViews.size();
for (int i = 0; i < cachedCount; i++) {
//RecyclerView 的缓存单位是 ViewHolder,获取缓存对象
final ViewHolder holder = mCachedViews.get(i);
//获得 LayoutParams
LayoutParams layoutParams = (LayoutParams) holder.itemView.getLayoutParams();
if (layoutParams != null) {
//将其 mInsetsDirty 字段一样置为 true
layoutParams.mInsetsDirty = true;
d.回过头在看看addItemDecoration中requestLayout方法
requestLayout 方法用一种责任链的方式,层层向上传递,最后传递到 ViewRootImpl,然后重新调用 view 的 measure、layout、draw 方法来展示布局
@CallSuper
public void requestLayout() {
if (mMeasureCache != null) mMeasureCache.clear();
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
// Only trigger request-during-layout logic if this is the view requesting it,
// not the views in its parent hierarchy
ViewRootImpl viewRoot = getViewRootImpl();
if (viewRoot != null && viewRoot.isInLayout()) {
if (!viewRoot.requestLayoutDuringLayout(this)) {
return;
mAttachInfo.mViewRequestingLayout = this;
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {
mAttachInfo.mViewRequestingLayout = null;
e.在 RecyclerView 中搜索 mItemDecorations 集合
在onDraw中
@Override
public void onDraw(Canvas c) {
super.onDraw(c);
final int count = mItemDecorations.size();
for (int i = 0; i < count; i++) {
mItemDecorations.get(i).onDraw(c, this, mState);
在draw方法中
@Override
public void draw(Canvas c) {
super.draw(c);
final int count = mItemDecorations.size();
for (int i = 0; i < count; i++) {
mItemDecorations.get(i).onDrawOver(c, this, mState);
//省略部分代码
可以看到在 View 的以上两个方法中,分别调用了 ItemDecoration 对象的 onDraw onDrawOver 方法。
这两个抽象方法,由我们继承 ItemDecoration 来自己实现,他们区别就是 onDraw 在 item view 绘制之前调用,onDrawOver 在 item view 绘制之后调用。
所以绘制顺序就是 Decoration 的 onDraw,ItemView的 onDraw,Decoration 的 onDrawOver。
RecycledViewPool
RecyclerViewPool用于多个RecyclerView之间共享View。只需要创建一个RecyclerViewPool实例,然后调用RecyclerView的setRecycledViewPool(RecycledViewPool)方法即可。RecyclerView默认会创建一个RecyclerViewPool实例。
下列源码,是我借助于有道词典翻译部分注释内容……
看出mScrap是一个的映射,mMaxScrap是一个的映射,这两个成员变量代表可复用View池的基本信息。调用setMaxRecycledViews(int viewType, int max)时,当用于复用的mScrap中viewType对应的ViewHolder个数超过maxNum时,会从列表末尾开始丢弃超过的部分。调用getRecycledView(int viewType)方法时从mScrap中移除并返回viewType对应的List的末尾项
public static class RecycledViewPool {
private static final int DEFAULT_MAX_SCRAP = 5;
static class ScrapData {
final ArrayList<ViewHolder> mScrapHeap = new ArrayList<>();
int mMaxScrap = DEFAULT_MAX_SCRAP;
long mCreateRunningAverageNs = 0;
long mBindRunningAverageNs = 0;
SparseArray<ScrapData> mScrap = new SparseArray<>();
private int mAttachCount = 0;
//丢弃所有视图
public void clear() {
for (int i = 0; i < mScrap.size(); i++) {
ScrapData data = mScrap.valueAt(i);
data.mScrapHeap.clear();
//设置丢弃前要在池中持有的视图持有人的最大数量
public void setMaxRecycledViews(int viewType, int max) {
ScrapData scrapData = getScrapDataForType(viewType);
scrapData.mMaxScrap = max;
final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
while (scrapHeap.size() > max) {
scrapHeap.remove(scrapHeap.size() - 1);
//返回给定视图类型的RecycledViewPool所持有的当前视图数
public int getRecycledViewCount(int viewType) {
return getScrapDataForType(viewType).mScrapHeap.size();
//从池中获取指定类型的ViewHolder,如果没有指定类型的ViewHolder,则获取{@Codenull}
@Nullable
public ViewHolder getRecycledView(int viewType) {
final ScrapData scrapData = mScrap.get(viewType);
if (scrapData != null && !scrapData.mScrapHeap.isEmpty()) {
final ArrayList<ViewHolder> scrapHeap = scrapData.mScrapHeap;
return scrapHeap.remove(scrapHeap.size() - 1);
return null;
//池持有的视图持有者总数
int size() {
int count = 0;
for (int i = 0; i < mScrap.size(); i++) {
ArrayList<ViewHolder> viewHolders = mScrap.valueAt(i).mScrapHeap;
if (viewHolders != null) {
count += viewHolders.size();
return count;
//向池中添加一个废视图保存器。
//如果那个ViewHolder类型的池已经满了,它将立即被丢弃。
public void putRecycledView(ViewHolder scrap) {
final int viewType = scrap.getItemViewType();
final ArrayList<ViewHolder> scrapHeap = getScrapDataForType(viewType).mScrapHeap;
if (mScrap.get(viewType).mMaxScrap <= scrapHeap.size()) {
return;
if (DEBUG && scrapHeap.contains(scrap)) {
throw new IllegalArgumentException("this scrap item already exists");
scrap.resetInternal();
scrapHeap.add(scrap);
long runningAverage(long oldAverage, long newValue) {
if (oldAverage == 0) {
return newValue;
return (oldAverage / 4 * 3) + (newValue / 4);
void factorInCreateTime(int viewType, long createTimeNs) {
ScrapData scrapData = getScrapDataForType(viewType);
scrapData.mCreateRunningAverageNs = runningAverage(
scrapData.mCreateRunningAverageNs, createTimeNs);
void factorInBindTime(int viewType, long bindTimeNs) {
ScrapData scrapData = getScrapDataForType(viewType);
scrapData.mBindRunningAverageNs = runningAverage(
scrapData.mBindRunningAverageNs, bindTimeNs);
boolean willCreateInTime(int viewType, long approxCurrentNs, long deadlineNs) {
long expectedDurationNs = getScrapDataForType(viewType).mCreateRunningAverageNs;
return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
boolean willBindInTime(int viewType, long approxCurrentNs, long deadlineNs) {
long expectedDurationNs = getScrapDataForType(viewType).mBindRunningAverageNs;
return expectedDurationNs == 0 || (approxCurrentNs + expectedDurationNs < deadlineNs);
void attach(Adapter adapter) {
mAttachCount++;
void detach() {
mAttachCount--;
//分离旧适配器并附加新适配器。如果它只附加了一个适配器,并且新适配器使用与oldAdapter不同的ViewHolder,
//则RecycledViewPool将清除其缓存。
void onAdapterChanged(Adapter oldAdapter, Adapter newAdapter,boolean compatibleWithPrevious) {
if (oldAdapter != null) {
detach();
if (!compatibleWithPrevious && mAttachCount == 0) {
clear();
if (newAdapter != null) {
attach(newAdapter);
private ScrapData getScrapDataForType(int viewType) {
ScrapData scrapData = mScrap.get(viewType);
if (scrapData == null) {
scrapData = new ScrapData();
mScrap.put(viewType, scrapData);
return scrapData;
ViewCacheExtension
ViewCacheExtension是一个由开发者控制的可以作为View缓存的帮助类。调用Recycler.getViewForPosition(int)方法获取View时,Recycler先检查attachedscrap和一级缓存,如果没有则检查ViewCacheExtension.getViewForPositionAndType(Recycler, int, int),如果没有则检查RecyclerViewPool。注意:Recycler不会在这个类中做缓存View的操作,是否缓存View完全由开发者控制。
public abstract static class ViewCacheExtension {
abstract public View getViewForPositionAndType(Recycler recycler, int position, int type);
Recycler
后续再深入分析
7.2 Recyclerview.getLayoutPosition()问题
在RecycleView中的相关方法中,有两种类型的位置
布局位置:从LayoutManager的角度看,条目在最新布局计算中的位置。
返回布局位置的方法使用最近一次布局运算后的位置,如getLayoutPosition()和findViewHolderForLayoutPosition(int)。这些位置包含了最近一次布局运算后的变化。你可以根据这些位置来与用户正在屏幕上看到的保持一致。比如,你有一个条目列表,当用户请求第5个条目时,你可以使用这些方法来匹配用户看到的。
适配器位置:从适配器的角度看,条目在是适配器中的位置。
另外一系列方法与AdapterPosition关联,比如getAdapterPosition()和findViewHolderForAdapterPosition(int)。当你想获得条目在更新后的适配器中的位置使用这些方法,即使这些位置变化还没反映到布局中。比如,你想访问适配器中条目的位置时,就应该使用getAdapterPosition()。注意,notifyDataSetChanged()已经被调用而且还没计算新布局,这些方法或许不能够计算适配器位置。所以,你要小心处理这些方法返回NO_POSITION和null的情况。
注意: 这两种类型的位置是等同的,除非在分发adapter.notify*事件和更新布局时。
关于两者的区别
网上查了一些资料,发现相关内容很少,最后在stackoverflow上终于看到有大神这样解释两者的区别
具体区别就是adapter和layout的位置会有时间差(<16ms), 如果你改变了Adapter的数据然后刷新视图, layout需要过一段时间才会更新视图, 在这段时间里面, 这两个方法返回的position会不一样。
在notifyDataSetChanged之后并不能马上获取Adapter中的position, 要等布局结束之后才能获取到
在notifyItemInserted之后,Layout不能马上获取到新的position,因为布局还没更新(需要<16ms的时间刷新视图), 所以只能获取到旧的,但是Adapter中的position就可以马上获取到最新的position。
public final int getAdapterPosition() {
if (mOwnerRecyclerView == null) {
return NO_POSITION;
return mOwnerRecyclerView.getAdapterPositionFor(this);
public final int getLayoutPosition() {
return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;
可能会导致的错误
这种情况有点难以复现,在 ViewHolder 中处理 item 的点击事件的时候,发现多个 item 同时点击就会出现闪退,debug 看到 position = -1
解决办法:使用 ViewHolder#getLayoutPosition() 获取 position,而不要通过 ViewHolder#getAdapterPosition() 来获取 position 的
8.RecyclerView嵌套方案滑动冲突解决方案
8.1 如何判断RecyclerView控件滑动到顶部和底部
有一种使用场景,购物商城的购物车页面,当RecyclerView滑动到顶部时,让刷新控件消费事件;当RecyclerView滑动到底部时,让下一页控件[猜你喜欢]消费事件。
代码如下所示:
public class VerticalRecyclerView extends RecyclerView {
private float downX;
private float downY;
/** 第一个可见的item的位置 */
private int firstVisibleItemPosition;
/** 第一个的位置 */
private int[] firstPositions;
/** 最后一个可见的item的位置 */
private int lastVisibleItemPosition;
/** 最后一个的位置 */
private int[] lastPositions;
private boolean isTop;
private boolean isBottom;
public VerticalRecyclerView(Context context) {
this(context, null);
public VerticalRecyclerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
public VerticalRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
LayoutManager layoutManager = getLayoutManager();
if (layoutManager != null) {
if (layoutManager instanceof GridLayoutManager) {
lastVisibleItemPosition = ((GridLayoutManager) layoutManager).findLastVisibleItemPosition();
firstVisibleItemPosition = ((GridLayoutManager) layoutManager).findFirstVisibleItemPosition();
} else if (layoutManager instanceof LinearLayoutManager) {
lastVisibleItemPosition = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
firstVisibleItemPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
if (lastPositions == null) {
lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
firstPositions = new int[staggeredGridLayoutManager.getSpanCount()];
staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions);
staggeredGridLayoutManager.findFirstVisibleItemPositions(firstPositions);
lastVisibleItemPosition = findMax(lastPositions);
firstVisibleItemPosition = findMin(firstPositions);
} else {
throw new RuntimeException("Unsupported LayoutManager used. Valid ones are LinearLayoutManager, GridLayoutManager and StaggeredGridLayoutManager");
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = ev.getX();
downY = ev.getY();
//如果滑动到了最底部,就允许继续向上滑动加载下一页,否者不允许
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
float dx = ev.getX() - downX;
float dy = ev.getY() - downY;
boolean allowParentTouchEvent;
if (Math.abs(dy) > Math.abs(dx)) {
if (dy > 0) {
//位于顶部时下拉,让父View消费事件
allowParentTouchEvent = isTop = firstVisibleItemPosition == 0 && getChildAt(0).getTop() >= 0;
} else {
//位于底部时上拉,让父View消费事件
int visibleItemCount = layoutManager.getChildCount();
int totalItemCount = layoutManager.getItemCount();
allowParentTouchEvent = isBottom = visibleItemCount > 0 && (lastVisibleItemPosition) >= totalItemCount - 1 && getChildAt(getChildCount() - 1).getBottom() <= getHeight();
} else {
//水平方向滑动
allowParentTouchEvent = true;
getParent().requestDisallowInterceptTouchEvent(!allowParentTouchEvent);
return super.dispatchTouchEvent(ev);
private int findMax(int[] lastPositions) {
int max = lastPositions[0];
for (int value : lastPositions) {
if (value >= max) {
max = value;
return max;
private int findMin(int[] firstPositions) {
int min = firstPositions[0];
for (int value : firstPositions) {
if (value < min) {
min = value;
return min;
public boolean isTop() {
return isTop;
public boolean isBottom() {
return isBottom;
8.2 RecyclerView嵌套RecyclerView条目自动上滚的Bug
RecyclerViewA嵌套RecyclerViewB 进入页面自动跳转到RecyclerViewB上面页面会自动滚动。
两种解决办法
一,recyclerview去除焦点
recyclerview.setFocusableInTouchMode(false);
recyclerview.requestFocus();
二,在代码里面 让处于ScrollView或者RecyclerView1 顶端的某个控件获得焦点即可
比如顶部的一个textview
tv.setFocusableInTouchMode(true);
tv.requestFocus();
第一种方式:
重写父控件,让父控件 ScrollView 直接拦截滑动事件,不向下分发给 RecyclerView,具体是定义一个ScrollView子类,重写其 onInterceptTouchEvent()方法
public class NoNestedScrollview extends NestedScrollView {
private int downX;
private int downY;
private int mTouchSlop;
public NoNestedScrollview(Context context) {
super(context);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
public NoNestedScrollview(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
public NoNestedScrollview(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
int action = e.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:
downX = (int) e.getRawX();
downY = (int) e.getRawY();
break;
case MotionEvent.ACTION_MOVE:
//判断是否滑动,若滑动就拦截事件
int moveY = (int) e.getRawY();
if (Math.abs(moveY - downY) > mTouchSlop) {
return true;
break;
default:
break;
return super.onInterceptTouchEvent(e);
第二种解决方式
a.禁止RecyclerView滑动
recyclerView.setLayoutManager(new GridLayoutManager(mContext,2){
@Override
public boolean canScrollVertically() {
return false;
@Override
public boolean canScrollHorizontally() {
return super.canScrollHorizontally();
recyclerView.setLayoutManager(new LinearLayoutManager(mContext, LinearLayout.VERTICAL,false){
@Override
public boolean canScrollVertically() {
return false;
b.重写LayoutManager
代码设置LayoutManager.setScrollEnabled(false);
public class ScrollLayoutManager extends LinearLayoutManager {
private boolean isScrollEnable = true;
public ScrollLayoutManager(Context context) {
super(context);
public ScrollLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
public ScrollLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
@Override
public boolean canScrollVertically() {
return isScrollEnable && super.canScrollVertically();
* 设置 RecyclerView 是否可以垂直滑动
* @param isEnable
public void setScrollEnable(boolean isEnable) {
this.isScrollEnable = isEnable;
可能会出现的问题
虽然上面两种方式解决了滑动冲突,但是有的手机上出现了RecyclerView会出现显示不全的情况。
针对这种情形,使用网上的方法一种是使用 RelativeLayout 包裹 RecyclerView 并设置属性:android:descendantFocusability="blocksDescendants"
android:descendantFocusability="blocksDescendants",该属>性是当一个view 获取焦点时,定义 ViewGroup 和其子控件直接的关系,常用来>解决父控件的焦点或者点击事件被子空间获取。
beforeDescendants: ViewGroup会优先其子控件获取焦点
afterDescendants: ViewGroup只有当其子控件不需要获取焦点时才获取焦点
blocksDescendants: ViewGroup会覆盖子类控件而直接获得焦点
相关代码案例:https://github.com/yangchong211/LifeHelper
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="blocksDescendants">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_hot_review"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foregroundGravity="center" />
</RelativeLayout>
8.4 viewPager嵌套水平RecyclerView横向滑动到底后不滑动ViewPager
继承RecyclerView,重写dispatchTouchEvent,根据ACTION_MOVE的方向判断是否调用getParent().requestDisallowInterceptTouchEvent去阻止父view拦截点击事件
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
/*---解决垂ViewPager嵌套直RecyclerView嵌套水平RecyclerView横向滑动到底后不滑动ViewPager start ---*/
ViewParent parent = this;
while(!((parent = parent.getParent()) instanceof ViewPager));
// 循环查找viewPager
parent.requestDisallowInterceptTouchEvent(true);
return super.dispatchTouchEvent(ev);
9.RecyclerView复杂布局封装库案例
开源项目库的地址:https://github.com/yangchong211/YCRefreshView
9.1 能够实现业务的需求和功能
1.1 支持上拉加载,下拉刷新,可以自定义foot底部布局,支持添加多个自定义header头部布局。
1.2 支持切换不同的状态,比如加载中[目前是ProgressBar,加载成功,加载失败,加载错误等不同布局状态。当然也可以自定义这些状态的布局
1.3 支持复杂界面使用,比如有的页面包含有轮播图,按钮组合,横向滑动,还有复杂list,那么用这个控件就可以搞定。
1.4 已经用于实际开发项目投资界,新芽,沙丘大学中……
1.5 轻量级侧滑删除菜单,直接嵌套item布局即可使用,使用十分简单。
1.6 支持插入或者删除某条数据,支持CoordinatorLayout炫酷的效果
1.7 支持粘贴头部的需求效果
1.8 RecyclerView实现条目Item拖拽排序与滑动删除
9.2 具备的优势分析
自定义支持上拉加载更多,下拉刷新,支持自由切换状态【加载中,加载成功,加载失败,没网络等状态】的控件,拓展功能[支持长按拖拽,侧滑删除]可以选择性添加 。具体使用方法,可以直接参考demo。
轻量级侧滑删除菜单,支持recyclerView,listView,直接嵌套item布局即可使用,整个侧滑菜单思路是:跟随手势将item向左滑动
10.针对阿里VLayout代码分析
关于Vlayout的使用和相关介绍的博客有许多。具体可以看这篇博客:https://blog.csdn.net/m0_37700275/article/details/79006266
关于使用Vlayout实现复杂页面的案例有:https://github.com/yangchong211/YCAudioPlayer,https://github.com/yangchong211/YCVideoPlayer
实现的复杂界面效果展示:
11.版本更新说明
v1.0.0 2016年5月5日
v1.1.0 更新于2017年2月1日
v1.1.1 更新于2017年6月9日
v2.0.0 更新于2018年9月26日
关于其他内容介绍
01.关于博客汇总链接
1.技术博客汇总
2.开源项目汇总
3.生活博客汇总
4.喜马拉雅音频汇总
5.其他汇总
02.关于我的博客
我的个人站点:www.yczbj.org,www.ycbjie.cn
github:https://github.com/yangchong211
知乎:https://www.zhihu.com/people/yang-chong-69-24/pins/posts
简书:http://www.jianshu.com/u/b7b2c6ed9284
csdn:http://my.csdn.net/m0_37700275
喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
开源中国:https://my.oschina.net/zbj1618/blog
泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
邮箱:yangchong211@163.com
阿里云博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
segmentfault头条:https://segmentfault.com/u/xiangjianyu/articles
VerticalTextView Android 垂直TextView(Vertical TextView),文字垂直显示文本
MarqueeView 自定义跑马灯MarqueeView,用TextView 出现了各种坑啊 , 尤其是在页面中同时存在EditText 的时候,简单的用法,完善的功能,希望您能喜欢!
1.2.EditText
MaterialEditText
android-edittext-validator
ClearEditText
android-edittext-validator
PasswordEditText
CloudEditText EditText内容分不同块显示,支持校验,删除块,添加块,得到块代表的字符串集合
1.3.ImageView
CircleImageView
android-gif-drawable
MultiImageSelector
GalleryFinal
PhotoPicker
ImagePicker
Android-Universal-Image-Loader
android-crop
DWCorePhoto_Android
AndroidTimelineView
WeChatImageClipping
ImageEditor-Android
NineGridView
android-multiple-images-selector
android-shape-imageview
android-combination-avatar
1.4.Button
android-floating-action-button
FloatingActionButton
ToggleButton
ShineButton
FloatingActionButton
SwitchButton
RapidFloatingActionButton
android-flat-button
1.5.Spinner
nice-spinner
MaterialSpinner
BetterSpinner
Material-Spinner
SearchableSpinner
MultiImageSelector
CountdownView
bottomsheet
ShowcaseView
ExpandTable
WeiboPopupWindow
MultiChoiceAdapter
SmoothCheckBox
AnimateCheckBox
ListView-DragAndDrop
MultiThreadDownloader
AppUpdater
1.6.ProgressBar
SmoothProgressBar
NumberProgressBar
android-square-progressbar
MaterialLoadingProgressBar
circular-music-progressbar
circular-progress-button
android-process-button
materialish-progress
CircleProgress
Android-RoundCornerProgressBar
GoogleProgressBar
DownloadProgressBar
MusicPlayerView
android-upload-service
FABProgressCircle
ACProgressLite
Android-SVProgressHUD
MaterialProgressBar
ProgressRoundButton
CircularProgressBar
Android-ProgressBarWidthNumber
CustomLoading:heart:
LoadingView
LoadingAndRetryManager
ProgressRoundButton(2016-09-29)
ProgressLayout|一款新颖的进度条布局
1.7.CheckBox
SmoothCheckBox
AnimateCheckBox
SwipeSelector
1.8.Seekbar
RangeSeekBar 双向SeekBar
IndicatorSeekBar
仿微信设置字体大小控件1
仿微信设置字体大小控件2
android-range-seek-bar 双向SeekBar
progresshint
android-range-seek-bar
RangeSeekbar
NumberSeekBar
SeekBarHint
DoubleSeekBar
1.9.Toast
SuperToasts
Android-AppMsg
Crouton
Alerter|Android Dropdown 风格的弹窗,这个库旨在克服Toasts和Snackbars的限制,同时降低布局的复杂性。An Android Alerting Library
undoBar(人性化的设计,方便用户操作)
Toasty
Sneaker|效果和Alerter类似
RxTools
ExToast
1.10.Toobar
ImmersionBar
AndroidMaterialDesignToolba
SystemBarUsing|Toolbar与Translucent System Bar使用及封装
FlycoSystemBar
1.11.Notification
cordova-plugin-local-notifications Android Local-Notification Plugin
NotifyUtil
2.列表控件
2.1.ListView
PinnedHeaderListView 列表标题悬浮
pinned-section-listview 列表标题悬浮
Android-QuickSideBar
MultiChoiceAdapter
ListViewAnimations
Android-SlideExpandableListView
android-swipelistview-sample
2.2.GridView
AndroidStaggeredGrid
TableFixHeaders
2.3.ScrollView
Android-ObservableScrollView
2.4.RecyclerView
Learning-RecyclerView(a collection repository for repositories about recyclerView(RecyclerView大集合))
recyclerview-animators
android-advancedrecyclerview
twoway-view
StickyHeaderListView
Smart-HeaderFooter-RecyclerView
HeaderAndFooterRecyclerView
SwipeRecyclerView(recycler+侧滑删除+排序+下拉刷新+)
GangedRecyclerview Recyclerview实现双列表联动,列表悬停
2.5.WebView
VasSonic专业提高Html5首屏加载
AgentWeb
Android-AdvancedWebView
JsBridge
SlidingLayout
WebViewStudy
SpringView SpringView 是一个提供了上下拖拽的功能组件,能够进行高度自定义,实现各种下拉上拉动画效果
CacheWebView 如何让Android WebView访问更快
HeaderViewPager具有共同头部的 ViewPager,支持与ListView,GridView,ScrollView,WebView,RecyclerView 嵌套使用。具有连续的滑动事件 和 滑动监听, 支持下拉刷新
NestedScrollWebView
spy-debugger
HtmlNative
DragScrollDetailsLayout 商品详情布局
SafeWebView
safe-java-js-webview-bridge
2.6. 官方新式控件
Google Android 新ui控件
2.6.1.官方资料
android.support.design.widget|| desgin库
代表控件:AppBarLayout BottomNavigationView BottomSheetDialog CoordinatorLayout FloatingActionButton NavigationView Snackbar TabLayout TextInputLayout
android.support.design.widget 源码 Android6.0.0版
android.support.design.widget 源码 Android7.1.1版
android.support.design.widget 源码 Android8.1.0版
android.support.v4.widget || V4库
代表控件: DrawerLayout NestedScrollView SwipeRefreshLayout
android.support.v4.widget 源码 Android4.4.4版
android.support.v4.widget 源码 Android5.0.0版
android.support.v4.widget 源码 Android6.0.0版
android.support.v4.widget 源码 Android7.1.1版
android.support.v4.widget 源码 Android8.0.0版
android.support.v4.widget 源码 Android8.1.0版
2.6.2.综合项目
Coder Android Material Design 风格控件的学习及遇到的问题;Tablayout | 横向布局标签,TextInputLayout | 文字输入布局 ,FloatingActionButton | 悬浮按钮, CoordinatorLayout APPBarLayout CollapsingTabLayout实现折叠头布局,BottomSheetDialog | 底部对话框,Touch Feedback| 触摸反馈,Reveal Effect| 揭示效果,Curved motion | 曲线运动,Animated Vector Drawables | 矢量图片动画
MaterialDesignSamples Material Design 系列控件samples,讲了Material Design 系列新控件的使用方法和一些场景示例
2.6.3.BottomNavigationView
BottomNavigationViewExAn android lib for enhancing BottomNavigationView. 一个增强BottomNavigationView的安卓库。
2.6.4.BottomSheetDialog
BottomSheetDialog使用详解
2.6.5.Tablayout
CoordinatorTabLayout
tablayout-android
XTabLayout 可修改选中项字体大小和指示器长度的TabLayout
SnapTabLayout
PlayTabLayout
ColorTrackTabLayout
JPagerSlidingTabStrip
AppOrder
2.6.6.Coordinatorlayout
CoordinatorExamples Different CoordinatorLayout usages, tips & examples
CoordinatorLayoutExample 这是使用CoordinatorLayout打造 打造炫酷效果的例子
ScalingLayout
behavior-learnCoordinatorLayout 自定义Behavior 高仿美团商家详情界面 实现页面内容复杂联动效果
2.6.7.Coordinatorlayout.Behavior
2.6.8.TextInputLayout
2.6.9.FloatingActionButton
2.6.10.CardView
2.6.11.AppBarLayout
2.6.12.CollapsingToolbarLayout
2.6.13.Touch Feedback
2.6.14.Reveal Effect
2.6.15.Curved motion
2.6.16.Animated Vector Drawables
3 布局控件
3.1.ViewPager
InfiniteCycleViewPager 画廊效果 3D效果翻转
DiscreteScrollView
ExpandingPager
UltraViewPager
RecyclerViewSnap
android-auto-scroll-view-pager
AndroidImageSlider
ViewPagerIndicator
WizardPager
3DViewPagerGallery 3D画廊
RecyclerViewCardGallery
3.2.Layout
Tangram-Android Tangram是一套动态化构建 Native 页面的框架,它包含 Tangram Android、Tangram iOS,管理后台等一些列基础设施。本工程是 Tangram Android 的sdk 项目地址,底层依赖于vlayout 和 UltraViewPager。
DragSlopLayout
overscroll-decor 仿IOSlistview,scroller,recycleview回弹效果
SwipeBackLayout 滑动返回
Dragger 拖拽界面
DiagonalLayout material design风格
flexbox-layout Flexbox for Android
ChipsLayoutManager tag布局
vlayout RecyclerView分组布局 来自阿里
ResideLayout 老风格的侧滑布局
DraggablePanel
Slidr
InboxLayout
MarqueeLayoutLibrary跑马灯
SpaceTabLayout
ZLayoutManager
CoordinatorTabLayout
Dachshund-Tab-Layout
SmartTabLayout
JKeyboardPanelSwitch
BlurKit-Android
StickyGridHeaders
AndroidSwipeLayout
ExpandableLayout
AndroidViewHover
smooth-app-bar-layout
android-pile-layout
AdaptiveTableLayout 表格布局
greedo-layout-for-android
ExpandableLayout
SlidingUpPanelLayout
ScrollableLayout
BGASwipeItemLayout-Android
android-uitableview
android-viewbadger
AndroidSlidingUpPanel(百度地图,高德地图上滑面板控件)|添加更多描点,面板上下滑动随意停在指定位置 Android 上拉面板
SlideLayout|侧滑布局
android-youtube-drag-layout
MaterialDrawer(侧滑布局)
excelPanel|表格课程布局
3.3.刷新控件
SmartRefreshLayout
SmoothRefreshLayout
RecyclerRefreshLayout
android-PullRefreshLayout
SuperSwipeRefreshLayout SwipeRefreshLayout的二次开发
BeautifulRefreshLayout 重点推荐,刷新控件总结大全
Phoenix
android-Ultra-Pull-To-Refresh(名库-非常强大的刷新控件)
Android-PullToRefresh(名库-微博使用的下拉刷新)
SwipeRefreshLayoutDemo(SwipeRefreshLayout demo (Google官方下拉刷新组件))
PullToRefreshAndLoad
XListView(不推荐使用)
PullToRefresh
CommonPullToRefresh:heart:(在android-Ultra-Pull-To-Refresh的基础上增加了加载更多的支持)
CBPullRefreshListView(下拉刷新+侧滑菜单+listview+自定义刷新样式)
Android-PullToRefresh-SwipeMenuListView-Sample(下拉刷新+侧滑菜单+listview)
Android-Swipe-StickyHeader-PullToRefresh-Indexable-ListView
Android-PullToRefresh-Extention
3.4.PopupWindow
MaryPopup
BasePopup
3.5.选项卡
选项卡FlycoTabLayout
3.6.Panel(面板)
DraggablePanel
ExpansionPanel
excelPanel
ScrollablePanel 锁定首行首列的表格面板
CardSlidePanel 卡片面板
SlidingUpPanelLayout
3.7.Layout
LayoutManagerGroup
4.自定义控件
4.1.角标(corner label)
cornerlabelview(2016-09-29)
4.2.Segmented
android-segmented-control:heart:(ios UISegmentedControl for android)
SHSegmentControl(a simple SegmentControl Widget)
SegmentedBarView-Android(Custom UI control for android which is showing data as a segments and a value inside them)()
AndroidSegmentControl
android-segmentedtab
4.3.WheelView
WheelView
WheelPicker
AndroidPicker
android-spinnerwheel
HorizontalWheelView
WheelView-Android
CursorWheelLayout
Android-PickerView-saiwu-bigkoo
Android-PickerView-Bigkoo
androidWheelView
4.4.对话框
AndroidSuperDialog :heart:
[material-dialogs:heart:](https://github.com/afollestad/material-dialogs)
android-styled-dialogs
sweet-alert-dialog
NiftyDialogEffects
dialogplus
MaterialDialog
L-Dialogs
EasyDialog
android-simple-tooltip A simple library based on PopupWindow to create Tooltips on Android. :heart:
4.5.树
AndroidTreeView
RecyclerTreeView
4.6.标签
FlowLayout
AndroidTagGroup
FlowlayoutTags
FlowTag
TagCloudView
AndroidTagView(拖拽)
EasyTagDragView(仿网易新闻分类tag)
DragExpandGrid
4.7.拖拽
DragExpandGrid(500+)
drag-sort-listview
DragTopLayout
4.8.Blur
android-stackblur
4.9.搜索
MaterialSearchView
MaterialDialogSearchView
SearchView
[Android-Material-SearchView[推荐]](https://github.com/EugeneHoran/Android-Material-SearchView)
floatingsearchview
Material
4.10.菜单
StickyNavLayout-悬浮菜单
FloatMenuSample(悬浮菜单)
SwipeMenuListView
MultiMenuSelect
[AndroidResideMenu[QQ侧滑风格]](https://github.com/SpecialCyCi/AndroidResideMenu)
圆形菜单,旋转
多选菜单DropDownMenu
BottomDialog(固定式静态菜单)
抄袭微信Android6.0版本底部菜单渐变效果
BottomSheet(底部菜单)1455+
bottomsheet(底部菜单)2151+
BottomBar(底部菜单)3245+
ahbottomnavigation(底部菜单)1003+
BottomNavigatio(底部菜单)700+
LuseenBottomNavigation(底部菜单)500+
SlideBottomPanel(知乎底部菜单)500+ 底部划动菜单,滑动时背景图透明度渐变,支持嵌套 LiewView 或 ScrollView
BottomSheet(底部菜单)400+
Material-BottomNavigation(底部菜单)300+
AndroidBottomSheet(底部菜单)200+
ExpandTable
FloatingActionButton:heart: Android Floating Action Button based on Material Design specification, api14+
android-floating-action-button
FloatingActionButton
4.11.侧滑菜单
MaterialDrawer
android-menudrawer
SlidingMenu
左右侧滑菜单库
SlidingMenu下载地址
4.11.StepView
Steppers
stepper-indicator
StepView
Android-StepsView
material-stepper
4.12.Calendar
CalendarView功能非常强大的自定义日历控件 推荐2018-07-06
material-calendarview(A Material design back port of Android's CalendarView. The goal is to have a Material look and feel, rather than 100% parity with the platform's implementation.)(2016-9-27)
Caldroid(A better calendar for Android)(2016-9-27)
android-times-square(Standalone Android widget for picking a single date from a calendar view.)(2016-9-27)
Android-Week-View(Android Week View is an android library to display calendars (week view or day view) within the app. It supports custom styling.)(2016-9-27)
android-calendar-view(显示阳历,农历,节假日和二十四节气 实现对某月日期的单选或者多选的andorid 日历控件库。Easy to use, powerful, easy to expand the android calendar view library.)(2016-09-29)
Simple-Calendar
4.13.Shadow
ZDepthShadow
ShadowViewHelper
shadow-layout
4.14.折叠菜单
folding-cell-android
Android-SlideExpandableListView
ExpandableTextView
4.15.图表
AndroidChart
XCL-Charts
MPAndroidChart(推荐)
hellocharts-android
DashboardViewDemo
汽车速度仪表盘
XCL-Charts
仿支付宝蚂蚁积分仪表控件
JZAndroidChart 股票图表库
FinancialCustomerView各种金融类的自定义View,基金走势图、分时图、蜡烛图、各种指标等,一步一步构建庞大的基金自定View...
4.16.列表联动
LazyWaimai-Android 外卖点菜 列表联动
4.17.状态布局
StatusLayoutManager 切换不同的数据状态布局,包含加载中、空数据和出错状态,可自定义状态布局。
4.18.面板
Bottom-Sheets 官方控件
bottomsheet
BottomSheet
FabulousFilter
BottomSheet
BottomSheetBuilder
BottomSheets
AndroidBottomSheet
4.19.自定义控件
RippleView 水波纹效果
ShapeRipple 水波纹效果
SeatTable 电影选票效果
android-empty-layout
progress-activity
FlightSeat:heart:(机票座位选择)
GoodView(点赞动画效果)
SwipeBack-仿简书返回效果
EmojiChat-聊天界面
二级分类菜单
BlurEffectForAndroidDesign
仿UCL浏览器下拉眼镜
微信小视频
ZhuanpainView
自定义状态的View展示
自定义九宫格
APP换肤
发表说说界面
懒加载的Fragment
微信支付案例
类似QQ空间,微信朋友圈,微博主页等,展示图片的九宫格控件,自动根据图片的数量确定图片大小和控件大小
仿微信嵌入网页下拉滑动效果
仿新版微信底部tab渐变效果
类似微信朋友圈QQ空间,也可用做商品评价或者其他需要图文展示的列表
主流app图片展示案例
仿微信图片选择
仿微信图片选择
微信发送图片样式自定义ImageView
ChatImageView
微信公众号查询课程成绩系统(web项目)
图片裁剪高仿微信头像裁剪
精选微信公共号文章app
这是一个仿微信群组成员管理的界面,其核心重点在于对Adapter的掌握和变化处理
百度地图定位和选址
高德地图定位
工具类站点---zftlive
记录跑步路线
仿照一号专车的地图界面
网络爬虫获取图文效果的案例--Now
p2p网站APP 案例源码
QuantityView)(Android quantity view with add and remove button.)
OverScrollDecor(类似IOS的over-scrolling效果,即对于滑动到顶部的View继续滑动时会超出,松手后自动还原到原始位置。支持ListView,GridView,ScrollView,WebView,RecyclerView,以及其他的任意View和ViewGroup)
android-vertical-slide-view(仿照淘宝和聚美优品,在商品详情页,向上拖动时,可以加载下一页。使用ViewDragHelper,滑动比较流畅。scrollView滑动到底部的时候,再行向上拖动时,添加了一些阻力。)
VerticalSlideView(类似淘宝的商品详情页,继续拖动查看详情,其中拖动增加了阻尼,并且重写了ListView,GridView,ScrollView,WebView,RecyclerView 的 dispatchTouchEvent 方法,使用的时候无须额外的代码,可以任意嵌套使用。)
AlphaIndicatorView
ScrollableLayout
4.20.公共库
CommonUILibrary 常用的自定义view和第三方类库,欢迎Follow、Fork、Star
CommonUtilLibrary
UIWidget 一个集成UIAlertDialog、UIActionSheetDialog、UIProgressDialog、TitleBarView(自带沉浸式标题栏)、CollapsingTitleBarLayout、RadiusView(圆角及状态背景设置View解放shape文件)、KeyboardHelper(软键盘控制及遮挡控制类)、StatusViewHelper(状态栏沉浸帮助类)、NavigationViewHelper(导航栏沉浸式帮助类)、AlphaViewHelper(View透明度控制帮助类) 等项目常用UI库
5.主题与适配
5.1.MaterialDesign
Awesome-MaterialDesign(学习材料设计风格一站式导航资源)
MaterialDesignSamples
DesignSupportLibraryDemo
android-new-widgets-demo
5.2.Styel
Android-Bootstrap(Bootstrap style widgets for Android, with Glyph Icons)(2016-9-27)
5.3.屏幕适配
Android屏幕适配方案探索对比完全讲解 Android 目前稳定高效的UI适配方案
骚年你的屏幕适配方式该升级了!-今日头条适配方案
今日头条屏幕适配方案终极版正式发布!
AndroidAutoLayout
AndroidAutoSize A low-cost Android screen adaptation solution (今日头条屏幕适配方案终极版,一个极低成本的 Android 屏幕适配方案)
6.1.适配器
FlexibleAdapter
baseAdapter
6.2.工具
ADT-23.0.7 eclipse ADT 插件 用于elipse android应用开发
FatJar 适用于sdk多module打包和合并多个jar的gradle插件
RxTools Android开发人员不得不收集的工具类集合
gradle-bintray-upload Android Studio上传Library库到JCenter,并同步到Maven Central
AndroidCommon Android工具类封装
NotifyUtil本地通知工具类
LazyAndroid常用工具类
android-resource-remover python 脚本工具 :根据 lint 的提示删除项目中无用的资源,减少包的大小
6.3.文件
superFileView基于腾讯浏览服务Tbs,使用X5Webkit内核,实现文件的展示功能,支持多种文件格式
TbsFileSamples 基于腾讯浏览服务Tbs,使用X5Webkit内核,实现文件的展示功能,支持多种文件格式
aFileChooser
MaterialFilePicker
NoNonsense-FilePicker
6.4.二维码
zxing
Seashell-app
BarcodeScanner
zxing(Official ZXing ("Zebra Crossing") project home)
BGAQRCode-Android(扫描二维码、扫描条形码、相册获取图片后识别、生成带 Logo 二维码、支持微博微信 QQ 二维码扫描样式)
android-zxingLibrary(几行代码快速集成二维码扫描功能)
NextQRCode(基于ZXing Android实现的QRCode扫描支持库。包括生成二维码图片和相机扫描二维码图片即时解码两部分功能。)
6.5.视频
MusicStation
VideoRecorder
jiecaovideoplayer
weishijie-develop
aFileChooser
jjdxm_ijkplayer(基于ijkplayer简单的UI界面 当前项目是基于ijkplayer项目进行的播放器界面UI封装。 是一个适用于 Android 的 RTMP 播放界面 SDK,可高度定制化和二次开发。特色是同时支持 H.264 软编/硬编和 AAC 软编/硬编。主要是支持RIMP、HLS、MP4、M4A等视频格式的播放。)(2016-09-28)
6.6.动画
BaseAnimation
Android-Animation-Set Android 所有动画系列详尽教程。 Explain all animations in Android.
6.7.权限
PermissionsDispatcher
RxPermissions
easypermissions
Dexter
AndPermission
PermissionHelper
PermissionGen
MPermissions
HiPermission
TedPermission
AndroidAcp
Permissify
6.8.安全
android-security-awesome
6.9.换肤
Android-Skin-Loader(动态加载技术)
MultipleTheme
Colorful
MaterialDesignDemo
6.10.异常
CustomActivityOnCrash(Android library that allows launching a custom activity when your app crashes, instead of showing the hated "Unfortunately, X has stopped" dialog.)
AndroidCrashHelper
acra(Application Crash Reports for Android)
bugsnag-android:heart:(Bugsnag's Android crash reporting library automatically detects crashes in your Android apps, collecting diagnostic information and immediately notifying your development team, helping you to understand and resolve issues as fast as possible.)
AppCrashTracker:heart:(Its a kind of toolkit to track the exception arising in the application and it will generate a json and can upload in your server using your own post url.)
AppCrash
6.11.日志
logger
6.12.数据库
greenDAO
6.13.支付(支付宝&微信&银联)
支付宝RAS签名步骤
weixin-java-tools(推荐) 可能是目前最好最全的微信Java开发工具包,支持包括微信支付、开放平台、小程序、企业号和公众号等的开发
IJPay 支付宝,微信,银联支付JDK
IJPay-Demo 支付宝,微信,银联支付 Java后端程序demo
JPay Android端 支付宝,微信,银联支付
JPay wiki
WXWeChatToolkits(我的微信SDK,包括公众平台管理、微信支付等各个版本)
PayMap(Java后端实现三方支付集成支付宝(国内、国际、移动端、PC端)、微信、银联(acp、upop)、光大(网关、网页)、邮政支付)
AlipayZeroSdk(Lightest Alipay Transfer Helper - 最轻量的支付宝转账工具类(Android))
6.14.蓝牙
BluetoothKit 推荐 经典蓝牙和Ble蓝牙混合扫描
RxAndroidBle
BluetoothHelper
Android-BLE
EasyBle Multi-devices process Bluetooth library for Android
FastBle 不支持扫描手机
BluetoothChat 经典蓝牙
LMBluetoothSdk 经典蓝牙 A library to make classic bluetooth or BLE easier to use in Android.
6.15.音乐
murmur
6.16.测试
tsung
6.17.分享
ESSocialSDK(社交登录授权、分享SDK,支持微信、微博和QQ)
6.18.照相和相册
Album
SmartCamera 卡片边框识别
6.19.富文本
icarus-android
6.20.红点
BGABadgeView-Android
ShortcutBadger
ShortcutHelper
android-badge
DraggableFlagView
BadgeView-qstumn
BadgeView AlexLiuSheng
6.21.引导视图
GuideView-master
6.22.购物车
AnimShopButton
android-shoppingcart 购物车: 包含侧滑删除,商品筛选,商品增加和减少,价格计算,店铺分类等功能
6.23.城市列表
citypicker
SuspensionIndexBar
6.24.自动抢红包
WeChatLuckyMoney
LuckyMoney
6.25.串口通讯
Android-SerialPort-API
AndroidSerialPort
AndroidSerialPort
6.26.APK升级更新
AppUpdate-master
AppUpdate Android 版本更新 a library for android version update
https://github.com/AlexLiuSheng/CheckVersionLib
update
7. 网络
7.1.网络请求
okhttp-utils
okhttp
OkHttpUtils
android-async-http
retrofit
android-volley
volley
7.2.即时通信
asmack
netty
dubbo(阿里PRC框架)
motan(微博PRC框架)
7.3.下载
FileDownloader
7.4.第三方封装
Glint
8.1.MVP
TheMVP
MVP+Rxjava
android-mvp-architecture
8.2.组件化
CompontentDemo 组件化,通过gradle脚本,实现module在编译期隔离,运行期按需加载,实现组件间解耦,高效单独调试
dexcount-gradle-plugin(方法数统计)
methodscount(在线类库方法数查询)
DynamicAPK(DynamicAPK是一套用于实现多dex/apk加载的解决方案。它可以帮助你重新组织Android工程的配置和开发模式,实现多个子工程并行开发(以android studio module的形式),同时支持hot fix(在线修复有问题的功能), 插件式载入不常用的功能(下载插件后再载入)。所有动态加载的插件不仅包含代码,也可以包含资源(资源的动态加载比代码要麻烦很多),因此是以APK形式实现的。)
8.3.源码分析
AndroidSdkSourceAnalysis
8.4.面试资料
android-interview-guide An interview guide for Android development engineers.
Android-Interview
LearningNotes【热】(系统学习Android知识的一个开源笔记)(推荐)
AndroidInterview-Q-A【热】|The top Internet companies android interview questions and answers
《Android 开发艺术探索》读书笔记【热】
hit-alibaba【热】|总结比较好的InterView
easy-job|偏重java基础
InterviewQuestion
AndroidTips|Android开发总结
Android-Interview
Point-of-Android|Android 一些重要知识点解析整理
收集Android方方面面的经典知识, 最新技术.
interview|Java / Android 笔试、面试 知识整理
my-backend|后端
AndroidDifficultAnalysis|android重难点知识
android-skill-summary|Android 技能总结,各种基础和进阶内容的资料收集
InterviewQuestion|整理的常见的问题
android_interviewAndroid校招面试指南(17-12-13)
[android-interview-questions-cn]142受 android-interview-questions 项目启发,这里想发挥众多 Android 中国开发者的力量,整理一份高质量、范围全的 Android 面试指南,旨在帮助更多的 Android 开发者提升技术,找到工作。
CommonDevKnowledge 史上最全的BAT大厂Android面试题汇集,以及常用的Android开发的一些技能点,冷门知识点汇总,开发中遇到的坑汇总等干货。
blog_backups
android-Interview
Android_Interview
Android-Interview
8.5.生命周期
RxLifecycle
android-lifecycle
RxLifecycle-zhihu
8.6.算法解析
leetcode【热】|LeetCode算法题典
datastructure|java算法
algorithm-essentials|gitbook 算法精粹--举一反三,抛弃题海战术
Algorithms|算法第四版书中代码以及后面的习题
jianzhioffer|剑指offer算法题的java实现
algorithms|《算法 (第4版)》、牛课堂、《剑指Offer》、
java-learning|旨在打造在线最佳的 Java 学习笔记,含博客讲解和源码实例,包括 Java SE 和 Java Web ()
java-core-learning-example
DataMiningAlgorithm|数据挖掘算法
Note|常规Java工具,算法,加密,数据库,面试题,源代码分析,解决方案
MineKnowContainer|个人知识小仓库粗体文本
8.7.设计模式
java-design-patterns
史上最全设计模式导学目录 刘伟
android_design_patterns_analysis Android源码设计模式分析项目
AndroidSdkSourceAnalysis Android sdk 源码解析
Android源码设计模式 Android源码设计模式分析项目
android-architecture Android架构资料---google
Java开发中的23种设计模式详解
Android 源码设计模式 博客
设计之禅源码
大话设计模式源码
研磨设计源码|github地址设计模式源码
[Java之美[从菜鸟到高手演变]之设计模式](http://blog.csdn.net/zhangerqing/article/details/8194653)
8.8.进程通信
ABridge Android 进程间通信最牛方案,为简单而生
8.9.快速开发
Android快速开发框架,目的是为了快速开发产品。适合中小型快速迭代项目
MVVMHabit 基于谷歌最新AAC架构,MVVM设计模式的一套快速开发库,整合Okhttp+RxJava+Retrofit+Glide等主流模块,满足日常开发需求。使用该框架可以快速开发一个高质量、易维护的Android应用。
XDroidMvp 轻量级的Android MVP快速开发框架
Android-ZBLibrary Android MVP快速开发框架,做国内 「Demo最全面」「注释最详细」「使用最简单」「代码最严谨」的Android开源UI框架
App-Architecture App-Architecture是一个关于移动应用一整套架构的解决方案开源项目。主要目的是整合流行开发模式结合自己本人的工作经验形成的一整套App快速开发解决方案。本套解决方案的app工程和simple工程主要基于Android实现。framework工程实现了主要架构,主要目的是抛开平台相关性。
BaseProject BaseProject是一个Android基础程序架构库(安卓屏幕适配ResolutionAdaption,安卓分辨率适配,网络通信,公用方法等),使用它可以快速稳定高效的建立一个Android工程, 它集成了一个项目最基本,可以说每个项目必备的一些库。 省的自己导入BaseActivity,BaseFragment,集成好分辨率适配(屏幕适配),使开发者可以 用侵入性最小的代价完成Android屏幕适配(安卓分辨率适配),网络请求(RxJava2+Retrofit2), 有大量常用的工具类。让你更加专注去实现自己产品需求, 业务逻辑,而不是浪费时间在重复的工作上!,包含四大部分:一、分辨率适配 二、网络请求框架Retrofit2封装 三、Android基类封装和项目常用Utils 方
LCRapidDevelop android快速开发框架--快速实现 异常奔溃统一管理 页面 加载中 加载失败 无数据等状态以及下拉刷新和自动加载
AndroidFireAndroidFire,一款新闻阅读 App框架,基于 Material Design + MVP + RxJava + Retrofit + Glide,基本涵盖了当前 Android 端开发最常用的主流框架,基于此框架可以快速开发一个app
XDroid轻量级Android快速开发框架
androidone One整个框架为MVC模式搭建,基于android framework为核心,集成Android世界中的主流技术选型, 以Pragmatic风格的Android应用参考示例,是android项目最佳实践的总结与演示。 以“复杂的世界里,一个就够了”为理念,励志帮助Android开发人员快速搭建一个简单高效的android开发框架。
EasyAndroid 一套整合主流HTTP网络、图片加载、MVP(Clean+Dagger2)架构的快速高效的开发框架
xUtils3 android orm, bitmap, http, view inject...
xUtils android orm, bitmap, http, view inject...
ThinkAndroidThinkAndroid是一个免费的开源的、简易的、遵循Apache2开源协议发布的Android开发框架,其开发宗旨是简单、快速的进行 Android应用程序的开发,包含Android mvc、简易sqlite orm、ioc模块、封装Android httpclitent的http模块, 具有快速构建文件缓存功能,无需考虑缓存文件的格式,都可以非常轻松的实现缓存,它还基于文件缓存模块实现了图片缓存功能, 在android中加载的图片的时候,对oom的问题,和对加载图片错位的问题都轻易解决。他还包括了一个手机开发中经常应用的实用工具类, 如日志管理,配置文件管理,android下载器模块,网络切换检测等等工具。 http://www.thinkandroid.cn
afinalAfinal是一个android的ioc,orm框架,内置了四大模块功能:FinalAcitivity,FinalBitmap,FinalDb,FinalHttp。通过finalActivity,我们可以通过注解的方式进行绑定ui和事件。通过finalBitmap,我们可以方便的加载bitmap图片,而无需考虑oom等问题。通过finalDB模块,我们一行代码就可以对android的sqlite数据库进行增删改查。通过FinalHttp模块,我们可以以ajax形式请求http数据
FastLib 一个Android项目级快速开发框架,节约大部分写常用功能时间以实现更多项目业务功能及体验上的优化..有问题欢迎issue。主要实现功能: 1、基于Retrofit2.x及RxJava2.x的网络请求封装、网络请求与生命周期绑定、快速观察者、快速loading观察者、快速返回常用错误 2、 常用功能库二次封装方便调用:Glide加载图片封装、TabLayout+ViewPager Fragment切换封装、Logger日志打印封装 3、 多种常用界面布局:标题+多状态+下拉刷新+列表、标题+ViewPager等方便快速创建常用布局增加layout复用 4、Fragment 懒加载封装 5、 快速实现Activity滑动返回、下拉刷新加载更多、沉浸式等
AndroidProject An advanced template project 当我们日复一日年复一年的搬砖的时候,你是否曾想过提升一下开发效率,如果一个模板的项目摆在你的面前,你还会选择自己搭架构么
KJFrameForAndroidKJFrameForAndroid 又叫KJLibrary,是一个android的orm 和 ioc 框架。同时封装了android中的Bitmap与Http操作的框架,使其更加简单易用;KJFrameForAndroid的设计思想是通过封装Android原生SDK中复杂的复杂操作而达到简化Android应用级开发,最终实现快速而又安全的开发APP。我们提倡用最少的代码,完成最多的操作,用最高的效率,完成最复杂的功能。
LoonAndroid
AndBase AndroidBase android 应用开发框架 1.andbase中包含了大量的开发常用手段。 如网络下载,多线程与线程池的管理,数据库ORM,图片缓存管理,图片文件下载上传,Http请求工具,SOAP工具类,异步Task,常用工具类(字符串,日期,文件处理,图片处理工具类等),能够使您的应用在团队开发中减少冗余代码,很大的提高了代码的维护性与开发高效性,能很好的规避由于开发疏忽而导致常犯的错误。 2.andbase封装了大量的常用控件。 如list分页,grid分页,下拉刷新,进度框,图片轮播,表格,多线程下载器,侧边栏,图片上传,轮子选择,图表,Tab滑动,日历选择器等。 3.强大的AbActivity,您没有理由不继承它。 继承它你能够获得一个简单强大可设置的操作栏,以及一…
AndroidFineAndroid快速开发框架
XFrameXFrame - Android快速开发框架:主要是封装有Http网络隔离框架、日志、缓存、加载等待、toast、页面状态布局管理、权限、自定义view等常用的集合框架,减少开发成本,提高软件体验!
FastAndroid 这是一个融入了MVP模式,集成了多个开源项目后,进行整合形成的Android快速开发框架。
AndroidRapidLibrary Android 快速开发库,主要想实现一条属于自己的开发框架。包括网络访问,数据,UI等等
RWidgetHelper Android UI 快速开发,专治原生控件各种不服-WanAndroid基于Architecture Components dependencies (Lifecycles,LiveData,ViewModel,Room)构建的WanAndroid开源项目。 你值得拥有的MVVM快速开发框架:https://github.com/jenly1314/MVVMFrame
AndroidQuickAndroidQuick项目旨在提供一套让能Android开发者快速开发APP的框架。 AndroidQuick从开发一个APP所涉及到的常用的架构、模块、功能等方面出发,向Android开发者提供一套快速开发框架和demo实例,避免开发过程中重复造轮子。
AndroidBaseMvp 一个快速搭建MVP+RxJava2+Retrofit 基础框架,主要是封装有Http网络请求、日志、缓存、加载等待、toast、页面状态布局管理、权限、RxBus、Glide图片加载等组件,方便快速开发新项目、减少开发成本。
8.10.性能优化
androidProject android产品研发过程中常用的技术,技巧,实践等
awesome-android-performance Android performance optimization tutorials, videos and tools list(Android性能优化视频,文档以及工具)
Android_article Android热更新、异步并发、性能优化、编译打包、适配相关等文档
8.11.移动推送
这领域的知识涉及到多个方面,包含了服务器端编程,网络通信协议,移动端编程。属于知识复杂度较高的一块。有精力的人士最好把各个方面吃透。
mpush 基于Netty自定义协议实现的消息推送系统
9.1.Android
open-source-android-apps
开源项目完整列表
mpush 【推荐】|MPush开源实时消息推送系统 Netty开发 重点推荐的有技术含量的项目 需要有服务器开发的底子
ListenerMusicPlayer【推荐】|A Grace Material Design Music Player
LabCoat【推荐】|LabCoat git lab客户端
GitClub|An elegent Android Client for Github. 不仅仅是Github客户端,而且是一个发现优秀Github开源项目的app
GithubTrends 【推荐】|关注github 项目流行趋势
graduation_github | github项目搜索项目
GithubApp
monkey-android
Git.NB
code-reader 【推荐】 | github项目离线下载,阅读
CloudReader
CoCoin 【推荐】(收支记录app)
Douya(豆芽)
top-github 【推荐】|Android app for browsing GitHub top repositories
ForkHub【推荐】|github client
Tuikan|「推看」是一款集知乎头条,美图,视频于一体的休闲阅读app。
u2020
DylanStepCount|计步器
PocketHub |PocketHub Android App
MarkdownEditors |md编辑器 app
SmarterStreaming|直播
BookReader|"任阅" 网络小说阅读器
octodroid 【推荐】|Android toolkit for the GitHub API|应用商店下载
plaid【推荐】|由谷歌工程师开发,展示Google Material风格设计,项目代码量大,但是结构清晰,还是很好理解的。
LookLook
SeeWeather|天气预报
owncloud
Talon-for-Twitter
android-design-template |材料设计模板
qksms|短信sms
SmartRecom|音乐 (2017-12-13)
browser (scoute-dich 绿色浏览器)
SimpleBrowser Android全手势浏览器 仿新版微信的小程序下拉栏
StylishMusicPlayer
MaterializeYourApp
MicroReader(一个小而美的阅读客户端)
Bingo(Bingo是一款IT阅读学习类的开源软件)
CNode社区第三方Android客户端
LeeCo
KJBlog
github app客户端
开源中国V2
音乐播放-android-UniversalMusicPlayer
AmazeFileManager 文件夹管理app
AnimeTaste动画项目-代码家
AndroidSlidingUpPanel
YiYuanYunGou
iosched
u2020
SuesNews
asm-android-client-for-newsmth
ACEMusicPlayer
writeily-pro.git)
SoundRecorder
上百个开源项目
LingDong2.0(“面对面文件快传”,Android端灵动快传,安卓互传文件,局域网,无网传输文件,Android,file transfer)(2016-09-29)
code-reader(One Multi program language code reader 含多语言,md解析,day night主题的多语言代码阅读器CoReader )(2016-09-30)
9.2.Android-Api
Android API
Android API 指南
Android各个版本源码在线查看,包括系统,api,support-library源码
Android API源码
Android support-library源码
Android 版本最新市场分布情况
Android support-library 安装与使用
Android support-library 详细依赖关系查询
Android support-library 详细依赖关系查询 中文
Android Plugin for Gradle Release Notes|android studio版本与com.android.tools.build:gradle版本的对应关系 官方地址
APK安装错误码
9.3.React-Native
react-native-open-project
f8app
reading
react-weather
gitbook-reader-rn
react-native-gitfeed
react-native-nba-app
Shop-React-Native EleTeam开源项目 - 电商全套解决方案之 React Native 版 - Shop-React-Native。一个类似京东/天猫/淘宝的商城,有对应的服务端支持,由EleTeam团队维护!
9.4.Flutter
GSYGithubAppFlutter
9.5.Weex
GSYGithubAppWeex
10.书签
10.1.Android
codekk(Trinea运营的专注Android开发的技术网站)
awesome-android-snowdream
awesome-android-JStumpp
TimLiu-Android
awesome-android-performance
awesome-android-libraries
mobdevgroup(github地址)(2016-10-06)
XXApple
Android_Data-Android
android-training-course-in-chinese
awesome-android-cn
AndroidLibs
AndroidTips
android-open-source-project-cracking
android sdk源码分析
android_design_patterns_analysis
share
AndroidGuide
android-dev-bookmarks
CoreLink 日常积累
Android-Tips
android-dev-cn
AndroidNote-GcsSloop
AndroidNote-CharonChui
AndroidNote-venshine
AndroidNote-linsir6
AndroidDifficultAnalysis
Android-bookmark-shares
android-best-practices Android 开发最佳实践 从Futurice公司Android开发者中学到的经验。 遵循以下准则,避免重复发明轮子
10.2.移动前端
SUI-Mobile (SUI Mobile (MSUI)是由阿里巴巴国际UED前端出品的移动端UI库,轻量精美 )
SUI Mobile 是一套基于 Framework7 开发的UI库。它非常轻量、精美,只需要引入我们的CDN文件就可以使用,并且能兼容到 iOS 6.0+ 和 Android 4.0+。(2016-09-30)。 阿里背景
frozenui Frozen UI是一个开源的简单易用,轻量快捷的移动端UI框架。基于手Q样式规范,选取最常用的组件,做成手Q公用离线包减少请求,升级方式友好,文档完善,目前全面应用在腾讯手Q增值业务中。
兼容android 2.3 +,ios 4.0 + 。腾讯背景
framework7 Framework7 - is a free and open source mobile HTML framework to develop hybrid mobile apps or web apps with iOS & Android native look and feel. It is also an indispensable prototyping apps tool to show working app prototype as soon as possible in case you need to.
mint-ui(Mobile UI elements for Vue.js 饿了么前端)(2016-09-30) 效果演示
weui(WeUI 是一套同微信原生视觉体验一致的基础样式库,由微信官方设计团队为微信内网页和微信小程序量身设计,令用户的使用感知更加统一。)(2016-09-30)
amazeui(基于 React.js 的移动端 Web 组件库)(2016-09-30)
weex(A framework for building Mobile cross-platform UI)(2016-09-30)
vux(Mobile web UI Components based on Vue and WeUI. Be Cool with Vue & WeUI. https://vux.li/)(2016-09-30)
10.3.Awesome
awesome-android
awesome-android-ui
awesome-machine-learning
github-cheat-sheet
awesome-interview-questions
Awesome-RxJava
awesome-android-libraries
awesome-adb
Awesome_APIs
awesome-android-performance
android-security-awesome
awesome-android-tips
material-design-data 关于 Material Design 的一切资料都在这里
awesome-github-android-ui
awesome-github
react-native-guide
11. 联系方式
CSDN:http://blog.csdn.net/qingfeng812
Github:https://github.com/Arisono
微信公众号:Android 实战开发
如果你有好的资源希望分享,请点击链接留言
12.原文地址
http://cloud.yundashi168.com/archives/427
PyQt5 笔记(01):嵌套布局
PyQt5 有四种布局:水平(QHBoxLayout)、竖直(QVBoxLayout)、网格(QGridLayout)、表单(QFormLayout)在窗体中单一的布局应该不难,但若是比较复杂的布局,一般涉及到布局的嵌套,这就头疼了。本文的四个知识点:1. 布局不能直接嵌套(如果我错了,欢迎指正!)2. 内层的布局必须先“附着”在一个空 QWidget 上3. 然后把这个“承载”着内层布局的空部件添加至外层布局4. 最后,别忘记把全局布局“附着”到窗体本尊
0. 先看效果图
下面对布局进行分析
1. 全局布局分析
全局布局使用了一个水平布局
wlayout = QtWidgets.QHBoxLayout() # 全局布局(1个):水平
hlayout = QtWidgets.QHBoxLayout() # 局部布局(4个):水平、竖直、网格、表单
vlayout = QtWidgets.QVBoxLayout()
glayout = QtWidgets.QGridLayout()
flayout = QtWidgets.QFormLayout()
hlayout.addWidget(QtWidgets.QPushButton(str(1))) # 局部布局添加部件(例如:按钮)
hlayout.addWidget(QtWidgets.QPushButton(str(2)))
vlayout.addWidget(QtWidgets.QPushButton(str(3)))
vlayout.addWidget(QtWidgets.QPushButton(str(4)))
glayout.addWidget(QtWidgets.QPushButton(str(5)),0,0)
glayout.addWidget(QtWidgets.QPushButton(str(6)),0,1)
glayout.addWidget(QtWidgets.QPushButton(str(7)),1,0)
glayout.addWidget(QtWidgets.QPushButton(str(8)),1,1)
flayout.addWidget(QtWidgets.QPushButton(str(9)))
flayout.addWidget(QtWidgets.QPushButton(str(10)))
flayout.addWidget(QtWidgets.QPushButton(str(11)))
flayout.addWidget(QtWidgets.QPushButton(str(12)))
hwg = QtWidgets.QWidget() # 准备四个部件
vwg = QtWidgets.QWidget()
gwg = QtWidgets.QWidget()
fwg = QtWidgets.QWidget()
hwg.setLayout(hlayout) # 四个部件设置局部布局
vwg.setLayout(vlayout)
gwg.setLayout(glayout)
fwg.setLayout(flayout)
wlayout.addWidget(hwg) # 四个部件加至全局布局
wlayout.addWidget(vwg)
wlayout.addWidget(gwg)
wlayout.addWidget(fwg)
self.setLayout(wlayout) # 窗体本尊设置全局布局
if __name__=="__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
win = MyWindow()
win.show()
sys.exit(app.exec_())
关于目前使用Vlayout框架的案例有
https://github.com/yangchong211/YCVideoPlayer
https://github.com/yangchong211/LifeHelper
关于截图如下所示,如果觉得好,请star。
1.Vlayout简单介绍
阿里的开源框架,地址是:https://github.com/alibaba/vlayout/
VirtualLayout是一个针对RecyclerView的LayoutManager扩展, 主要提供一整套布局方案和布局间的组件复用的问题。
2.主要功能介绍
2.1 主要功能思维导图
2.2 主要功能说明
默认通用布局实现,解耦所有的View和布局之间的关系: Linear, Grid, 吸顶, 浮动, 固定位置等
* 1:LinearLayoutHelper: 线性布局
* 2:GridLayoutHelper: Grid布局, 支持横向的colspan
* 3:FixLayoutHelper: 固定布局,始终在屏幕固定位置显示
* 4:ScrollFixLayoutHelper: 固定布局,但之后当页面滑动到该图片区域才显示, 可以用来做返回顶部或其他书签等
* 5:FloatLayoutHelper: 浮动布局,可以固定显示在屏幕上,但用户可以拖拽其位置
* 6:ColumnLayoutHelper: 栏格布局,都布局在一排,可以配置不同列之间的宽度比值
* 7:SingleLayoutHelper: 通栏布局,只会显示一个组件View
* 8:OnePlusNLayoutHelper: 一拖N布局,可以配置1-5个子元素
* 9:StickyLayoutHelper: stikcy布局, 可以配置吸顶或者吸底
* 10:StaggeredGridLayoutHelper: 瀑布流布局,可配置间隔高度/宽度
3.使用方法与案例
3.0 具体可以参考我的实际案例:https://github.com/yangchong211/LifeHelper
3.1 初始化
创建VirtualLayoutManager对象,与RecycleView绑定
//创建VirtualLayoutManager对象
VirtualLayoutManager layoutManager = new VirtualLayoutManager(activity);
recyclerView.setLayoutManager(layoutManager);
3.2 设置回收复用池
设置回收复用池大小
//设置回收复用池大小,(如果一屏内相同类型的 View 个数比较多,需要设置一个合适的大小,防止来回滚动时重新创建 View)
RecyclerView.RecycledViewPool viewPool = new RecyclerView.RecycledViewPool();
recyclerView.setRecycledViewPool(viewPool);
viewPool.setMaxRecycledViews(0, 20);
3.3 设置RecycleView适配器
设置 V - Layout的Adapter有两种方式:
方式1:继承 自 DelegateAdapter
方式2:继承 自 VirtualLayoutAdapter
目前只讨论方式1:继承 自 DelegateAdapter
定义:DelegateAdapter是V - Layout专门为管理 LayoutHelper定制的 Adapter
继承自VirtualLayoutAdapter
作用:通过管理不同布局的Adapter,继而管理不同的 LayoutHelper,从而实现使用不同组合布局
特别注意:虽不可直接绑定LayoutHelper,但是它内部有一个继承自RecyclerView.Adapter的内部类Adapter可以绑定LayoutHelper;
即通过一个List把绑定好的Adapter打包起来,再放去DelegateAdapter,这样就可以实现组合使用不同的布局
具体做法:
//设置适配器
DelegateAdapter delegateAdapter = new DelegateAdapter(layoutManager, true);
recyclerView.setAdapter(delegateAdapter
* ================================================
* 作 者:杨充
* 版 本:1.0
* 创建日期:2017/9/18
* 描 述:Vlayout框架基类适配器
* 修订历史:
* ================================================
public class BaseDelegateAdapter extends DelegateAdapter.Adapter<BaseViewHolder> {
private LayoutHelper mLayoutHelper;
private int mCount = -1;
private int mLayoutId = -1;
private Context mContext;
private int mViewTypeItem = -1;
protected BaseDelegateAdapter(Context context, LayoutHelper layoutHelper, int layoutId, int count, int viewTypeItem) {
this.mContext = context;
this.mCount = count;
this.mLayoutHelper = layoutHelper;
this.mLayoutId = layoutId;
this.mViewTypeItem = viewTypeItem;
@Override
public LayoutHelper onCreateLayoutHelper() {
return mLayoutHelper;
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == mViewTypeItem) {
return new BaseViewHolder(LayoutInflater.from(mContext).inflate(mLayoutId, parent, false));
return null;
@Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
* 必须重写不然会出现滑动不流畅的情况
@Override
public int getItemViewType(int position) {
return mViewTypeItem;
//条目数量
@Override
public int getItemCount() {
return mCount;
3.4 添加模块的LayoutHelper
这里代码以LinearLayoutHelper为例子。详细可以参考我的案例
BaseDelegateAdapter titleAdapter = new BaseDelegateAdapter(activity, new LinearLayoutHelper(),
R.layout.view_vlayout_title, 1, Constant.viewType.typeTitle) {
@Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
super.onBindViewHolder(holder, position);
holder.setText(R.id.tv_title, title);
3.5 将生成的LayoutHelper 交给Adapter,并绑定到RecyclerView 对象
相关代码如下所示:
private void initRecyclerView() {
DelegateAdapter delegateAdapter = presenter.initRecyclerView(recyclerView);
//把轮播器添加到集合
BaseDelegateAdapter bannerAdapter = presenter.initBannerAdapter();
mAdapters.add(bannerAdapter);
//初始化九宫格
BaseDelegateAdapter menuAdapter = presenter.initGvMenu();
mAdapters.add(menuAdapter);
//初始化
BaseDelegateAdapter marqueeAdapter = presenter.initMarqueeView();
mAdapters.add(marqueeAdapter);
//初始化标题
BaseDelegateAdapter titleAdapter = presenter.initTitle("猜你喜欢");
mAdapters.add(titleAdapter);
//初始化list1
BaseDelegateAdapter girdAdapter = presenter.initList1();
mAdapters.add(girdAdapter);
//初始化标题
titleAdapter = presenter.initTitle("热门新闻");
mAdapters.add(titleAdapter);
//初始化list2
BaseDelegateAdapter linearAdapter = presenter.initList2();
mAdapters.add(linearAdapter);
//初始化标题
titleAdapter = presenter.initTitle("新闻导航");
mAdapters.add(titleAdapter);
//初始化list3
BaseDelegateAdapter girdAdapter3 = presenter.initList3();
mAdapters.add(girdAdapter3);
//初始化标题
titleAdapter = presenter.initTitle("为您精选");
mAdapters.add(titleAdapter);
//初始化list3
BaseDelegateAdapter plusAdapter = presenter.initList4();
mAdapters.add(plusAdapter);
//初始化折叠式指示器控件
//initSticky();
//mAdapters.add(stickyAdapter);
//初始化list控件
titleAdapter = presenter.initTitle("优质新闻");
mAdapters.add(titleAdapter);
linearAdapter = presenter.initList5();
mAdapters.add(linearAdapter);
//设置适配器
delegateAdapter.setAdapters(mAdapters);
4.相关代码说明
4.1 VirtualLayoutAdapter
* 定义:数据适配器。继承自系统的Adaper
* 作用:创建组件 & 绑定数据到组件
* 额外:定义了两个接口:
* getLayoutHelper():用于返回某个位置组件对应的一个 LayoutHelper
* setLayoutHelpers():调用此方法设置整个页面所需要的一系列 LayoutHelper
* 这两方法的具体实现委托给 VirtualLayoutManager 完成
4.2 VirtualLayoutManager
* 定义:布局管理器。继承自系统的 LinearLayoutManager
* 作用:
* 在 RecyclerView 加载组件或者滑动时调用 VirtualLayoutManager 的 layoutChunk(),返回当前还有哪些空白区域可摆放组件
* 管理 LayoutHelper 列表
* 额外:实现了 VirtualLayoutAdapter 的 getLayoutHelper() & setLayoutHelpers()
4.3 LayoutHelper
* 定义:LayoutHelper 寻找器
* 作用:根据页面状态 寻找对应的 LayoutHelper 并返回给 VirtualLayoutManager
* VirtualLayoutManager 会持有一个 LayoutHelperFinder
* 当 layoutChunck() 被调用时会传入一个位置参数,告诉 VirtualLayoutManager 当前要布局第几个组件
* VirtualLayoutManager 通知持有的 LayoutHelperFinder 找到传入参数位置对应的 LayoutHelper(每个 LayoutHelper 都会绑定它负责的布局区域的起始位置和结束位置)
4.4 LayoutHelperFinder
* 定义:布局协助器
* 作用:负责具体的布局逻辑
4.5 MarginLayoutHelper
* 定义:继承自 LayoutHelper
* 作用:扩展 LayoutHelper,提供了布局常用的 内边距padding、外边距margin 的计算功能
4.6 BaseLayoutHelper
* 定义:MarginLayoutHelper 的第一层具体实现
* 作用:填充 当前LayoutHelper 在屏幕范围内的具体区域 背景色、背景图等逻辑
4.7 子LayoutHelper
* 定义:MarginLayoutHelper 的第二层具体实现
* 作用:负责具体的布局逻辑
* 每种 子LayoutHelper 负责一种布局逻辑
* 重点实现了 beforeLayout()、doLayout()、afterLayout()
* 特别是 doLayout():会获取一组件,并对组件进行尺寸计算、界面布局
* V - Layout 默认实现了10种默认布局:(对应同名的LayoutHelper)
* 特别注意:
* 每一种 LayoutHelper 负责布局一批组件范围内的组件,不同组件范围内的组件之间,如果类型相同,可以在滑动过程中回收复用。因此回收粒度比较细,且可以跨布局类型复用.
* 支持扩展外部:即注册新的 LayoutHelper,实现特殊的布局方式。下面会详细说明
5.相关属性介绍
5.1 LayoutHelper方法说明
* 5.1.1 margin, padding
* Margin, padding就是外边距、内边距
* 它不是整个RecyclerView页面的margin和padding,它是每一块LayoutHelper所负责的区域的margin和padding。
* 一个页面里可以有多个LayoutHelper,意味着不同LayoutHelper可以设置不同的margin和padding。
* LayoutHelper的margin和padding与页面RecyclerView的margin和padding可以共存。
* 目前主要针对非fix类型的LayoutHelper实现了margin和padding,fix类型LayoutHelper内部没有相对位置关系,不处理边距。
* 5.1.2 dividerHeight
* LinearLayoutHelper的属性,LinearLayoutHelper是像ListView一样的线性布局,dividerHeight就是每个组件之间的间距。
* 5.1.3 aspectRatio
* 为了保证布局过程中视图的高度一致,我们设计了aspectRatio属性,它是宽与高的比例,LayoutHelper里有aspectRatio属性,通过vlayout添加的视图的LayoutParams也有aspectRatio属性,后者的优先级比前者高,但含义不一样。
* LayoutHelper定义的aspectRatio,指的是一行视图整体的宽度与高度之比,当然整体的宽度是减去了RecyclerView和对应的LayoutHelper的margin, padding。
* 视图的LayoutParams定义的aspectRatio,指的是在LayoutHelper计算出视图宽度之后,用来确定视图高度时使用的,它会覆盖通过LayoutHelper的aspectRatio计算出来的视图高度,因此具备更高优先级。
* 5.1.4 bgColor, bgImg
* 背景颜色或者背景图,这其实不是布局属性,但是由于在vlayout对视图进行了直接布局,不同区域的视图的父节点都是RecyclerView,如果想要针对某一块区域单独绘制背景,就很难做到了。vlayout框架对此做了特殊处理,对于非fix、非float类型的LayoutHelper,支持配置背景色或背景图。同样目前主要针对非fix类型的LayoutHelper实现这个特性。
* 5.1.5 weights
* ColumnLayoutHelper, GridLayoutHelper的属性,它们都是提供网格状的布局能力,建议使用GridLayoutHelper,它的能力更加强大,参考下文介绍。默认情况下,每个网格中每一列的宽度是一样的,通过weights属性,可以指定让每一列的宽度成比例分配,就像LinearLayout的weight属性一样。 weights属性是一个float数组,每一项代表某一列占父容器宽度的百分比,总和建议是100,否则布局会超出容器宽度;如果布局中有4列,那么weights的长度也应该是4;长度大于4,多出的部分不参与宽度计算;如果小于4,不足的部分默认平分剩余的空间。
* 比如,setweights(0.6 , 0.2 , 0.2);那么分配比例是3:1:1
* 5.1.6 vGap, hGap
* GridLayoutHelper与StaggeredGridLayoutHelper都有这两个属性,分别控制视图之间的垂直间距和水平间距。
* 5.1.7 spanCount, spanSizeLookup
* GridLayoutHelper的属性,参考于系统的GridLayoutManager,spanCount表示网格的列数,默认情况下每一个视图都占用一个网格区域,但通过提供自定义的spanSizeLookUp,可以指定某个位置的视图占用多个网格区域
* 5.1.8 autoExpand
* GridLayoutHelper的属性,当一行里视图的个数少于spanCount值的时候,如果autoExpand为true,视图的总宽度会填满可用区域;否则会在屏幕上留空白区域。
* 5.1.9 lane
* StaggeredGridLayoutHelper中有这个属性,与GridLayoutHelper里的spanCount类似,控制瀑布流的列数。
5.2 fix类型的LayoutHelper方法说明
* 5.2.1 fixAreaAdjuster
* fix类型的LayoutHelper,在可能需要设置一个相对父容器四个边的偏移量,比如整个页面里有一个固定的标题栏添加在vlayout容器上,vlayout内部的fix类型视图不希望与外部的标题有所重叠,那么就可以设置一个fixAreaAdjuster来做偏移。
* 5.2.2 alignType, x, y
* FixLayoutHelper, ScrollFixLayoutHelper, FloatLayoutHelper的属性,表示吸边时的基准位置,有四个取值,分别是TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT。x和y是相对这四个位置的偏移量,最终的偏移量还要受上述的fixAreaAdjuster影响。
* TOP_LEFT:基准位置是左上角,x是视图左边相对父容器的左边距偏移量,y是视图顶边相对父容器的上边距偏移量;
* TOP_RIGHT:基准位置是右上角,x是视图右边相对父容器的右边距偏移量,y是视图顶边相对父容器的上边距偏移量;
* BOTTOM_LEFT:基准位置是左下角,x是视图左边相对父容器的左边距偏移量,y是视图底边相对父容器的下边距偏移量;
* BOTTOM_RIGHT:基准位置是右下角,x是视图右边相对父容器的右边距偏移量,y是视图底边相对父容器的下边距偏移量;
* 5.2.3 showType
* ScrollFixLayoutHelper的属性,取值有SHOW_ALWAYS, SHOW_ON_ENTER, SHOW_ON_LEAVE。
* SHOW_ALWAYS:与FixLayoutHelper的行为一致,固定在某个位置;
* SHOW_ON_ENTER:默认不显示视图,当页面滚动到这个视图的位置的时候,才显示;
* SHOW_ON_LEAVE:默认不显示视图,当页面滚出这个视图的位置的时候显示;
* 5.2.4 stickyStart, offset
* StickyLayoutHelper的属性,当视图的位置在屏幕范围内时,视图会随页面滚动而滚动;当视图的位置滑出屏幕时,StickyLayoutHelper会将视图固定在顶部(stickyStart = true)或者底部(stickyStart = false),固定的位置支持设置偏移量offset。
6.存在的bug
7.源码分析
8.关于其他更多
8.1 关于本篇博客更新日志
v1.0.0 17年9月19日
v1.0.1 17年12月11日
Android开发技术周报 Issue#11
Android开发技术周报 Issue#11
声明:所有内容收集整理自网络。如有侵权,请联系删除。微信公众号上请点击“阅读原文”阅读完整版本。
1. 个人开发者可申请小程序
小程序开放个人开发者申请注册,个人用户可访问微信公众平台,扫码验证个人身份后即可完成小程序帐号申请并进行代码开发。
请访问:微信公众平台
2. 天猫团队开源跨平台模块化 UI 界面开发框架 Tangram
Tangram,七巧板,是天猫团队刚刚开源的跨平台模块化 UI 界面方案。据悉,之所以命名为 Tangram ,是希望它能像七巧板一样可以通过几块积木就搭出丰富多彩的界面。
Android开发
1. 移动应用设计:综述、导航和浏览
应用程序现在是主流的提供内容和服务的方式,并已经广受用户信赖。但在一个已经高度被开发的市场里,一款移动应用如何做到有用,有意义并且有价值,以使客户满意并留存呢? Google 的 UX 研究主管 Jenny Gove 为您详细介绍了创建一款优秀的移动应用的 25 条原则,我们会在这次连载中分批次为您逐一详述。
2. Android View进阶之RecyclerView 实现滑动删除和拖拽功能
从Android 5.0开始,谷歌推出了新的控件RecyclerView,相对于早它之前的ListView,优点多多,功能强大,也给我们的开发着提供了极大的便利,今天自己学习一下RecyclerView轻松实现滑动删除及拖拽的效果。
3. 实现一个带下拉弹簧动画的 ScrollView
在刚推出的 Support Library 25.3.0 里面新增了一个叫 SpringAnimation 的动画,也就是弹簧动画。要是用它来做一个滑动控件下拉回弹的效果,应该不错吧。
4. Android 渲染优化
Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染,要每次渲染都成功,这样就必须达到流畅的画面所需要的60fps,否则会发生丢帧的现象,丢帧越多,用户明确感到卡顿。卡顿现象,由于复杂的布局或界面过度绘制未能在每帧16ms内完成导致的。本文讲解了渲染优化的技巧。
5. 天猫 Android Tangram 的基础 —— vlayout
vlayout 是手机天猫 Android 版内广泛使用的一个基础 UI 框架项目,提供了一个用于 RecyclerView 的自定义的 LayoutManger,可以实现不同布局格式的混排,目标是支撑客户端 native 页面的快速开发。它也是 Tangram 框架的基础模块,现已开源。
6. 是时候和 Implict Broadcast 说再见了
Android O对于系统广播(Broadcast)的改变归根结底都是为了进一步的节省功耗。Google在Android Marshmallo(6.0, API level 23)中引入了Doze and App Standby来改进Android系统的电池表现。本文主要介绍了Android O中对Broadcast的改变。
7. Android内存优化总结&实践
Andorid内存优化一直是一个比较重要的话题,我们可以通过各种内存泄露检测组件,MAT查看内存占用,Memory Monitor跟踪整个App的内存变化情况, Heap Viewer查看当前内存快照, Allocation Tracker追踪内存对象的来源,以及利用崩溃上报平台从多个方面对App内存进行监控和优化。本文列举了一些常见的情况,介绍了Android内存优化的方案。
8. Android 优化APP 构建速度的17条建议
较长的构建时间将会减缓项目的开发进度,特别是对于大型的项目,app的构建时间长则十几分钟,短则几分钟,长的构建时间已经成了开发瓶颈,本篇文章根据Google官方文档,外加作者的一些理解,目的是提供一些提升app构建速度的优化建议。
开源库&项目&工具
1. Tangram-Android
Tangram是一套动态化构建 Native 页面的框架,它包含 Tangram Android、Tangram iOS,管理后台等一些列基础设施。本工程是 Tangram Android 的sdk 项目地址,底层依赖于vlayout 和 UltraViewPager。
2. AppMethodOrder
一个能让你了解所有函数调用顺序的Android库(无需侵入式代码)
Email:yanghui1986527#gmail.com
Github: https://github.com/snowdream
Blog: http://snowdream.github.io/blog/
简书:http://www.jianshu.com/u/748f0f7e6432
云栖博客:https://yq.aliyun.com/u/snowdream86
QQ群: 529327615
微信公众号: sn0wdr1am
1. Android Studio 2.3 正式版发布
Android Studio 2.3 正式版发布了,该版本包含一些新特性,包括对 WebP 支持的更新;ConstraintLayout 库支持更新和布局编辑器的部件面板。提供一个新的 App Link 助手可以帮助你在应用中构建 URI 的统一视图。新的运行按钮提供更直观和可靠的立即运行体验。最后是 Android 模拟器的测试,支持文本的复制和粘贴。详细介绍请看官方发行说明。
Android开发
1. 从源码出发浅析Android TV的焦点移动原理-上篇
2. 从源码出发浅析Android TV的焦点移动原理-下篇
相对于手机上用手指点击屏幕产生的Click事件, 在使用Android TV的过程中,遥控器是一个主流的操作工具,通过点击遥控器的方向键来控制焦点的移动。当焦点移动到目标控件上之后,按下遥控器的确定键,才会触发一个Click事件,进而去做下一步的处理。焦点的移动如下图所示。
3. RecyclerView 实现滑动删除和拖拽功能
从Android 5.0开始,谷歌推出了新的控件RecyclerView,相对于早它之前的ListView,优点多多,功能强大,也给我们的开发着提供了极大的便利,今天自己学习一下RecyclerView轻松实现滑动删除及拖拽的效果。
4. Android性能优化(一)之启动加速35%
从应用的启动优化开始,根据实际案例,打造闪电般的App启动速度。
5. Android 7.0应用冷启动流程分析
最近在为自己Moto G定制Rom,顺便重新读了一遍Android 7.0的相关源码,特此记录当做笔记.
6. Android LayoutInflater源码解析
大家对LayoutInflater一定不陌生,LayoutInflater是一个用于将xml布局文件加载为View或者ViewGroup对象的工具,我们可以称之为布局加载器。在Fragment的onCreateView方法、ListView Adapter的getView方法等许多地方都可以见到它的身影。本文详细介绍了LayoutInflater的用法以及加载布局的工作原理。
7. Android应用安全风险与防范
Android开发除了部分功能采用C/C++编码外,其余主要都是采用Java进行编码开发功能。Java应用非常容易被反编译,Android自然也不例外。只要利用apktool等类似的反编译工具,就可以通过安装包获取源代码。Google为了保护开发者的知识产权,为Android提供了ProGuard混淆方案,以增加反编译后源码阅读,但对于Android开发老司机和逆向工程师来说,解读还原出源代码只是时间问题。
8. Android架构那些事之第三方库的隔离
我们都知道一个好的架构会使我们的开发变得事半功倍。设计架构的目的在于使我们的客户端易于扩展、方便单元测试、可复用。做到使模块之间低耦合,模块内部高内聚。
9. 微信tinker快速集成
微信tinker快速集成
开源库&项目&工具
1. vlayout
vlayout是手机天猫Android版内广泛使用的一个基础UI框架项目。提供了一个用于RecyclerView的自定义的LayoutManger,可以实现不同布局格式的混排,目标是支撑客户端native页面的快速开发。它也是Tangram框架的基础模块。
2. AndroidSkinAnimator
皮肤切换动画,支持全局View animation everywhere
3. CameraKit-Android
CameraKit is an extraordinarily easy to use utility to work with the infamous Android Camera and Camera2 APIs. Built by Dylan McIntyre.
4. Android-CleanArchitecture
This is a sample app that is part of a series of blog posts I have written about how to architect an android application using Uncle Bob's clean architecture approach.
Email:yanghui1986527#gmail.com
Github: https://github.com/snowdream
Blog: http://snowdream.github.io/blog/
简书:http://www.jianshu.com/u/748f0f7e6432
云栖博客:https://yq.aliyun.com/u/snowdream86
QQ群: 529327615
微信公众号: sn0wdr1am
PyQt5 笔记(01):嵌套布局
PyQt5 有四种布局:水平(QHBoxLayout)、竖直(QVBoxLayout)、网格(QGridLayout)、表单(QFormLayout)在窗体中单一的布局应该不难,但若是比较复杂的布局,一般涉及到布局的嵌套,这就头疼了。本文的四个知识点:1. 布局不能直接嵌套(如果我错了,欢迎指正!)2. 内层的布局必须先“附着”在一个空 QWidget 上3. 然后把这个“承载”着内层布局的空部件添加至外层布局4. 最后,别忘记把全局布局“附着”到窗体本尊
0. 先看效果图
下面对布局进行分析
1. 全局布局分析
全局布局使用了一个水平布局
wlayout = QtWidgets.QHBoxLayout()
hlayout = QtWidgets.QHBoxLayout()
vlayout = QtWidgets.QVBoxLayout()
glayout = QtWidgets.QGridLayout()
flayout = QtWidgets.QFormLayout()
wlayout = QtWidgets.QHBoxLayout() # 全局布局(1个):水平
hlayout = QtWidgets.QHBoxLayout() # 局部布局(4个):水平、竖直、网格、表单
vlayout = QtWidgets.QVBoxLayout()
glayout = QtWidgets.QGridLayout()
flayout = QtWidgets.QFormLayout()
hlayout.addWidget(QtWidgets.QPushButton(str(1))) # 局部布局添加部件(例如:按钮)
hlayout.addWidget(QtWidgets.QPushButton(str(2)))
vlayout.addWidget(QtWidgets.QPushButton(str(3)))
vlayout.addWidget(QtWidgets.QPushButton(str(4)))
glayout.addWidget(QtWidgets.QPushButton(str(5)),0,0)
glayout.addWidget(QtWidgets.QPushButton(str(6)),0,1)
glayout.addWidget(QtWidgets.QPushButton(str(7)),1,0)
glayout.addWidget(QtWidgets.QPushButton(str(8)),1,1)
flayout.addWidget(QtWidgets.QPushButton(str(9)))
flayout.addWidget(QtWidgets.QPushButton(str(10)))
flayout.addWidget(QtWidgets.QPushButton(str(11)))
flayout.addWidget(QtWidgets.QPushButton(str(12)))
hwg = QtWidgets.QWidget() # 准备四个部件
vwg = QtWidgets.QWidget()
gwg = QtWidgets.QWidget()
fwg = QtWidgets.QWidget()
hwg.setLayout(hlayout) # 四个部件设置局部布局
vwg.setLayout(vlayout)
gwg.setLayout(glayout)
fwg.setLayout(flayout)
wlayout.addWidget(hwg) # 四个部件加至全局布局
wlayout.addWidget(vwg)
wlayout.addWidget(gwg)
wlayout.addWidget(fwg)
self.setLayout(wlayout) # 窗体本尊设置全局布局
if __name__=="__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
win = MyWindow()
win.show()
sys.exit(app.exec_())