zoukankan      html  css  js  c++  java
  • Android笔记(六十七) 自定义控件

          实际编程中,系统提供的控件往往无法满足我们的需求,一来是样子丑陋,二来是一些复杂的组合需要多次使用的话,每次都写一堆控件的组合会很耗费时间,所以我们将这些组件的组合自定义为一个新的控件,以后使用的时候直接用该控件,方便又简单。最常见的例子就是软件中的titleTar

          实现自定义控件的步骤:

          1.设置控件的属性

          2.实现我们的View

          3.引用我们自定的View

      官方文档:http://developer.android.com/training/custom-views/create-view.html

    下面开始:

    设置控件的属性:

      分析一下上面四个titleBar,都是由一个居中的文本和一个右边一个图片按钮组成,只不过后面两个没有按钮而已

      那么我们自定义的这个组件就得包含一下几个基本属性:文本内容,文本大小,文本颜色,按钮路径

      在value中新建一个xml文件attrs.xml,把这些属性创建出来

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <declare-styleable name="MyCustomTitleBar">
            <attr name="titleText" format="string"/>
            <attr name="titleTextSize" format="dimension"/>
            <attr name="titleTextColor" format="color"/>
            <attr name="titleButtonImage" format="reference"/>
        </declare-styleable>
    </resources>
      declare-styleable标签是用来定义自定义控件的属性的,我们的控件属性都以attr形式存放在declare-styleable标签中,format是用来形容该属性的类型,有如下值可选:string,color,dimension,integer,enum,reference,float,boolean,fraction,flag

    实现我们的View

          定义一个继承自RelativeLayout的类MyCustomTitltBar,然后在他的构造方法中获取我们自定义的控件样式

    package cn.lixyz.customviewdemo;
    
    import android.annotation.SuppressLint;
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.drawable.Drawable;
    import android.util.AttributeSet;
    import android.widget.ImageButton;
    import android.widget.RelativeLayout;
    import android.widget.TextView;
    
    @SuppressLint("NewApi")
    public class MyCustomTitleBar extends RelativeLayout {
    
        // 定义自定义控件包含的组件
        private TextView title;
        private ImageButton button;
    
        // 定义控件的属性
        private String titleText;
        private float titleTextSize;
        private int titleTextColor;
        private Drawable titleButtonImage;
    
        // 为每个控件定义LayoutParams
        private LayoutParams textLayoutParams;
        private LayoutParams buttonLayoutParams;
    
        public MyCustomTitleBar(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            // 获取我们定义的属性
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyCustomTitleBar);
    
            titleText = array.getString(R.styleable.MyCustomTitleBar_titleText);
            titleTextColor = array.getColor(R.styleable.MyCustomTitleBar_titleTextColor, 0);
            titleTextSize = array.getDimension(R.styleable.MyCustomTitleBar_titleTextSize, 10);
            titleButtonImage = array.getDrawable(R.styleable.MyCustomTitleBar_titleButtonImage);
    
            // 回收,以防出错
            array.recycle();
    
            // 新建包含的子组件
            title = new TextView(context);
            button = new ImageButton(context);
    
            // 为子组件赋值
            title.setText(titleText);
            title.setTextColor(titleTextColor);
            title.setTextSize(titleTextSize);
            button.setBackground(titleButtonImage);
    
            // 设置背景色
            setBackgroundColor(0xFF38373c);
    
            // 设置包含控件的属性并添加到view中
            textLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
            textLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
            addView(title, textLayoutParams);
            buttonLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
            buttonLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
            addView(button, buttonLayoutParams);
        }
    }

       其中LayoutParams是用于child view(子视图) 向 parent view(父视图)传达自己的意愿的一个东西

      在获取属性的时候,android定义的格式为控件名_属性名  例如上面的MyCustomTitleBar_titleText、MyCustomTitleBar_titleTextSize

          LayoutParams继承于Android.View.ViewGroup.LayoutParams. 
          LayoutParams相当于一个Layout的信息包,它封装了Layout的位置、高、宽等信息。假设在屏幕上一块区域是由一个Layout占领的,如果将一个View添加到一个Layout中,最好告诉Layout用户期望的布局方式,也就是将一个认可的layoutParams传递进去。 
          可以这样去形容LayoutParams,在象棋的棋盘上,每个棋子都占据一个位置,也就是每个棋子都有一个位置的信息,如这个棋子在4行4列,这里的“4行4列”就是棋子的LayoutParams。 
          但LayoutParams类也只是简单的描述了宽高,宽和高都可以设置成三种值: 
               1,一个确定的值; 
               2,FILL_PARENT,即填满(和父容器一样大小); 
               3,WRAP_CONTENT,即包裹住组件就好。 

               http://byandby.iteye.com/blog/816718

      TypedArray实例是个属性的容器,通过context.obtainStyledAttributes()方法返回得到

    引用我们自定的View

          在布局文件中引入我们自定义的控件

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:custom="http://schemas.android.com/apk/res/cn.lixyz.customviewdemo"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context="cn.lixyz.customviewdemo.MainActivity" >
    
        <cn.lixyz.customviewdemo.MyCustomTitleBar
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            custom:titleButtonImage="@drawable/image"
            custom:titleText="通讯录"
            custom:titleTextColor="#ffffff"
            custom:titleTextSize="5dp" >
        </cn.lixyz.customviewdemo.MyCustomTitleBar>
    
        <cn.lixyz.customviewdemo.MyCustomTitleBar
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            custom:titleButtonImage="@drawable/plus"
            custom:titleText="微信"
            custom:titleTextColor="#ffffff"
            custom:titleTextSize="5dp" >
        </cn.lixyz.customviewdemo.MyCustomTitleBar>
    
        <cn.lixyz.customviewdemo.MyCustomTitleBar
            android:layout_width="wrap_content"
            android:layout_height="30dp"
            android:layout_marginTop="10dp"
            custom:titleText="发现"
            custom:titleTextColor="#ffffff"
            custom:titleTextSize="5dp" >
        </cn.lixyz.customviewdemo.MyCustomTitleBar>
    
        <cn.lixyz.customviewdemo.MyCustomTitleBar
            android:layout_width="wrap_content"
            android:layout_height="30dp"
            android:layout_marginTop="10dp"
            custom:titleText="我"
            custom:titleTextColor="#ffffff"
            custom:titleTextSize="5dp" >
        </cn.lixyz.customviewdemo.MyCustomTitleBar>
    
    </LinearLayout>

          一定要在布局中引入我们的命名空间

      格式为:xmlns:自定义名称="http://schemas.android.com/apk/res/包名"
      因为只有引入了我们的命名空间,才可以使用xxx:xxx格式来设置我们定义的属性,我们平时使用的android:xx也是因为上面的xmlns:android="http://schemas.android.com/apk/res/android"
      这样运行一下软件查看效果

          为按钮设置点击事件

          观察我们平时为按钮设置点击事件时候的方法:

    bt.setOnClickListener(new View.OnClickListener() {
                
                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                    
                }
            });

          我们通过按钮的setOnClickListener方法传入一个匿名内部类OnClickListener为按钮设置点击事件,我们查看OnClickListener的介绍

        /**
         * Interface definition for a callback to be invoked when a view is clicked.
         */
        public interface OnClickListener {
            /**
             * Called when a view has been clicked.
             *
             * @param v The view that was clicked.
             */
            void onClick(View v);
        }

      

          我们也可以仿照这个形式,为我们的MyCustomTieleBar设置一个setCustomTitleBarListener,然后传入一个内部类来让这个按钮的点击调用这个类的click方法

          修改MyCustomTitleBar:

    package cn.lixyz.customviewdemo;
    
    import android.annotation.SuppressLint;
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.drawable.Drawable;
    import android.util.AttributeSet;
    import android.view.View;
    import android.widget.ImageButton;
    import android.widget.RelativeLayout;
    import android.widget.TextView;
    
    @SuppressLint("NewApi")
    public class MyCustomTitleBar extends RelativeLayout {
    
        // 定义自定义控件包含的组件
        private TextView title;
        private ImageButton button;
    
        // 定义控件的属性
        private String titleText;
        private float titleTextSize;
        private int titleTextColor;
        private Drawable titleButtonImage;
    
        // 为每个控件定义LayoutParams
        private LayoutParams textLayoutParams;
        private LayoutParams buttonLayoutParams;
    
        private customTitleBarListener listener;
    
        public interface customTitleBarListener {
            public void click();
        }
    
        public void setCustomTitleBarListener(customTitleBarListener listener) {
            this.listener = listener;
        }
    
        public MyCustomTitleBar(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            // 获取我们定义的属性
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyCustomTitleBar);
    
            titleText = array.getString(R.styleable.MyCustomTitleBar_titleText);
            titleTextColor = array.getColor(R.styleable.MyCustomTitleBar_titleTextColor, 0);
            titleTextSize = array.getDimension(R.styleable.MyCustomTitleBar_titleTextSize, 10);
            titleButtonImage = array.getDrawable(R.styleable.MyCustomTitleBar_titleButtonImage);
    
            // 回收,以防出错
            array.recycle();
    
            // 新建包含的子组件
            title = new TextView(context);
            button = new ImageButton(context);
    
            // 为子组件赋值
            title.setText(titleText);
            title.setTextColor(titleTextColor);
            title.setTextSize(titleTextSize);
            button.setBackground(titleButtonImage);
    
            // 设置背景色
            setBackgroundColor(0xFF38373c);
    
            // 设置包含控件的属性并添加到view中
            textLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
            textLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
            addView(title, textLayoutParams);
            buttonLayoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
            buttonLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
            addView(button, buttonLayoutParams);
    
            button.setOnClickListener(new View.OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    listener.click();
                }
            });
    
        }
    }

      这样我们在MainActivity中就可以为按钮设置点击事件了

    package cn.lixyz.customviewdemo;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import android.widget.Toast;
    
    public class MainActivity extends Activity {
    
        private MyCustomTitleBar addressListBar, wechatBar;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Button bt = new Button(this);
            bt.setOnClickListener(new View.OnClickListener() {
                
                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                    
                }
            });
    
            addressListBar = (MyCustomTitleBar) findViewById(R.id.addressListBar);
            wechatBar = (MyCustomTitleBar) findViewById(R.id.wechatBar);
    
            addressListBar.setCustomTitleBarListener(new MyCustomTitleBar.customTitleBarListener() {
    
                @Override
                public void click() {
                    Toast.makeText(MainActivity.this, "通讯录按钮被点击", Toast.LENGTH_SHORT).show();
                }
            });
            wechatBar.setCustomTitleBarListener(new MyCustomTitleBar.customTitleBarListener() {
    
                @Override
                public void click() {
                    Toast.makeText(MainActivity.this, "微信按钮被点击", Toast.LENGTH_SHORT).show();
                }
            });
    
        }
    
    }

     
    
    
     
  • 相关阅读:
    BackgroundWorker原理剖析
    委托异步调用时BeginInvoke的陷阱处理
    线程静态在对象缓存中的妙用
    值得珍藏的.NET源码,不保存就没机会了
    .NET 4.5.1 参考源码索引
    .NET 4.5 参考源码索引
    .NET 4.0 参考源码索引
    WWF3.5SP1 参考源码索引
    WCF3.5 SP1 参考源码索引
    .NET 3.5.1 参考源码索引
  • 原文地址:https://www.cnblogs.com/xs104/p/5077406.html
Copyright © 2011-2022 走看看