• (二十九)自定义控件


    一、创建自定义控件的3种主要实现方式:

    1)继承已有的控件来实现自定义控件: 主要是当要实现的控件和已有的控件在很多方面比较类似, 通过对已有控件的扩展来满足要求。
    2)通过继承一个布局文件实现自定义控件,一般来说做组合控件时可以通过这个方式来实现。
        注意此时不用onDraw方法,在构造广告中通过inflater加载自定义控件的布局文件,再addView(view),自定义控件的图形界面就加载进来了。
    3)通过继承view类来实现自定义控件,使用GDI绘制出组件界面,一般无法通过上述两种方式来实现时用该方式。
    二、属性文件中format可选项 
    自定义控件就需要首先自定义该控件的属性。在开始前,我们需要检查在values目录下是否有attrs.xml,如果没有则创建。attrs.xml的例子如下所示:

    <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="fustyle"> <attr name="text_msg" format="boolean"></attr> <attr name="text_custom" format="string"></attr> </declare-styleable> </resources>

    format属性类型如下所示:

    1)"reference" //引用
    2)"color" //颜色
    3) "boolean" //布尔值
    4)"dimension" //尺寸值
    5)"float" //浮点值
    6)"integer" //整型值
    7) "string" //字符串
    8)"fraction" //百分数 比如200%
    三、View结构原理
    Android系统的视图结构的设计也采用了组合模式,即View作为所有图形的基类,Viewgroup对View继承扩展为视图容器类。
     View定义了绘图的基本操作
    基本操作由三个函数完成:measure()、layout()、draw(),其内部又分别包含了onMeasure()、onLayout()、onDraw()三个子方法。具体操作如下:
    1、measure操作
         measure操作主要用于计算视图的大小,即视图的宽度和长度。在view中定义为final类型,要求子类不能修改。measure()函数中又会调用下面的函数:
         (1)onMeasure(),视图大小的将在这里最终确定,也就是说measure只是对onMeasure的一个包装,子类可以覆写onMeasure()方法实现自己的计算视图大小的方式,并通过setMeasuredDimension(width, height)保存计算结果。
     
    2、layout操作
         layout操作用于设置视图在屏幕中显示的位置。在view中定义为final类型,要求子类不能修改。layout()函数中有两个基本操作:
         (1)setFrame(l,t,r,b),l,t,r,b即子视图在父视图中的具体位置,该函数用于将这些参数保存起来;
         (2)onLayout(),在View中这个函数什么都不会做,提供该函数主要是为viewGroup类型布局子视图用的;
     
    3、draw操作
         draw操作利用前两部得到的参数,将视图显示在屏幕上,到这里也就完成了整个的视图绘制工作。子类也不应该修改该方法,因为其内部定义了绘图的基本操作:
         (1)绘制背景;
         (2)如果要视图显示渐变框,这里会做一些准备工作;
         (3)绘制视图本身,即调用onDraw()函数。在view中onDraw()是个空函数,也就是说具体的视图都要覆写该函数来实现自己的显示(比如TextView在这里实现了绘制文字的过程)。而对于ViewGroup则不需要实现该函数,因为作为容器是“没有内容“的,其包含了多个子view,而子View已经实现了自己的绘制方法,因此只需要告诉子view绘制自己就可以了,也就是下面的dispatchDraw()方法;
         (4)绘制子视图,即dispatchDraw()函数。在view中这是个空函数,具体的视图不需要实现该方法,它是专门为容器类准备的,也就是容器类必须实现该方法;
         (5)如果需要(应用程序调用了setVerticalFadingEdge或者setHorizontalFadingEdge),开始绘制渐变框;
         (6)绘制滚动条;
          从上面可以看出自定义View需要最少覆写onMeasure()和onDraw()两个方法。
    4、例子程序(主要转载于http://www.cnblogs.com/xiaohou/articles/2179092.html)
    4.1 第一个实现一个带图片和文字的按钮,如图所示:

    整个过程可以分四步走。第一步,定义一个layout,实现按钮内部的布局。代码如下:

    <?xml version="1.0" encoding="utf-8"?>  
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
        android:orientation="horizontal"  
        android:layout_width="fill_parent"  
        android:layout_height="fill_parent"  
        >  
    <ImageView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:id="@+id/iv"  
        android:src="@drawable/confirm"  
        android:paddingTop="5dip"  
        android:paddingBottom="5dip"  
        android:paddingLeft="40dip"  
        android:layout_gravity="center_vertical"  
        />  
    <TextView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:text="确定"  
        android:textColor="#000000"  
        android:id="@+id/tv"  
        android:layout_marginLeft="8dip"  
        android:layout_gravity="center_vertical"  
        />  
    </LinearLayout>  

    这个xml实现一个左图右字的布局,接下来写一个类继承LinearLayout,导入刚刚的布局,并且设置需要的方法,从而使的能在代码中控制这个自定义控件内容的显示。代码如下:

     
    package com.notice.ib;  
      
    import android.content.Context;  
    import android.util.AttributeSet;  
    import android.view.LayoutInflater;  
    import android.widget.ImageView;  
    import android.widget.LinearLayout;  
    import android.widget.TextView;  
      
    public class ImageBt extends LinearLayout {  
      
        private ImageView iv;  
        private TextView  tv;  
        
    //在代码里面创建对象的时候,使用此构造方法
    public ImageBt(Context context) { this(context, null); }
    //在布局文件中声明的view,创建时由系统自动调用
    public ImageBt(Context context, AttributeSet attrs) { super(context, attrs); // 导入布局 LayoutInflater.from(context).inflate(R.layout.custombt, this, true); iv = (ImageView) findViewById(R.id.iv); tv = (TextView) findViewById(R.id.tv); } /** * 设置图片资源 */ public void setImageResource(int resId) { iv.setImageResource(resId); } /** * 设置显示的文字 */ public void setTextViewText(String text) { tv.setText(text); } }

    第三步,在需要使用这个自定义控件的layout中加入这控件,只需要在xml中加入即可。方法如下:

    <RelativeLayout  
             android:orientation="horizontal"  
             android:layout_width="fill_parent"  
             android:layout_height="wrap_content"  
             android:layout_gravity="bottom"  
             >  
             <com.notice.ib.ImageBt  
                 android:id="@+id/bt_confirm"  
                 android:layout_height="wrap_content"  
                 android:layout_width="wrap_content"  
                 android:layout_alignParentBottom="true"  
                 android:background="@drawable/btbg"  
                 android:clickable="true"  
                 android:focusable="true"  
                 />  
             <com.notice.ib.ImageBt  
                 android:id="@+id/bt_cancel"  
                 android:layout_toRightOf="@id/bt_confirm"  
                 android:layout_height="wrap_content"  
                 android:layout_width="wrap_content"  
                 android:layout_alignParentBottom="true"  
                 android:background="@drawable/btbg"  
                 android:clickable="true"  
                 android:focusable="true"  
                />  
             </RelativeLayout>  

    注意的是,控件标签使用完整的类名即可。为了给按钮一个点击效果,你需要给他一个selector背景,这里就不说了。

    最后一步,即在activity中设置该控件的内容。当然,在xml中也可以设置,但是只能设置一个,当我们需要两次使用这样的控件,并且显示内容不同时就不行了。在activity中设置也非常简单,我们在ImageBt这个类中已经写好了相应的方法,简单调用即可。代码如下

    public class MainActivity extends Activity {  
      
        private ImageBt ib1;  
        private ImageBt ib2;  
      
        /** Called when the activity is first created. */  
        @Override  
        public void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.login);  
      
            ib1 = (ImageBt) findViewById(R.id.bt_confirm);  
            ib2 = (ImageBt) findViewById(R.id.bt_cancel);  
      
            ib1.setTextViewText("确定");  
            ib1.setImageResource(R.drawable.confirm);  
            ib2.setTextViewText("取消");  
            ib2.setImageResource(R.drawable.cancel);  
      
            ib1.setOnClickListener(new OnClickListener() {  
      
                @Override  
                public void onClick(View v) {  
                        //在这里可以实现点击事件  
                }  
            });  
      
        }  
    }  

    这样,一个带文字和图片的组合按钮控件就完成了。这样梳理一下,使用还是非常简单的。组合控件能做的事还非常多,主要是在类似上例中的ImageBt类中写好要使用的方法即可。

    4.2 再来看一个组合控件,带删除按钮的EidtText。即在用户输入后,会出现删除按钮,点击即可取消用户输入。

    定义方法和上例一样。首先写一个自定义控件的布局:

    <?xml version="1.0" encoding="utf-8"?>  
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
        android:layout_width="fill_parent"  
        android:layout_height="fill_parent"  
        >  
    <EditText  
        android:id="@+id/et"  
        android:layout_width="fill_parent"  
        android:layout_height="wrap_content"  
        android:singleLine="true"  
        />  
    <ImageButton  
        android:id="@+id/ib"  
        android:visibility="gone"  
        android:src="@drawable/menu_delete"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:background="#00000000"  
        android:layout_alignRight="@+id/et" />  
    </RelativeLayout>  

    实现输入框右侧带按钮效果,注意将按钮隐藏。然后写一个EditCancel类,实现删除用户输入功能。这里用到了TextWatch这个接口,监听输入框中的文字变化。使用也很简单,实现他的三个方法即可。看代码:

    package com.example.customdeleteedittext;
    
    import android.content.Context;
    import android.text.Editable;
    import android.text.TextWatcher;
    import android.util.AttributeSet;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.widget.EditText;
    import android.widget.ImageButton;
    import android.widget.LinearLayout;
    
    public class EditCancel extends LinearLayout implements EdtInterface {
        ImageButton ib;
        EditText et;
    
        public EditCancel(Context context, AttributeSet attrs) {
            super(context, attrs);
            LayoutInflater.from(context).inflate(R.layout.custom_editview, this,
                    true);
            init();
        }
    
        public EditCancel(Context context) {
            super(context);
            // TODO Auto-generated constructor stub
        }
    
        private void init() {
            // TODO Auto-generated method stub
            ib = (ImageButton) findViewById(R.id.ib);
            et = (EditText) findViewById(R.id.et);
            et.addTextChangedListener(new TextWatcher() {
    
                @Override
                public void onTextChanged(CharSequence s, int start, int before,
                        int count) {
                    // TODO Auto-generated method stub
    
                }
    
                @Override
                public void beforeTextChanged(CharSequence s, int start, int count,
                        int after) {
                    // TODO Auto-generated method stub
    
                }
    
                @Override
                public void afterTextChanged(Editable s) {
                    // TODO Auto-generated method stub
                    if (s.length() == 0) {
                        hideBtn();// 隐藏按钮
                    } else {
                        showBtn();// 显示按钮
                    }
                }
            });
            ib.setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    // TODO Auto-generated method stub
                    hideBtn();// 隐藏按钮
                    String str = et.getText().toString().trim();
                    String newStr = str.substring(0, str.length() - 1);
                    et.setText(newStr);// 设置输入框内容
                }
            });
        }
    
        @Override
        public void hideBtn() {
            // TODO Auto-generated method stub
            if (ib.isShown())
                ib.setVisibility(View.GONE);
        }
    
        @Override
        public void showBtn() {
            // TODO Auto-generated method stub
            if (!ib.isShown())
                ib.setVisibility(View.VISIBLE);
        }
    
    }
    
    interface EdtInterface {
    
        public void hideBtn();
    
        public void showBtn();
    
    }

    在TextWatch接口的afterTextChanged方法中对文字进行判断,若长度为0,就隐藏按钮,否则,显示按钮。

    另外,实现ImageButton(即那个叉)的点击事件,删除输入框中的内容,并隐藏按钮。

  • 相关阅读:
    【Dubbo】2.7.x新增内容说明
    【Dubbo】架构各层及实现
    【Dubbo】服务提供方并发控制
    把缅彝语支语言中汉语同源词用汉字写出来会是怎样一种情况?
    Using HTTP Methods for RESTful Services
    An Introduction to CodeMix and Python Developmen
    [转]MySQL 前缀索引--------mysql索引指定指定一个前缀长度
    [git使用]Git的使用--如何将本地项目上传到Github(两种简单、方便的方法)
    [转]centos7搭建postfix邮件服务器
    [转]Ubuntu搭建简易Postfix邮箱服务器
  • 原文地址:https://www.cnblogs.com/fuyanan/p/4179721.html
Copyright © 2020-2023  润新知