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

Posted pszh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义控件的高级自定义属性相关的知识,希望对你有一定的参考价值。

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

看了标题肯定小伙伴们肯定会狠狠的鄙视一把,丢下一句 不就是自定义控件的自定义属性么拉倒了,这玩意谁不会,会一点自定义的谁都会了,然后就要关掉网页了,客官不急,请看下图,系统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步而写出来的。。。







以上是关于自定义控件的高级自定义属性的主要内容,如果未能解决你的问题,请参考以下文章

WPF 自定义控件中,集合属性怎么做?

如何在VB6.0里为按钮控件添加自定义属性?

自定义控件添加自定义属性问题

android自定义高级控件(价格标)

将自定义控件内的 TextBlock 绑定到同一自定义控件的依赖属性

背水一战 Windows 10 (78) - 自定义控件: 基础知识, 依赖属性, 附加属性