Kotlin实战填坑

话说,怎么才算实战?在此以前我已经使用Kotlin写过几个项目了,从最初的一边看文档一边面向QQ群编程,到如今本身也能够和众人侃侃而谈Kotlin的基础语法,那么问题来了——我为何如今才开始叫“Kotlin实战”呢?由于我以前敲代码都是规规矩矩没有任何问题,可是最近在使用一个项目的时候,遇到了很多问题,所以作一个总结,但愿能够帮到其余人。html

项目简介

先给你们简单介绍一下项目的来源:重构UIWidgetRadiusView.为何要重构呢?由于在使用过程当中以为这个模块的功能很强大,可是在使用的过程当中有一些默认属性不是很熟悉,甚至以为是bug,因而打算熟悉此此项目并重构并放到本身的项目模板中。可是我在重构过程当中遇到一些困难:java

  • 泛型使用问题
  • 类的继承问题

泛型使用问题

我对泛型的了解,仅限于Java中的泛型方法、泛型类,以为没有什么难度,因而看见代码直接干。先看看java代码是什么样的?android

public class RadiusViewDelegate<T extends RadiusViewDelegate> {
    ...
}
复制代码

soSeay,这个类的声明确实很简单,因而我三下五除二的很快写好了,代码以下:git

咦?咋不行呢?没事,我还有一招,javaKotlin代码,巴拉巴拉魔法变:github

咋这招也不灵呢?不是说好java能够作的事情,Kotlin也能够作的吗?原来童话都是骗人的?编程

童话没有骗人,是讲童话的人骗了你!咱们尝试修改一下泛型呢?

open class RadiusViewDelegate<T : RadiusViewDelegate<T>>(
    val a: View,
    open val b: Context,
    open val c: AttributeSet?
)
open class B<T : RadiusViewDelegate<T>>(a: View, b: Context, c: AttributeSet?) :RadiusViewDelegate<T >(a,b,c){

}
复制代码

看来童话确实是存在的,Java能够干的活Kotlin照样能干!可是还有一个问题,如何声明B这个对象呢?直接写确定是不行的,不信的话你能够试一下,固然,我也能够给你截一张图:api

那么咱们应该怎么解决呢?不是说好童话是存在的吗?咱们尝试一下声明C继承B而后使用C这个对象,代码以下:bash

class C(a: View, b: Context, c: AttributeSet?):B<C>(a, b, c){

}
val b = C(radiusSwitch,this,null)
复制代码

看来这样是能够的,那么咱们为何要经过继承来实现呢?或者,咱们为何要使用这样的泛型呢?回答了第二个问题,第一个问题都不是问题了。此处使用泛型的缘由是为了使子类能够链式调用父类的方法。众所周知,为了链式调用,咱们能够对调用的方法增长一个返回值this,这样就能够实现链式调用了。可是涉及子类继承呢?子类调用父类的方法返回的是父类,虽然能够强转也不会报错,可是这样强转之后的“链式调用”还算链式调用吗?话说回来,咱们怎么经过增长一个泛型实现链式调用呢? 咱们在顶层父类增长一个返回this的方法,而后包括子类全部须要返回this的地方都调用这个方法便可。app

open class RadiusViewDelegate<T : RadiusViewDelegate<T>>(
    val a: View,
    open val b: Context,
    open val c: AttributeSet?
){
      fun back(): T {
        return this as T
    }
}
复制代码

到这里咱们就解决了为何使用泛型、怎么使用泛型,可是咱们尚未认真去了解一下什么是泛型?知其然,知其因此然才能更好的了解童话是否是骗人的,所以咱们去看看官方文档jvm

Java 类型系统中最棘手的部分之一是通配符类型(参见 Java Generics FAQ)。 而 Kotlin 中没有。 相反,它有两个其余的东西:声明处型变(declaration-site variance)与类型投影(type projections)。

看完之后,第一个感受就是咱们此处的泛型属于声明处型变,而且咱们的泛型约束还能够换一种写法,所以修改一下代码:

open class RadiusViewDelegate<out T> @JvmOverloads constructor(
    val view: View,
    val context: Context,
    val attrs: AttributeSet? = null
) where T : RadiusViewDelegate<T> {
    fun back(): T {
        return this as T
    }
}
复制代码

这里的链式调用有童鞋建议说可使用构造者模式和接口来实现,优势是在back方法中能够避免这种强行转换的方式,可是这种写法基本上咱们的RadiusViewDelegate及其子类都须要一个单独的接口,我认为代码可读性有降低,所以没有继续纠结,有兴趣的朋友能够尝试一下。

类的继承

Kotlin当中,一个类容许被继承只须要在class前添加一个关键字open,使用的时候用:替换java中的extends便可。一句话的事情,我为何会单独拿出来讲事呢?由于在使用过程当中我遇到了两个坑。

构造方法参数的关键字

我写了一个类RadiusTextDelegate继承自上文中的RadiusViewDelegate,在使用的时候发现一个很是奇葩的问题:其父类RadiusViewDelegate的代码报错,可是单独使用其父类RadiusViewDelegate的时候一切正常。报错代码以下:

val mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.RadiusView)
 
 //日志
 java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.TypedArray android.content.Context.obtainStyledAttributes(android.util.AttributeSet, int[])' on a null object reference
        at com.vincent.baselibrary.widget.radius.delegate.RadiusViewDelegate.<init>(RadiusViewDelegate.kt:96)
复制代码

添加断点检查的时候,父类和子类的参数都同样,貌似均不为空。

就是这个断点,让我误觉得contextattrs均不为空,后来通过请教朱凯大佬才明白attrs已经为空了! 咱们再来看看这个源码:

open class RadiusViewDelegate<out T> @JvmOverloads constructor(
    val view: View,
    open val context: Context,
    open val attrs: AttributeSet? = null
) where T : RadiusViewDelegate<T> {
      init {
        val mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.RadiusView)
        initAttributes(mTypedArray)

        ...
    }
    ...
}

open class RadiusTextDelegate<T> constructor(
    private val textView: TextView,
    override val context: Context,
    override val attrs: AttributeSet?
) :
    RadiusViewDelegate<T>(textView, context, attrs) where T : RadiusViewDelegate<T> {
        
    }
复制代码

看了一下源码更加不明白,为何contextattrs传给父类后就空了呢?经过面向QQ群编程发现无效,后来尝试将Kotlin代码转化为Java代码后发现了问题:

package com.vincent.baseproject.ui;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@Metadata(
   mv = {1, 1, 15},
   bv = {1, 0, 3},
   k = 1,
   d1 = {"\u0000 \n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0006\b\u0016\u0018\u0000*\u000e\b\u0000\u0010\u0001*\b\u0012\u0004\u0012\u0002H\u00010\u00022\b\u0012\u0004\u0012\u0002H\u00010\u0002B\u001f\u0012\u0006\u0010\u0003\u001a\u00020\u0004\u0012\u0006\u0010\u0005\u001a\u00020\u0006\u0012\b\u0010\u0007\u001a\u0004\u0018\u00010\b¢\u0006\u0002\u0010\tR\u0016\u0010\u0007\u001a\u0004\u0018\u00010\bX\u0096\u0004¢\u0006\b\n\u0000\u001a\u0004\b\n\u0010\u000bR\u0014\u0010\u0005\u001a\u00020\u0006X\u0096\u0004¢\u0006\b\n\u0000\u001a\u0004\b\f\u0010\rR\u000e\u0010\u0003\u001a\u00020\u0004X\u0082\u0004¢\u0006\u0002\n\u0000¨\u0006\u000e"},
   d2 = {"Lcom/vincent/baseproject/ui/RadiusTextDelegate;", "T", "Lcom/vincent/baseproject/ui/RadiusViewDelegate;", "textView", "Landroid/widget/TextView;", "context", "Landroid/content/Context;", "attrs", "Landroid/util/AttributeSet;", "(Landroid/widget/TextView;Landroid/content/Context;Landroid/util/AttributeSet;)V", "getAttrs", "()Landroid/util/AttributeSet;", "getContext", "()Landroid/content/Context;", "app_debug"}
)
public class RadiusTextDelegate extends RadiusViewDelegate {
   private final TextView textView;
   @NotNull
   private final Context context;
   @Nullable
   private final AttributeSet attrs;

   @NotNull
   public Context getContext() {
      return this.context;
   }

   @Nullable
   public AttributeSet getAttrs() {
      return this.attrs;
   }

   public RadiusTextDelegate(@NotNull TextView textView, @NotNull Context context, @Nullable AttributeSet attrs) {
      Intrinsics.checkParameterIsNotNull(textView, "textView");
      Intrinsics.checkParameterIsNotNull(context, "context");
      super((View)textView, context, attrs);
      this.textView = textView;
      this.context = context;
      this.attrs = attrs;
   }
}
// RadiusViewDelegate.java
package com.vincent.baseproject.ui;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;
import com.vincent.baseproject.R.styleable;
import kotlin.Metadata;
import kotlin.jvm.JvmOverloads;
import kotlin.jvm.internal.DefaultConstructorMarker;
import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

@Metadata(
   mv = {1, 1, 15},
   bv = {1, 0, 3},
   k = 1,
   d1 = {"\u0000,\n\u0002\u0018\u0002\n\u0000\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\b\b\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\b\u0016\u0018\u0000*\u0010\b\u0000\u0010\u0001 \u0001*\b\u0012\u0004\u0012\u0002H\u00010\u00002\u00020\u0002B#\b\u0007\u0012\u0006\u0010\u0003\u001a\u00020\u0004\u0012\u0006\u0010\u0005\u001a\u00020\u0006\u0012\n\b\u0002\u0010\u0007\u001a\u0004\u0018\u00010\b¢\u0006\u0002\u0010\tJ\u0012\u0010\u0010\u001a\u00020\u00112\b\u0010\u0012\u001a\u0004\u0018\u00010\u0013H\u0002R\u0016\u0010\u0007\u001a\u0004\u0018\u00010\bX\u0096\u0004¢\u0006\b\n\u0000\u001a\u0004\b\n\u0010\u000bR\u0014\u0010\u0005\u001a\u00020\u0006X\u0096\u0004¢\u0006\b\n\u0000\u001a\u0004\b\f\u0010\rR\u0011\u0010\u0003\u001a\u00020\u0004¢\u0006\b\n\u0000\u001a\u0004\b\u000e\u0010\u000f¨\u0006\u0014"},
   d2 = {"Lcom/vincent/baseproject/ui/RadiusViewDelegate;", "T", "", "view", "Landroid/view/View;", "context", "Landroid/content/Context;", "attrs", "Landroid/util/AttributeSet;", "(Landroid/view/View;Landroid/content/Context;Landroid/util/AttributeSet;)V", "getAttrs", "()Landroid/util/AttributeSet;", "getContext", "()Landroid/content/Context;", "getView", "()Landroid/view/View;", "initAttributes", "", "typedArray", "Landroid/content/res/TypedArray;", "app_debug"}
)
public class RadiusViewDelegate {
   @NotNull
   private final View view;
   @NotNull
   private final Context context;
   @Nullable
   private final AttributeSet attrs;

   private final void initAttributes(TypedArray typedArray) {
   }

   @NotNull
   public final View getView() {
      return this.view;
   }

   @NotNull
   public Context getContext() {
      return this.context;
   }

   @Nullable
   public AttributeSet getAttrs() {
      return this.attrs;
   }

   @JvmOverloads
   public RadiusViewDelegate(@NotNull View view, @NotNull Context context, @Nullable AttributeSet attrs) {
      Intrinsics.checkParameterIsNotNull(view, "view");
      Intrinsics.checkParameterIsNotNull(context, "context");
      super();
      this.view = view;
      this.context = context;
      this.attrs = attrs;
      TypedArray typedArray = this.getContext().obtainStyledAttributes(this.getAttrs(), styleable.RadiusView);
      this.initAttributes(typedArray);
   }

   // $FF: synthetic method
   public RadiusViewDelegate(View var1, Context var2, AttributeSet var3, int var4, DefaultConstructorMarker var5) {
      if ((var4 & 4) != 0) {
         var3 = (AttributeSet)null;
      }

      this(var1, var2, var3);
   }

   @JvmOverloads
   public RadiusViewDelegate(@NotNull View view, @NotNull Context context) {
      this(view, context, (AttributeSet)null, 4, (DefaultConstructorMarker)null);
   }
}
复制代码

仔细查看RadiusViewDelegate的构造方法RadiusViewDelegate(@NotNull View view, @NotNull Context context, @Nullable AttributeSet attrs)方法下第七行代码得知,contextattrs并非使用构造方法的参数,而是经过get方法获取的,而问题就出在get方法。由于RadiusTextDelegate的构造方法参数context使用了两个关键字override val,因此将该文件转成Java代码后子类生成了以下方法:

@NotNull
   public Context getContext() {
      return this.context;
   }
复制代码

话说回来,父类经过getContext()方法获取Context时,子类尚未来得及将参数赋值给成员变量this.context,所以就产生了异常'android.content.res.TypedArray android.content.Context.obtainStyledAttributes(android.util.AttributeSet, int[])' on a null object reference。 解决方案很简单,将子类的构造方法参数修改一下便可:

open class RadiusViewDelegate<out T> @JvmOverloads constructor(
    val view: View,
    val context: Context,
    val attrs: AttributeSet? = null
) where T : RadiusViewDelegate<T> {
    
}

open class RadiusTextDelegate<T> constructor(
    private val textView: TextView,
    context: Context,
    attrs: AttributeSet?
) :
    RadiusViewDelegate<T>(textView, context, attrs) where T : RadiusViewDelegate<T> {
        
    }
复制代码

经过上面的例子能够简单的说一下构造方法参数关键字的用法,valvar就不解释了,open表明子类会重写父类的get方法和set方法(若是是var类型参数),而子类若是使用和父类彻底一致的参数名称,则不能不添加override关键字。添加override关键字之后,若是父类的参数是var则子类也不能修改成val类型,反之,子类构造方法参数的类型则不受限制了。

构造方法的关键字

通常咱们在自定义View的时候,常见构造方法有以下两种有隐患的写法,以下:

/// 第一种
class CountdownView: TextView{
    constructor(context: Context) : this(context,null)
    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs,0)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
}
/// 第二种
class CountdownView @JvmOverloads constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : TextView(context, attrs, defStyleAttr) {
    
}
复制代码

我在自定义RadiusSwitch的时候使用了第二种构造方法,结果遇到了一堆异常。

java.lang.NullPointerException: Attempt to invoke interface method 'int java.lang.CharSequence.length()' on a null object reference
        at android.widget.Switch.makeLayout(Switch.java:896)
        at android.widget.Switch.onMeasure(Switch.java:815)
复制代码

网上百度、谷歌找到如下几种解决方案:

  • theme设置错误——未发现错误
  • Context设置错误——断点检查是当前Activity
  • 添加textOfftextOn属性——添加之后Switch的样式显示有问题,可是能解决闪退。

经过和原生的Switch添加断点对比之后发现是mShowText未被设置为true,可是经过xml文件配置属性可解决。接下来又发现Switch的点击事件无效了。最后查看源码发现是clickable也被设置为false,致使点击事件无效。固然,也能够经过xml配置属性解决。 可是始终是没有找到问题的根源,最后再次检查源码发现原来是构造方法使用错误,由于我调用的是Switch(Context context, AttributeSet attrs, int defStyleAttr),而xml布局文件中的控件是使用的构造方法应该是Switch(Context context, AttributeSet attrs),而两者的构造方法区别在于下面:

public Switch(Context context, AttributeSet attrs) {
        this(context, attrs, com.android.internal.R.attr.switchStyle);
    }
public Switch(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, 0);
    }
复制代码

找到错误的根本缘由之后,解决就很简单了:固然再也不是xml配置上面提到的这些默认属性了,而是修改构造方法,以下:

class RadiusSwitch : Switch {
    var delegate: RadiusSwitchDelegateImp? = null

    constructor(context: Context) : super(context) {
        delegate = RadiusSwitchDelegateImp(this, context, null)
    }

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs,0) {
        delegate = RadiusSwitchDelegateImp(this, context, attrs)
    }

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int = 0) : super(context, attrs, defStyleAttr) {
        delegate = RadiusSwitchDelegateImp(this, context, attrs)
    }
}
复制代码

项目使用

到此实战填坑就结束了,接下来仍是给你们看看这个模块的效果图,而后告诉你们怎么使用。

效果图1

效果图2

效果图3

<!--View默认样式-->
    
    <!-- 圆角矩形背景色 -->
    <attr name="rv_backgroundColor" format="color"/>
    <!-- 圆角矩形背景色Pressed -->
    <attr name="rv_backgroundPressedColor" format="color"/>
    <!-- 圆角矩形背景色Disabled -->
    <attr name="rv_backgroundDisabledColor" format="color"/>
    <!-- 圆角矩形背景色Selected -->
    <attr name="rv_backgroundSelectedColor" format="color"/>
    <!-- 圆角矩形背景色Checked -->
    <attr name="rv_backgroundCheckedColor" format="color"/>
    <!-- 圆角矩形背景色按下状态透明度(0-255默认0 仅当未设置backgroundPressedColor有效) -->
    <attr name="rv_backgroundPressedAlpha" format="integer"/>
    <!-- 圆角边框颜色-->
    <attr name="rv_strokeColor" format="color"/>
    <!-- 圆角边框颜色Pressed -->
    <attr name="rv_strokePressedColor" format="color"/>
    <!-- 圆角边框颜色Disabled -->
    <attr name="rv_strokeDisabledColor" format="color"/>
    <!-- 圆角边框颜色Selected -->
    <attr name="rv_strokeSelectedColor" format="color"/>
    <!-- 圆角边框颜色Checked -->
    <attr name="rv_strokeCheckedColor" format="color"/>
    <!-- 圆角矩形边框色按下状态透明度(0-255默认0 仅当未设置strokePressedColor有效) -->
    <attr name="rv_strokePressedAlpha" format="integer"/>

    <!-- 圆角线框,单位dp-->
    <attr name="rv_strokeWidth" format="dimension"/>
    <!-- 圆角线框虚线的宽度,单位dp-->
    <attr name="rv_strokeDashWidth" format="dimension"/>
    <!-- 圆角线框虚线的间隙,单位dp-->
    <attr name="rv_strokeDashGap" format="dimension"/>

    <!-- 圆角弧度是高度一半-->
    <attr name="rv_radiusHalfHeightEnable" format="boolean"/>
    <!-- 圆角矩形宽高相等,取较宽高中大值-->
    <attr name="rv_widthHeightEqualEnable" format="boolean"/>
    <!-- 圆角弧度,单位dp-->
    <attr name="rv_radius" format="dimension"/>
    <!-- 圆角弧度,单位dp,TopLeft-->
    <attr name="rv_topLeftRadius" format="dimension"/>
    <!-- 圆角弧度,单位dp,TopRight-->
    <attr name="rv_topRightRadius" format="dimension"/>
    <!-- 圆角弧度,单位dp,BottomLeft-->
    <attr name="rv_bottomLeftRadius" format="dimension"/>
    <!-- 圆角弧度,单位dp,BottomRight-->
    <attr name="rv_bottomRightRadius" format="dimension"/>
    <!-- 水波纹颜色-->
    <attr name="rv_rippleColor" format="color"/>
    <!-- 是否有Ripple效果,api21+有效-->
    <attr name="rv_rippleEnable" format="boolean"/>
    <!--View选中状态-->
    <attr name="rv_selected" format="boolean"/>
    <!--View背景状态进入状态延时(非水波纹)-->
    <attr name="rv_enterFadeDuration" format="integer"/>
    <!--View背景状态退出状态延时(非水波纹)-->
    <attr name="rv_exitFadeDuration" format="integer"/>

    <!-- 文字颜色-->
    <attr name="rv_textColor" format="color"/>
    <!-- 文字颜色Pressed-->
    <attr name="rv_textPressedColor" format="color"/>
    <!-- 文字颜色Disabled-->
    <attr name="rv_textDisabledColor" format="color"/>
    <!-- 文字颜色Selected-->
    <attr name="rv_textSelectedColor" format="color"/>
    <!-- 文字颜色Checked-->
    <attr name="rv_textCheckedColor" format="color"/>

    <!--设置EditText在调用setText后默认光标置于末尾-->
    <attr name="rv_selectionEndEnable" format="boolean"/>
    <!--设置EditText在调用setText后默认光标置于末尾只执行一次-->
    <attr name="rv_selectionEndOnceEnable" format="boolean"/>

    <!--如下为TextView Drawable Left Top Right Bottom 宽高及各类状态属性-->
    <!--是否系统自带drawableLeft样式-->
    <attr name="rv_leftDrawableSystemEnable" format="boolean"/>
    <!--设置drawable为颜色值(ColorDrawable)时的圆角弧度-->
    <attr name="rv_leftDrawableColorRadius" format="dimension"/>
    <!--设置drawable为颜色值(ColorDrawable)时是否圆形-->
    <attr name="rv_leftDrawableColorCircleEnable" format="boolean"/>
    <!--drawable宽高属性当drawable为颜色值(ColorDrawable)时必须设置不然显示不出-->
    <attr name="rv_leftDrawableWidth" format="dimension"/>
    <attr name="rv_leftDrawableHeight" format="dimension"/>
    <attr name="rv_leftDrawable" format="reference|color"/>
    <attr name="rv_leftPressedDrawable" format="reference|color"/>
    <attr name="rv_leftDisabledDrawable" format="reference|color"/>
    <attr name="rv_leftSelectedDrawable" format="reference|color"/>
    <attr name="rv_leftCheckedDrawable" format="reference|color"/>

    <!--drawable宽高属性当drawable为颜色值(ColorDrawable)时必须设置不然显示不出-->
    <!--是否系统自带drawableTop样式-->
    <attr name="rv_topDrawableSystemEnable" format="boolean"/>
    <!--设置drawable为颜色值(ColorDrawable)时的圆角弧度-->
    <attr name="rv_topDrawableColorRadius" format="dimension"/>
    <!--设置drawable为颜色值(ColorDrawable)时是否圆形-->
    <attr name="rv_topDrawableColorCircleEnable" format="boolean"/>
    <!--drawable宽高属性当drawable为颜色值(ColorDrawable)时必须设置不然显示不出-->
    <attr name="rv_topDrawableWidth" format="dimension"/>
    <attr name="rv_topDrawableHeight" format="dimension"/>
    <attr name="rv_topDrawable" format="reference|color"/>
    <attr name="rv_topPressedDrawable" format="reference|color"/>
    <attr name="rv_topDisabledDrawable" format="reference|color"/>
    <attr name="rv_topSelectedDrawable" format="reference|color"/>
    <attr name="rv_topCheckedDrawable" format="reference|color"/>

    <!--drawable宽高属性当drawable为颜色值(ColorDrawable)时必须设置不然显示不出-->
    <!--是否系统自带drawableRight样式-->
    <attr name="rv_rightDrawableSystemEnable" format="boolean"/>
    <!--设置drawable为颜色值(ColorDrawable)时的圆角弧度-->
    <attr name="rv_rightDrawableColorRadius" format="dimension"/>
    <!--设置drawable为颜色值(ColorDrawable)时是否圆形-->
    <attr name="rv_rightDrawableColorCircleEnable" format="boolean"/>
    <!--drawable宽高属性当drawable为颜色值(ColorDrawable)时必须设置不然显示不出-->
    <attr name="rv_rightDrawableWidth" format="dimension"/>
    <attr name="rv_rightDrawableHeight" format="dimension"/>
    <attr name="rv_rightDrawable" format="reference|color"/>
    <attr name="rv_rightPressedDrawable" format="reference|color"/>
    <attr name="rv_rightDisabledDrawable" format="reference|color"/>
    <attr name="rv_rightSelectedDrawable" format="reference|color"/>
    <attr name="rv_rightCheckedDrawable" format="reference|color"/>

    <!--drawable宽高属性当drawable为颜色值(ColorDrawable)时必须设置不然显示不出-->
    <!--是否系统自带drawableBottom样式-->
    <attr name="rv_bottomDrawableSystemEnable" format="boolean"/>
    <!--设置drawable为颜色值(ColorDrawable)时的圆角弧度-->
    <attr name="rv_bottomDrawableColorRadius" format="dimension"/>
    <!--设置drawable为颜色值(ColorDrawable)时是否圆形-->
    <attr name="rv_bottomDrawableColorCircleEnable" format="boolean"/>
    <!--drawable宽高属性当drawable为颜色值(ColorDrawable)时必须设置不然显示不出-->
    <attr name="rv_bottomDrawableWidth" format="dimension"/>
    <attr name="rv_bottomDrawableHeight" format="dimension"/>
    <attr name="rv_bottomDrawable" format="reference|color"/>
    <attr name="rv_bottomPressedDrawable" format="reference|color"/>
    <attr name="rv_bottomDisabledDrawable" format="reference|color"/>
    <attr name="rv_bottomSelectedDrawable" format="reference|color"/>
    <attr name="rv_bottomCheckedDrawable" format="reference|color"/>
    <!--以上为TextView Drawable Left Top Right Bottom 宽高及各类状态属性-->

    <!--如下为CompoundButton ButtonDrawable 宽高及不一样状态属性-->
    <!--drawable宽高属性当drawable为颜色值(ColorDrawable)时方有效-->
    <!--是否系统自带Button样式-->
    <attr name="rv_buttonDrawableSystemEnable" format="boolean"/>
    <!--设置drawable为颜色值(ColorDrawable)时的圆角弧度-->
    <attr name="rv_buttonDrawableColorRadius" format="dimension"/>
    <!--设置drawable为颜色值(ColorDrawable)时是否圆形-->
    <attr name="rv_buttonDrawableColorCircleEnable" format="boolean"/>
    <attr name="rv_buttonDrawableWidth" format="dimension"/>
    <attr name="rv_buttonDrawableHeight" format="dimension"/>
    <attr name="rv_buttonDrawable" format="reference|color"/>
    <attr name="rv_buttonPressedDrawable" format="reference|color"/>
    <attr name="rv_buttonDisabledDrawable" format="reference|color"/>
    <attr name="rv_buttonSelectedDrawable" format="reference|color"/>
    <attr name="rv_buttonCheckedDrawable" format="reference|color"/>
复制代码

以上属性也能够经过代码来设置,以下:

rtv_javaBg.delegate?.run {
            this.setTextCheckedColor(Color.BLUE)
            .setBackgroundCheckedColor(Color.WHITE)
            .setRadius(resources.getDimension(R.dimen.dp_radius))
            .setStrokeWidth(resources.getDimensionPixelSize(R.dimen.dp_stroke_width))
            .setStrokeColor(ContextCompat.getColor(this@RadiusActivity,android.R.color.holo_purple))
            .setStrokeDashWidth(resources.getDimension(R.dimen.dp_dash_width))
            .setStrokeDashGap(resources.getDimension(R.dimen.dp_dash_gap))
            .initShape()
        }
复制代码

以上为我的理解,水平有限,还请各位斧正!

源码

示例

参考:泛型

相关文章
相关标签/搜索