android的自定义控件

来源:互联网 发布:三端口环形器原理 编辑:程序博客网 时间:2024/05/21 18:33

1.toast

[java] view plaincopy
  1. Toast toast = Toast.makeText(this"自定义的toast", Toast.LENGTH_SHORT);  
  2.         toast.setGravity(Gravity.TOP, 3030);  
  3.         View view = LayoutInflater.from(this).inflate(R.layout.toast, null);  
  4.         toast.setView(view);  
  5.         toast.show();  

2.Notification

[java] view plaincopy
  1. mNotify = new Notification(R.drawable.icon, "通知", System.currentTimeMillis());  
  2.         // 正在运行,不允许被清除  
  3.         mNotify.flags = Notification.FLAG_ONGOING_EVENT;  
  4.                  //震动  
  5.         mNotify.defaults |= Notification.DEFAULT_VIBRATE;  
  6.          long[] vibrates = new long[]{0100200300};  
  7.          mNotify.vibrate = vibrates ;  


声明震动权限<uses-permission android:name="android.permission.VIBRATE"></uses-permission>   注意模拟器没有震动功能,需要真机测试

[java] view plaincopy
  1. // 定制intent  
  2.         Intent intent = new Intent(this, MyService.class);  
  3.         PendingIntent pendingIntent = PendingIntent.getService(this1, intent,  
  4.                 PendingIntent.FLAG_UPDATE_CURRENT);  
  5.                 mNotify.contentIntent = pendingIntent;  
  6. // 自定义通知布局  
  7.         RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.updatenotify);  
  8.         mNotify.contentView = contentView;  
  9. // 使用通知  
  10. mNotifyMgr = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);  
  11.         mNotifyMgr.notify(R.string.hello, mNotify);  

[java] view plaincopy
  1. //更新通知  
  2. mNotify.contentView.setProgressBar(R.id.progressBar1, NOTITY_PROGRESS_MAX, progress, false);  
  3.         mNotifyMgr.notify(R.string.hello, mNotify);  
  4.         if (progress == NOTITY_PROGRESS_MAX) {  
  5.             Toast.makeText(this"下载完成,请查看通知安装", Toast.LENGTH_SHORT).show();  
  6.         }  

3.dialog

[java] view plaincopy
  1. protected Dialog onCreateDialog(int id) {  
  2.         Dialog dialog = new Dialog(this);  
  3.         dialog.setContentView(R.layout.dialog);  
  4.         dialog.setTitle("标题");  
  5.         return dialog;  
  6.     }  
  7. //在需要显示的地方写  
  8. showDialog(1);  

4.menu

自定义菜单,分析菜单的特征

什么条件下显示: 按Menu

什么条件下关闭:
1) 当菜单显示时,再点击一次菜单按钮
2) 某一个菜单项被点击
3) back  Activity不响应back
4) 其它Activity激活
5) 点击菜单和状态栏以外的区域
补充:当菜单存在时,点击菜单以外区域,界面不能响应用户操作
[java] view plaincopy
  1. public class MainActivity extends Activity implements OnClickListener {  
  2.     private PopupWindow mOptionsMenu;  
  3.     private int[] menuItemIDs = new int[] { R.id.menuitem1, R.id.menuitem2, R.id.menuitem3,  
  4.             R.id.menuitem4, R.id.menuitem5, R.id.menuitem6, R.id.menuitem7, R.id.menuitem8,  
  5.             R.id.empty };  
  6.   
  7.     @Override  
  8.     public void onCreate(Bundle savedInstanceState) {  
  9.         super.onCreate(savedInstanceState);  
  10.         setContentView(R.layout.main);  
  11.   
  12.         View contentView = LayoutInflater.from(this).inflate(R.layout.optionsmenu, null);  
  13.         mOptionsMenu = new PopupWindow(contentView, LayoutParams.FILL_PARENT,  
  14.                 LayoutParams.WRAP_CONTENT);  
  15.         initMenuItem(contentView);  
  16.     }  
  17.   
  18.     @Override  
  19.     protected void onPause() {  
  20.         super.onPause();  
  21.         closeMenuIfExist();  
  22.     }  
  23.   
  24.     private boolean closeMenuIfExist() {  
  25.         if (mOptionsMenu.isShowing()) {  
  26.             mOptionsMenu.dismiss();  
  27.             return true;  
  28.         }  
  29.         return false;  
  30.     }  
  31.   
  32.     @Override  
  33.     public boolean onKeyDown(int keyCode, KeyEvent event) {  
  34.         if (keyCode == KeyEvent.KEYCODE_BACK) {  
  35.             if (closeMenuIfExist()) {  
  36.                 return true;  
  37.             }  
  38.         }  
  39.         return super.onKeyDown(keyCode, event);  
  40.     }  
  41.   
  42.     private void initMenuItem(View contentView) {  
  43.         for (int i = 0; i < menuItemIDs.length; i++) {  
  44.             contentView.findViewById(menuItemIDs[i]).setOnClickListener(this);  
  45.         }  
  46.     }  
  47.   
  48.     @Override  
  49.     public boolean onCreateOptionsMenu(Menu menu) {  
  50.         if (!closeMenuIfExist()) {  
  51.             mOptionsMenu.showAtLocation(findViewById(R.id.main), Gravity.BOTTOM, 00);  
  52.         }  
  53.         return false;  
  54.     }  
  55.   
  56.     @Override  
  57.     public void onClick(View v) {  
  58.         switch (v.getId()) {  
  59.         case R.id.menuitem1:  
  60.             Intent intent = new Intent();  
  61.             intent.setClass(this, Second.class);  
  62.             startActivity(intent);  
  63.             break;  
  64.         case R.id.menuitem2:  
  65.   
  66.             break;  
  67.         case R.id.menuitem3:  
  68.             break;  
  69.         case R.id.menuitem4:  
  70.             break;  
  71.         case R.id.menuitem5:  
  72.             break;  
  73.         case R.id.menuitem6:  
  74.             break;  
  75.         case R.id.menuitem7:  
  76.             break;  
  77.         case R.id.menuitem8:  
  78.             break;  
  79.         case R.id.empty:  
  80.             break;  
  81.   
  82.         default:  
  83.             break;  
  84.         }  
  85.         mOptionsMenu.dismiss();  
  86.     }  
  87.   
  88. }  

注意,R.id.empty指的是菜单外面的view,可以用帧布局放置
可以用selector 为菜单设置点击效果
菜单项显示图片和文本
两类:
1) 图片和文本做成一张图片
2) GridView
   布局嵌套  


5.progress

[html] view plaincopy
  1. <SeekBar  
  2.                 android:id="@+id/progress"  
  3.                 style="?android:attr/progressBarStyleHorizontal"  
  4.                 android:progressDrawable="@drawable/seek_background"  
  5.                 android:thumb="@drawable/thumb"  
  6.                 android:layout_width="346dip"  
  7.                 android:layout_height="32dip"  
  8.                 android:maxHeight="10dip"  
  9.                 android:paddingLeft="8dip"  
  10.                 android:paddingRight="8dip"  
  11.                 android:paddingTop="2dip"  
  12.                 android:paddingBottom="6dip"  
  13.                 android:max="1000"/>  
  14. <!--                android:minHeight="10dip"-->  
[html] view plaincopy
  1. seek_background.xml  
[html] view plaincopy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <layer-list  
  3.   xmlns:android="http://schemas.android.com/apk/res/android">  
  4.     <item android:id="@android:id/background" android:drawable="@drawable/time_line_bg" />  
  5.     <item android:id="@android:id/progress" android:drawable="@drawable/progress_time" />  
  6. </layer-list>  

tumb.xml
[html] view plaincopy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <selector  
  3.     xmlns:android="http://schemas.android.com/apk/res/android">  
  4.     <!-- 按下状态 -->  
  5.     <item  
  6.         android:state_pressed="true"  
  7.         android:drawable="@drawable/drag_btn_down" />  
  8.   
  9.     <!-- 普通无焦点状态 -->  
  10.     <item  
  11.         android:state_focused="false"  
  12.         android:state_pressed="false"  
  13.         android:drawable="@drawable/drag_btn" />  
  14. </selector>  



6.view

[java] view plaincopy
  1. public class LabelView extends View {  
  2.     private Paint mTextPaint;  
  3.     private String mText;  
  4.     private int mAscent;  
  5.   
  6.     /** 
  7.      * 定义一个构造器来初始化这个自定义的view,这个构造器可以在java代码中用来生成view 如 new LabelView(context) 
  8.      *  
  9.      * @param context 
  10.      *            上下文 
  11.      */  
  12.     public LabelView(Context context) {  
  13.         super(context);  
  14.         initLabelView();  
  15.     }  
  16.   
  17.     /** 
  18.      * @param context 
  19.      *            上下文 
  20.      * @param attrs 
  21.      *            从带styleable的xml中读到的属性 
  22.      */  
  23.     public LabelView(Context context, AttributeSet attrs) {  
  24.         super(context, attrs);  
  25.         initLabelView();  
  26.         // 从R.styleable.LabelView文件中获得属性集合  
  27.         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LabelView);  
  28.         // 通过typedArray.getString或者typedArray.getInt获得对应的属性设定的值  
  29.         // 属性的名称为styleable名称“LabelView”,加上“_”,再加上属性名称"text",组成“LabelView_text”  
  30.         CharSequence s = a.getString(R.styleable.LabelView_text);  
  31.         if (s != null) {  
  32.             // 设置text的content  
  33.             setText(s.toString());  
  34.         }  
  35.         // 设置text的color,如果没有设置,默认值为0xFF000000  
  36.         setTextColor(a.getColor(R.styleable.LabelView_textColor, 0xFF000000));  
  37.         // 设置text的文字大小  
  38.         int textSize = a.getDimensionPixelOffset(R.styleable.LabelView_textSize, 0);  
  39.         if (textSize > 0) {  
  40.             setTextSize(textSize);  
  41.         }  
  42.         // 之前设置的属性,设置循环利用  
  43.         a.recycle();  
  44.     }  
  45.   
  46.     /** 
  47.      * 新建画笔和设置画笔的样式 
  48.      */  
  49.     private final void initLabelView() {  
  50.         mTextPaint = new Paint();  
  51.         mTextPaint.setAntiAlias(true);  
  52.         mTextPaint.setTextSize(16);  
  53.         mTextPaint.setColor(0xFF000000);  
  54.         setPadding(3333);  
  55.     }  
  56.   
  57.   
  58.     /** 
  59.      * 设置文字内容,可以类外使用new view后,再使用view.setText设置内容,在构造器处被调用 
  60.      * @param text 
  61.      */  
  62.     public void setText(String text) {  
  63.         mText = text;  
  64.         requestLayout();  
  65.         invalidate();  
  66.     }  
  67.   
  68.       
  69.     /**设置字体大小,在构造器处被调用 
  70.      * @param size 
  71.      */  
  72.     public void setTextSize(int size) {  
  73.         mTextPaint.setTextSize(size);  
  74.         requestLayout();  
  75.         invalidate();  
  76.     }  
  77.   
  78.       
  79.     /**设置字体颜色,在构造器处被调用 
  80.      * @param color 
  81.      */  
  82.     public void setTextColor(int color) {  
  83.         mTextPaint.setColor(color);  
  84.         invalidate();  
  85.     }  
  86.   
  87.       
  88.     /**设置自定义view的显示的大小 
  89.      * @see android.view.View#onMeasure(int, int) 
  90.      */  
  91.     @Override  
  92.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  93.         //measureWidth和measureHeight事自定义的两个用来设置宽度和高度的方法  
  94.         setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec));  
  95.     }  
  96.   
  97.     /** 
  98.      * 设置宽度,写法一般都遵循这样的框架,需要getMode和getSize来分辨用户设置的“match_parent”还是“wrap_content”等 
  99.      * @param 具体的值          
  100.      * @return 返回宽度 
  101.      */  
  102.     private int measureWidth(int measureSpec) {  
  103.         int result = 0;  
  104.         int specMode = MeasureSpec.getMode(measureSpec);  
  105.         int specSize = MeasureSpec.getSize(measureSpec);  
  106.   
  107.         if (specMode == MeasureSpec.EXACTLY) {  
  108.             // 确切地知道大小,即设置width=“123”等这些具体数值  
  109.             result = specSize;  
  110.         } else {  
  111.             // 这里相当于设置宽度为wrapContent  
  112.             result = (int) mTextPaint.measureText(mText) + getPaddingLeft() + getPaddingRight();  
  113.             if (specMode == MeasureSpec.AT_MOST) {  
  114.                 // 相当于设置成UNSPECIFIED  
  115.                 result = Math.min(result, specSize);  
  116.             }  
  117.         }  
  118.   
  119.         return result;  
  120.     }  
  121.   
  122.     /** 
  123.      *与measureWidth方法类似 
  124.      */  
  125.     private int measureHeight(int measureSpec) {  
  126.         int result = 0;  
  127.         int specMode = MeasureSpec.getMode(measureSpec);  
  128.         int specSize = MeasureSpec.getSize(measureSpec);  
  129.   
  130.         mAscent = (int) mTextPaint.ascent();  
  131.         if (specMode == MeasureSpec.EXACTLY) {  
  132.             result = specSize;  
  133.         } else {  
  134.             result = (int) (-mAscent + mTextPaint.descent()) + getPaddingTop() + getPaddingBottom();  
  135.             if (specMode == MeasureSpec.AT_MOST) {  
  136.                 result = Math.min(result, specSize);  
  137.             }  
  138.         }  
  139.         return result;  
  140.     }  
  141.   
  142.     /** 
  143.      * 描述view的画法的一个方法,在invalidate时回调 
  144.      */  
  145.     @Override  
  146.     protected void onDraw(Canvas canvas) {  
  147.         super.onDraw(canvas);  
  148.         canvas.drawText(mText, getPaddingLeft(), getPaddingTop() - mAscent, mTextPaint);  
  149.     }  
  150. }  

其中context.obtainStyledAttributes(attrs, R.styleable.LabelView);指的是从xml中获取属性集合
你可以新建一项位于values下的xml文件     resource下面包含以下代码

[html] view plaincopy
  1. <declare-styleable name="LabelView">  
  2.        <attr format="string" name="text" />  
  3.        <attr format="color" name="textColor" />  
  4.        <attr format="dimension" name="textSize" />  
  5.    </declare-styleable>  

或者像这样写
[html] view plaincopy
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <resources>  
  3.     <declare-styleable name="EditTextExt">  
  4.         <attr name="Text" format="reference|string"></attr>  
  5.         <attr name="Oriental">  
  6.             <enum name="Horizontal" value="1"></enum>  
  7.             <enum name="Vertical" value="0"></enum>  
  8.         </attr>  
  9.     </declare-styleable>  
  10. </resources>  
上面里面有一个enum枚举两个选项,供用户只能在这两个选项中选一个,否则编译报错

分析measureWidth和measureHeight方法
依据specMode的值,(MeasureSpec有3种模式分别是UNSPECIFIED, EXACTLY和AT_MOST)

如果是AT_MOST,specSize 代表的是最大可获得的空间; 

如果是EXACTLY,specSize 代表的是精确的尺寸; 
如果是UNSPECIFIED,对于控件尺寸来说,没有任何参考意义。

那么这些模式和我们平时设置的layout参数fill_parent, wrap_content有什么关系呢?
经过代码测试就知道,当我们设置width或height为fill_parent时,容器在布局时调用子 view的measure方法传入的模式是EXACTLY,因为子view会占据剩余容器的空间,所以它大小是确定的。
而当设置为 wrap_content时,容器传进去的是AT_MOST, 表示子view的大小最多是多少,这样子view会根据这个上限来设置自己的尺寸。当子view的大小设置为精确值时,容器传入的是EXACTLY, 而MeasureSpec的UNSPECIFIED模式目前还没有发现在什么情况下使用。 
   View的onMeasure方法默认行为是当模式为UNSPECIFIED时,设置尺寸为mMinWidth(通常为0)或者背景drawable的最小尺寸,当模式为EXACTLY或者AT_MOST时,尺寸设置为传入的MeasureSpec的大小。 
   有个观念需要纠正的是,fill_parent应该是子view会占据剩下容器的空间,而不会覆盖前面已布局好的其他view空间,当然后面布局子 view就没有空间给分配了,所以fill_parent属性对布局顺序很重要。以前所想的是把所有容器的空间都占满了,难怪google在2.2版本里把fill_parent的名字改为match_parent.

  在两种情况下,你必须绝对的处理这些限制。在一些情况下,它可能会返回超出这些限制的尺寸,在这种情况下,你可以让父元素选择如何对待超出的View,使用裁剪还是滚动等技术。

最后,这个view就可以像平时我们在xml里面布局对待其他控件一样

[html] view plaincopy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout  
  3.     xmlns:android="http://schemas.android.com/apk/res/android"  
  4.     xmlns:app="http://schemas.android.com/apk/res/org.yuchen.customview"  
  5.     android:orientation="vertical"  
  6.     android:layout_width="fill_parent"  
  7.     android:layout_height="fill_parent">  
  8.     <TextView  
  9.         android:layout_width="fill_parent"  
  10.         android:layout_height="wrap_content"  
  11.         android:text="@string/hello" />  
  12.     <org.yuchen.customview.LabelView  
  13.         android:background="@drawable/blue"  
  14.         android:layout_width="match_parent"  
  15.         android:layout_height="wrap_content"  
  16.         app:text="Blue"  
  17.         app:textSize="20dp" />  
  18.     <org.yuchen.customview.LabelView  
  19.         android:background="@drawable/red"  
  20.         android:layout_width="match_parent"  
  21.         android:layout_height="wrap_content"  
  22.         app:text="Red" />  
  23. </LinearLayout>  

注意需要有命名空间,所以你明白为什么我们每个xml布局都包含xmlns:android="http://schemas.android.com/apk/res/android"这句话了吧,因为用到android:layout_width等的引用
[html] view plaincopy
  1. xmlns:app="http://schemas.android.com/apk/res/org.yuchen.customview"  

[html] view plaincopy
  1. 必须包含完整包名路径  
  2. lt;org.yuchen.customview.LabelView  
下面两个是我们在xml中自定义的两个属性 
[html] view plaincopy
  1. app:text="Blue"  
  2. app:textSize="20dp"  

自定义view还可以有第二种写法

[java] view plaincopy
  1. package com.terry.attrs;  
  2.   
  3. import android.content.Context;  
  4. import android.util.AttributeSet;  
  5. import android.widget.EditText;  
  6. import android.widget.LinearLayout;  
  7. import android.widget.TextView;  
  8.   
  9. public class EditTextExt1 extends LinearLayout {  
  10.   
  11.     private String Text = "";  
  12.   
  13.     public EditTextExt1(Context context) {  
  14.         this(context, null);  
  15.         // TODO Auto-generated constructor stub  
  16.     }  
  17.   
  18.     public EditTextExt1(Context context, AttributeSet attrs) {  
  19.         super(context, attrs);  
  20.         // TODO Auto-generated constructor stub  
  21.         int resouceId = -1;  
  22.   
  23.         TextView tv = new TextView(context);   
  24.         EditText et = new EditText(context);  
  25.   
  26.         resouceId = attrs.getAttributeResourceValue(null"Text"0);  
  27.         if (resouceId > 0) {  
  28.             Text = context.getResources().getText(resouceId).toString();  
  29.         } else {  
  30.             Text = "";  
  31.         }  
  32.         tv.setText(Text);  
  33.   
  34.         addView(tv);  
  35.         addView(et, new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,  
  36.                 LayoutParams.WRAP_CONTENT));  
  37.         this.setGravity(LinearLayout.VERTICAL);  
  38.   
  39.     }  
  40.   
  41. }  

这种写法,简单明了,不需要额外XML的配置,就可以在我们的VIEW文件下使用。

以上代码通过构造函数中引入的AttributeSet 去查找XML布局的属性名称,然后找到它对应引用的资源ID去找值。使用也时分方便。所以一直以来我也是很喜欢这种写法。

如上,自定好VIEW文件就可以在XML布局下如此使用:

[html] view plaincopy
  1. <com.terry.attrs.EditTextExt1 android:id="@+id/ss3"  
  2.         android:layout_width="wrap_content" android:layout_height="wrap_content"  
  3.         Text="@string/app_name" ></com.terry.attrs.EditTextExt1>  

部分代码参考自http://www.cnblogs.com/TerryBlog/archive/2010/11/03/1868431.html


转载自http://blog.csdn.net/iamkila/article/details/7294137