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,对于控件尺寸来说,没有任何参考意义。
经过代码测试就知道,当我们设置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