CustomView
2016/1/14 10:24:56
自定义View的基础知识
- getWidth()、getHeight()指的是xml文件中该View指定的width,height。
- onMeasure、onLayout、onDraw先后执行顺序。
获取屏幕宽高
WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE); width = wm.getDefaultDisplay().getWidth(); height = wm.getDefaultDisplay().getHeight();
2016/1/15 0:32:26
实现自定义控件有3种方式:
- 继承一个控件 (继承已有控件)
- 继承一个布局(继承已有布局)
- 组合View (布局根标签应该设置成warp_content,保证各个控件的高宽属性正常。)
2015-10-05 09:51:46
组合View
基本思路:
- xml布局文件添加已有的控件,例如(ImageView跟Button)。
- 创建java文件继承包含这些控件的布局,例如(RelativeLayout)。
- 重写其构造方法(加载xml布局文件,利用通过LayoutInflate)。
- 在其构造方法里获取自定义属性。(可选,在values文件夹创建一个attrs的xml文件)
为自定义View添加自定义属性
在attrs文件中创建declare-styleable标签,其name属性是控件名字(不需要加包名)
在上述标签下 创建attr标签,name属性是属性名、format指明改属性的格式<attr name="src"></attr> // name默认不加前缀 则调用方法为app:src <attr name="text"></attr>
属性里的值可以通过enum标签来实现
reference 表示引用,参考某一资源ID
string 表示字符串
color 表示颜色值
dimension 表示尺寸值
boolean 表示布尔值
integer 表示整型值
float 表示浮点值
fraction 表示百分数
enum 表示枚举值
flag 表示位运算
定义的属性需赋值给原控件的属性
- 利用TypedArray 获取所有属性
//通过context的obtainStyledAttributes(attrs,R.styleable.控件名)方法得到一个TypedArray对象,参数分别为attrs、控件id
TypedArray array=context.obtainStyledAttributes(attrs,R.styleable.自定义控件名);
利用typedArray对象设置控件默认值(R.styleable.值)
CharSequence text= array.getText(R.styleable.自定义控件名_android_text); Drawable drawable= array.getDrawable(R.styleable.自定义控件名n_android_src);
通过原有控件的方法设置这些属性即可。
editText.setHint(text);
TypedArray对象调用recycle方法
array.recycle();
在使用该控件的布局文件里设置命名空间
xmlns:eri="http://schemas.android.com/apk/res/包名" //包名指整个项目的包名
通过该命名空间调用属性 如 eri:属性名
eri:text="Erintrus"
2015-10-08 18:20:56
实战开发帮你教项目中的意见反馈功能
1.写一个布局包括一个EditText 和TextView
2.写attrs文件,添加一个hint属性,用于设置提示
3.重写java 文件,获取到属性的值后,赋值给原控件
如下:
//获取自己定义的属性,然后赋值给原控件的属性。从而实现自定义属性的功能
TypedArray array=context.obtainStyledAttributes(attrs,R.styleable.EtextWithTv);
//得到自定义属性的值
CharSequence text= array.getText(R.styleable.EtextWithTv_hint);
array.recycle();
//调用EditText原有属性
editText.setHint(text);
4.写一个getText方法,用于获取EditText的值(使用该控件时候,可以调用该方法)
public String getText()
{
return editText.getText().toString();
}
2015-10-05 09:51:07
完全自定义View(进阶)
1.绘图,通过重写onDraw方法控制View在屏幕上的渲染效果
2.交互,通过重写onTouchEvent方法或者使用手势来控制用户的交互
3.测量,通过重写onMeasure方法来对控件进行测量
4.属性,可以通过xml自定义控件的属性,然后通过TypedArray来进行使用
5.状态的保存,为了避免配置改变时丢失View状态,通过重写onSaveInstanceState,onRestoreInstanceState方法来保存和恢复状态。
自定义View的layout文件根标签为match_parent导致CustomView设置warp_content时,会填满整个布局文件
2015-10-05 09:51:16
自定义View — 重写onMeasure
- 判断是否需要重写OnMeasure (下述需求判断是否重写)
(下述无效,待更新)
- 不重写onMeasure: (弊端: view为warp、match时,都会填满父容器)
1. 父容器为warp_content或match_parent时,view的宽高不是固定值,则view填满父容器。
2. 父容器为warp_content或match_parent时,view的宽高是固定值,则view大小取决固定值。
3. 父容器为固定值时(view的最大显示值),view的宽高必须小于等于其固定值。
4. 父容器为固定值时(view的最大显示值),view的宽高不是固定值,则view填满父容器。
特点: view的宽高为warp_content时,view填满父容器。
(当View为warp_content时,想要设置一个固定值,就应该重写)
- 重写onMeasure(可实现:view为warp时,显示默认值,而不是填满父容器)
1. 可以给View设定默认值,当view宽高为warp_content时,显示默认值。
2. 父容器为warp_content或match_parent时,view的宽高为warp_content,则view显示默认值。
3. 父容器为warp_content或match_parent时,view的宽高为match_parent,则view填满控件。
4. 父容器为固定值时(view的最大显示值),view的宽高必须小于等于其固定值。
特点: view的宽高为warp_content时,view大小为默认值。
- 重写onMeasure的代码
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
int width = getSize(DEFAULT_WIDTH,widthMeasureSpec);
int height = getSize(DEFAULT_HEIGHT,heightMeasureSpec);
setMeasuredDimension(width, height);
}
public int getSize(int defaultSize, int measureSpec) {
int result = defaultSize;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);//获取测量值,即使用时传入的值。
if (specMode == MeasureSpec.EXACTLY) {
result = specSize; //当设置其宽高为固定值或者match时,显示为固定值或者填满屏幕。
} else {
result = Math.min(defaultSize, specSize); //当设置其宽高为warp时,显示默认值。
}
return result;
}
1.ViewGroup(View的父容器)提供两个参数给View进行设置测量,并且根据测量值跟自己提供的尺寸大小来确定View的大小。
2.View的大小由Size和Mode决定,相关类是MeasureSpec。Size、Mode封装在resolveSizeAndState方法里面。
3.resolveSizeAndState方法的参数(View的宽或高、父容器提供的尺寸值、掩码默认0)
4.如何重写OnMeasure
//获取View的宽度
int minw = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth();
//讲View的宽度跟ViewGroup给的值进行测量,得出一个整型.
int w = resolveSizeAndState(minw, widthMeasureSpec, 1);
//同理View的高度
int minh = MeasureSpec.getSize(w) + getPaddingBottom() + getPaddingTop();
int h = resolveSizeAndState(MeasureSpec.getSize(w), heightMeasureSpec,0);
2015-10-05 10:02:11
自定义监听器
1.写一个接口,并且添加抽象方法
public interface onStateChangedListener {
public void stateOn();
public void stateOff();
}
2.在自定义控件中创建接口引用,并且生成set方法,用于外面调用
public class SwtBtnWithTv extends LinearLayout {
private onStateChangedListener listener;
public void setListener(onStateChangedListener listener) {
this.listener = listener;
}
3.自定义控件实现原有的接口,在这里通过引用调用抽象方法
switchButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
listener.stateOn();
} else {
listener.stateOff();
}
}
});
4.在Main中调用set方法来实现该功能
swtBtnWithTv.setListener(new onStateChangedListener() {
@Override
public void stateOn() {
Toast.makeText(getActivity(), "成功", 0).show();
}
@Override
public void stateOff() {
Toast.makeText(getActivity(), "失败", 0).show();
}
});
总结:
Main外面写方法的实现 第5
自定义控件内部写在哪里调用 第3.4
2016/1/15 0:37:22
实际开发 — 分割线
思路:
- 继承View。
- 在attrs.xml文件添加两个属性分别是方向、颜色。
- 构造方法获取属性、获取屏幕宽高、初始化默认宽高值。
- 重写onMeasure、设置默认值。(参考上述)
- 重写onDraw。
* Created by Erintrus on 2015/10/9.
* Email: Erintrus@126.com
* 两点确定一条线
* 获取屏幕高、宽
* 当要改变wrap_content时,需要设置默认值
public class CustomDivider extends View {
private int orientation = 0;
private static final int HORIZONTAL = LinearLayout.HORIZONTAL;
private static final int VERTICAL = LinearLayout.VERTICAL;
private static final int DEFAULT_COLOR = R.color.common_divider_gray; //默认红色的线
private Paint paint;
private static final int DEFAULT_LENGTH = 3;
private int length;
private int color;
private int width;
private int height;
public CustomDivider(Context context) {
this(context, null);
}
public CustomDivider(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomDivider(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
WindowManager wm = (WindowManager) getContext()
.getSystemService(Context.WINDOW_SERVICE);
width = wm.getDefaultDisplay().getWidth();
height = wm.getDefaultDisplay().getHeight();
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CustomDivider);
orientation = array.getInteger(R.styleable.CustomDivider_dividerOrientation, HORIZONTAL);
color = array.getColor(R.styleable.CustomDivider_dividerColor, getResources().getColor(DEFAULT_COLOR));
if (orientation == HORIZONTAL) {
length = width;
} else {
length = height;
}
array.recycle();
paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(color);
paint.setStrokeWidth(5);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (orientation == HORIZONTAL) {
canvas.drawLine(DEFAULT_LENGTH, DEFAULT_LENGTH, length, DEFAULT_LENGTH, paint);
} else {
canvas.drawLine(DEFAULT_LENGTH, DEFAULT_LENGTH, DEFAULT_LENGTH, length, paint);
}
}
//必须要重写这段代码,使其warp_content可以为某个值
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int w;
int h;
if (orientation == HORIZONTAL) {
w = getSize(width, widthMeasureSpec);
h = DEFAULT_LENGTH;
} else {
w = DEFAULT_LENGTH;
h = getSize(height, heightMeasureSpec);
}
setMeasuredDimension(w, h);
}
public int getSize(int defaultSize, int measureSpec) {
int result = defaultSize;
int specSize = MeasureSpec.getSize(measureSpec);
int specMode = MeasureSpec.getMode(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else {
result = Math.min(defaultSize, specSize);
}
return result;
}
}
2016/1/11 11:20:59
为RecycleView重写SwipeRefreshLayout
2016/1/21 15:56:49
实现请求View RequestView
作用:
- 请求的时候显示进度条加载。
- 请求失败时显示失败页面,点击页面可以重新请求。(显示失败的时候才可以点击)
实现:
继承RelativeLayout
布局中使用谷歌的ProgressBar、ImageView、TextView等控件。
自定义pgbType属性,用于决定ProgressBar的显示类型,并在构造函数中获取。
由于请求时,不能点击下层的按钮。所以需要设置布局文件clickable为false