Android应用的绝大部分UI组件都放在android.widget包及其子包、android.view包及其子包中,
Android应用的所有UI组件都继承了View类。它代表一个空白的矩形区域。
View类还有一个重要的子类:ViewGroup,但ViewGroup通常作为其他组件的容器使用。
Android的所有UI都是建立在View、ViewGroup基础之上的,因此ViewGroup也可以被当成View使用。
ViewGroup里除了可以包含普通View组件之外,还可以再次包含ViewGroup组件。
Android定义用户界面:
1)在XML布局文件中通过XML属性进行控制。
2)在Java程序代码中通过调用方法进行控制。
Drawable是Android提供的一个抽象类,它代表了“可以被绘制出来的某种东西”,
Drawable包括了大量子类,比如:
BitmapDrawable代表位图Drawable;
ColorDrawable代表颜色Drawable;
ShapeDrawable代表几何形状Drawable;
各种Drawable可以用于定制UI组件的背景等外观。
ViewGroup继承了View类,也可以当成普通View来使用。但ViewGroup主要还是当成容器类使用。但由ViewGroup
是一个抽象类,因此实际使用中通常总是使用ViewGroup的子类来作为容器,例如各种布局管理器。
ViewGroup容器控制其子组件的分布依赖于ViewGroup.LayoutParams、ViewGroup.MarginLayoutParams两个内部类。
这两个内部类中都提供了一些XML属性,ViewGroup容器中的子组件可以指定这些XML属性。
ViewGroup.LayoutParams所支持的两个XML属性。
android:layout_width 指定该子组件的布局高度。
android:layout_height 指定该子组件的布局宽度。
属性值:
fill_parent:指定子组件的高度、宽度与父容器组件的高度、宽度相同(实际上还要减去填充的空白距离)。
match_parent:与fill_parent相同,从Android2.2开始推荐使用这个属性来代替fill_parent。
wrap_content:指定子组件的大小恰好能包裹它的内容即可。
【ViewGroup.MarginLayoutParams支持的属性】
android:layout_marginBottom 指定该子组件下边的页边距。
android:layout_marginLeft 指定该子组件左边的页边距。
android:layout_marginRight 指定该子组件右边的页边距。
android:layout_marginTop 指定该子组件上边的页边距。
相关方法:setMargins(int,int,int,int)
实例:在代码中控制UI界面
public class CodeView extends Activity { // 当第一次创建该Activity时回调该方法
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 创建一个线性布局管理器 LinearLayout layout = new LinearLayout(this); // 设置该Activity显示layout super.setContentView(layout); layout.setOrientation(LinearLayout.VERTICAL); // 创建一个TextView final TextView show = new TextView(this); // 创建一个按钮 Button bn = new Button(this); bn.setText("单击我"); bn.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)); // 向Layout容器中添加TextView layout.addView(show); // 向Layout容器中添加按钮 layout.addView(bn); // 为按钮绑定一个事件监听器 bn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { show.setText("Hello , Android , " + new java.util.Date()); } }); } }
从上面的代码看出,该程序中所用到的UI组件都是通过new关键字创建出来的。然后程序使用LinearLayout容器来“盛装”这些UI组件,这样就组成了图形用户界面。
从上面的程序代码中可以看出,无论创建哪种UI组件,都需要传入一个this参数,这是由于创建UI组件时传入一个
Context代表访问Android应用环境的全局信息的API。让UI组件持有一个Context参数,可让这些UI组件通过该Context
参数来获取Android应用环境的全局信息。
Context本身是一个抽象类,Android应用的Activity、Service都继承了Context,因此Activity、Service都可以直
接作为Context使用。
实例:使用XML布局文件和Java代码混合控制UI界面(简单图片浏览器)。
<?xml version="1.0" encoding="utf-8"?>
<!-- 定义一个线性布局容器 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/root" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" >
</LinearLayout>
1 public class MixView extends Activity { 2 // 定义一个访问图片的数组
3 int[] images = new int[] { R.drawable.java, R.drawable.ee, 4 R.drawable.classic, R.drawable.ajax, R.drawable.xml, }; 5 int currentImg = 0; 6
7 @Override 8 public void onCreate(Bundle savedInstanceState) { 9 super.onCreate(savedInstanceState); 10 setContentView(R.layout.main); 11 // 获取LinearLayout布局容器 12 LinearLayout main = (LinearLayout) findViewById(R.id.root); 13 // 程序创建ImageView组件 14 final ImageView image = new ImageView(this); 15 // 将ImageView组件添加到LinearLayout布局容器中 16 main.addView(image); 17 // 初始化时显示第一张图片 18 image.setImageResource(images[0]); 19 image.setOnClickListener(new OnClickListener() { 20 @Override 21 public void onClick(View v) { 22 // 改变ImageView里显示的图片 23 image.setImageResource(images[++currentImg % images.length]); 24 } 25 }); 26 } 27 }
【开发自定义View】
首先定义一个继承View基类的子类,然后重写View类的一个或多个方法,通常可以被用户重写的方法如下:
构造器:重写构造器是定制View的最基本方式,当Java代码创建一个View实例,或根据XML布局文件加载并构建界面
时将需要调用该构造器。
# onFinishInflate():这是一个回调方法,当应用从XML布局文件加载该组件并利用它来构建界面之后,该方法将会被回调。
# onMeasure(int,int):调用该方法来检测View组件及它所包含的所有子组件的大小。
# onLayout(boolean,int,int,int,int):当该组件需要分配其子组件的位置、大小时,该方法就会被回调。
# onSizeChanged(int,int,int,int):当该组件的大小被改变时回调该方法。
# onDraw(Canvas):当该组件将要绘制它的内容时回调该方法进行绘制。
# onKeyDown(int,KeyEvent):当某个键被按下时触发该方法。
# onKeyUp(int,KeyEvent):当松开某个键时触发该方法。
# onTrackballEvent(MotionEvent):当发生轨迹球事件时触发该方法。
# onTouchEvent(MotionEvent):当发生触摸屏幕事件时触发该方法。
# onWindowFocusChanged(boolean):当该组件得到、失去焦点时触发该方法。
# onAttachedToWindow():当把该组件放入某个窗口时触发该方法。
# onDetachedFromWindow():当把该组件从某个窗口上分离时触发该方法。
# onWindowVisibilltyChanged(int):当包含该组件的窗口的可见性发生改变时触发该方法。
当需要开发自定义View时,开发者并不需要重写上面列出的所有方法,而是可以根据业务需要重写上面的部分方法,
例如下面的示例程序就只重写onDraw(Canvas)方法。
实例:跟随手指的小球(可任意拖动)
这个UI组件将会在指定位置绘制一个小球,这个位置可以动态改变。当用户通过手指在屏幕上拖动时,程序监听
到这个手势动作,并把手指动作的位置传入自定义UI组件,并通知该组件绘制即可。
public class DrawView extends View { public float currentX = 40; public float currentY = 50; // 定义、并创建画笔 Paint p = new Paint(); public DrawView(Context context) { super(context); } public DrawView(Context context, AttributeSet set) { super(context, set); } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); // 设置画笔的颜色 p.setColor(Color.RED); // 绘制一个小圆(作为小球) canvas.drawCircle(currentX, currentY, 15, p); } // 为该组件的触碰事件重写事件处理方法 @Override public boolean onTouchEvent(MotionEvent event) { // 修改currentX、currentY两个属性 currentX = event.getX(); currentY = event.getY(); // 通知当前组件重绘自己 invalidate(); // 返回true表明该处理方法已经处理该事件 return true; } }
上面的DrawView组件继承了View基类,并重写了onDraw方法---该方法负责在该组件的指定位置绘制一个小球,除此
之外,该组件还重写了onTouchEvent(MotionEvent),该方法用于处理该组件的触碰事件,当用户手指触碰该组件时
将会激发该方法。当手指在触摸屏上移动时,将会不断地触发触摸屏事件,事件监听器中负责触发事件的坐标将被
传入DrawView组件,并通过该组件重绘---这样就可保证DrawView上小球跟随手指移动而移动。
接下来可以通过Java代码把该组件添加到指定的容器中,这样就可以看到该组件的运行结果了。
public class CustomView extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // 获取布局文件中的LinearLayout容器 LinearLayout root = (LinearLayout) findViewById(R.id.root); // 创建DrawView组件 final DrawView draw = new DrawView(this); // 设置自定义组件的最大宽度、高度 draw.setMinimumWidth(300); draw.setMinimumHeight(500); root.addView(draw); } }
二、Android应用的界面编程(六)ProgressBar及其子类[SeekBar、RatingBar]er
通常用于向用户显示某个耗时操作完成的百分比。 Android支持几种风格的进度条,通过style属性可以为ProgressBar指定风格。该属性支持如下几个属性值。 # @android:style/Widget.ProgressBar.Horizontal; 水平进度条。 # @android:style/Widget.ProgressBar.Inverse; 普通大小的环形进度条。 # @android:style/Widget.ProgressBar.Large; 大环形进度条。 # @android:style/Widget.ProgressBar.Large.Inverse; 大环形进度条。 # @android:style/Widget.ProgressBar.Small; 小环形进度条。 # @android:style/Widget.ProgressBar.Small.Inverse; 小环形进度条。
【ProgressBar常用的XML属性和方法】 android:max 设置该进度条的最大值 android:progress 设置该进度条的已完成进度值 android:progressDrawable 设置该进度条的轨道对应的Drawable对象 android:indeterminate 该属性设为true,设置进度条不精确显示进度 android:indeterminateDrawable 设置绘制不显示进度的进度条的Drawable对象 android:indeterminateDuration 设置不精确显示进度的持续时间
getMax():返回这个进度条的范围的上限。 getProgress():返回进度。 getSecondaryProgress():返回次要进度。 incrementProgressBy(int diff):指定增加的进度或减少(+进度增加,-进度减少)。 isIndeterminate():指示进度条是否在不确定模式下。 setIndeterminate(boolean indeterminate):设置不确定模式下。 setVisibility(int v):设置该进度条是否可视。
android:progressDrawable用于指定进度条的轨道的绘制形式,该属性可指定为一个LayerDrawable对象 (该对象可通过在XML文件中用<layer-list>元素进行配置)的引用。
范例:进度条各种样式。
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" 5 android:orientation="vertical" > 7 <LinearLayout 8 android:layout_width="match_parent" 9 android:layout_height="wrap_content" 10 android:orientation="horizontal" > 12 <!-- 定义一个大环形进度条 --> 13 <ProgressBar 14 style="@android:style/Widget.ProgressBar.Large" 15 android:layout_width="wrap_content" 16 android:layout_height="wrap_content" /> 18 <!-- 定义一个中等大小的环形进度条 --> 19 <ProgressBar 20 android:layout_width="wrap_content" 21 android:layout_height="wrap_content" /> 23 <!-- 定义一个小环形进度条 --> 24 <ProgressBar 25 style="@android:style/Widget.ProgressBar.Small" 26 android:layout_width="wrap_content" 27 android:layout_height="wrap_content" /> 28 </LinearLayout> 29 30 <TextView 31 android:layout_width="fill_parent" 32 android:layout_height="wrap_content" 33 android:text="任务完成的进度" /> 34 35 <!-- 定义一个水平进度条 --> 36 <ProgressBar 37 android:id="@+id/bar" 38 style="@android:style/Widget.ProgressBar.Horizontal" 39 android:layout_width="fill_parent" 40 android:layout_height="wrap_content" 41 android:max="100" /> 43 <!-- 定义一个水平进度条,并改变轨道外观 --> 44 <ProgressBar 45 android:id="@+id/bar2" 46 style="@android:style/Widget.ProgressBar.Horizontal" 47 android:layout_width="fill_parent" 48 android:layout_height="wrap_content" 49 android:max="100" 50 android:progressDrawable="@drawable/my_bar" /> 52 </LinearLayout>其中android:progressDrawable="@drawable/my_bar" 的样式如下:
<?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/no" /> <!-- 定义轨道上已完成部分的样式 (对号小图标) --> <item android:id="@android:id/progress" android:drawable="@drawable/ok" /> </layer-list>
1 public class ProgressBarTest extends Activity { 2 // 该程序模拟填充长度为100的数组 3 private int[] data = new int[100]; 4 int hasData = 0; 5 // 记录ProgressBar的完成进度 6 int status = 0; 7 ProgressBar bar, bar2; 8 // 创建一个负责更新的进度的Handler 9 Handler mHandler = new Handler() { 10 @Override 11 public void handleMessage(Message msg) { 12 // 表明消息是由该程序发送的 13 if (msg.what == 0x111) { 14 bar.setProgress(status); 15 bar2.setProgress(status); 16 } 17 } 18 }; 19 20 @Override 21 public void onCreate(Bundle savedInstanceState) { 22 super.onCreate(savedInstanceState); 23 setContentView(R.layout.main); 24 bar = (ProgressBar) findViewById(R.id.bar); 25 bar2 = (ProgressBar) findViewById(R.id.bar2); 26 27 // 启动线程来执行任务 28 new Thread() { 29 public void run() { 30 while (status < 100) { 31 // 获取耗时操作的完成百分比 32 status = doWork(); 33 // 发送消息 34 mHandler.sendEmptyMessage(0x111); 35 } 36 } 37 }.start(); 38 } 39 40 // 模拟一个耗时的操作 41 public int doWork() { 42 // 为数组元素赋值 43 data[hasData++] = (int) (Math.random() * 100); 44 try { 45 Thread.sleep(100); 46 } catch (InterruptedException e) { 47 e.printStackTrace(); 48 } 49 return hasData; 50 } 51 }范例:显示在标题上的进度条
1) 调用Activity的requestWindowFeature(),该方法根据传入的参数可启用特定的窗口特征,例如
传入Window.FEATURE_INDETERMINATE_PROGRESS在窗口标题上显示不带进度的进度条;传入
Window.FEATURE_PROGRESS则显示带进度的进度条。
2) 调用Activity的setProgressBarVisibility(boolean)setProgressBarIndeterminateVisibility(boolean)
即可控制进度条的显示和隐藏。public class TitleProgressBar extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // //设置窗口特征:启用显示进度的进度条 // requestWindowFeature(Window.FEATURE_PROGRESS); //① // 设置窗口特征:启用不显示进度的进度条 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); // ② setContentView(R.layout.main); Button bn1 = (Button) findViewById(R.id.bn1); Button bn2 = (Button) findViewById(R.id.bn2); bn1.setOnClickListener(new OnClickListener() { @Override public void onClick(View source) { // 显示不带进度的进度条。 setProgressBarIndeterminateVisibility(true); // 显示带进度的进度条。 setProgressBarVisibility(true); // 设置进度条的进度 setProgress(4500); } }); bn2.setOnClickListener(new OnClickListener() { @Override public void onClick(View source) { // 隐藏不带进度的进度条。 setProgressBarIndeterminateVisibility(false); // 隐藏带进度的进度条。 setProgressBarVisibility(false); } }); } }
===== 拖动条(SeekBar)的功能和用法 =====
# 拖动条是通过滑块的位置来标识数值,而且拖动条允许用户拖动滑块来改变值,比如:调节音量等。
# 由于SeekBar继承了ProgressBar,因此ProgressBar所支持的XML属性和方法完全适用于SeekBar。
# android:thumb:指定一个Drawable对象,该对象将作为自定义滑块,改变拖动条的滑块外观。
# 为了让程序能响应拖动条滑块位置的改变,程序可以考虑为它绑定一个OnSeekBarChangeListener监听器。
范例:通过拖动滑块来改变图片的透明度1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:orientation="vertical" 4 android:layout_width="fill_parent" 5 android:layout_height="fill_parent"> 6 <ImageView 7 android:id="@+id/image" 8 android:layout_width="fill_parent" 9 android:layout_height="240px" 10 android:src="@drawable/lijiang" /> 11 <!-- 定义一个拖动条,并改变它的滑块外观 --> 12 <SeekBar 13 android:id="@+id/seekbar" 14 android:layout_width="fill_parent" 15 android:layout_height="wrap_content" 16 android:max="255" 17 android:progress="255" 18 android:thumb="@drawable/ic_launcher" /> 19 </LinearLayout>1 public class SeekBarTest extends Activity { 2 ImageView image; 3 4 @Override 5 public void onCreate(Bundle savedInstanceState) { 6 super.onCreate(savedInstanceState); 7 setContentView(R.layout.main); 8 image = (ImageView) findViewById(R.id.image); 9 SeekBar seekBar = (SeekBar) findViewById(R.id.seekbar); 10 11 seekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { 12 // 当拖动条的滑块位置发生改变时触发该方法 13 @Override 14 public void onProgressChanged(SeekBar arg0, int progress, 15 boolean fromUser) { 16 // 动态改变图片的透明度 17 image.setAlpha(progress); 18 } 20 @Override 21 public void onStartTrackingTouch(SeekBar bar) { 22 } 24 @Override 25 public void onStopTrackingTouch(SeekBar bar) { 26 } 27 }); 28 } 29 }===== 星级评分条(RatingBar)的功能和用法 =====
# RatingBar与SeekBar最大区别在于:RatingBar通过星星来表示进度。
【RatingBar支持的常见XML属性】
android:rating 设置该星级评分条默认的星级
android:numStars 设置该星级评分条总共有多少个星级
android:stepSize 设置每次最少需要改变多少个星级
android:isIndicator 设置该星级评分条是否允许用户改变(true为不允许修改)为了让程序能相应星级评分的改变,程序可以考虑为它绑定一个OnRatingBarChangeListener监听器。
范例:通过星级改变图片的透明度
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:orientation="vertical" 4 android:layout_width="fill_parent" 5 android:layout_height="fill_parent"> 6 <ImageView 7 android:id="@+id/image" 8 android:layout_width="fill_parent" 9 android:layout_height="240px" 10 android:src="@drawable/lijiang" /> 11 <!-- 定义一个星级评分条 --> 12 <RatingBar 13 android:id="@+id/rating" 14 android:layout_width="wrap_content" 15 android:layout_height="wrap_content" 16 android:numStars="5" 17 android:max="255" 18 android:progress="255" 19 android:stepSize="0.5" /> 20 </LinearLayout>1 public class RatingBarTest extends Activity { 2 ImageView image; 3 4 @Override 5 public void onCreate(Bundle savedInstanceState) { 6 super.onCreate(savedInstanceState); 7 setContentView(R.layout.main); 8 image = (ImageView) findViewById(R.id.image); 9 RatingBar ratingBar = (RatingBar) findViewById(R.id.rating); 10 11 ratingBar.setOnRatingBarChangeListener(new OnRatingBarChangeListener() { 12 // 当拖动条的滑块位置发生改变时触发该方法 13 @Override 14 public void onRatingChanged(RatingBar arg0, float rating, 15 boolean fromUser) { 16 // 动态改变图片的透明度,其中255是星级评分条的最大值 17 // 5个星星就代表最大值255 18 image.setAlpha((int) (rating * 255 / 5)); 19 } 20 }); 21 } 22 }由于上面定义RatingBar时指定了android:stepSize="0.5",因此该星级评分条中星级的最小变化为0.5,也就是
最少要变化半个星级。
【改变样式】其中XML属性 style="@style/roomRatingBar"的样式如下:
styles.xml <?xml version="1.0" encoding="utf-8"?> <resources> <style name="roomRatingBar" parent="@android:style/Widget.RatingBar"> <item name="android:progressDrawable">@drawable/room_rating_bar</item> <item name="android:minHeight">16dip</item> <item name="android:maxHeight">16dip</item> </style> </resources>
上面的意思是继承@android:style/Widget.RatingBar,重写android:progressDrawable属性,换成我们自定义@drawable/room_rating_bar文件。
控制该组件的最大和最小高度。好我们继续去找这自定义文件@drawable/room_rating_bar。
room_rating_bar.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/room_unselect" /> <item android:id="@+android:id/secondaryProgress" android:drawable="@drawable/room_unselect" /> <item android:id="@+android:id/progress" android:drawable="@drawable/room_select" /> </layer-list>