系列文章目录
背景
说到外发光的控件第一时间想到的就是CardView,但是CardView仍然有其局限性。比如:外发光的颜色有要求,布局比较复杂的时候,这个时候就需要使用到自定义ViewGroup。
记下来主要描述的是如何实现一个外发光ViewGroup。
一、实现思路
首先要做的是明确思路我们需要做的事情有以下部分:
- 外发光效果的实现
- 内部布局,也就是子控件的摆放方式
- 点击事件的设置
二、实现思路
1.外发光效果
外发光的效果使用BlurMaskFilter来实现。
mPaint.setColor(mShaderColor);
mPaint.setMaskFilter(new BlurMaskFilter(mOutRadius-3,BlurMaskFilter.Blur.SOLID));
canvas.drawRoundRect(mOutsideRect,mRadius,mRadius,mPaint);
mPaint.setMaskFilter(null);
在new BlurMaskFilter中第一个参数代表的是外发光的半径,第二个是外发光的填充方式。这里的SOLID代表的是内部填充的方式。
setColor设置的颜色就是外发光的颜色。
drawRoundRect()函数中第一个参数代表的是绘制的矩形,第二个和第三个代表的是x方向上的圆角半径和y方向上的圆角半径,第四个是用于绘制的画笔。
2.布局设置
在自定义ViewGroup中对于布局相关的设置有两个函数分别是onMeasure(int widthMeasureSpec, int heightMeasureSpec)和onLayout(boolean changed, int l, int t, int r, int b)。要注意一点计时外发光的距离是算在总控件里面的所以需要提前留好距离。防止子控件的位置出现在视觉布局以外。
1.边距设置
除此之外还需要重写以下函数用于获取子控件的外边距参数,
//可以作为固定的格式来记
@Override
protected LayoutParams generateLayoutParams(LayoutParams p) {
return new MarginLayoutParams(p);
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new MarginLayoutParams(getContext(),attrs);
}
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new MarginLayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.MATCH_PARENT);
}
2.onMeasure函数设置
对于**onMeasure(int widthMeasureSpec, int heightMeasureSpec)**函数用于测量相关的子布局数据和设置ViewGroup的尺寸数据。
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
int measureHeight = MeasureSpec.getSize(heightMeasureSpec);
int measureHeightMode = MeasureSpec.getMode(heightMeasureSpec);
int measureWidthMode = MeasureSpec.getMode(widthMeasureSpec);
int height = 0;
int width = 0;
int count = getChildCount();
for(int i=0;i<count;i++){
View child = getChildAt(i);
if(child.getVisibility() == View.GONE){
continue;
}
//用于获取子控件的外边距
LayoutParams layoutParams = child.getLayoutParams();
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
//测量子控件
measureChild(child,widthMeasureSpec,heightMeasureSpec);
//**按照布局方式计算所需的宽和高
int childHeight = ;
int childWidth = ;
}
//设置自定义ViewGroup的高和宽
setMeasuredDimension((measureWidthMode == MeasureSpec.EXACTLY)?measureWidth : width,(measureHeightMode == MeasureSpec.EXACTLY) ? measureHeight:height);
}
2.onLayout函数设置
这个函数可以获取到onMeasure中设置的尺寸参数用于设置子控件具体布局。
@Override
//用于获取控件的外边距
MarginLayoutParams lp = null;
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int startPoint = centerStartPoint;
int count = getChildCount();
for(int i=0;i<count;i++){
View child = getChildAt(i);
if(child.getVisibility() == View.GONE ){
continue;
}
lp = (MarginLayoutParams) child.getLayoutParams();
int childHeight = child.getMeasuredHeight() ;
int childWidth = child.getMeasuredWidth() ;
/**
* 用于布局的逻辑
*/
}
}
3.自定义属性设置
首先在values的attrs.xml(没有就新建一个)中添加代码
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="类名">
<attr name="属性名" format="类型"/>
</declare-styleable>
</resources>
在自定义控件中获取属性值
if(null != attrs){
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.类名);
mBackgroundColorStart = typedArray.getColor(R.styleable.类名_属性名,mBackgroundColorStart);
}
3.点击事件
自定义ViewGroup需要自己重写点击事件,重写onKeyDown。如果捕获到的按下事件和抬起事件的时间差大于500毫秒就判断为触发了点击事件。
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN){
isDown = true;
time = System.currentTimeMillis();
}
if(event.getAction() == MotionEvent.ACTION_UP && isDown){
isDown = false;
if(System.currentTimeMillis() - time >500){
return true;
}
callOnClick();
}
return false;
}