一、前提知识
Linearlayout.LayoutParams params = new LayoutParams(...);
SimapleBanner banner = new SimapleBanner(context);
banner.setLayoutParams(params);
linear.add(banner)
0.1 LayoutInflater的inflate加载布局
LayoutInflater作用是将layout的xml布局文件实例化为View类对象。
获取LayoutInflater的方法有如下三种:
LayoutInflater inflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.main, null);
LayoutInflater inflater = LayoutInflater.from(context); (该方法实质就是第一种方法,可参考源代码)
View layout = inflater.inflate(R.layout.main, null);
LayoutInflater inflater = getLayoutInflater();(在Activity中可以使用,实际上是View子类下window的一个函数)
View layout = inflater.inflate(R.layout.main, null);
具体的调用:
public View inflate(XmlPullParser parser, ViewGroup root) {
return inflate(parser, root, root != null);
}
public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
if (DEBUG) {
Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" ("
+ Integer.toHexString(resource) + ")");
}
final XmlResourceParser parser = res.getLayout(resource);
try {
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
boolean attachToRoot,是否将目标xml加载到指定root中
- false; 不加载;将这个root对象的LayoutParams属性附加到resource对象的根布局对象上,并返回目标xml本身
- true;加载
inflate方法在第二个参数root不为空时,返回的View就是root,而当root为空时,返回的才是加载的layout的根节点
- 如果提供root(不传null)时,返回值其实就是这个root,这个方法就是把xml解析成view之后挂载这个root下。
- 如果传null(不提供root),返回值也是View,它就是xml布局里面的根节点
0.2 ViewGroup#addView()函数会生成默认父布局属性
public void addView(View child, int index) {
LayoutParams params = child.getLayoutParams();
if (params == null) {
params = generateDefaultLayoutParams();
if (params == null) {
throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
}
}
addView(child, index, params);
}
子控件的布局属性类型应该由父布局决定
我们向一个线性布局加入子控件时:
//步骤1:生成线性布局的布局参数
Linearlayout.LayoutParams params = new LayoutParams(...);
//步骤2:生成子控件
SimapleBanner banner = new SimapleBanner(context);
//步骤3:为子控件设置父布局的布局参数
banner.setLayoutParams(params);
//步骤4:父布局将子控件加入自己的布局树中
linear.addView(banner)
可以看到步骤3:为子控件设置父布局的布局参数,如果不设置,那么ViewGroup#addView的时候会加入默认的布局属性
至于为什么,子控件的布局属性是父布局类型决定的,是因为 具体的measure,layout阶段都是父布局获取了子控件的布局属性,做相关操作的,具体可查看(4.1.37.1)深入理解setContentView过程和View绘制过程
模板式写法
定义支持的xml配置属性:
<declare-styleable name="DiyWidget">
<attr name="diy_topDividerShow" format="boolean"/>
<attr name="diy_topDividerIndent" format="boolean"/>
<attr name="diy_topDividerIndentValue" format="dimension"/>
<attr name="diy_bottomDividerShow" format="boolean"/>
<attr name="diy_bottomDividerIndent" format="boolean"/>
<attr name="diy_bottomDividerIndentValue" format="dimension"/>
<attr name="diy_contentPaddingleft" format="dimension"/>
<attr name="diy_contentPaddingRight" format="dimension"/>
<attr name="diy_contentPaddingTop" format="dimension"/>
<attr name="diy_contentPaddingBottom" format="dimension"/>
<attr name="diy_focusedWhenTouched" format="boolean"/>
</declare-styleable>
定义自己的组合布局:
<!-- 根布局为LinearLayout,与Java的继承类型一致 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/ll_root">
<include
android:id="@+id/dw_top_divider"
layout="@layout/view_divider"
android:visibility="gone"/>
<include
android:id="@+id/dw_bottom_divider"
layout="@layout/view_divider"
android:visibility="gone"/>
</LinearLayout>
定义自己的继承类:
public abstract class DiyWidget extends LinearLayout{
private boolean initCalled;
protected Context context;
private LinearLayout llRoot;
private View topDivider;
private View bottomDivider;
private View content;
/**
*以下为支持的配置参数
*/
private int dividerStrokeWidth;
private boolean topDividerShow;
private boolean bottomDividerShow;
private boolean topDividerIndent;
private boolean bottomDividerIndent;
private int topDividerIndentValue;
private int bottomDividerIndentValue;
protected int contentPaddingLeft;
protected int contentPaddingRight;
protected int contentPaddingTop;
protected int contentPaddingBottom;
private boolean focusedWhenTouch;
private boolean initialFocusedWhenTouched;
public DiyWidget(Context context) {
super(context);
if(!initCalled){
initCalled = true;
init(context, null);
}
}
public DiyWidget(Context context, AttributeSet attrs) {
super(context, attrs);
if(!initCalled){
initCalled = true;
init(context, attrs);
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public DiyWidget(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if(!initCalled){
initCalled = true;
init(context, attrs);
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public DiyWidget(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
if(!initCalled){
initCalled = true;
init(context, attrs);
}
}
private void init(Context context, AttributeSet attrs){
this.context = context;
onPreInit();
initMembers();
assignDefaultXml();//为上述相关配置参数,设定默认值
assignXml(attrs);//传入attrs对象,获取在xml文件配置的相关配置参数
//将目标xml加载到当前布局中,并配置默认布局属性
//实例化根布局存储
LayoutInflater inflater = LayoutInflater.from(context);
llRoot = (LinearLayout) inflater.inflate(R.layout.view_diy_widget, this).findViewById(R.id.ll_root);
int layout = getLayout();//返回“自己实质需要的组合View的布局”
content = inflater.inflate(layout, llRoot, false);
LinearLayout.LayoutParams lllp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0);
lllp.weight = 1;
initDividers();
llRoot.addView(content, 1, lllp);//将其加入到当前布局中
initLayout(content);//findViewById实例化“自己实质需要的组合View的布局”其中的view
xmlTakeEffects();//根据配置参数作出对应响应
}
//。。。省略一堆
protected void initDividers(){
topDivider = llRoot.findViewById(R.id.dw_top_divider);
bottomDivider = llRoot.findViewById(R.id.dw_bottom_divider);
}
protected void onPreInit(){
}
protected void initMembers(){
}
protected void assignDefaultXml(){
Resources res = getResources();
dividerStrokeWidth = res.getDimensionPixelSize(R.dimen.public_height_line);
topDividerIndentValue = bottomDividerIndentValue = res.getDimensionPixelSize(R.dimen.diy_widget_divider_indent_default);
}
protected void assignXml(AttributeSet attrs){
if(attrs != null){
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.DiyWidget);
if(typedArray != null){
topDividerShow = typedArray.getBoolean(R.styleable.DiyWidget_diy_topDividerShow, topDividerShow);
topDividerIndent = typedArray.getBoolean(R.styleable.DiyWidget_diy_topDividerIndent, topDividerIndent);
topDividerIndentValue = typedArray.getDimensionPixelSize(R.styleable.DiyWidget_diy_topDividerIndentValue, topDividerIndentValue);
bottomDividerShow = typedArray.getBoolean(R.styleable.DiyWidget_diy_bottomDividerShow, bottomDividerShow);
bottomDividerIndent = typedArray.getBoolean(R.styleable.DiyWidget_diy_bottomDividerIndent, bottomDividerIndent);
bottomDividerIndentValue = typedArray.getDimensionPixelSize(R.styleable.DiyWidget_diy_bottomDividerIndentValue, bottomDividerIndentValue);
contentPaddingLeft = typedArray.getDimensionPixelSize(R.styleable.DiyWidget_diy_contentPaddingleft, contentPaddingLeft);
contentPaddingRight = typedArray.getDimensionPixelSize(R.styleable.DiyWidget_diy_contentPaddingRight, contentPaddingRight);
contentPaddingTop = typedArray.getDimensionPixelSize(R.styleable.DiyWidget_diy_contentPaddingTop, contentPaddingTop);
contentPaddingBottom = typedArray.getDimensionPixelSize(R.styleable.DiyWidget_diy_contentPaddingBottom, contentPaddingBottom);
initialFocusedWhenTouched = typedArray.getBoolean(R.styleable.DiyWidget_diy_focusedWhenTouched, initialFocusedWhenTouched);
typedArray.recycle();
}
}
}
protected void initLayout(View layout){
}
protected void xmlTakeEffects(){
showTopDivider(topDividerShow);
showBottomDivider(bottomDividerShow);
setTopDividerIndent(topDividerIndent);
setBottomDividerIndent(bottomDividerIndent);
// setContentHorizontalPadding(contentPaddingLeft, contentPaddingRight);
setContentPadding(contentPaddingLeft, contentPaddingTop, contentPaddingRight, contentPaddingBottom);
if(initialFocusedWhenTouched){
setFocusedWhenTouched(true);
}
}
protected abstract int getLayout();
public void setContentHorizontalPadding(int paddingLeft, int paddingRight){
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) content.getLayoutParams();
if(lp == null){
lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
lp.weight = 1;
}
lp.leftMargin = paddingLeft;
lp.rightMargin = paddingRight;
content.setLayoutParams(lp);
}
public void setContentVerticlePadding(int paddingTop, int paddingBottom){
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) content.getLayoutParams();
if(lp == null){
lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
lp.weight = 1;
}
lp.topMargin = paddingTop;
lp.bottomMargin = paddingBottom;
content.setLayoutParams(lp);
}
public void setContentPadding(int paddingLeft, int paddingTop, int paddingRight, int paddingBottom){
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) content.getLayoutParams();
if(lp == null){
lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
lp.weight = 1;
}
lp.leftMargin = paddingLeft;
lp.topMargin = paddingTop;
lp.rightMargin = paddingRight;
lp.bottomMargin = paddingBottom;
content.setLayoutParams(lp);
}
public void setContentPaddingRight(int inPx){
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) content.getLayoutParams();
if(lp == null){
lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
lp.weight = 1;
}
lp.rightMargin = inPx;
content.setLayoutParams(lp);
}
public int getContentPaddingLeft(){
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) content.getLayoutParams();
if(lp != null){
return lp.leftMargin;
}
return 0;
}
public int getContentPaddingRight(){
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) content.getLayoutParams();
if(lp != null){
return lp.rightMargin;
}
return 0;
}
public void showTopDivider(boolean show){
topDivider.setVisibility(show ? View.VISIBLE : View.GONE);
}
public void setTopDividerIndentValue(int indentValue){
if(indentValue == topDividerIndentValue){
return;
}
topDividerIndentValue = indentValue;
setTopDividerIndent(true);
}
public void setTopDividerIndent(boolean indent){
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) topDivider.getLayoutParams();
if(lp == null){
lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, dividerStrokeWidth);
}
if(indent) {
lp.leftMargin = topDividerIndentValue;
}else {
lp.leftMargin = 0;
}
topDivider.setLayoutParams(lp);
}
public void showBottomDivider(boolean show){
bottomDivider.setVisibility(show ? View.VISIBLE : View.GONE);
}
public void setBottomDividerIndentValue(int indentValue){
if(indentValue == bottomDividerIndentValue){
return;
}
bottomDividerIndentValue = indentValue;
setBottomDividerIndent(true);
}
public void setBottomDividerIndent(boolean indent){
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) bottomDivider.getLayoutParams();
if(lp == null){
lp = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, dividerStrokeWidth);
}
if(indent) {
lp.leftMargin = bottomDividerIndentValue;
}else {
lp.leftMargin = 0;
}
bottomDivider.setLayoutParams(lp);
}
protected Size getScreenDimen(){
DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
if(displayMetrics != null){
return new Size(displayMetrics.widthPixels, displayMetrics.heightPixels);
}
return null;
}
public int getDividerStrokeWidth(){
return dividerStrokeWidth;
}
public void setFocusedWhenTouched(boolean focusedWhenTouch){
this.focusedWhenTouch = focusedWhenTouch;
setFocusable(focusedWhenTouch);
setFocusableInTouchMode(focusedWhenTouch);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(focusedWhenTouch){
if(!requestFocus()) {
requestFocusFromTouch();
}
}
return super.dispatchTouchEvent(ev);
}
}