一、Layouts
1
在XML中,标签名对应于代码中的类名,属性名对应于代码中的方法名
2 android:id="@+id/start"
@ 让XML解析器知道后面的字符串应该解析为一个 Resource ID
+ 表明是自己定义的新的ID,不是系统built-in的,
如果使用系统的应该是这样android:id="@android:id/empty"
3 ViewGroup.LayoutParams
这是一个类,只不过是一个内部静态类,不过在这里把它当做一个类即可.
一个View应该具有一个ViewGroup.LayoutParams对象,并对这个对象进行
适当的初始化,这样它的parent才能知道如何放置它.
想一想当我们在XML中定义一个按钮时它总是具有一个 layout_width/layout_height属性,
这就相当于
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(layout_width,layout_height);
View.setLayoutParams(layoutParams);
View.getLeft()/View.getTop()
The size of a view is expressed with a width and a height.
A view actually possess two pairs of width and height values.
measured width/height --> getMeasuredWidth/Height() //在parent内一个View有多大
width/height --> getWidth/Height() //一个View真正的尺寸
View可以有padding --> setPadding(int,int,int,int) getPaddingLeft/Top/Right/Bottom()
View可以有margin,但是是通过 ViewGroup.MarginLayoutParams 来设置的.
4
注意像 ListView/GridView 都是ViewGroup
5 如果设置一个View的background
android:background setBackgroundResource(int) A drawable to use as the background.
对于按钮又可以在 /res/drawable 目录下定义如下xml文件:
custom_background.xml (把此文件当做一个 drawable 来用)
[html] view
plaincopy
- <!-- 具体定义此文件的语法可以在 Resource Type 相关的API Guide中找到 -->
- <?xml version="1.0" encoding="utf-8"?>
- <selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@drawable/button_pressed"
- android:state_pressed="true" />
- <item android:drawable="@drawable/button_focused"
- android:state_focused="true" />
- <item android:drawable="@drawable/button_default" />
- </selector>
6 EditView 常用设置
android:inputType 用来指定是数字,email,password等输入类型
android:textmultiLine 指定EditView可以有多行
android:imeOptions 指定一个按钮在EditView的末尾,以便用户输入完成后点击
7
AutoCompleteTextView 需要设置一个 Adapter
8 Input Events
View类内部定义的事件接口有:(注意有些方法返回的是boolean型,搞明白为什么)
View.OnClickListener --> public void onClick(View v)
View.OnLongClickListener --> boolean onLongClick (View v)
View.OnFocusChangeListener --> void onFocusChange (View v, boolean hasFocus)
View.OnKeyListener --> boolean onKey (View v, int keyCode, KeyEvent event)
View.OnTouchListener --> boolean onTouch (View v, MotionEvent event)
//这个事件是在onLongClick的情况下发生的
View.OnCreateContextMenuListener
-->void onCreateContextMenu (ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo)
9 Touch Mode
For a touch-capable device, once the user touches the screen,
the device will enter touch mode.
Any time a user hits a directional key or scrolls with a trackball, the device will exit
touch mode, and find a view to take focus.
10 Focus
Focus movement 有一个普遍的算法,即会把焦点移动到离现在View最近的那个View;但有时这种算法
并不适合开发者,开发者可以通过以下方法声明焦点转移的顺序.
[html] view
plaincopy
- <LinearLayout android:orientation="vertical"
- ... >
- <Button android:id="@+id/top"
- android:nextFocusUp="@+id/bottom"
- ... />
- <Button android:id="@+id/bottom"
- android:nextFocusDown="@+id/top"
- ... />
- </LinearLayout>
共四种:nextFocusDown, nextFocusLeft, nextFocusRight, and nextFocusUp
和焦点相关的属性还有:
android:focusable
android:focusableInTouchMode
requestFocus()
二、Menus
三种菜单:
Options menu(用Menu按键触发,android3.0已经取消这个按键,用action bar代替)
Context menu
Popup menu
1 定义一个Menu in XML
game_menu.xml:
[html] view
plaincopy
- <?xml version="1.0" encoding="utf-8"?>
- <menu xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:id="@+id/new_game"
- android:icon="@drawable/ic_new_game"
- android:title="@string/new_game"
- <!-- 此属性和action bar相关 -->
- android:showAsAction="ifRoom">
- <!-- item里可以嵌套menu -->
- <menu>
- ...
- </menu>
- </item>
- <item android:id="@+id/help"
- android:icon="@drawable/ic_help"
- android:title="@string/help" />
- </menu>
用 MenuInflater.inflate() 去解析XML菜单;
2 制定Option menu
覆盖Activity. onCreateOptionsMenu() 去生成自己的菜单,此方法只在第一次生成options menu时
被调用,对于以后菜单的更改可以在 onPrepareOptionsMenu(Menu) 中完成.
当点击option menuitem 时会触发 onOptionsItemSelected()
3 制定Context menu(具体请参考API Guid)
registerForContextMenu() and pass it the View;
Implement the onCreateContextMenu() method in your Activity;
Implement onContextItemSelected().
4 制定 Popup Menu(API11 才有)
5 Create Menu Group(看API Guide)
6 Using checkable menu
[html] view
plaincopy
- <?xml version="1.0" encoding="utf-8"?>
- <menu xmlns:android="http://schemas.android.com/apk/res/android">
- <group android:checkableBehavior="single">
- <item android:id="@+id/red"
- android:title="@string/red" />
- <item android:id="@+id/blue"
- android:title="@string/blue" />
- </group>
- </menu>
single
Only one item from the group can be checked (radio buttons)
all
All items can be checked (checkboxes)
none
No items are checkable
另外当某个菜单项被按下时你必须自己改变其check状态,系统不会自动改变,就像:
[java] view
plaincopy
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case R.id.vibrate:
- case R.id.dont_vibrate:
- //改变这些menu item的状态
- if (item.isChecked()) item.setChecked(false);
- else item.setChecked(true);
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
三、Dialogs
<-- AlertDialog
<-- ProgressDialog
Dialog <-- DatePickerDialog
<-- TimePickerDialog
A dialog is always created and displayed as a part of an Activity.
一般情况下你会调用 Activity.onCreateDialog(int) 方法创建Dialog,这时Android System会管理
Dialog的状态并把此Dialog的拥有者设为此Activity.
Activity.onCreateDialog(int) --> (只在第一次创建Dialog时被调用)
Activity.onPrepareDialog(int) (显示前设置Dialog属性)--> Activity.showDialog(int) -->
Activity.dismissDialog(int)/Dialog.dismiss() (Dialog不在显示但Activity会保存其状态) -->
Activity.removeDialog(int) (Dialog被从Activity中删除,这个Dialog的引用被置null)
创建Dialog的一般步骤为:
[java] view
plaincopy
- //定义好Dialog ID
- static final int DIALOG_PAUSED_ID = 0;
- static final int DIALOG_GAMEOVER_ID = 1;
- //创建时调用
- protected Dialog onCreateDialog(int id) {
- Dialog dialog;
- switch(id) {
- case DIALOG_PAUSED_ID:
- // do the work to define the pause Dialog
- break;
- case DIALOG_GAMEOVER_ID:
- // do the work to define the game over Dialog
- break;
- default:
- dialog = null;
- }
- return dialog;
- }
- //显示Dialog
- showDialog(DIALOG_PAUSED_ID);
AlertDialog的创建可以使用其Builder,比较简单,具体可以参考API Guide.
ProgressDialog 包括两种,一种为进度条长度不能确定的 spinning circle,另一种有进度条长度且可以
更新这个进度条(此情况下一般会新建一个更新进度条的Thread),这两种情况都不是很复杂,具体请参看
API Guide.
定制自己的Dialog,可以继承自 Dialog也可以继承自 AlertDialog(这样可以利用它的按钮的相关特征),
具体请参看API Guide.
四、Notifications
1 Toast Notifications
A toast can be created and displayed from an Activity or Service.
2 设置一个 Toast 显示的位置
toast.setGravity(Gravity.TOP|Gravity.LEFT, 0, 0); 或
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0);
如果想往右一点就设置第2个参数,如果想往下一点就设置第3个参数,具体请参考Toast API
3 Status Notifications
一般是由background的service发出,要使用 Notification和NotificationManager
4
一个PendingIntent只是代表一个系统内部的 IIntentSender, PendingIntent本身只是一个符号.
下面是文档中的一个例子,花了一点时间研究了一下,下面做一下注释,以笔记下研究成果.(虽然很小)
[java] view
plaincopy
- static Intent[] makeMessageIntentStack(Context context, CharSequence from,
- CharSequence msg) {
- Intent[] intents = new Intent[4];
- //查看 Intent.makeRestartActivityTask说明
- //指向一个 root activity
- intents[0] = Intent.makeRestartActivityTask(new ComponentName(context,
- com.example.android.apis.ApiDemos.class));
- // "App"
- intents[1] = new Intent(context, com.example.android.apis.ApiDemos.class);
- intents[1].putExtra("com.example.android.apis.Path", "App");
- // "App/Notification"
- intents[2] = new Intent(context, com.example.android.apis.ApiDemos.class);
- intents[2].putExtra("com.example.android.apis.Path", "App/Notification");
- // Now the activity to display to the user. Also fill in the data it
- // should display.
- intents[3] = new Intent(context, IncomingMessageView.class);
- intents[3].putExtra(IncomingMessageView.KEY_FROM, from);
- intents[3].putExtra(IncomingMessageView.KEY_MESSAGE, msg);
- return intents;
- }
- /**
- * The notification is the icon and associated expanded entry in the
- * status bar.
- */
- void showAppNotification() {
- // look up the notification manager service
- NotificationManager nm = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
- // The details of our fake message
- CharSequence from = "Joe";
- CharSequence message;
- switch ((new Random().nextInt()) % 3) {
- case 0: message = "r u hungry? i am starved"; break;
- case 1: message = "im nearby u"; break;
- default: message = "kthx. meet u for dinner. cul8r"; break;
- }
- //这种行为很像 startActivity(Intent[] intents)
- //intents[0] 表示一个root activity
- //intents[1] 相当于 intents[0].startActivity()
- //intents[] 的最后一个Intent所关联的Activity是与用户交互的,用户可以使用BACK键
- //回到intents[top-1]
- //顺便学习了 Intent.fillIn() 和 PendingIntent.send() 方法
- //上面两个方法要查看相关文档
- PendingIntent contentIntent = PendingIntent.getActivities(this, 0,
- makeMessageIntentStack(this, from, message), PendingIntent.FLAG_CANCEL_CURRENT);
- // The ticker text, this uses a formatted string so our message could be localized
- String tickerText = getString(R.string.imcoming_message_ticker_text, message);
- // construct the Notification object.
- Notification notif = new Notification(R.drawable.stat_sample, tickerText,
- System.currentTimeMillis());
- // Set the info for the views that show in the notification panel.
- notif.setLatestEventInfo(this, from, message, contentIntent);
- // We'll have this notification do the default sound, vibration, and led.
- // Note that if you want any of these behaviors, you should always have
- // a preference for the user to turn them off.
- notif.defaults = Notification.DEFAULT_ALL;
- // Note that we use R.layout.incoming_message_panel as the ID for
- // the notification. It could be any integer you want, but we use
- // the convention of using a resource id for a string related to
- // the notification. It will always be a unique number within your
- // application.
- nm.notify(R.string.imcoming_message_ticker_text, notif);
- }
Notification.FLAG_AUTO_CANCEL 标记,当用记点击时会自己从"通知栏"消失(加红)
调用setLatestEventInfo() 对已经发出的Notification进行修正,主要是修改其text message
5 定制自己的 Notification (看完 Sytle和Theme再回来看)
五、Sytles and Themes
自己定义一个 style
[html] view
plaincopy
- <?xml version="1.0" encoding="utf-8"?>
- <!-- 注意resources -->
- <resources>
- <style name="CodeFont" parent="@android:style/TextAppearance.Medium">
- <item name="android:layout_width">fill_parent</item>
- <item name="android:layout_height">wrap_content</item>
- <item name="android:textColor">#00FF00</item>
- <item name="android:typeface">monospace</item>
- </style>
- </resources>
如何继承自己定义的 style, 当然可以使用 parent 属性,但你也可以这样做:
[html] view
plaincopy
- <style name="CodeFont.Red">
- <item name="android:textColor">#FF0000</item>
- </style>
还可以再继承:
[html] view
plaincopy
- <style name="CodeFont.Red.Big">
- <item name="android:textSize">30sp</item>
- </style>
你只能以这种形式继承自己定义的stye,不能以这种形式继承系统内建的style.
如果一个style中的属性,这个View不支持,它只是把它不支持的属性忽略掉.
注意 View 的XML中可以有一个 style=""@style/MyStyle" 属性,而Activity和Application可以有一个
android:theme="@style/MyStyle" 属性.
如果把一个style应用于一个ViewGroup,则仅仅这个ViewGroup应用这个属性,它的孩子不会应用这个属性.
使用系统自定义的style
<activity android:theme="@android:style/Theme.Dialog">
无论style还是theme都是用<style></style>来定义的,所以平台内建的 style和theme 都可以
在 android.R.style 中找到常量值,如果对这些常量值不太理解可以通过查看 styles.xml/themes.xml
查看源代码以帮助理解.现在的问题是,系统内建的style会用到一些属性,比如
[html] view
plaincopy
- <style name="Theme">
- <item name="colorForeground">@android:color/bright_foreground_dark</item>
- <item name="colorForegroundInverse">@android:color/bright_foreground_dark_inverse</item>
- <item name="colorBackground">@android:color/background_dark</item>
- <item name="colorBackgroundCacheHint">?android:attr/colorBackground</item>
- </style>
那么我们如何知道会有哪些属性需要定义呢?
对于普通的View 可以查看其 Reference 得到其 相关于 XML attributes,还有一些属性
不是针对某一个View的,而是专门用于Theme类的,如android:windowBackground,我们可以在
R.styleable.Theme中找到(其实这些attr是在attrs.xml中定义的,只不过在Theme大类下)
另外:
For a reference of available style attributes that you can use to define a style
or theme (e.g., "windowBackground" or "textAppearance"), see R.attr or the respective
View class for which you are creating a style.
6 定制自己的组件
Fully Customized Components:
首先要设计好自己View的特性,即有哪些字段,如下面这个LabelView,它有两个字段:
[java] view
plaincopy
- public class LabelView extends View {
- private String mText;
- private Paint mPaint;
- }
然后完成对各字段设置的函数:
[java] view
plaincopy
- public class LabelView extends View {
- //第一步:确定自己View的字段
- private String mText;
- private Paint mPaint;
- //第二步:设置自己字段的方法
- public void setText(String text) {
- mText = text;
- }
- public void setTextColor(int color) {
- mPaint.setColor(color);
- }
- public void setTextSize(int size) {
- mPaint.setTextSize(size);
- }
- }
再完成构造函数:
[java] view
plaincopy
- public class LabelView extends View {
- //第一步:确定自己View的字段
- private String mText;
- private Paint mPaint;
- //第二步:设置自己字段的方法
- public void setText(String text) {
- mText = text;
- }
- public void setTextColor(int color) {
- mPaint.setColor(color);
- }
- public void setTextSize(int size) {
- mPaint.setTextSize(size);
- }
- //第三步:完成构造函数
- public LabelView(Context context) {
- super(context);
- ...
- }
- public LabelView(Context context, AttributeSet attrs) {
- super(context, attrs);
- ...
- }
- //第四步:完成onMearsure()方法
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- setMeasuredDimension(int, int);
- }
- //第五步:完成onDraw(Canvas)
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- canvas.drawXXX();
- }
- }
下面是一个完整的例子(SDK 自带的例子):
[java] view
plaincopy
- import android.content.Context;
- import android.content.res.TypedArray;
- import android.graphics.Canvas;
- import android.graphics.Paint;
- import android.util.AttributeSet;
- import android.view.View;
- public class LabelView extends View {
- //第一步
- private String mText;
- private Paint mPaint;
- private int mAscent; /*此字段是在写类的过程中添加的,不是原先设计好的*/
- //第二步
- public void setText(String text) {
- mText = text;
- /*
- 在整个View的大小改变时需要调用
- 此方法,调用此方法后会触发 onMeasure() 方法,
- 重新计算和View相关的大小信息
- */
- requestLayout();
- /*
- 当View的任何内容改变时(包括大小/样式/文字颜色)都
- 需要调用此方法,调用此方法后会触发 onDraw() 方法
- */
- invalidate();
- }
- public void setTextColor(int color) {
- mPaint.setColor(color);
- /*只是改变字体颜色,不改变View大小,所以不用调用requestLayout*/
- invalidate();
- }
- public void setTextSize(int size) {
- mPaint.setTextSize(size);
- requestLayout();
- invalidate();
- }
- //第三步
- public LabelView(Context context) {
- super(context);
- initLabelView();
- }
- public LabelView(Context context, AttributeSet attrs) {
- super(context, attrs);
- initLabelView();
- /*
- Context.obtainStyledAttributes(AttributeSet set, int[] attrs)
- 对于此方法第一个参数是XML文件中对此View的属性设置;
- 第二个参数是在/res/valus/attrs.xml文件中定义的此文件的属性集合(包含许多属性)
- */
- TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LableView);
- /*
- R.styleable.LableView_text是一个int[] attrs的一个下标值
- */
- String s = a.getString(R.styleable.LableView_text);
- if(s != null) {
- setText(s);
- }
- setTextColor(a.getColor(R.styleable.LableView_textColor, 0xFF000000));
- int textSize = a.getDimensionPixelOffset(R.styleable.LableView_textSize, 0);
- if(textSize > 0) {
- setTextSize(textSize);
- }
- a.recycle();
- }
- private void initLabelView() {
- mPaint = new Paint();
- mPaint.setColor(0xFF000000);
- mPaint.setTextSize(16);
- /* 使画出来的形状的"边沿"比较光滑 */
- mPaint.setAntiAlias(true);
- setPadding(3, 3, 3, 3);
- }
- //第四步
- @Override
- /*
- 此函数要决定这个View的大小,本View的Container会把widthMeasureSpec参数
- 传入到此函数,注意widthMeasureSpec不是简单的一个整数,它代表了SpecMode/
- SpecSize的信息
- */
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- setMeasuredDimension(measureWidth(widthMeasureSpec),
- measureHeight(heightMeasureSpec));
- }
- private int measureWidth(int widthMeasureSpec) {
- int result = 0;
- int specMode = MeasureSpec.getMode(widthMeasureSpec);
- int specSize = MeasureSpec.getSize(widthMeasureSpec);
- if(MeasureSpec.EXACTLY == specMode) {
- result = specSize;
- } else {
- result = (int)mPaint.measureText(mText) + getPaddingLeft()
- + getPaddingRight();
- if(MeasureSpec.AT_MOST == specMode) {
- result = Math.min(result, specSize);
- }
- }
- return result;
- }
- private int measureHeight(int heightMeasureSpec) {
- int result = 0;
- int specMode = MeasureSpec.getMode(heightMeasureSpec);
- int specSize = MeasureSpec.getSize(heightMeasureSpec);
- mAscent = (int)mPaint.ascent();
- if(MeasureSpec.EXACTLY == specMode) {
- result = specSize;
- } else {
- result = -mAscent + (int)mPaint.descent() + getPaddingTop()
- + getPaddingBottom();
- if(MeasureSpec.AT_MOST == specMode) {
- result = Math.min(result, specSize);
- }
- }
- return result;
- }
- //第五步
- @Override
- protected void onDraw(Canvas canvas) {
- super.onDraw(canvas);
- canvas.drawText(mText, getPaddingLeft(), getPaddingTop() - mAscent, mPaint);
- }
- }
在 res/values/attrs.xml 中定义的 LabelView的相关 attr:
[html] view
plaincopy
- <?xml version="1.0" encoding="utf-8"?>
- <resources>
- <!-- declare-styleable是一个大组,编译为 R.styleable,且R.styleable中常量都为一个数组,数组中的值是 R.attr 中值 -->
- <!-- attr 为一个小属性,是declare-styleable的子元素,编译为 R.attrs -->
- <declare-styleable name="LableView">
- <attr name="text" format="string" />
- <attr name="textColor" format="color"/>
- <attr name="textSize" format="dimension"/>
- </declare-styleable>
- </resources>
编译后的 R 文件为:
[java] view
plaincopy
- public static final class attr {
- public static final int text=0x7f010000;
- public static final int textColor=0x7f010001;
- public static final int textSize=0x7f010002;
- }
[java] view
plaincopy
- public static final class styleable {
- public static final int[] LableView = {
- 0x7f010000, 0x7f010001, 0x7f010002
- };
- //数组的下标,方便在代码中使用,工具自动生成
- public static final int LableView_text = 0;
- public static final int LableView_textColor = 1;
- public static final int LableView_textSize = 2;
- };