Android自定义View实现LayoutParams的方法详解

目录
  • 第一层理解
  • 第二层理解
  • 第三层理解

这一期我们来讲一讲LayoutParams这个玩意儿。Android入门的第一行代码就牵扯到这个东西,然而,你真的理解够了吗?

第一层理解

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
</RelativeLayout>

layout_width和layout_height这个是不是最开始学的时候,就要搞清楚的基础知识,match_parent代表填充屏幕,wrap_content代表包裹内容。这些其实是系统控件定义的属性,通过TypedArray进行解析。

第二层理解

val lp = RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
lp.addRule(RelativeLayout.CENTER_VERTICAL)
lp.addRule(RelativeLayout.BELOW, viewId)
lp.setMargins(10, 20, 10, 20)

使用代码动态布局的时候设置LayoutParams。

第三层理解

好了,知识是在不断打破旧的认识中进步的,第一层实际还没到LayoutParams,还只是AttributeSet。系统何时将布局中的AttributeSet解析成LayoutParams的呢?

@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
    return new RelativeLayout.LayoutParams(getContext(), attrs);
}

protected LayoutParams generateDefaultLayoutParams() {
    return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}

ViewGroup有个关键的方法,generateLayoutParams()。

public LayoutParams(Context c, AttributeSet attrs) {
    super(c, attrs);

    TypedArray a = c.obtainStyledAttributes(attrs,
            com.android.internal.R.styleable.RelativeLayout_Layout);

    final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
    mIsRtlCompatibilityMode = (targetSdkVersion < JELLY_BEAN_MR1 ||
            !c.getApplicationInfo().hasRtlSupport());

    final int[] rules = mRules;
    //noinspection MismatchedReadAndWriteOfArray
    final int[] initialRules = mInitialRules;

    final int N = a.getIndexCount();
    for (int i = 0; i < N; i++) {
        int attr = a.getIndex(i);
        switch (attr) {
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing:
                alignWithParent = a.getBoolean(attr, false);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf:
                rules[LEFT_OF] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf:
                rules[RIGHT_OF] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above:
                rules[ABOVE] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below:
                rules[BELOW] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline:
                rules[ALIGN_BASELINE] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft:
                rules[ALIGN_LEFT] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop:
                rules[ALIGN_TOP] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight:
                rules[ALIGN_RIGHT] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom:
                rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft:
                rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0;
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop:
                rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0;
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight:
                rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0;
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom:
                rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0;
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent:
                rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0;
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal:
                rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0;
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical:
                rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0;
               break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toStartOf:
                rules[START_OF] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toEndOf:
                rules[END_OF] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignStart:
                rules[ALIGN_START] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignEnd:
                rules[ALIGN_END] = a.getResourceId(attr, 0);
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentStart:
                rules[ALIGN_PARENT_START] = a.getBoolean(attr, false) ? TRUE : 0;
                break;
            case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentEnd:
                rules[ALIGN_PARENT_END] = a.getBoolean(attr, false) ? TRUE : 0;
                break;
        }
    }
    mRulesChanged = true;
    System.arraycopy(rules, LEFT_OF, initialRules, LEFT_OF, VERB_COUNT);

    a.recycle();
}

这个代码熟悉吧,这就是我们之前讲过的自定义属性啊!没错,xml布局中的属性会先被解析成LayoutParams。那么我问你个问题,你觉得generateLayoutParams()和generateDefaultLayoutParams()的这个LayoutParams是给自己用的呢?还是给它的子控件用的呢?它是给子控件用的。自己的那个直接在构造方法中就从AttributeSet解析出来了。这样你就理解了,为什么RelativeLayout的那些个

android:layout_centerVertical="true"
android:layout_alignParentEnd="true"

怎么全部定义在子控件里面了。然后ViewGroup的addView()方法中就可以带上这个LayoutParams了。

/**
 * Adds a child view. If no layout parameters are already set on the child, the
 * default parameters for this ViewGroup are set on the child.
 *
 * <p><strong>Note:</strong> do not invoke this method from
 * {@link #draw(android.graphics.Canvas)}, {@link #onDraw(android.graphics.Canvas)},
 * {@link #dispatchDraw(android.graphics.Canvas)} or any related method.</p>
 *
 * @param child the child view to add
 * @param index the position at which to add the child
 *
 * @see #generateDefaultLayoutParams()
 */
public void addView(View child, int index) {
    if (child == null) {
        throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
    }
    LayoutParams params = child.getLayoutParams();
    if (params == null) {
        params = generateDefaultLayoutParams();
        if (params == null) {
            throw new IllegalArgumentException(
                    "generateDefaultLayoutParams() cannot return null  ");
        }
    }
    addView(child, index, params);
}

你不重写generateLayoutParams()方法,怎么在添加子控件的时候,让子控件用你的LayoutParams呢?

public static class LayoutParams extends ViewGroup.MarginLayoutParams {
}

以上是LinearLayout.LayoutParams的摘要,我们自定义ViewGroup的时候,是不是也可以继承个ViewGroup的LayoutParams玩一玩呢?然后重写generateLayoutParams()和generateDefaultLayoutParams()方法。

@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
    return new LinearLayout.LayoutParams(getContext(), attrs);
}

这里return你的ViewGroup的LayoutParams,然后在你的ViewGroup的LayoutParams的构造方法中就可以解析自定义属性attrs了。如果忘记了解析方式,我给你个提示,使用context的obtainStyledAttributes()方法。

大部分停留在第二层理解,你如果学会了第三层,那么你自定义View又可以玩出新的高度了。

到此这篇关于Android自定义View实现LayoutParams的方法详解的文章就介绍到这了,更多相关Android LayoutParams内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Android自定义view实现滚动选择控件详解

    目录 前言 需求 编写代码 主要问题 前言 上篇文章通过一个有header和footer的滚动控件(Viewgroup)学了下MeasureSpec.onMeasure以及onLayout,接下来就用一个滚动选择的控件(View)来学一下onDraw的使用,并且了解下在XML自定义控件参数. 需求 这里就是一个滚动选择文字的控件,还是挺常见的,之前用别人的,现在选择手撕一个,核心思想如下: 1.有三层不同大小及透明度的选项,选中项放在中间 2.接受一个列表的数据,静态时显示三个值,滚动时显示四个

  • Android自定义view实现侧滑栏详解

    目录 前言 需求 效果图 编写代码 主要问题 前言 上一篇文章学了下自定义View的onDraw函数及自定义属性,做出来的滚动选择控件还算不错,就是逻辑复杂了一些.这篇文章打算利用自定义view的知识,直接手撕一个安卓侧滑栏,涉及到自定义LayoutParams.带padding和margin的measure和layout.利用requestLayout实现动画效果等,有一定难度,但能重新学到很多知识! 需求 这里类似旧版QQ(我特别喜欢之前的侧滑栏),有两层页面,滑动不是最左侧才触发的,而是从

  • Android LayoutParams使用案例详解

    LayoutParams是什么? LayoutParams主要保存了一个View的布局参数,因此可以使用LayoutParams来改变布局参数从而达到View位置的效果,一般在自定义View的时候使用. LayoutParams怎么用? 如果父控件是LinearLayout,需要使用LinearLayout.LayoutParams 代码如下: LinearLayout.LayoutParams layoutParams=(LinearLayout.LayoutParams)getLayoutP

  • Android自定义view之利用drawArc方法实现动态效果(思路详解)

    目录 前言 一.准备 1.测量 2.初始化画笔 3.自定义属性 二.关键方法介绍 drawArc 三.实现 1.思路 2.效果图 前言 前几天看了一位字节Android工程师的一篇博客,他实现的是歌词上下滚动的效果,实现的关键就是定义一个偏移量,然后根据情况去修改这个值,最后触发View的重绘来达到效果.于是今天根据这个思路来写一篇简单的文章.欢迎留言 一.准备 在这之前呢,还是得简单描述一下自定义view中的一些准备工作 1.测量 @Override protected void onSize

  • Android学习之Span的使用方法详解

    目录 Span集合 段落类Span 其他Span 展示效果 小试牛刀 小结 Span集合 段落类Span BulletSpan 为段落开头增加项目符号并支持大小.颜色.弧度 span.append(SpannableString("BulletSpan").also { it.setSpan(BulletSpan(40, Color.RED), 0, 10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) }) QuoteSpan 为段落开头增加垂直引用线 sp

  • Android 自定义imageview实现图片缩放实例详解

    Android 自定义imageview实现图片缩放实例详解 觉得这个自定义的imageview很好用 性能不错  所以拿出来分享给大家  因为不会做gif图  所以项目效果 就不好贴出来了  把代码贴出来 1.项目结构图 2.Compat.class package com.suo.image; import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.view.View; pu

  • Android适配底部虚拟按键的方法详解

    最近项目进行适配的时候发现部分(如华为手机)存在底部虚拟按键的手机会因为虚拟按键的存在导致挡住部分界面,因为需要全屏显示,故调用虚拟按键隐藏方法使之隐藏,然而发现出现如下问题: 手动操作隐藏虚拟按键后出现长白条区域 不自动隐藏 滑出状态栏后虚拟按键也出来,状态栏隐藏后虚拟却不跟着隐藏 在没有虚拟按键的设备上影响了SurfaceView全屏显示图传(原本全屏显示的图传在切出去再进来时变成了小屏显示) 通过google了很多方法并尝试终于解决了这个问题,达到如下效果: 每次进入界面时虚拟按键自动隐藏

  • Android程序打包为APK的方法详解

    Andriod安装包文件(Android Package),简称APK,后缀名为.apk. 1.生成未签名的安装包 Build -> Build Bundle(s)/APK(s) -> Build APK(s)    会生成一个未签名的apk文件,默认为debug版,可以正常安装使用. 可以 Build -> Select Build Variant -> 选择生成的apk版本(debug.release),再 Build -> Build Bundle(s)/APK(s)

  • Android 通过代码安装 APK的方法详解

    在 APK 开发中,通过 Java 代码来打开系统的安装程序以安装 APK 并不是什么难事,一般的 Android 系统都有开放这一功能. 但随着 Android系统版本的迭代,其对于权限的把控越来越严格,或者说是变得越来越注重安全性.这就导致了以前可以通过很简单的几行代码就能实现的功能,现在要复杂很多. 对于通过代码打开系统安装程序这一功能的限制,其分水岭在 Android7.0,即 Android N 上.通常在 Android N以上的系统使用一种做法,以下则使用另一种做法. 传统的通过代

  • 利用Android实现光影流动特效的方法详解

    目录 前言 MaskFilter 类简介 MaskFilter 的几种效果对比 光影流动 光影流动效果1 光影流动效果2 光影流动效果3 光影流动效果4:光影沿贝塞尔曲线流动 总结 前言 Flutter 的画笔类 Paint 提供了很多图形绘制的配置属性,来供我们绘制更丰富多彩的图形.前面几篇我们介绍了 shader 属性来绘制全屏渐变的聊天气泡背景.渐变流动的边框和毛玻璃效果的背景图片,具体可以参考下面几篇文章. 让你的聊天气泡丰富多彩! 手把手教你实现一个流动的渐变色边框 利用光影变化构建立

  • Java实现自定义Excel数据排序的方法详解

    目录 1.引入jar包 2.自定义排序 通常,我们可以在Excel中对指定列数据执行升序或者降序排序,排序时可依据单元格中的数值.单元格颜色.字体颜色或图标等.在需要自定义排序情况下,我们也可以自行根据排序需要编辑数据排列顺序.本文,将通过Java应用程序来实现如何自定义排序. 1.引入jar包 使用jar包:Spire.Xls.jar version: 12.8.4 导入方法1:手动下载jar到本地,解压,然后找到lib文件夹下的Spire.Xls.jar文件.然后在IDEA中打开“Proje

  • Android自定义view绘制表格的方法

    本文实例为大家分享了Android自定义view绘制表格的具体代码,供大家参考,具体内容如下 先上效果图 平时很少有这样的表格需求,不过第一想法就是自定义view绘制表格,事实上我确实是用的canvas来绘制的,整个过程看似复杂,实为简单,计算好各个点的坐标后事情就完成一半了.不废话show code import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; imp

  • Android使用jni调用c++/c方法详解

    1.下载ndk 2.编写jni的加载类 参考例子: public class JniTest { public native String append(String str1, String str2); static { System.loadLibrary("JniTest"); } } 以上append方法就是要调用c++/c中的方法. JniTest是在Android.mk里约束好的,关于Android.mk的编写具体在后面详解. 3.使用javah -jni生成.h文件 编

随机推荐