Android学习之自定义view(三)

自定义view第三篇,废话少说,直接进入正题。

第一步:在attrs.xml文件中定义属性名和值,并在构造函数中获取该值

<resources>

   <declare-styleablename="MyCustomView3">

        <attrname="horizonal_spacing"format="dimension"/>

        <attrname="vertical_spacing"format="dimension"/>

        <attrname="layout_vertical_spacing"format="dimension"/>

   </declare-styleable>

</resources>

 

public MyCustomView3(Context context, AttributeSet attrs) {

      super(context, attrs);

      //获取属性值

      TypedArrayta = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView3);

      for(inti=0;i<ta.getIndexCount();i++){

         int attr = ta.getIndex(i);

         switch(attr){

         case R.styleable.MyCustomView3_horizonal_spacing :

            horizonal_spacing= ta.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(

                   TypedValue.COMPLEX_UNIT_SP,16, getResources().getDisplayMetrics()));

            break;

         case R.styleable.MyCustomView3_vertical_spacing :

            vertical_spacing =ta.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(

                   TypedValue.COMPLEX_UNIT_SP,16, getResources().getDisplayMetrics()));

            break;

         }

      }

      ta.recycle();

   }

 

注意:在构造函数中,horizonal_spacingvertical_spacing的默认值通过(int)TypedValue.applyDimension(

                   TypedValue.COMPLEX_UNIT_SP,16, getResources().getDisplayMetrics())这个获取,默认为16dp

也可以在dimension.xml文件中定义一个大小,然后通过getResources().getDimensionPixelSize(R.dimen.horizonal_spacing)获取。

 

第二步:重写onMeasure方法

在这里,当容器的宽和高被定义为wrap_content的时候:

宽度=(每张卡片的宽度+定义的水平间距) * 3;

高度=(每张卡片的高度+容器定义的垂直间距+视图定义的垂直间距) * 3;

//记录当为warp_content的时候容器的宽和高

      int width = 0;

      int height = 0;

     

      for(inti=0;i<getChildCount();i++){

         ViewchildView = getChildAt(i);

         mParams =(MyLayoutParams)childView.getLayoutParams();

         width+=  mParams.width +horizonal_spacing;

         //用布局定义的组件高度+定义的垂直间距+容器定义的垂直间距来决定子视图组件的高度

         height+= mParams.height +mParams.layout_vertical_spacing+vertical_spacing;

        

      }

 

第三步:重写onLayout方法

对子视图进行布局;

@Override

   protectedvoid onLayout(boolean changed, int l,int t,int r,int b) {

      //分别代表卡片的左x坐标,上y坐标,x坐标,y坐标

      int cl = 0, ct = 0, cr = 0,cb = 0;

      for(inti=0;i<getChildCount();i++){

         ViewchildView = getChildAt(i);

         mParams =(MyLayoutParams)childView.getLayoutParams();

         switch(i){

         case 0 :

            //第一张卡片的左坐标为0,右坐标为视图定义的宽度;上y坐标为子视图自身定义的间距,下y坐标=y坐标+视图定义的宽度

            cr= mParams.width;

            ct+= mParams.layout_vertical_spacing;

            cb= mParams.height +mParams.layout_vertical_spacing+vertical_spacing;

            break;

         case 1 :

            cl+= horizonal_spacing;

            cr+= horizonal_spacing;

            ct+= mParams.layout_vertical_spacing+vertical_spacing;

            cb+= mParams.layout_vertical_spacing+vertical_spacing;

           

            break;

         case 2 :

            cl+= horizonal_spacing;

            cr+= horizonal_spacing;

            ct+= mParams.layout_vertical_spacing+vertical_spacing;

            cb+= mParams.layout_vertical_spacing+vertical_spacing;

            break;

         }

         childView.layout(cl,ct, cr, cb);

      }

   }

 

 

第四步:比较关键。定义一个内部类,用于保存子视图的布局参数

/**

    * 用于获取子视图的布局参数

    * @authorwyb

    *

    */

   privateclass MyLayoutParamsextends ViewGroup.LayoutParams{

      //和布局中子视图定义的垂直间距想对应

      publicintlayout_vertical_spacing;

      public MyLayoutParams(Contextcontext, AttributeSet attrs) {

         super(context, attrs);

         //获取垂直间距

         TypedArraya = context.obtainStyledAttributes(attrs, 

                   R.styleable.MyCustomView3); 

            try

               layout_vertical_spacing= a 

                  .getDimensionPixelSize( 

                      R.styleable.MyCustomView3_layout_vertical_spacing

                      -1); 

            } finally

              a.recycle(); 

            }

      }

 

      public MyLayoutParams(int arg0,int arg1) {

         super(arg0, arg1);

         //TODO Auto-generated constructor stub

      }

 

      publicMyLayoutParams(LayoutParams arg0) {

         super(arg0);

         //TODO Auto-generated constructor stub

      }

     

   }

在这个案例中MyLayoutParams类的layout_vertical_spacing就是保存子视图的垂直间距。虽然在容器的构造函数中可以获取这个属性值,用于容器的测量和子视图的布局,但这样显得容器类不是很高类聚,因为这个属性是属于子视图的,所以应该定义在这个内部类中

 

好,此时如果你就开始使用这个容器的话,在onMeasure方法的

mParams =(MyLayoutParams)childView.getLayoutParams();这一行就会报错

出现这个错误的原因是还没有重写下面这三个方法

@Override

   public LayoutParams generateLayoutParams(AttributeSetattrs) {

      //TODO Auto-generated method stub

      returnnewMyLayoutParams(getContext(),attrs);

   }

 

   @Override

   protected LayoutParamsgenerateLayoutParams(LayoutParams p) {

      //TODO Auto-generated method stub

      return new MyLayoutParams(p);

   }

 

   @Override

   protected LayoutParamsgenerateDefaultLayoutParams() {

      //TODO Auto-generated method stub

      returnnewMyLayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);

   }

 

这三个方法的作用是为了告诉子视图有什么样的布局参数。比如现在MyLayoutParams是继承ViewGroup.LayoutParams,那么子视图想获取leftMargin这些属性的时候是无法获取,可以通过继承MarginLayoutParams来获取;其他属性依次类推,如想获取线性布局的orientation属性,就必须继承LinearLayout.LayoutParams这个类。

当然,也可以不用写这个内部类,然后在重写上面三个方法的时候直接返回某个类的对象即可;在这儿写这个内部类主要是让子视图拥有layout_vertical_spacing这个属性而已。

 

用法就很简单,直接在子视图中定义,如下:

<View 

           android:layout_width="70dp" 

           android:layout_height="100dp" 

            custom:layout_vertical_spacing="10dp"

           android:background="#00FF00"/>

 

 

第五步:使用这个自定义的ViewGroup

<com.example.mycustomview3.MyCustomView3

        android:layout_width="wrap_content"

       android:layout_height="wrap_content"

        android:background="#000000"

        custom:horizonal_spacing="20dp"

        custom:vertical_spacing="30dp">

        <View 

           android:layout_width="70dp" 

           android:layout_height="100dp" 

           android:background="#FF0000"/> 

 

       <View 

           android:layout_width="70dp" 

           android:layout_height="100dp" 

            custom:layout_vertical_spacing="10dp"

           android:background="#00FF00"/> 

 

       <View 

           android:layout_width="70dp" 

           android:layout_height="100dp" 

           android:background="#0000FF"/>

 

</com.example.mycustomview3.MyCustomView3>

 

当为:wrap_content的时候:

 

当为300dp的时候:

 

 

到此,自定义ViewGroup就算完成了。当然,这只是很初步的定义,后面还会进一步学习。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQLAlchemy 是一个 SQL 工具包和对象关系映射(ORM)库,用于 Python 编程语言。它提供了一个高级的 SQL 工具和对象关系映射工具,允许开发者以 Python 类和对象的形式操作数据库,而无需编写大量的 SQL 语句。SQLAlchemy 建立在 DBAPI 之上,支持多种数据库后端,如 SQLite, MySQL, PostgreSQL 等。 SQLAlchemy 的核心功能: 对象关系映射(ORM): SQLAlchemy 允许开发者使用 Python 类来表示数据库表,使用类的实例表示表中的行。 开发者可以定义类之间的关系(如一对多、多对多),SQLAlchemy 会自动处理这些关系在数据库中的映射。 通过 ORM,开发者可以像操作 Python 对象一样操作数据库,这大大简化了数据库操作的复杂性。 表达式语言: SQLAlchemy 提供了一个丰富的 SQL 表达式语言,允许开发者以 Python 表达式的方式编写复杂的 SQL 查询。 表达式语言提供了对 SQL 语句的灵活控制,同时保持了代码的可读性和可维护性。 数据库引擎和连接池: SQLAlchemy 支持多种数据库后端,并且为每种后端提供了对应的数据库引擎。 它还提供了连接池管理功能,以优化数据库连接的创建、使用和释放。 会话管理: SQLAlchemy 使用会话(Session)来管理对象的持久化状态。 会话提供了一个工作单元(unit of work)和身份映射(identity map)的概念,使得对象的状态管理和查询更加高效。 事件系统: SQLAlchemy 提供了一个事件系统,允许开发者在 ORM 的各个生命周期阶段插入自定义的钩子函数。 这使得开发者可以在对象加载、修改、删除等操作时执行额外的逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值