1.toast
Toast toast = Toast.makeText(this, "自定义的toast", Toast.LENGTH_SHORT); toast.setGravity(Gravity.TOP, 30, 30); View view = LayoutInflater.from(this).inflate(R.layout.toast, null); toast.setView(view); toast.show();
2.Notification
mNotify = new Notification(R.drawable.icon, "通知", System.currentTimeMillis()); // 正在运行,不允许被清除 mNotify.flags = Notification.FLAG_ONGOING_EVENT; //震动 mNotify.defaults |= Notification.DEFAULT_VIBRATE; long[] vibrates = new long[]{0, 100, 200, 300}; mNotify.vibrate = vibrates ;
声明震动权限<uses-permission android:name="android.permission.VIBRATE"></uses-permission> 注意模拟器没有震动功能,需要真机测试
// 定制intent Intent intent = new Intent(this, MyService.class); PendingIntent pendingIntent = PendingIntent.getService(this, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT); mNotify.contentIntent = pendingIntent; // 自定义通知布局 RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.updatenotify); mNotify.contentView = contentView; // 使用通知 mNotifyMgr = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); mNotifyMgr.notify(R.string.hello, mNotify);
//更新通知 mNotify.contentView.setProgressBar(R.id.progressBar1, NOTITY_PROGRESS_MAX, progress, false); mNotifyMgr.notify(R.string.hello, mNotify); if (progress == NOTITY_PROGRESS_MAX) { Toast.makeText(this, "下载完成,请查看通知安装", Toast.LENGTH_SHORT).show(); }
3.dialog
protected Dialog onCreateDialog(int id) { Dialog dialog = new Dialog(this); dialog.setContentView(R.layout.dialog); dialog.setTitle("标题"); return dialog; } //在需要显示的地方写 showDialog(1);
4.menu
自定义菜单,分析菜单的特征
什么条件下显示: 按Menu
什么条件下关闭:
1) 当菜单显示时,再点击一次菜单按钮
2) 某一个菜单项被点击
3) back Activity不响应back
4) 其它Activity激活
5) 点击菜单和状态栏以外的区域
补充:当菜单存在时,点击菜单以外区域,界面不能响应用户操作
public class MainActivity extends Activity implements OnClickListener { private PopupWindow mOptionsMenu; private int[] menuItemIDs = new int[] { R.id.menuitem1, R.id.menuitem2, R.id.menuitem3, R.id.menuitem4, R.id.menuitem5, R.id.menuitem6, R.id.menuitem7, R.id.menuitem8, R.id.empty }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); View contentView = LayoutInflater.from(this).inflate(R.layout.optionsmenu, null); mOptionsMenu = new PopupWindow(contentView, LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); initMenuItem(contentView); } @Override protected void onPause() { super.onPause(); closeMenuIfExist(); } private boolean closeMenuIfExist() { if (mOptionsMenu.isShowing()) { mOptionsMenu.dismiss(); return true; } return false; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { if (closeMenuIfExist()) { return true; } } return super.onKeyDown(keyCode, event); } private void initMenuItem(View contentView) { for (int i = 0; i < menuItemIDs.length; i++) { contentView.findViewById(menuItemIDs[i]).setOnClickListener(this); } } @Override public boolean onCreateOptionsMenu(Menu menu) { if (!closeMenuIfExist()) { mOptionsMenu.showAtLocation(findViewById(R.id.main), Gravity.BOTTOM, 0, 0); } return false; } @Override public void onClick(View v) { switch (v.getId()) { case R.id.menuitem1: Intent intent = new Intent(); intent.setClass(this, Second.class); startActivity(intent); break; case R.id.menuitem2: break; case R.id.menuitem3: break; case R.id.menuitem4: break; case R.id.menuitem5: break; case R.id.menuitem6: break; case R.id.menuitem7: break; case R.id.menuitem8: break; case R.id.empty: break; default: break; } mOptionsMenu.dismiss(); } }
注意,R.id.empty指的是菜单外面的view,可以用帧布局放置
可以用selector 为菜单设置点击效果
菜单项显示图片和文本
两类:
1) 图片和文本做成一张图片
2) GridView
布局嵌套
5.progress
<SeekBar android:id="@+id/progress" style="?android:attr/progressBarStyleHorizontal" android:progressDrawable="@drawable/seek_background" android:thumb="@drawable/thumb" android:layout_width="346dip" android:layout_height="32dip" android:maxHeight="10dip" android:paddingLeft="8dip" android:paddingRight="8dip" android:paddingTop="2dip" android:paddingBottom="6dip" android:max="1000"/> <!-- android:minHeight="10dip"-->
seek_background.xml
<?xml version="1.0" encoding="UTF-8"?> <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@android:id/background" android:drawable="@drawable/time_line_bg" /> <item android:id="@android:id/progress" android:drawable="@drawable/progress_time" /> </layer-list>
tumb.xml
<?xml version="1.0" encoding="UTF-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- 按下状态 --> <item android:state_pressed="true" android:drawable="@drawable/drag_btn_down" /> <!-- 普通无焦点状态 --> <item android:state_focused="false" android:state_pressed="false" android:drawable="@drawable/drag_btn" /> </selector>
6.view
public class LabelView extends View { private Paint mTextPaint; private String mText; private int mAscent; /** * 定义一个构造器来初始化这个自定义的view,这个构造器可以在java代码中用来生成view 如 new LabelView(context) * * @param context * 上下文 */ public LabelView(Context context) { super(context); initLabelView(); } /** * @param context * 上下文 * @param attrs * 从带styleable的xml中读到的属性 */ public LabelView(Context context, AttributeSet attrs) { super(context, attrs); initLabelView(); // 从R.styleable.LabelView文件中获得属性集合 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LabelView); // 通过typedArray.getString或者typedArray.getInt获得对应的属性设定的值 // 属性的名称为styleable名称“LabelView”,加上“_”,再加上属性名称"text",组成“LabelView_text” CharSequence s = a.getString(R.styleable.LabelView_text); if (s != null) { // 设置text的content setText(s.toString()); } // 设置text的color,如果没有设置,默认值为0xFF000000 setTextColor(a.getColor(R.styleable.LabelView_textColor, 0xFF000000)); // 设置text的文字大小 int textSize = a.getDimensionPixelOffset(R.styleable.LabelView_textSize, 0); if (textSize > 0) { setTextSize(textSize); } // 之前设置的属性,设置循环利用 a.recycle(); } /** * 新建画笔和设置画笔的样式 */ private final void initLabelView() { mTextPaint = new Paint(); mTextPaint.setAntiAlias(true); mTextPaint.setTextSize(16); mTextPaint.setColor(0xFF000000); setPadding(3, 3, 3, 3); } /** * 设置文字内容,可以类外使用new view后,再使用view.setText设置内容,在构造器处被调用 * @param text */ public void setText(String text) { mText = text; requestLayout(); invalidate(); } /**设置字体大小,在构造器处被调用 * @param size */ public void setTextSize(int size) { mTextPaint.setTextSize(size); requestLayout(); invalidate(); } /**设置字体颜色,在构造器处被调用 * @param color */ public void setTextColor(int color) { mTextPaint.setColor(color); invalidate(); } /**设置自定义view的显示的大小 * @see android.view.View#onMeasure(int, int) */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //measureWidth和measureHeight事自定义的两个用来设置宽度和高度的方法 setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); } /** * 设置宽度,写法一般都遵循这样的框架,需要getMode和getSize来分辨用户设置的“match_parent”还是“wrap_content”等 * @param 具体的值 * @return 返回宽度 */ private int measureWidth(int measureSpec) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) { // 确切地知道大小,即设置width=“123”等这些具体数值 result = specSize; } else { // 这里相当于设置宽度为wrapContent result = (int) mTextPaint.measureText(mText) + getPaddingLeft() + getPaddingRight(); if (specMode == MeasureSpec.AT_MOST) { // 相当于设置成UNSPECIFIED result = Math.min(result, specSize); } } return result; } /** *与measureWidth方法类似 */ private int measureHeight(int measureSpec) { int result = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); mAscent = (int) mTextPaint.ascent(); if (specMode == MeasureSpec.EXACTLY) { result = specSize; } else { result = (int) (-mAscent + mTextPaint.descent()) + getPaddingTop() + getPaddingBottom(); if (specMode == MeasureSpec.AT_MOST) { result = Math.min(result, specSize); } } return result; } /** * 描述view的画法的一个方法,在invalidate时回调 */ @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawText(mText, getPaddingLeft(), getPaddingTop() - mAscent, mTextPaint); } }
其中context.obtainStyledAttributes(attrs, R.styleable.LabelView);指的是从xml中获取属性集合
你可以新建一项位于values下的xml文件 resource下面包含以下代码
<declare-styleable name="LabelView"> <attr format="string" name="text" /> <attr format="color" name="textColor" /> <attr format="dimension" name="textSize" /> </declare-styleable>
或者像这样写
<?xml version="1.0" encoding="UTF-8"?> <resources> <declare-styleable name="EditTextExt"> <attr name="Text" format="reference|string"></attr> <attr name="Oriental"> <enum name="Horizontal" value="1"></enum> <enum name="Vertical" value="0"></enum> </attr> </declare-styleable> </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里面布局对待其他控件一样
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res/org.yuchen.customview" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> <org.yuchen.customview.LabelView android:background="@drawable/blue" android:layout_width="match_parent" android:layout_height="wrap_content" app:text="Blue" app:textSize="20dp" /> <org.yuchen.customview.LabelView android:background="@drawable/red" android:layout_width="match_parent" android:layout_height="wrap_content" app:text="Red" /> </LinearLayout>
注意需要有命名空间,所以你明白为什么我们每个xml布局都包含xmlns:android="http://schemas.android.com/apk/res/android"这句话了吧,因为用到android:layout_width等的引用
xmlns:app="http://schemas.android.com/apk/res/org.yuchen.customview"
必须包含完整包名路径 <org.yuchen.customview.LabelView
下面两个是我们在xml中自定义的两个属性
app:text="Blue" app:textSize="20dp"
自定义view还可以有第二种写法
package com.terry.attrs; import android.content.Context; import android.util.AttributeSet; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; public class EditTextExt1 extends LinearLayout { private String Text = ""; public EditTextExt1(Context context) { this(context, null); // TODO Auto-generated constructor stub } public EditTextExt1(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub int resouceId = -1; TextView tv = new TextView(context); EditText et = new EditText(context); resouceId = attrs.getAttributeResourceValue(null, "Text", 0); if (resouceId > 0) { Text = context.getResources().getText(resouceId).toString(); } else { Text = ""; } tv.setText(Text); addView(tv); addView(et, new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); this.setGravity(LinearLayout.VERTICAL); } }
这种写法,简单明了,不需要额外XML的配置,就可以在我们的VIEW文件下使用。
以上代码通过构造函数中引入的AttributeSet 去查找XML布局的属性名称,然后找到它对应引用的资源ID去找值。使用也时分方便。所以一直以来我也是很喜欢这种写法。
如上,自定好VIEW文件就可以在XML布局下如此使用:
<com.terry.attrs.EditTextExt1 android:id="@+id/ss3" android:layout_width="wrap_content" android:layout_height="wrap_content" Text="@string/app_name" ></com.terry.attrs.EditTextExt1>
部分代码参考自http://www.cnblogs.com/TerryBlog/archive/2010/11/03/1868431.html