2016-12-12 08:38 3898人阅读 评论(8) 收藏 举报
 分类:
Android(25)   设计模式(3)   快速开发(3) 

版权声明:本文为博主原创文章,未经博主允许不得转载。

 

目录(?)[+]

 

转载请标明出处:  
http://blog.csdn.net/zxt0601/article/details/53576092 
本文出自:【张旭童的博客】(http://blog.csdn.net/zxt0601
代码传送门:喜欢的话,随手点个star。多谢 
https://github.com/mcxtzhang/all-base-adapter

概述

开发中,经常会用到动态在ScrollView、LinearLayout里addView的事,尤其是ItemView一样时,每次都要写一大堆代码 inflater 动态addView,很烦。

还有就是在嵌套ListView、ScrollView时,想采用LinearLayout替代(这样性能更佳,不明白的看一个控件搞定嵌套ListView),但动态addView步骤神烦。

这个时候就开始期待,能不能有一种快速为任意ViewGroup添加子View的东西。

我之前为此事特意写过一篇LinearLayout封装博文,封装了一个控件使用。试图一个控件搞定嵌套ListView。但是后来发现,采用继承某个ViewGroup做这个事情不够优雅 ,对代码有侵入性,如果有其他ViewGroup需要动态addView,就会写重复的代码 。

前几天有人在群里问,如何方便的给ScrollView动态添加不同种类型的childView,类似RecyclerView那样。我之前的封装由于内部有一个简单的重用机制,只支持单一ItemType,也不支持多种类型的childView

那么需求就来了: 
快速简单使用  
* 支持任意ViewGroup 
无耦合 
无侵入性 
* Item支持多种类型

除此之外,我还加入: 
* 为ItemView设置OnItemClickListener 
* 为ItemView设置OnItemLongClickListener

本文就封装了这么一个东西。

核心: 
* 利用Adapter模式封装getView的操作 
* 搭配一个工具类,为所有ViewGroup addView。 
* 再封装出两个使用快速简单的Adapter 分别用于添加 单一Item布局、多种Item布局。

PS:所以本文也算是填了之前的一个坑,在之前适配器模式博文文末,我就提到要写一篇为流式布局增加Adapter的文章,作为Adapter的实战演练。使用本文封装的Adapter自然可以达到这一点。

由于采用Adapter隔离ViewGroup和ItemView,在切换ViewGroup时,十分方便。 
如:在需求让你把一个HorizontalScrollView包裹的水平标签转换成流式布局时,只需要在xml替换控件即可。Adapter将自动完成适配的工作。其他代码一句不用修改。

不BB了,先看看以后如何使用吧,够不够简单粗暴

使用预览

单一Item类型:

Adapter泛型传入JavaBean,构造函数传入数据集和layout布局,一句代码搞定:

        //单一ItemView
ViewGroupUtils.addViews(mLinearLayout, new SingleAdapter<TestBean>(this, mDatas, R.layout.item_test) {
@Override
public void onBindView(ViewGroup parent, View itemView, TestBean data, int pos) {
Glide.with(LinearLayoutActivity.this)
.load(data.getAvatar())
.into((ImageView) itemView.findViewById(R.id.ivAvatar));
((TextView) itemView.findViewById(R.id.tvName)).setText(data.getName());
}
});

效果:

 
以前会用ScrollView嵌套ListView,现在只要用ScrollView套LinearLayout即可,性能更佳。

多种Item类型:

多种Item类型分两种情况:

数据结构相同:

数据结构相同依然可以给Adapter传入泛型,避免强转:

        //多种ItemViewType,但是数据结构相同,可以传入数据结构泛型,避免强转
ViewGroupUtils.addViews(linearLayout, new MulTypeAdapter<MulTypeBean>(this, initDatas()) {
@Override
public void onBindView(ViewGroup parent, View itemView, MulTypeBean data, int pos) {
((TextView) itemView.findViewById(R.id.tvWords)).setText(data.getName() + "");
Glide.with(MulTypeActivity.this)
.load(data.getAvatar())
.into((ImageView) itemView.findViewById(ivAvatar));
}
});

效果:

数据结构不同:

如果数据结构不同,则不用传入泛型,但是使用时需要强转:

        //多种Item类型:数据结构不同 不传泛型了 使用时需要强转javaBean,判断ItemLayoutId
ViewGroupUtils.addViews((ViewGroup) findViewById(R.id.activity_mul_type_mul_bean), new MulTypeAdapter(this, datas) {
@Override
public void onBindView(ViewGroup parent, View itemView, IMulTypeHelper data, int pos) {
switch (data.getItemLayoutId()) {
case R.layout.item_mulbean_1:
MulBean1 mulBean1 = (MulBean1) data;
Glide.with(MulTypeMulBeanActivity.this)
.load(mulBean1.getUrl())
.into((ImageView) itemView);
break;
case R.layout.item_mulbean_2:
MulBean2 mulBean2 = (MulBean2) data;
TextView tv = (TextView) itemView;
tv.setText(mulBean2.getName());
}
}
});

数据结构:

public class MulBean1 implements IMulTypeHelper {
private String url;
@Override
public int getItemLayoutId() {
return R.layout.item_mulbean_1;
}
}
public class MulBean2 implements IMulTypeHelper {
private String name;
@Override
public int getItemLayoutId() {
return R.layout.item_mulbean_2;
}
}

Item1布局是一个ImageView,Item2布局是一个TextView  
效果: 

Item点击事件

item的点击和长按等事件,有两种方法设置,这里以点击事件为例,长按事件同理:

Adapter.onBindView()里设置

Adapter.onBindView()方法里能拿到ItemView,自然就可以设置各种事件。类似RecyclerView。

在这里设置优先级更高。原因后文会提到。

@Override
public void onBindView(ViewGroup parent, View itemView, final MulTypeBean data, int pos) {
....
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Toast.makeText(mContext, "onBindView里设置:文字是:" + data.getName(), Toast.LENGTH_SHORT).show();
}
});
}

通过ViewGroupUtils设置

可以在ViewGroupUtils.addViews直接作为参数传入.

也可以用ViewGroupUtils.setOnItemClickListener()设置 。

优先级比Adapter.onBindView()里设置低,原因后文会提到。

        //设置OnItemClickListener
OnItemClickListener onItemClickListener = new OnItemClickListener() {
@Override
public void onItemClick(ViewGroup parent, View itemView, int position) {
Toast.makeText(MulTypeActivity.this, "通过OnItemClickListener设置:" + position, Toast.LENGTH_SHORT).show();
}
};
//可以在`ViewGroupUtils.addViews`直接作为参数传入.\
ViewGroupUtils.addViews(linearLayout, adapter ,onItemClickListener);
//或者 也可以用`ViewGroupUtils.setOnItemClickListener()`设置
ViewGroupUtils.setOnItemClickListener(linearLayout,onItemClickListener);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

看起来还是挺好的,嗯~至少我自己这么觉得,我个人比较喜欢这种0耦合,每一个库都像可组装拆卸的机关枪一样,拿起来就用。而不是笨重功能繁多的重装坦克。 
搭配我的得意之作,每次必安利的史上集成最简单侧滑菜单控件。 
效果如下:

无特殊设置,仅仅替换ViewGroup为流式布局,替换Item根布局为我撸的侧滑菜单库,能感受到这种0耦合的库的魅力了么。23333333 。

设计思路

下面就让我手摸手带大家实现它。 
先看类图。

UML类图:

先简要概括 
* 我们的顶层接口IViewGroupAdapter暴露出两个方法供ViewGroup使用。

  • ViewGroupUtils 是为任意ViewGroup 动态addView的工具类,只依赖于 IViewGroupAdapter 接口即可完成工作。

  • BaseAdapter是第二层,在这一层引入了数据集,用List<T>保存。实现IViewGroupAdapter的方法,重载一个三参数的getView()方法,供子类去实现。

  • SingleAdapter是第三层,一个简化的Adapter,只支持单种Item,以LayoutId 构建View。实现getView()方法,并暴露出onBindView()供用户快速使用。

  • MulTypeAdapter也同处第三层,一个支持多种Item的Adapter。依赖IMulTypeHelper接口,利用其getItemLayoutId() 方法去实现getView()方法,并暴露出onBindView()供用户快速使用。

顶层接口设计

顶层接口,即IViewGroupAdapter

根据迪米特法则(最少知道原则),我们应该抽象出一个顶层的接口,对ViewGroup暴露出最少的方法供使用。

我们想一下,对于ViewGroup,它最少只需要哪些就能完成我们的需求。 
* ChildView是什么—> View 
* 有多少ChildView 需要 添加—>count 
所以,我们的最顶层接口如下编写:

public interface IViewGroupAdapter {
/**
* ViewGroup调用获取ItemView
*
* @param parent
* @param pos
* @return
*/
View getView(ViewGroup parent, int pos); /**
* ViewGroup调用,得到ItemCount
*
* @return
*/
int getCount();
}

ok,代码写到这里,后面的我们暂且不提,我们就可以写动态addView的工具类了。因为我们的ViewGroup依赖的所有信息都由IViewGroupAdapter这个接口提供了。

工具类

ViewGroupUtils 是为任意ViewGroup 动态addView的工具类,不考虑点击事件的情况下,只依赖于 IViewGroupAdapter 接口即可完成工作。 
如下编写:

    /**
* 为任意ViewGroup 添加ItemViews.
*
* @param viewGroup 必传
* @param adapter 必传,至少提供要add的View和需要add的count
* @param removeViews 是否需要remove掉之前的Views
*/
public static void addViews(final ViewGroup viewGroup, IViewGroupAdapter adapter
, boolean removeViews) {
if (viewGroup == null || adapter == null) {
return;
}
//如果需要remove掉之前的Views
if (removeViews && viewGroup.getChildCount() > 0) {
viewGroup.removeAllViews();
}
//开始添加子Views,通过Adapter获得需要添加的Count
int count = adapter.getCount();
for (int i = 0; i < count; i++) {
//通过Adapter获得ItemView
View itemView = adapter.getView(viewGroup, i);
viewGroup.addView(itemView); }
}

如此即可完成 动态给任意ViewGroup addView 的工作。

不过我们开头提过,我还是想引入ItemView的点击和长按事件的。但是关于这两个ItemListener,还是有一些东西要考虑的。

ItemListener的设计

为ViewGroup提供OnItemClickListener,有个问题需要考虑: 
如果使用者调用了setOnItemClickListener,且在Adapter里自己又对ItemView设置了OnClickListener,那么究竟该触发哪个Listener,即它们的优先级。

我们不应该自己靠脑子想答案,还是参照系统原有的设计比较好。既然ListView提供了OnItemClickListener,那么我们参照它的设计来就行。

先说结论:通过参照ListView的源码,以及实验得知,对ItemViewOnClickListener优先级 > ViewGroup的OnItemClickListener

为什么?答案在源码中,感兴趣看,不感兴趣直接跳过。

从AbsListView的onTouchEvent()->onTouchUp()->PerformClick->performItemClick-> AdapterView.performItemClick() -> AdapterView.mOnItemClickListener,即可找到答案。

这里不详细分析源码,不是本文重点,简单的说,从入口处是AbsListView的onTouchEvent() ,我们可以知道,ListView本身并没有干预ItemView的点击事件(即没有为其设置OnClickListener),是在ItemView不消耗Touch事件时 才进行Item点击事件的触发。 
因此若ItemView设置了OnClickListener,AbsListView的onTouchEvent()将收不到MotionEvent.ACTION_UP事件,因而也不会触发OnItemClickListener。所以这决定了ItemViewOnClickListener优先级高。

还有一个问题,我们可以通过View.hasOnClickListeners()这个方法来判断View是否设置了OnClickListener,但是这个方法在API15才加入,为了能兼容低版本,我采用了另一种方法判断,itemView.isClickable(),如果true,我当做有点击事件,如果false,我当做没有。

其实在ListView中这个问题也是一样的,itemView.isClickable()为true的话,点击事件就被拦截住,不会分发至AbsListView的onTouchEvent()里了。所以我们这么写是没问题,并且是正确的。

ItemListener的完整实现:

既然如此,那么我们在程序中,应该如下编写:

        for (int i = 0; i < count; i++) {
View itemView = adapter.getView(viewGroup, i);
viewGroup.addView(itemView);
//添加点击事件
if (null != onItemClickListener && !itemView.isClickable()) {
final int finalI = i;
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onItemClickListener.onItemClick(viewGroup, view, finalI);
}
});
}
//添加点击事件
if (null != onItemLongClickListener && !itemView.isLongClickable()) {
final int finalI = i;
itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
return onItemLongClickListener.onItemLongClick(viewGroup, view, finalI);
}
});
}
}

所以完整的addViews()如下:

    /**
* 为任意ViewGroup 添加ItemViews.
*
* @param viewGroup 必传
* @param adapter 必传,至少提供要add的View和需要add的count
* @param removeViews 是否需要remove掉之前的Views
* @param onItemClickListener Item点击事件
* @param onItemLongClickListener Item长按事件
*/
public static void addViews(final ViewGroup viewGroup, IViewGroupAdapter adapter
, boolean removeViews
, final OnItemClickListener onItemClickListener
, final OnItemLongClickListener onItemLongClickListener) {
if (viewGroup == null || adapter == null) {
return;
}
//如果需要remove掉之前的Views
if (removeViews && viewGroup.getChildCount() > 0) {
viewGroup.removeAllViews();
}
//开始添加子Views,通过Adapter获得需要添加的Count
int count = adapter.getCount();
for (int i = 0; i < count; i++) {
//通过Adapter获得ItemView
View itemView = adapter.getView(viewGroup, i);
viewGroup.addView(itemView);
//添加点击事件,itemView之前没有点击事件才会去设置
if (null != onItemClickListener && !itemView.isClickable()) {
final int finalI = i;
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onItemClickListener.onItemClick(viewGroup, view, finalI);
}
});
}
//添加长按事件itemView之前没有长按事件才会去设置
if (null != onItemLongClickListener && !itemView.isLongClickable()) {
final int finalI = i;
itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
return onItemLongClickListener.onItemLongClick(viewGroup, view, finalI);
}
});
}
}
}

实际中,我们可能不需要设置Listener,为了快速使用,我又提供了两个重载方法:

    /**
* 为任意ViewGroup 添加ItemViews.
* 并且会清除掉之前所有add过的View
*
* @param viewGroup 必传
* @param adapter 必传,至少提供要add的View和需要add的count
*/
public static void addViews(final ViewGroup viewGroup, IViewGroupAdapter adapter) {
addViews(viewGroup, adapter, true, null, null);
} /**
* 为任意ViewGroup 添加ItemViews.
* 并且会清除掉之前所有add过的View
*
* @param viewGroup 必传
* @param adapter 必传,至少提供要add的View和需要add的count
* @param onItemClickListener Item点击事件
*/
public static void addViews(final ViewGroup viewGroup, IViewGroupAdapter adapter
, final OnItemClickListener onItemClickListener) {
addViews(viewGroup, adapter, true, onItemClickListener, null);
}

若不在addViews()里设置ItemListener,也可以通过setOnItemClickListener()setOnItemLongClickListener() 设置,不过这两个方法必须在addViews()方法之后调用:

    /**
* 为任意ViewGroup设置OnItemClickListener.
* 该方法必须在addViews()方法之后调用,否则无效。
* 因为ItemView 必须被添加在ViewGroup里才能遍历到。
* 建议直接在addViews()方法里传入OnItemClickListener进行设置,性能更高
*
* @param viewGroup
* @param onItemClickListener
*/
public static void setOnItemClickListener(final ViewGroup viewGroup, final OnItemClickListener onItemClickListener) {
if (viewGroup == null || onItemClickListener == null) {
return;
}
int childCount = viewGroup.getChildCount();
for (int i = 0; i < childCount; i++) {
final View itemView = viewGroup.getChildAt(i);
//itemView之前没有点击事件才会去设置
if (null != itemView && !itemView.isClickable()) {
final int finalI = i;
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onItemClickListener.onItemClick(viewGroup, itemView, finalI);
}
});
}
}
} /**
* 为任意ViewGroup设置OnItemLongClickListener.
* 该方法必须在addViews()方法之后调用,否则无效。
* 因为ItemView 必须被添加在ViewGroup里才能遍历到。
* 建议直接在addViews()方法里传入OnItemLongClickListener进行设置,性能更高
*
* @param viewGroup
* @param onItemLongClickListener
*/
public static void setOnItemLongClickListener(final ViewGroup viewGroup, final OnItemLongClickListener onItemLongClickListener) {
if (viewGroup == null || onItemLongClickListener == null) {
return;
}
int childCount = viewGroup.getChildCount();
for (int i = 0; i < childCount; i++) {
final View itemView = viewGroup.getChildAt(i);
//itemView之前没有长按事件才会去设置
if (null != itemView && !itemView.isLongClickable()) {
final int finalI = i;
itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View view) {
return onItemLongClickListener.onItemLongClick(viewGroup, itemView, finalI);
}
});
}
}
}

Adapter

终于到了我们的重头戏,Adapter。

BaseAdapter

BaseAdapter是第二层,在这一层引入了数据集,用List<T>保存。实现IViewGroupAdapter的方法,重载一个三参数的getView()方法,供子类去实现。

它和我们平时写的ListView、RecyclerView的Adapter就比较像了,我也是参照平时的写法。

核心就是实现IViewGroupAdaptergetView(ViewGroup parent, int pos)方法,增加一个数据,工作转交给三参数的getView(ViewGroup parent, int pos, T data)方法。

子类应该 实现 getView(ViewGroup parent, int pos, T data)方法,在其中inflate or new 出 ItemView,并绑定数据

public abstract class BaseAdapter<T> implements IViewGroupAdapter {
protected List<T> mDatas;
protected Context mContext;
protected LayoutInflater mInflater; /**
* ViewGroup调用获取ItemView,create bind一起做
*
* @param parent
* @param pos
* @return
*/
@Override
public View getView(ViewGroup parent, int pos) {
return getView(parent, pos, mDatas.get(pos));
} /**
* 实际的createItemView的地方
*
* @param parent
* @param pos
* @param data
* @return
*/
public abstract View getView(ViewGroup parent, int pos, T data); /**
* ViewGroup调用,得到ItemCount
*
* @return
*/
@Override
public int getCount() {
return mDatas != null ? mDatas.size() : 0;
} }

SingleAdapter

SingleAdapter是第三层,一个简化的Adapter,只支持单种Item,以LayoutId 构建View。实现getView()方法,并暴露出onBindView()供用户快速使用。

使用时,一般将数据结构的泛型传入,配合构造函数传入的ItemLayoutId使用。

它继承自BaseAdapter,所以它了实现getView(ViewGroup parent, int pos, T data)方法。在根据传入的itemLayoutId inflate出ItemView后,会回调onBindView(ViewGroup parent, View itemView, T data, int pos)方法,并返回ItemView供ViewGroup使用。

onBindView(ViewGroup parent, View itemView, T data, int pos)方法里,我们完成数据绑定的工作。例如加载图片,也可以设置点击事件。


public abstract class SingleAdapter<T> extends BaseAdapter<T> { private int mItemLayoutId; @Override
public View getView(ViewGroup parent, int pos, T data) {
//实现getView
View itemView = /*onCreateView(parent, pos)*/mInflater.inflate(mItemLayoutId, parent, false);
onBindView(parent, itemView, data, pos);
return itemView;
} /**
* 暴漏这个 让外部bind数据
*
* @param parent
* @param itemView
* @param data
* @param pos
*/
public abstract void onBindView(ViewGroup parent, View itemView, T data, int pos); }

MulTypeAdapter

MulTypeAdapter也同处第三层,一个支持多种Item的Adapter。依赖IMulTypeHelper接口,利用其getItemLayoutId() 方法去实现getView()方法,并暴露出onBindView()供用户快速使用。

public abstract class MulTypeAdapter<T extends IMulTypeHelper> extends BaseAdapter<T> {

    @Override
public View getView(ViewGroup parent, int pos, T data) {
View itemView = mInflater.inflate(data.getItemLayoutId(), parent, false);
onBindView(parent, itemView, data, pos);
return itemView;
} /**
* 暴漏这个 让外部bind数据
*
* @param parent
* @param itemView
* @param data
* @param pos
*/
public abstract void onBindView(ViewGroup parent, View itemView, T data, int pos);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

IMulTypeHelper接口,同样简单:

public interface IMulTypeHelper {
int getItemLayoutId();
}
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

总结

代码传送门:喜欢的话,随手点个star。多谢 
https://github.com/mcxtzhang/all-base-adapter

因为想支持任意ViewGroup,对ViewGroup代码无侵入性,因而对部分功能进行了取舍,例如设置OnItemXXXListener,如果采用继承ViewGroup,嵌入代码,可以做到不强制addViews()setOnItemClickListener()顺序。

通过上文我们能感受到一些面向接口编程的奥义,例如我们只需要设计好顶层的接口IViewGroupAdapter,在没写剩下的xxxAdapter时,工具类都可以写好,编译通过。 
这在以后扩展时,例如,我数据集不能用List来保存了,我可能是多个List or Map 等等,只需要实现IViewGroupAdapter接口即可定制一个Adapter。其他代码不需任何修改。

有人说过,设计模式怎么学,就是先学完一遍所有的设计模式。然后再全部忘掉他们,只要记住SOLID原则即可。 
我觉得很有道理,就像我之前一直在纠结代理模式和装饰者模式的区别,后来我想,编码时,我管它是什么模式,只要写出来的是易维护的代码即可。

to do list

  • 考虑加入复用缓存池
  • 考虑替换onBindView()ItemView->通用的ViewHolder,这样可以少写一些findViewById()代码。
  • 整合DataBinding 的通用Adapter入库。
  • 整合 RecyclerView、ListView的通用Adapter入库。
  • 加入一些自定义ViewGroup入库,例如流式布局,九宫格,Banner轮播图。

下文预告:

一个用ScrollView很容易实现,但RecyclerView、ListView就无法实现的动画效果,仿淘宝会员中心等级动画。凸显为所有ViewGroup增加Adapter模式的重要性。

DataBinding篇隆重登场

感兴趣可以去阅读 
http://blog.csdn.net/zxt0601/article/details/53618694

转载请标明出处:  
http://blog.csdn.net/zxt0601/article/details/53576092 
本文出自:【张旭童的博客】(http://blog.csdn.net/zxt0601
代码传送门:喜欢的话,随手点个star。多谢 
https://github.com/mcxtzhang/all-base-adapter

 
14
1
 
  相关文章推荐
查看评论
8楼 Xiaohui783 2017-05-04 11:59发表  [回复]
大神,看了你Git上的代码,添加了ViewHolder ,但是发现还是创建很多ViewHolder,这里的ViewHolder只是单纯的减少findViewById 的代码吗?
7楼 Xiaohui783 2017-05-04 11:56发表  [回复]
大神,刚去看了你Git 上的代码,看到你写了复用ViewHolder,但是日志发现还是会创建datas长度个数的的ViewHolder ,这里单单只是为了少用findViewById吗
6楼 Only_Engineer 2016-12-28 15:43发表  [回复]
好文很多啊
5楼 zzr吻火 2016-12-15 15:57发表  [回复]
为楼主的分享精神点赞,写得很详细。
4楼 verzqli 2016-12-12 14:35发表  [回复]
UML都画了 腻害
3楼 cmdos 2016-12-12 13:47发表  [回复]
图片挺好看的
2楼 梁山boy 2016-12-12 13:15发表  [回复]
已star, 期待加入ViewHolder以后的样子
1楼 ccc95 2016-12-12 12:20发表  [回复]
不明觉厉
 
您还没有登录,请[登录][注册]
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
 
 
 
 
 

(转载)Android快速开发偷懒必备,一句话搞定所有ViewGroup的Adapter . 支持自定义ViewGroup的更多相关文章

  1. 【Android】快速开发偷懒必备(二) 支持DataBinding啦~爽炸,一行实现花式列表[申明:来源于网络]

    [Android]快速开发偷懒必备(二) 支持DataBinding啦~爽炸,一行实现花式列表[申明:来源于网络] 地址:http://blog.csdn.net/zxt0601/article/de ...

  2. (转载)实例详解Android快速开发工具类总结

    实例详解Android快速开发工具类总结 作者:LiJinlun 字体:[增加 减小] 类型:转载 时间:2016-01-24我要评论 这篇文章主要介绍了实例详解Android快速开发工具类总结的相关 ...

  3. Android 快速开发系列 打造万能的ListView GridView 适配器

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38902805 ,本文出自[张鸿洋的博客] 1.概述 相信做Android开发的写 ...

  4. OpenCV3.4.1快速集成到Android studio中,10分钟搞定

    OpenCV3.4.1快速集成到Android studio中,10分钟搞定     转载 https://blog.csdn.net/yu540135101/article/details/8259 ...

  5. Android快速开发不可或缺的11个工具类

     Android快速开发不可或缺的11个工具类  :http://www.devst ore.cn/code/info/363.html

  6. Android快速开发不可或缺的11个工具类(下载)

    功能分类:工具     支持平台:Android     运行环境:Eclipse 开发语言:Java      开发工具:Eclipse         源码大小:11.45KB   下载地址:ht ...

  7. 【高德地图API】一句话搞定webmap(一)——轻地图组件

    原文:[高德地图API]一句话搞定webmap(一)——轻地图组件 摘要: 遥想当年,在APP中加入LBS元素相当困难:要刻苦学习java,要刻苦学习iOS开发,要刻苦学习javascript…… 而 ...

  8. 一句话搞定webmap(一)——轻地图组件

    摘要: 遥想当年.在APP中增加LBS元素相当困难:要刻苦学习java,要刻苦学习iOS开发,要刻苦学习javascript-- 而现在.要制作一张地图真是越来越easy了!居然仅仅须要一句话.就能够 ...

  9. AFNetworking3.0+MBProgressHUD二次封装,一句话搞定网络提示

    对AFNetworking3.0+MBProgressHUD的二次封装,使用更方便,适用性非常强: 一句话搞定网络提示: 再也不用担心网络库更新后,工程要修改很多地方了!网络库更新了只需要更新这个封装 ...

随机推荐

  1. 配置postgreSQL允许外部连接

    配置远 程连接PostgreSQL数据库的步骤很简单,只需要修改 %PostgreSQL_path%/data 目录下的 pg_hba.conf 和 postgresql.conf. 一.修改pg_h ...

  2. Linker scripts之SECTIONS

    1 Purpose The linker script describes how the sections in the input files should be mapped into the ...

  3. UWP Tiles

    1.我们建议安装通知库 NuGet 程序包 详细内容 2.我们建议安装NotificationsVisualizerLibrary 这是 The official NotificationsVisua ...

  4. deeplearning4j – 分布式DL开源项目

    原文链接:http://www.52ml.net/16157.html Deeplearning4j is the first commercial-grade deep learning libra ...

  5. PhotoZoom如何使用调整大小配置文件

      众所周知PhotoZoom是一款图片无失真放大软件,所以在PhotoZoom中会有对尺寸大小调节功能.这里我们所要讲解的也是和尺寸调节等有关的功能——调整大小配置文件. 单击“调整大小配置文件.. ...

  6. Javaee 内部引用

    1.final修饰类,修饰方法,修饰变量有什么特点? final修饰的类不能被继承太监类,可以被继承使用但不能修改,如果父类没有final修饰的方法子类可以添加上,成员变量需要在建类前被赋值且不能改动 ...

  7. SQLServer Oracle MySQL的区别

    table tr:nth-child(odd){ background: #FFFFCC; font-size: 18px; } table tr:nth-child(even){ backgroun ...

  8. loadrunner中的常见问题

    1.Loadrunner参数化默认只显示100条数据,我们如何改变呢 E:\Program Files (x86)\HP\LoadRunner\config 2.如何突破loadrunner的Cont ...

  9. 【JavaScript框架封装】实现一个类似于JQuery的动画框架的封装

    // 动画框架 (function (xframe) { // 需要参与链式访问的(必须使用prototype的方式来给对象扩充方法) xframe.extend({}); // 不需要参与链式访问的 ...

  10. 把SQLAlchemy查询对象转换成字典/json使用(分开)

    注:针对的是查询出来的是单条对象 多个对象的话可以使用for循环遍历查询出来的对象列表,也可以使用下面的方法 1.config.py文件 #!/usr/bin/env python #-*- codi ...