自定义控件的高级自定义属性

   情人节,代码和我可能更配吧。

看了标题肯定小伙伴们肯定会狠狠的鄙视一把,丢下一句 不就是自定义控件的自定义属性么拉倒了,这玩意谁不会,会一点自定义的谁都会了,然后就要关掉网页了,客官不急,请看下图,系统view调用自定义View的属性


实现的效果是:


这个时候 如果你对这个已经了解了 ,那就请丢下你的那句话,然后关掉这个网页吧,然后留下的吃瓜群众我们接着看

首先说下我们的实现思路 

    1.通过给有自定义属性的系统View嵌套一层控件,

     2.获取到xml中给系统view赋值的属性值

     3.然后把自定义的View的属性丢给这个控件,控件去实现这个自定义的属性就可以了

ok,完了 ,就是这么的简单,就是这么的easy,(那边的群众放下手中的西瓜刀,不要急躁,听小司机慢慢道来具体的实现)

准备工作:

    a.首先我们要定义一个ViewGroup(继承自LinearLayout的CustomViewLinearLayout)用来包裹系统的View,

    b. 自定义的属性的申明,(这里也啰嗦下)在valus下建立一个attrs文件,然后

<!--isChange 是否做个旋转动画 beginColor 开始的颜色  finishColor 结束的颜色-->
<declare-styleable name="customViewLinear">
    <attr name="isChange" format="boolean"></attr>
    <attr name="beginColor" format="color"></attr>
    <attr name="finishColor" format="color"></attr>
</declare-styleable>

c.自定义个控件CoverFragment,实现系统view含有的自定义的属性的效果,这里贴下代码

public class CoverFragment extends FrameLayout{



    Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            iniView();
        }
    };

    private boolean isChange;
    private int beginColor;
    private int finishColor;



    public void setChange(boolean change) {
        isChange = change;
    }


    public void setBeginColor(int beginColor) {
        this.beginColor = beginColor;
    }


    public void setFinishColor(int finishColor) {
        this.finishColor = finishColor;
    }

    public CoverFragment(Context context) {
        super(context);
        //这里为了看到效果,做了下延迟,不然initView执行的时候判断都是错的
        handler.sendEmptyMessage(1000);
    }

    public CoverFragment(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    //初始化
   public void iniView(){
        if(isChange){//做一次旋转吧
            float fromXScale = 1.0f;
            float toScaleX = 0.5f;
            float fromYScale = 1.0f;
            float toScaleY = 0.5f;
            float pivotX = getWidth() / 2;
            float pivotY = getHeight() / 2;
            Animation animate = new ScaleAnimation(fromXScale, toScaleX, fromYScale, toScaleY,pivotX, pivotY );
            animate.setDuration(3000);
            startAnimation(animate);

        }

        if(beginColor!=-1&&finishColor!=-1){//做一个颜色的渐变
            if(Build.VERSION.SDK_INT >= 21){
                ValueAnimator valueAnimator = ValueAnimator.ofArgb(beginColor, finishColor);
                valueAnimator.setDuration(3000);
                valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator animation) {
                        int color = (int)animation.getAnimatedValue();
                        setBackgroundColor(color);
                    }
                });
                valueAnimator.start();
            }
        }
    }
}


好了进入我们的主题了

1.给自定义Viewgroup的系统View套一层CoverFragment

       这个里面用到的方法是addView(View child, int index, ViewGroup.LayoutParams params) ,不考虑到性能代码是

@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
    
      CoverFragment cf = new CoverFragment(getContext());//给原始控件嵌套一层的Fragment,
        
       cf.addView(child);
      super.addView(cf, index, params);
    
}

2.获取到xml中给系统view赋值的属性值

    这里我们联想到的是LinearLayout怎么获取xml中的属性,应该是LinearLayout.LayoutParams对的吧,然后是通过generateLayoutParams(AttributeSet attrs),ok,没毛病吧,那我们也按照这个来进行仿写吧,首先自定义个layoutParams

//自定义LayoutParams,用来获取自定义的属性
public static class CustomViewLayoutParams extends LinearLayout.LayoutParams{
    public boolean isChange;
    public int beginColor;
    public int finishColor;
    public CustomViewLayoutParams(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.customViewLinear);
        isChange = ta.getBoolean(R.styleable.customViewLinear_isChange,false);
        beginColor = ta.getColor(R.styleable.customViewLinear_beginColor, -1);
        finishColor = ta.getColor(R.styleable.customViewLinear_finishColor,-1);
        ta.recycle();
    }

}

然后改写 generateLayoutParams(AttributeSet attrs)方法

//拿到child里面的自定义属性
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
    //返回自定义的LayoutParams
    return new CustomViewLayoutParams(getContext(),attrs);
}

好,接下来第三步了

3.系统View获取到的自定义属性赋值给CoverFragment

    依旧是在addView方法中,由于generateLayoutParams()方法是在addView()之前进行的,所以,这个时候params是带有属性的

@Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
    CustomViewLayoutParams layoutParams = (CustomViewLayoutParams) params;
    if(isCustomProperty(layoutParams)){//有自定义属性的才给他嵌套控件,并复制值给那个控件
        CoverFragment cf = new CoverFragment(getContext());//给原始控件嵌套一层的Fragment,
        //复制属性给CoverFragment
        cf.setChange(layoutParams.isChange);
        cf.setBeginColor(layoutParams.beginColor);
        cf.setFinishColor(layoutParams.finishColor);
        cf.addView(child);
        super.addView(cf, index, params);
    }else{
        super.addView(child, index, params);
    }
}

ok这样就大功告成了  最后贴一下整个CustomViewLinearLayout的代码吧,

最后说下: 项目中的代码实现在这个demo中看起来很恶心。。。

                  1.比如为什么要给系统View去套一层view;不可以直接去让系统的View去实现那个属性的效果么

                 

 2.在包裹的控件自定义方法中使用handle,(这里的作用是为了让 系统的自定义的属性值能赋值到嵌套的控件上,在addView()中自定义嵌套控件的时候立刻执行initView方法是没有自定义的属性值的,所以延迟1秒让后面的赋值操作完成,)。

        这个问题在你自定义一个滑动的控件的时候,当滑动到显示那个系统控件才做自定义属性的操作时,按照demo中的3个步骤就是没错的。而在这个demo中最好的方式是不嵌套view的,我不过是为了走这3步而写出来的。。。







  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值