前言:
android 开发技术多如繁星,完全掌握非一日之功,开发涉及的深度难以想象,越走越难,金字塔顶尖上的人越来越少,今天得demo主要涉及的是自定义组件,很多时候,我们在网上找了太多资料,要么时代久远、要么表达不清、要么知识零碎、要么思路紊乱,随之而来的是不堪重负的挫败感和失落感,导致信心直线下降,而这些,正是我们面临的问题。
demo样式展示:
1.简说:
android 中组件必须是View的直接子类和间接子类,其中view有一个为viewGroup的子类,用于定义容器组件类,线性布局等,
如果组件中还有子组件就一定是从viewGroup类继承,否则从view类继承。View 类定义了组件相关的通用功能,并打通了组件在 Activity 整个活动周期中的绘制流程和效果等任督二脉,通过 OOP 构建出基本的运行框架,作为开发者,了解并掌握 View 的工作原理是非常必要的。
2,关系流程图
从图上可知:
1) Activity 类似于一个框架,负责容器生命周期及活动,窗口通过 Window 来管理;
2) Window 负责窗口管理(实际是子类 PhoneWindow),窗口的绘制和渲染交给 DecorView
完成;
3) DecorView 是 View 树的根,开发人员为 Activity 定义的 layout 将成为 DecorView 的子
视图 ContentParent 的子视图(有点拗口,但确实是这样);
4) layout.xml 是开发人员定义的布局文件,最终 inflate 为 DecorView 的子组件。
View 类的 draw()方法是组件绘制的核心方法,主要做了下面几件事:
1) 绘制背景:background.draw(canvas)
2) 绘制自己:onDraw(canvas)
3) 绘制子视图:dispatchDraw(canvas)
4) 绘制滚动条:onDrawScrollBars(canvas)
3.我们为什么要自定义view?
当系统提供给我们的组件满足不了我们的需求时,为了做出更绚丽的效果,我们自定义view来实现我们的需求.
4.我们为自定义view分为四类:
1)view重写onDraw方法
主要实现一些不规则效果,静态或者动态显示不规则图形,不方便通过布局组合方式实现,需要自己支wrap_content,padding也需要自己处理;
2)继承viewGroup,派生特殊的layout我们实现自定的布局,除了系统提供的三种布局之外,自己定义一个布局,用来显示几种view组合在一起的布局。这个方法略复杂。需要合适处理wrap_content测量,布局两个过程,
3)继承特点的view(textView)扩展已有的功能,不需要自己支持wrap_content和padding;
4)几种view组合在一起,不需要处理viewGroup的测量和布局这两个过程;
5.自定义view时的注意事项;
1)让view支持wrap_content因为我们在继承view或者viewGroup时,在measure中对wrap_content进行特殊处理,当外部使用wrap_content时候,会达不到我们预期的效果;
2)让view支持padding当我们继承view控件时,应在draw方法中对padding进行处理,如果不做处理padding会失效;继承viewGroup控件时;应该在measure和onlayout中考虑padding和子元素margin带来的影响,不然导致padding和子元素的margin失效;
3)尽量不要再view中使用handler因为view本身带有一系类post方法,可以代替handler作用,除非明确的使用handler发送消息;
4)view带有滑动嵌套时,需要处理好滑动冲突。如果有滑动冲突,会对效果造成影响
5)view有线程和动画需要及时停止;view变得不可见时,需要停止线程和动画,如果处理不及时,会造成内存泄露;
6.开始主题,手势滑动的密码锁,(直接代码)
1)绘制样式
public class GestureContentView extends ViewGroup {
private int baseNum = 6;
private int[] screenDispaly;
/**
* 每个点区域的宽度
*/
private int blockWidth;
/**
* 声明一个集合用来封装坐标集合
*/
private List<GesturePoint> list;
private Context context;
private boolean isVerify;
private GestureDrawline gestureDrawline;
/**
* 包含9个ImageView的容器,初始化
*
* @param context
* @param isVerify 是否为校验手势密码
* @param passWord 用户传入密码
* @param callBack 手势绘制完毕的回调
*/
public GestureContentView(Context context, boolean isVerify, String passWord, GestureDrawline.GestureCallBack callBack) {
super(context);
screenDispaly = AppUtil.getScreenDispaly(context);
blockWidth = screenDispaly[0] / 3;
this.list = new ArrayList<GesturePoint>();
this.context = context;
this.isVerify = isVerify;
// 添加9个图标
addChild();
// 初始化一个可以画线的view
gestureDrawline = new GestureDrawline(context, list, isVerify, passWord, callBack);
}
private void addChild() {
for (int i = 0; i < 9; i++) {
ImageView image = new ImageView(context);
image.setBackgroundResource(R.drawable.gesture_node_normal);
this.addView(image);
invalidate();
// 第几行
int row = i / 3;
// 第几列
int col = i % 3;
// 定义点的每个属性
int leftX = col * blockWidth + blockWidth / baseNum;
int topY = row * blockWidth + blockWidth / baseNum;
int rightX = col * blockWidth + blockWidth - blockWidth / baseNum;
int bottomY = row * blockWidth + blockWidth - blockWidth / baseNum;
GesturePoint p = new GesturePoint(leftX, rightX, topY, bottomY, image, i + 1);
this.list.add(p);
}
}
public void setParentView(ViewGroup parent) {
// 得到屏幕的宽度
int width = screenDispaly[0];
LayoutParams layoutParams = new LayoutParams(width, width);
this.setLayoutParams(layoutParams);
gestureDrawline.setLayo